-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathImageJ_processing.py
199 lines (173 loc) · 8.02 KB
/
ImageJ_processing.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
import imagej
import numpy as np
import os
from skimage import io
from skimage import exposure
import xarray
from tqdm import tqdm
import logging
def ImageJ_processing(folderpath: str,
threshold1: dict,
threshold2: dict,
threshold3: dict,
num_slices_to_remove: int):
'''
Preprocesses images using ImageJ and applies various thresholding methods and filters.
Parameters:
-----------
folderpath : str
Path to the folder containing the images.
rawthreshold : int
Raw threshold value for thresholding method 1 -- ex. 2
threshtype2 : str
Threshold type for method 2 -- ex. 'Triangle'
threshtype3 : str
Threshold type for method 3 -- ex. 'Default'
minsize : int
Minimum size of particles to keep -- ex. 250
num_slices_to_remove : int
Number of slices to remove from the start of each image array.
These are usually faulty images that we don't want to include
Returns
-------
str
The path to the folder where the processed image files are saved.
'''
# initialize ImageJ2
ij = imagej.init(mode = 'headless') #mode can be changed to 'interactive' to view processing in real time
print(f" legacy service: {ij.legacy}")
print(f" legacy active? {ij.legacy and ij.legacy.isActive()}")
print(f"ImageJ2 version: {ij.getVersion()}")
#import all .tif files from folder
files = []
filenames = []
for filename in sorted(os.listdir(folderpath)):
if os.path.isfile(os.path.join(folderpath, filename)) and filename.endswith('.tif'):
files.append(os.path.join(folderpath, filename))
filenames.append(filename[:-4]) #remove .tif extension from filename
numfiles = len(files)
if numfiles > 0:
logging.info(f"There are {numfiles} .tif files to be processed")
else:
raise RuntimeError(f'No suitable .tif files found in {folderpath}, make sure you put your images there')
#create directory for processed images
savefolder = os.path.join(folderpath, "Processed")
if not os.path.exists(savefolder):
os.makedirs(savefolder)
def dump_info(image):
"""A handy function to print details of an image object if errors arise. Not run in code but included for debugging purposes.
Parameters:
-----------
image : object
The image object to retrieve details from.
Returns:
--------
None
"""
name = image.name if hasattr(image, 'name') else None # xarray
if name is None and hasattr(image, 'getName'): name = image.getName() # Dataset
if name is None and hasattr(image, 'getTitle'): name = image.getTitle() # ImagePlus
print(f" name: {name or 'N/A'}")
print(f" type: {type(image)}")
print(f"dtype: {image.dtype if hasattr(image, 'dtype') else 'N/A'}")
print(f"shape: {image.shape}")
print(f" dims: {image.dims if hasattr(image, 'dims') else 'N/A'}")
def process_image(i: int,
filepath: str,
filename: str,
ij):
'''
Process a single image using ImageJ and apply different thresholding methods.
Parameters:
-----------
i : int
Index of the image being processed.
filepath : str
Path to the image file.
filename : str
Name of the image file.
Returns:
--------
None
Notes:
------
Threshold1, Threshold2, Threshold3 dictionaries are not explicitly passed but are defined in the parent function
'''
#opening .tif image as a np array
arr = io.imread(filepath) #[num_slices_to_remove+1:,:,:]
if len(arr) > 150:
arr = arr[40:, :, :]
else:
arr = arr[num_slices_to_remove+1:,:,:]
logging.info(f'''Starting image:
Filename: {filename}
Pixel sum: {np.sum(arr)}
Num slices: {arr.shape[0]}
''')
#normalizing intensity from 0-255 and converting to uint8 datatype as xarray
arr = exposure.rescale_intensity(arr, out_range=(0, 255))
arr = (arr + 0.5).astype(np.uint8) #the +0.5 is so that it rounds to the nearest integer instead of always down
x_array = xarray.DataArray(arr, name='imp', dims=('pln','row','col'))
del arr
#defining the path for the processed images to be saved at
savepath = os.path.join(savefolder,filename)
#running thresholding method 1 (raw threshold)
imp = ij.py.to_imageplus(x_array) #create imagePlus object from arr
ij.ui().show(imp)
ij.IJ.run(imp, "8-bit", "")
ij.IJ.run(imp, "Gaussian Blur 3D...", f"x={threshold1['blur_sigma'][0]} y={threshold1['blur_sigma'][1]} z={threshold1['blur_sigma'][2]}")
ij.IJ.run(imp, "Subtract Background...", f"rolling={threshold1['sb_radius']} sliding disable stack")
imp.setTitle("PROCESSED.tif")
ij.IJ.setRawThreshold(imp, threshold1['rawthresh'], 255)
ij.IJ.run(imp, "Analyze Particles...", f"size={threshold1['minsize']}-Infinity show=Masks overlay stack")
result = ij.WindowManager.getImage("Mask of PROCESSED.tif")
ij.IJ.saveAs(result, "Tiff", f"{savepath}_thresh1.tif")
imp.close()
logging.info(f'Done thresholding method 1')
#running threshold method 2
imp = ij.py.to_imageplus(x_array)
ij.ui().show(imp)
ij.IJ.run(imp, "8-bit", "")
ij.IJ.run(imp, "Gaussian Blur 3D...", f"x={threshold2['blur_sigma'][0]} y={threshold2['blur_sigma'][1]} z={threshold2['blur_sigma'][2]}")
ij.IJ.run(imp, "Subtract Background...", f"rolling={threshold2['sb_radius']} sliding disable stack")
imp.setTitle("PROCESSED.tif")
ij.IJ.setAutoThreshold(imp, f"{threshold2['threshtype']} dark no-reset stack")
ij.IJ.run(imp, "Analyze Particles...", f"size={threshold2['minsize']}-Infinity show=Masks overlay stack")
result = ij.WindowManager.getImage("Mask of PROCESSED.tif")
ij.IJ.saveAs(result, "Tiff", f"{savepath}_thresh2.tif")
imp.close()
logging.info(f'Done thresholding method 2')
#running threshold method 3
imp = ij.py.to_imageplus(x_array)
ij.ui().show(imp)
ij.IJ.run(imp, "8-bit", "")
ij.IJ.run(imp, "Gaussian Blur 3D...", f"x={threshold3['blur_sigma'][0]} y={threshold3['blur_sigma'][1]} z={threshold3['blur_sigma'][2]}")
ij.IJ.run(imp, "Subtract Background...", f"rolling={threshold3['sb_radius']} sliding disable stack")
imp.setTitle("PROCESSED.tif")
ij.IJ.setAutoThreshold(imp, f"{threshold3['threshtype']} dark no-reset stack")
ij.IJ.run(imp, "Analyze Particles...", f"size={threshold3['minsize']}-Infinity show=Masks overlay stack")
result = ij.WindowManager.getImage("Mask of PROCESSED.tif")
ij.IJ.saveAs(result, "Tiff", f"{savepath}_thresh3.tif")
imp.close()
logging.info(f'Done thresholding method 3')
del x_array
#all files are processed iteratively
with tqdm(total=numfiles, desc='Processing Images', unit='img') as pbar:
for i in range(numfiles):
filepath = files[i]
filename = filenames[i]
pbar.set_description(f'Image {i+1}/{numfiles}')
if str(filename)+'_thresh3.tif' not in os.listdir(savefolder):
print(filename)
try:
process_image(i, filepath, filename, ij)
except IndexError:
pass
else:
print(f"{filename} processed already")
# Close all opened tabs to lighten memory (even if you can't see them)
ij.WindowManager.setTempCurrentImage(None)
ij.IJ.run("Close All")
pbar.update(1)
ij.context().dispose() #Close ImageJ instance
return savefolder