Skip to content

Commit 39abf2b

Browse files
committed
update image count filter parameter
1 parent 41c7d44 commit 39abf2b

2 files changed

Lines changed: 78 additions & 21 deletions

File tree

README.md

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -34,17 +34,17 @@ To print its available options, run:
3434
```shell
3535
apptainer exec docker://fnndsc/pl-dicom_filter dicom_filter --help
3636
```
37-
| Argument | Default | Description |
38-
| ----------------------------- | -------- | ---------------------------------------------------------------- |
39-
| `-d`, `--dicomFilter` | `""` | Comma-separated DICOM tags with values to filter files |
40-
| `-f`, `--fileFilter` | `"dcm"` | Input file filter glob pattern |
41-
| `-m`, `--minImgCount` | `1` | Minimum number of images in a series; smaller series are dropped |
42-
| `-o`, `--outputType` | `"dcm"` | Output file type/extension |
43-
| `-t`, `--textFilter` | `"txt"` | Input text file filter (for additional filtering) |
44-
| `-i`, `--inspectTags` | `None` | Comma-separated DICOM tags to inspect; optional |
45-
| `-p`, `--phiMode` | `"skip"` | PHI handling mode: `detect`, `allow`, or `skip` |
46-
| `-s`, `--similarityThreshold` | `0.95` | Minimum similarity threshold between two text entries |
47-
| `-V`, `--version` || Show plugin version |
37+
| Argument | Default | Description |
38+
|-------------------------------|----------|--------------------------------------------------------|
39+
| `-d`, `--dicomFilter` | `""` | Comma-separated DICOM tags with values to filter files |
40+
| `-f`, `--fileFilter` | `"dcm"` | Input file filter glob pattern |
41+
| `-m`, `--imgCount` | `">=1"` | Comma-separated image count filter expression. |
42+
| `-o`, `--outputType` | `"dcm"` | Output file type/extension |
43+
| `-t`, `--textFilter` | `"txt"` | Input text file filter (for additional filtering) |
44+
| `-i`, `--inspectTags` | `None` | Comma-separated DICOM tags to inspect; optional |
45+
| `-p`, `--phiMode` | `"skip"` | PHI handling mode: `detect`, `allow`, or `skip` |
46+
| `-s`, `--similarityThreshold` | `0.95` | Minimum similarity threshold between two text entries |
47+
| `-V`, `--version` || Show plugin version |
4848

4949

5050
## Examples

dicom_filter.py

Lines changed: 67 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,20 @@
77
from difflib import SequenceMatcher
88
from argparse import ArgumentParser, Namespace, ArgumentDefaultsHelpFormatter
99
from pydicom.pixel_data_handlers import convert_color_space
10+
from PIL import Image
1011
from chris_plugin import chris_plugin, PathMapper
12+
from pydicom.pixel_data_handlers import convert_color_space
1113
import pydicom as dicom
14+
import numpy as np
15+
import operator
1216
import cv2
1317
import json
14-
from pydicom.pixel_data_handlers import convert_color_space
15-
import numpy as np
1618
import re
1719
import os
1820
import sys
19-
from PIL import Image
2021

21-
__version__ = '1.2.9'
22+
23+
__version__ = '1.3.0'
2224

2325
DISPLAY_TITLE = r"""
2426
_ _ _ __ _ _ _
@@ -39,8 +41,15 @@
3941
help='comma separated dicom tags with values')
4042
parser.add_argument('-f', '--fileFilter', default='dcm', type=str,
4143
help='input file filter glob')
42-
parser.add_argument('-m', '--minImgCount', default=1, type=int,
43-
help='A configurable threshold—any series with fewer images is dropped.')
44+
parser.add_argument('-m', '--imgCount', default=">=1", type=str,
45+
help=(
46+
"Image count filter expression. "
47+
"Supports multiple conditions combined with AND.\n"
48+
"Examples:\n"
49+
" '>=10 <=200'\n"
50+
" '>5 !=13'\n"
51+
" '==42'"
52+
))
4453
parser.add_argument('-V', '--version', action='version',
4554
version=f'%(prog)s {__version__}')
4655
parser.add_argument('-o', '--outputType', default='dcm', type=str,
@@ -475,19 +484,67 @@ def zipper_mapper(mapper1, mapper2, fill_value=None):
475484

476485
yield (in1, in2, out1)
477486

487+
OPS = {
488+
">": operator.gt,
489+
">=": operator.ge,
490+
"<": operator.lt,
491+
"<=": operator.le,
492+
"==": operator.eq,
493+
"!=": operator.ne,
494+
}
495+
496+
_COND_RE = re.compile(r"(>=|<=|==|!=|>|<)\s*(\d+)$")
497+
498+
499+
def validate_img_count(count: int, expr: str) -> bool:
500+
"""
501+
Validate image count against multiple AND-ed conditions.
502+
503+
Example expressions:
504+
'>=10 <=200'
505+
'>5 !=13'
506+
'==42'
507+
"""
508+
conditions = expr.split(',')
509+
510+
for cond in conditions:
511+
match = _COND_RE.fullmatch(cond.strip())
512+
if not match:
513+
raise ValueError(
514+
f"Invalid imgCount condition '{cond}'. "
515+
"Valid examples: '>=10', '<=200', '!=13'"
516+
)
517+
518+
op_str, value = match.groups()
519+
value = int(value)
520+
521+
if not OPS[op_str](count, value):
522+
return False
523+
524+
return True
525+
478526
def check_setup_and_map(inputdir, outputdir, options):
479527
"""
480528
Check the input file space
481529
If textInspect option is specified, accurately zip both the mappers
482530
to yield a single mapper
483531
"""
484532
dcm_mapper = PathMapper.file_mapper(inputdir, outputdir, glob=f"**/*.{options.fileFilter}", fail_if_empty=False)
533+
count = len(dcm_mapper)
485534

486535
# Exit if minimum image count is not met
487-
if len(dcm_mapper) < options.minImgCount:
488-
print(
489-
f"Total no. of images found ({len(dcm_mapper)}) is less than specified ({options.minImgCount}). Exiting analysis..")
490-
sys.exit()
536+
try:
537+
if not validate_img_count(count, options.imgCount):
538+
print(
539+
f"Total no. of images found ({count}) does not satisfy "
540+
f"specified conditions ({options.imgCount}). "
541+
"Exiting analysis.."
542+
)
543+
sys.exit(1)
544+
545+
except ValueError as e:
546+
print(f"Argument error: {e}")
547+
sys.exit(2)
491548
print(f"Total no. of images found: {len(dcm_mapper)}")
492549

493550
text_mapper = PathMapper.file_mapper(inputdir, outputdir, glob=f"**/*.{options.textFilter}", fail_if_empty=False)

0 commit comments

Comments
 (0)