pdnl-process 0.3__tar.gz → 0.4__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pdnl_process
3
- Version: 0.3
3
+ Version: 0.4
4
4
  Summary: PDNL Process
5
5
  Project-URL: Homepage, https://github.com/PennCompPathology/pdnl_process
6
6
  Author-email: Noah Capp <noahmcapp@gmail.com>
@@ -10,5 +10,6 @@ Classifier: License :: OSI Approved :: MIT License
10
10
  Classifier: Operating System :: OS Independent
11
11
  Classifier: Programming Language :: Python :: 3
12
12
  Classifier: Topic :: Scientific/Engineering :: Medical Science Apps.
13
- Requires-Python: >=3.9
13
+ Requires-Python: >=3.12
14
14
  Requires-Dist: pdnl-sana
15
+ Requires-Dist: tdqm
@@ -0,0 +1,117 @@
1
+ #!/usr/bin/env python3
2
+
3
+ import os
4
+ import sys
5
+ import argparse
6
+ import json
7
+
8
+ import numpy as np
9
+ from tqdm import tqdm
10
+ import pdnl_sana.geo
11
+ import pdnl_sana.image
12
+ import pdnl_sana.process
13
+ import pdnl_sana.logging
14
+ import pdnl_sana.filter
15
+ import pdnl_sana.threshold
16
+
17
+ from matplotlib import pyplot as plt
18
+
19
+ def main():
20
+
21
+ parser = argparse.ArgumentParser()
22
+ parser.add_argument('-i', '--input_path', help="path to image data to process", required=True)
23
+ parser.add_argument('-o', '--output_path', help="path to write outputs to", required=True)
24
+ parser.add_argument('-s', '--staining_code', help="stain protocol used to stain the input image", default='H-DAB')
25
+ parser.add_argument('-p', '--parameters_file', help=".json file containing processing parameters", default=None)
26
+ args = parser.parse_args()
27
+
28
+ if args.parameters_file:
29
+ params = json.load(open(args.parameters_file))
30
+ else:
31
+ params = {}
32
+
33
+ os.makedirs(args.output_path, exist_ok=True)
34
+
35
+ # preprocess the chunks
36
+ # TODO: move all pdnl_sana.process code into here, then other scripts import these functions
37
+ print('Preprocessing chunks...', flush=True)
38
+ histogram = None
39
+ for d in tqdm(os.listdir(args.input_path)):
40
+ if not os.path.isdir(os.path.join(args.input_path, d)):
41
+ continue
42
+
43
+ i, j = list(map(int, d.split('_')))
44
+
45
+ in_d = os.path.join(args.input_path, f'{i}_{j}')
46
+ out_d = os.path.join(args.output_path, f'{i}_{j}')
47
+ os.makedirs(out_d, exist_ok=True)
48
+
49
+ logger = pdnl_sana.logging.Logger('normal', os.path.join(in_d, 'log.pkl'))
50
+ level = logger.data['level']
51
+ mpp = logger.data['mpp']
52
+ ds = logger.data['ds']
53
+ converter = pdnl_sana.geo.Converter(mpp, ds)
54
+ frame = pdnl_sana.image.Frame(os.path.join(in_d, 'frame.png'), level=level, converter=converter)
55
+ mask = pdnl_sana.image.Frame(os.path.join(in_d, 'mask.png'), level=level, converter=converter)
56
+
57
+
58
+ if args.staining_code == 'H-DAB':
59
+ processor = pdnl_sana.process.HDABProcessor(
60
+ logger, frame, main_mask=mask,
61
+ apply_smoothing=params.get('apply_smoothing', False),
62
+ normalize_background=params.get('normalize_background', False),
63
+ stain_vector=params.get('stain_vector', None)
64
+ )
65
+ hist = processor.dab.get_histogram(mask=processor.main_mask)
66
+ if histogram is None:
67
+ histogram = hist
68
+ else:
69
+ histogram = histogram + hist
70
+ processor.dab.save(os.path.join(out_d, 'stain.npy'))
71
+
72
+ # calculate the threshold
73
+ if not histogram is None:
74
+ triangular_strictness = params.get('triangular_strictness', 0.0)
75
+ threshold = pdnl_sana.threshold.triangular_method(
76
+ histogram,
77
+ strictness=triangular_strictness,
78
+ debug=True
79
+ )
80
+ else:
81
+ threshold = 0
82
+
83
+ minimum_threshold = params.get('minimum_threshold', 0)
84
+ if threshold < minimum_threshold:
85
+ threshold = minimum_threshold
86
+
87
+ # apply the threshold to the pre-processed chunks
88
+ print('Processing chunks...', flush=True)
89
+ for d in tqdm(os.listdir(args.output_path)):
90
+ if not os.path.isdir(os.path.join(args.input_path, d)):
91
+ continue
92
+
93
+ i, j = list(map(int, d.split('_')))
94
+
95
+ in_d = os.path.join(args.input_path, f'{i}_{j}')
96
+ out_d = os.path.join(args.output_path, f'{i}_{j}')
97
+
98
+ mask = pdnl_sana.image.Frame(os.path.join(in_d, 'mask.png'))
99
+ stain = pdnl_sana.image.Frame(os.path.join(out_d, 'stain.npy'))
100
+
101
+ stain.mask(mask)
102
+ stain.threshold(threshold)
103
+ morphology_filters = [pdnl_sana.filter.MorphologyFilter(**x) \
104
+ for x in params.get('morphology_filters', [])]
105
+ for filt in morphology_filters:
106
+ stain.apply_morphology_filter(filt)
107
+
108
+ stain.save(os.path.join(out_d, 'pos.png'))
109
+ logger = pdnl_sana.logging.Logger('normal', os.path.join(out_d, 'log.pkl'))
110
+ logger.data['triangular_strictness'] = triangular_strictness
111
+ logger.data['minimum_threshold'] = minimum_threshold
112
+ logger.data['morphology_filter'] = morphology_filters
113
+ logger.data['threshold'] = threshold
114
+ logger.write_data()
115
+
116
+ if __name__ == "__main__":
117
+ main()
@@ -4,16 +4,17 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "pdnl_process"
7
- version = "0.3"
7
+ version = "0.4"
8
8
  authors = [
9
9
  { name="Noah Capp", email="noahmcapp@gmail.com" },
10
10
  ]
