Skip to content

Commit 890d0ba

Browse files
authored
Merge pull request #95 from KitwareMedical/add_mention_about_date_retaining_in_readme
Add mention about date retaining in README
2 parents 724c1e2 + 2a761c5 commit 890d0ba

6 files changed

Lines changed: 342 additions & 324 deletions

File tree

.pre-commit-config.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
repos:
22
- repo: https://github.com/astral-sh/ruff-pre-commit
3-
rev: v0.4.4
3+
rev: v0.12.10
44
hooks:
55
- id: ruff
66
args: [ --fix ]

README.md

Lines changed: 11 additions & 304 deletions
Original file line numberDiff line numberDiff line change
@@ -21,314 +21,21 @@ Dicom fields are separated into different groups. Each groups will be anonymized
2121
| X_Z_U_STAR_TAGS | delete_or_empty_or_replace_UID | If it's a UID, then all numbers are randomly replaced. Else, replace with a zero length value, or a non-zero length value that may be a dummy value and consistent with the VR**|
2222
| ALL_TAGS | | Contains all previous defined tags
2323

24+
## Quirks
2425

25-
# How to install it?
26+
`dicom-anonymizer` is designed to retain date information within DICOM files, and will anonymize them rather than removing them entirely. This approach is taken to prevent potential crashes in certain applications that rely on these dates to work. This behavior complies with the DICOM standard, as mentioned in section [E.3.6](https://dicom.nema.org/medical/dicom/current/output/html/part15.html#sect_E.3.6).
2627

27-
Installation can be done via pip `pip install dicom-anonymizer` or conda `conda install -c conda-forge dicom-anonymizer`.
28+
## Documentation
2829

30+
- [Installation](./doc/Setup_build_test.md)
31+
- [Usage](./doc/Usage)
2932

30-
# Local Development Setup
33+
## State of the library
3134

32-
To get started with local development, follow these steps:
35+
At this present time, Kitware has no plan to implement new features in dicom-anonymizer.
36+
If you have funding to further develop this tool, feel free to reach out to Kitware.
37+
Whenever possible, Kitware will try to fix bugs that affect the use of the project and merge requests from the community will be considered, feel free to [open an issue](https://github.com/KitwareMedical/dicom-anonymizer/issues?q=is%3Aissue%20state%3Aopen%20label%3Aenhancement) to suggest new features.
3338

34-
1. Create a Virtual Environment:
35-
- On Windows:
36-
```sh
37-
virtualenv env
38-
.\env\Scripts\activate.bat
39-
```
40-
- On MacOS/Linux:
41-
```sh
42-
python -m venv env
43-
source env/bin/activate
44-
```
39+
## Financing
4540

46-
2. Install Dependencies:
47-
- Install an editable version of the package and the development requirements:
48-
```sh
49-
pip install -e .[dev]
50-
```
51-
52-
3. Set Up Pre-Commit Hooks:
53-
- Install the pre-commit hooks to ensure code quality:
54-
```sh
55-
pre-commit install
56-
```
57-
58-
59-
## How to test it?
60-
61-
To run the unit tests, use the following command:
62-
63-
```sh
64-
pytest
65-
```
66-
67-
68-
# How to build it?
69-
These instructions rely on wheel build-package format. Install it if you have not done it already using:
70-
`pip install wheel`
71-
72-
The sources files can be packaged by using:
73-
`python ./setup.py bdist_wheel`
74-
75-
This command will generate a wheel package in `dist` folder which can be then installed as a python package using
76-
`pip install ./dist/dicom_anonymizer-1.0.13-1-py2.py3-none-any.whl`
77-
78-
On Windows, if you see a warning message
79-
`'./dist/dicom_anonymizer-1.0.13-1-py2.py3-none-any.whl' looks like a filename, but the file does not exist`,
80-
this could be due to pip not being able to handle relative path (See issue https://github.com/pypa/pip/issues/10808). As a work-around, change directory to `dist` and then install it using
81-
`pip install dicom_anonymizer-1.0.13-1-py2.py3-none-any.whl`
82-
83-
84-
Installing this package will also install an executable named `dicom-anonymizer`. In order to use it, please refer to the next section.
85-
86-
87-
88-
# How to use it?
89-
90-
This package allows to anonymize a selection of DICOM field (defined or overridden).
91-
The way on how the DICOM fields are anonymized can also be overridden.
92-
93-
- **[required]** InputPath = Full path to a single DICOM image or to a folder which contains dicom files
94-
- **[required]** OutputPath = Full path to the anonymized DICOM image or to a folder. This folder has to exist.
95-
- [optional] ActionName = Defined an action name that will be applied to the DICOM tag.
96-
- [optional] Dictionary = Path to a JSON file which defines actions that will be applied on specific dicom tags (see below)
97-
98-
99-
100-
## Default behaviour
101-
102-
You can use the default anonymization behaviour describe above.
103-
104-
```python
105-
dicom-anonymizer Input Output
106-
```
107-
108-
109-
## Private tags
110-
111-
Default behavior of the dicom anonymizer is to delete private tags.
112-
But you can bypass it:
113-
- Solution 1: Use regexp to define which private tag you want to keep/update (cf [custom rules](#custom-rules))
114-
- Solution 2: Use dicom-anonymizer.exe option to keep all private tags : `--keepPrivateTags`
115-
116-
117-
118-
## Custom rules
119-
You can manually add new rules in order to have different behaviors with certain tags.
120-
This will allow you to override default rules:
121-
122-
**Executable**:
123-
```python
124-
dicom-anonymizer InputFilePath OutputFilePath -t '(0x0001, 0x0001)' ActionName -t '(0x0001, 0x0005)' ActionName2
125-
```
126-
This will apply the `ActionName` to the tag `'(0x0001, 0x0001)'` and `ActionName2` to `'(0x0001, 0x0005)'`
127-
128-
**Note**: ActionName has to be defined in [actions list](#actions-list)
129-
130-
Example 1: The default behavior of the patient's ID is to be replaced by an empty or null value. If you want to keep this value, then you'll have to run :
131-
```python
132-
python anonymizer.py InputFilePath OutputFilePath -t '(0x0010, 0x0020)' keep
133-
```
134-
This command will override the default behavior executed on this tag and the patient's ID will be kept.
135-
136-
Example 2: We just want to change the study date from 20080701 to 20080000, then we'll use the regexp
137-
```python
138-
python anonymizer.py InputFilePath OutputFilePath -t '(0x0008, 0x0020)' 'regexp' '0701$' '0000'
139-
```
140-
141-
Example 3: Change the tag value with an arbitrary value
142-
```python
143-
python anonymizer.py InputFilePath OutputFilePath -t '(0x0010, 0x0010)' 'replace_with_value' 'new_value'
144-
```
145-
146-
### DICOMDIR
147-
148-
> DICOMDIR anonymization is not specified. It is therefore discouraged and it is recommended to regenerate new DICOMDIR files after anonymizing the original DICOM files.
149-
150-
DICOMDIR files can have a `(0x0004, 0x1220) Directory Record Sequence` tag that can contain patient information.
151-
However, this tag is not part of the standard tag to anonymize set. If you still want dicom-anonymizer to anonymize it, you have to instruct it explicitly:
152-
153-
```python
154-
python anonymizer.py InputFilePath OutputFilePath -t '(0x0004, 0x1220)' replace
155-
```
156-
157-
## Custom rules with dictionary file
158-
159-
Instead of having a big command line with several new actions, you can create your own dictionary by creating a json file `dictionary.json` :
160-
```json
161-
{
162-
"(0x0002, 0x0002)": "ActionName",
163-
"(0x0003, 0x0003)": "ActionName",
164-
"(0x0004, 0x0004)": "ActionName",
165-
"(0x0005, 0x0005)": "ActionName"
166-
}
167-
```
168-
Same as before, the `ActionName` has to be defined in the [actions list](#actions-list).
169-
170-
```python
171-
dicom-anonymizer InputFilePath OutputFilePath --dictionary dictionary.json
172-
```
173-
174-
If you want to use the **regexp** action in a dictionary:
175-
```json
176-
{
177-
"(0x0002, 0x0002)": "ActionName",
178-
"(0x0008, 0x0020)": {
179-
"action": "regexp",
180-
"find": "0701$",
181-
"replace": "0000"
182-
}
183-
}
184-
```
185-
186-
## Custom/overrides actions
187-
188-
Here is a small example which keeps all metadata but updates the series description
189-
by adding a suffix passed as a parameter.
190-
191-
```python
192-
import argparse
193-
from dicomanonymizer import ALL_TAGS, anonymize, keep
194-
195-
196-
def main():
197-
parser = argparse.ArgumentParser(add_help=True)
198-
parser.add_argument(
199-
"input",
200-
help="Path to the input dicom file or input directory which contains dicom files",
201-
)
202-
parser.add_argument(
203-
"output",
204-
help="Path to the output dicom file or output directory which will contains dicom files",
205-
)
206-
args = parser.parse_args()
207-
208-
deletePrivateTags = False
209-
210-
input_dicom_path = args.input
211-
output_dicom_path = args.output
212-
213-
extra_anonymization_rules = {}
214-
215-
# Per https://www.hhs.gov/hipaa/for-professionals/privacy/special-topics/de-identification/index.html
216-
# it is all right to retain only the year part of the birth date for
217-
# de-identification purposes.
218-
def set_date_to_year(dataset, tag):
219-
element = dataset.get(tag)
220-
if element is not None:
221-
element.value = f"{element.value[:4]}0101" # YYYYMMDD format
222-
223-
# ALL_TAGS variable is defined on file dicomfields.py
224-
# the 'keep' method is already defined into the dicom-anonymizer
225-
# It will overrides the default behaviour
226-
for i in ALL_TAGS:
227-
extra_anonymization_rules[i] = keep
228-
229-
extra_anonymization_rules[(0x0010, 0x0030)] = set_date_to_year # Patient's Birth Date
230-
231-
# Launch the anonymization
232-
anonymize(
233-
input_dicom_path,
234-
output_dicom_path,
235-
extra_anonymization_rules,
236-
delete_private_tags=False,
237-
)
238-
239-
240-
if __name__ == "__main__":
241-
main()
242-
```
243-
244-
See the full application in the `examples` folder.
245-
246-
In your own file, you'll have to define:
247-
- Your custom functions. Be careful, your functions always have in inputs a dataset and a tag
248-
- A dictionary which map your functions to a tag
249-
250-
## Anonymize dicom tags for a dataset
251-
252-
You can also anonymize dicom fields in-place for pydicom's DataSet using `anonymize_dataset`. See this example:
253-
```python
254-
import pydicom
255-
256-
from dicomanonymizer import anonymize_dataset
257-
258-
def main():
259-
260-
# Create a list of tags object that should contains id, type and value
261-
fields = [
262-
{ # Replaced by Anonymized
263-
'id': (0x0040, 0xA123),
264-
'type': 'LO',
265-
'value': 'Annie de la Fontaine',
266-
},
267-
{ # Replaced with empty value
268-
'id': (0x0008, 0x0050),
269-
'type': 'TM',
270-
'value': 'bar',
271-
},
272-
{ # Deleted
273-
'id': (0x0018, 0x4000),
274-
'type': 'VR',
275-
'value': 'foo',
276-
}
277-
]
278-
279-
# Create a readable dataset for pydicom
280-
data = pydicom.Dataset()
281-
282-
# Add each field into the dataset
283-
for field in fields:
284-
data.add_new(field['id'], field['type'], field['value'])
285-
286-
anonymize_dataset(data)
287-
288-
if __name__ == "__main__":
289-
main()
290-
```
291-
292-
See the full application in the `examples` folder.
293-
294-
For more information about the pydicom's Dataset, please refer [here](https://pydicom.github.io/pydicom/stable/reference/generated/pydicom.dataset.Dataset.html).
295-
296-
You can also add `extra_anonymization_rules` as above:
297-
```python
298-
anonymize_dataset(data_ds, extra_anonymization_rules, delete_private_tags=True)
299-
```
300-
301-
# Actions list
302-
303-
| Action | Action definition |
304-
| --- | --- |
305-
| empty | Replace with a zero length value, or a non-zero length value that may be a dummy value and consistent with the VR** |
306-
| delete | Completely remove the tag |
307-
| keep | Do nothing on the tag |
308-
| replace_UID | Replace all UID's number with a random one in order to keep consistent. Same UID will have the same replaced value |
309-
| empty_or_replace | Replace with a non-zero length value that may be a dummy value and consistent with the VR** |
310-
| delete_or_empty | Replace with a zero length value, or a non-zero length value that may be a dummy value and consistent with the VR** |
311-
| delete_or_replace | Replace with a non-zero length value that may be a dummy value and consistent with the VR** |
312-
| deleteOrEmptyOrReplace | Replace with a non-zero length value that may be a dummy value and consistent with the VR** |
313-
| delete_or_empty_or_replace_UID | If it's a UID, then all numbers are randomly replaced. Else, replace with a zero length value, or a non-zero length value that may be a dummy value and consistent with the VR** |
314-
|regexp| Find a value in the tag using a regexp and replace it with an arbitrary value. See the examples in this file to learn how to use.|
315-
|replace_with_value| Replace the tag value with an arbitrary value. See the examples in this file to learn how to use.
316-
317-
318-
** VR: Value Representation
319-
320-
Work originally done by Edern Haumont
321-
322-
# Change the DICOM anonymization standard
323-
324-
You can customize the DICOM standard that will be used to anonymize the dataset by giving an argument `base_rules_gen` to the function `anonymize_dicom_file` or `anonymize_dataset`.
325-
The value should be a function returning a dict of anonymization rules. Use the function `initialize_actions` to create such dict from a anonymization database from the folder `dicomanonymizer/dicom_anonymization_databases`.
326-
327-
Example:
328-
```python
329-
from dicomanonymizer.simpledicomanonymizer import anonymize_dataset, initialize_actions
330-
331-
anonymize_dataset(
332-
dataset, base_rules_gen=lambda: initialize_actions("dicomfields_2024b")
333-
)
334-
```
41+
If you want a new feature in dicom-anonymizer or help manipulating DICOM files, feel free to reach out to us at contact@kitware.fr.

doc/Setup_build_test.md

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
# Installation
2+
3+
### Binary installation
4+
5+
Installation can be done via pip `pip install dicom-anonymizer` or conda `conda install -c conda-forge dicom-anonymizer`.
6+
7+
8+
### Local Development Setup
9+
10+
To get started with local development, follow these steps:
11+
12+
1. Create a Virtual Environment:
13+
- On Windows:
14+
```sh
15+
virtualenv env
16+
.\env\Scripts\activate.bat
17+
```
18+
- On MacOS/Linux:
19+
```sh
20+
python -m venv env
21+
source env/bin/activate
22+
```
23+
24+
2. Install Dependencies:
25+
- Install an editable version of the package and the development requirements:
26+
```sh
27+
pip install -e .[dev]
28+
```
29+
30+
3. Set Up Pre-Commit Hooks:
31+
- Install the pre-commit hooks to ensure code quality:
32+
```sh
33+
pre-commit install
34+
```
35+
36+
37+
## Testing
38+
39+
To run the unit tests, use the following command:
40+
41+
```sh
42+
pytest
43+
```
44+
45+
46+
## Building
47+
48+
These instructions rely on wheel build-package format. Install it if you have not done it already using:
49+
`pip install wheel`
50+
51+
The sources files can be packaged by using:
52+
`python ./setup.py bdist_wheel`
53+
54+
This command will generate a wheel package in `dist` folder which can be then installed as a python package using
55+
`pip install ./dist/dicom_anonymizer-1.0.13-1-py2.py3-none-any.whl`
56+
57+
On Windows, if you see a warning message
58+
`'./dist/dicom_anonymizer-1.0.13-1-py2.py3-none-any.whl' looks like a filename, but the file does not exist`,
59+
this could be due to pip not being able to handle relative path (See issue https://github.com/pypa/pip/issues/10808). As a work-around, change directory to `dist` and then install it using
60+
`pip install dicom_anonymizer-1.0.13-1-py2.py3-none-any.whl`
61+
62+
63+
Installing this package will also install an executable named `dicom-anonymizer`. In order to use it, please refer to the next section.

0 commit comments

Comments
 (0)