pdnl-process 0.5__tar.gz → 0.7__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
- Metadata-Version: 2.4
1
+ Metadata-Version: 2.5
2
2
  Name: pdnl_process
3
- Version: 0.5
3
+ Version: 0.7
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>
@@ -0,0 +1 @@
1
+ from .main import main, process_roi
@@ -0,0 +1,124 @@
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 process_roi(frame, mask, subroi_masks, frame_loc, roi_name, output_path,
20
+ apply_smoothing=False, normalize_background=False,
21
+ background_radius=200, background_overlap=0.5,
22
+ triangular_strictness=0.0, minimum_threshold=0,
23
+ morphology_filters=[],
24
+ staining_code='H-DAB', stain_vector=None, debug_level='normal'):
25
+
26
+ # TODO: move all pdnl_sana.process code into here, then other scripts import these functions
27
+ out_d = os.path.join(output_path, roi_name)
28
+ os.makedirs(out_d, exist_ok=True)
29
+ logger = pdnl_sana.logging.Logger(debug_level, os.path.join(out_d, 'log.pkl'))
30
+
31
+ # TODO: shouldn't be omitting the mask here, but leaving it here so all data is generated in the same way for now.
32
+ # this was a fix for the background code, but that code is solved now
33
+ if staining_code == 'H-DAB':
34
+ processor = pdnl_sana.process.HDABProcessor(
35
+ logger, frame,
36
+ #main_mask=mask,
37
+ apply_smoothing=apply_smoothing,
38
+ normalize_background=normalize_background,
39
+ stain_vector=stain_vector,
40
+ radius=background_radius, overlap=background_overlap,
41
+ )
42
+ if staining_code == 'CV-DAB':
43
+ processor = pdnl_sana.process.CVDABProcessor(
44
+ logger, frame,
45
+ #main_mask=mask,
46
+ apply_smoothing=apply_smoothing,
47
+ normalize_background=normalize_background,
48
+ stain_vector=stain_vector,
49
+ )
50
+ hist = processor.dab.get_histogram(mask=processor.main_mask)
51
+ stain = processor.dab.copy()
52
+ stain.save(os.path.join(out_d, 'stain.npy'))
53
+
54
+ # calculate the threshold
55
+ triangular_strictness = triangular_strictness
56
+ threshold = pdnl_sana.threshold.triangular_method(
57
+ hist,
58
+ strictness=triangular_strictness,
59
+ )
60
+ threshold = np.clip(threshold, minimum_threshold, None)
61
+
62
+ stain.mask(mask)
63
+ stain.threshold(threshold)
64
+ morphology_filters = [pdnl_sana.filter.MorphologyFilter(**x) \
65
+ for x in morphology_filters]
66
+ for filt in morphology_filters:
67
+ stain.apply_morphology_filter(filt)
68
+
69
+ # TODO: don't write anything out if output_path=None
70
+ if debug_level in ['debug', 'full']:
71
+ frame.save(os.path.join(out_d, 'frame.png'))
72
+ processor.bckg.save(os.path.join(out_d, 'bckg.png'))
73
+
74
+ if debug_level == 'full':
75
+ plt.show()
76
+ stain.save(os.path.join(out_d, 'pos.png'))
77
+ mask.save(os.path.join(out_d, 'mask.png'))
78
+ for subroi_name in subroi_masks:
79
+ subroi_masks[subroi_name].save(os.path.join(out_d, f'submask_{subroi_name}.png'))
80
+ logger.data['triangular_strictness'] = triangular_strictness
81
+ logger.data['minimum_threshold'] = minimum_threshold
82
+ logger.data['morphology_filter'] = morphology_filters
83
+ logger.data['threshold'] = threshold
84
+ logger.data['frame_loc'] = frame_loc
85
+ logger.write_data()
86
+
87
+ return frame, stain, logger
88
+
89
+ def main():
90
+
91
+ parser = argparse.ArgumentParser()
92
+ parser.add_argument('-i', '--input_path', help="path to image data to process", required=True)
93
+ parser.add_argument('-o', '--output_path', help="path to write outputs to", required=True)
94
+ parser.add_argument('-s', '--staining_code', help="stain protocol used to stain the input image", default='H-DAB')
95
+ parser.add_argument('-p', '--parameters_file', help=".json file containing processing parameters", default=None)
96
+ parser.add_argument('--debug_level', help="outputs extra files", default='normal')
97
+ args = parser.parse_args()
98
+
99
+ if args.parameters_file:
100
+ params = json.load(open(args.parameters_file))
101
+ else:
102
+ params = {}
103
+
104
+ os.makedirs(args.output_path, exist_ok=True)
105
+
106
+ # get the background image
107
+ # TODO: test how big the concatenated frame will be, and move to big roi mode if too big
108
+ rois = pdnl_sana.slide.concatenate_chunks(args.input_path)
109
+ for roi_name in rois:
110
+ frame, mask, subroi_masks, frame_loc = rois[roi_name]
111
+
112
+ process_roi(frame, mask, subroi_masks, frame_loc, roi_name, args.output_path,
113
+ apply_smoothing=params.get('apply_smoothing', False),
114
+ normalize_background=params.get('normalize_background', False),
115
+ triangular_strictness=params.get('triangular_strictness', 0.0),
116
+ minimum_threshold=params.get('minimum_threshold', 0),
117
+ morphology_filters=[pdnl_sana.filter.MorphologyFilter(**x) for x in params.get('morphology_filters')],
118
+ staining_code=args.staining_code,
119
+ stain_vector=params.get('stain_vector', None),
120
+ debug_level=args.debug_level,
121
+ )
122
+
123
+ if __name__ == "__main__":
124
+ main()
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "pdnl_process"
7
- version = "0.5"
7
+ version = "0.7"
8
8
  authors = [
9
9
  { name="Noah Capp", email="noahmcapp@gmail.com" },
10
10
  ]