11
11
  description = "PDNL Process"
12
12
  dependencies = [
13
- "pdnl_sana"
13
+ "pdnl_sana",
14
+ "tdqm",
14
15
  ]
15
16
  readme = "README.md"
16
- requires-python = ">=3.9"
17
+ requires-python = ">=3.12"
17
18
  classifiers = [
18
19
  "Programming Language :: Python :: 3",
19
20
  "License :: OSI Approved :: MIT License",
@@ -1,65 +0,0 @@
1
- #!/usr/bin/env python3
2
-
3
- import os
4
- import sys
5
- import argparse
6
- import json
7
-
8
- import numpy as np
9
- import pdnl_sana.image
10
- import pdnl_sana.process
11
- import pdnl_sana.logging
12
- import pdnl_sana.filter
13
-
14
- from matplotlib import pyplot as plt
15
-
16
- def main():
17
-
18
- parser = argparse.ArgumentParser()
19
- parser.add_argument('-i', '--input_file', help="path to image file to process", required=True)
20
- parser.add_argument('-o', '--output_path', help="path to write outputs to", required=True)
21
- parser.add_argument('-m', '--mask_file', help="path to mask file to apply to the image", default=None)
22
- parser.add_argument('-s', '--staining_code', help="stain protocol used to stain the input image", default='H-DAB')
23
- parser.add_argument('-p', '--parameters_file', help=".json file containing processing parameters", default=None)
24
- parser.add_argument('--debug', action='store_true', help="plots an output image to analyze")
25
- args = parser.parse_args()
26
-
27
- frame = pdnl_sana.image.Frame(args.input_file)
28
- if args.mask_file:
29
- mask = pdnl_sana.image.Frame(args.mask_file)
30
- else:
31
- mask = None
32
- if args.parameters_file:
33
- params = json.load(open(args.parameters_file))
34
- else:
35
- params = {}
36
-
37
- file_name = os.path.splitext(os.path.basename(args.input_file))[0]
38
- debug_level = 'full' if args.debug else 'normal'
39
- logger = pdnl_sana.logging.Logger(debug_level, os.path.join(args.output_path, 'log.pkl'))
40
-
41
- if args.staining_code == 'H-DAB':
42
- processor = pdnl_sana.process.HDABProcessor(
43
- logger, frame, main_mask=mask,
44
- apply_smoothing=params.get('apply_smoothing', False),
45
- normalize_background=params.get('normalize_background', False),
46
- stain_vector=params.get('stain_vector', None)
47
- )
48
-
49
- res = processor.run(
50
- triangular_strictness=params.get('triangular_strictness', 0.0),
51
- minimum_threshold=params.get('minimum_threshold', 0),
52
- od_threshold=params.get('od_threshold', None),
53
- morphology_filters=[pdnl_sana.filter.MorphologyFilter(**filter_params) for filter_params in params.get('morphology_filters', [])],
54
- )
55
-
56
- if args.debug:
57
- plt.show()
58
-
59
-
60
- res['stain'].save(os.path.join(args.output_path, 'stain.npy'))
61
- res['positive_stain'].save(os.path.join(args.output_path, 'pos.png'))
62
- logger.write_data()
63
-
64
- if __name__ == "__main__":
65
- main()
File without changes
File without changes