@@ -28,4 +28,4 @@ classifiers = [
28
28
  Homepage = "https://github.com/PennCompPathology/pdnl_process"
29
29
 
30
30
  [project.scripts]
31
- pdnl_process = "pdnl_process:main"
31
+ pdnl_process = "pdnl_process:main"
@@ -1 +0,0 @@
1
- from .main import main
@@ -1,124 +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
- 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
- if args.staining_code == 'CV-DAB':
66
- processor = pdnl_sana.process.CVDABProcessor(
67
- logger, frame, main_mask=mask,
68
- apply_smoothing=params.get('apply_smoothing', False),
69
- normalize_background=params.get('normalize_background', False),
70
- stain_vector=params.get('stain_vector', None)
71
- )
72
- hist = processor.dab.get_histogram(mask=processor.main_mask)
73
- if histogram is None:
74
- histogram = hist
75
- else:
76
- histogram = histogram + hist
77
- processor.dab.save(os.path.join(out_d, 'stain.npy'))
78
-
79
- # calculate the threshold
80
- if not histogram is None:
81
- triangular_strictness = params.get('triangular_strictness', 0.0)
82
- threshold = pdnl_sana.threshold.triangular_method(
83
- histogram,
84
- strictness=triangular_strictness,
85
- debug=True
86
- )
87
- else:
88
- threshold = 0
89
-
90
- minimum_threshold = params.get('minimum_threshold', 0)
91
- if threshold < minimum_threshold:
92
- threshold = minimum_threshold
93
-
94
- # apply the threshold to the pre-processed chunks
95
- print('Processing chunks...', flush=True)
96
- for d in tqdm(os.listdir(args.output_path)):
97
- if not os.path.isdir(os.path.join(args.input_path, d)):
98
- continue
99
-
100
- i, j = list(map(int, d.split('_')))
101
-
102
- in_d = os.path.join(args.input_path, f'{i}_{j}')
103
- out_d = os.path.join(args.output_path, f'{i}_{j}')
104
-
105
- mask = pdnl_sana.image.Frame(os.path.join(in_d, 'mask.png'))
106
- stain = pdnl_sana.image.Frame(os.path.join(out_d, 'stain.npy'))
107
-
108
- stain.mask(mask)
109
- stain.threshold(threshold)
110
- morphology_filters = [pdnl_sana.filter.MorphologyFilter(**x) \
111
- for x in params.get('morphology_filters', [])]
112
- for filt in morphology_filters:
113
- stain.apply_morphology_filter(filt)
114
-
115
- stain.save(os.path.join(out_d, 'pos.png'))
116
- logger = pdnl_sana.logging.Logger('normal', os.path.join(out_d, 'log.pkl'))
117
- logger.data['triangular_strictness'] = triangular_strictness
118
- logger.data['minimum_threshold'] = minimum_threshold
119
- logger.data['morphology_filter'] = morphology_filters
120
- logger.data['threshold'] = threshold
121
- logger.write_data()
122
-
123
- if __name__ == "__main__":
124
- main()
File without changes
File without changes