-
Notifications
You must be signed in to change notification settings - Fork 0
/
OrgM.py
172 lines (117 loc) · 5.8 KB
/
OrgM.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
'''
OrgM: Organoid Size Measurer - ImageJ Macro written in Python
Input - Directory containing Organoid bright field images
Output - CSV file containing size and shape descriptors for each organoid image
Written by: Eddie Cai and Rhalena Thomas - NeuroEDDU
'''
# Import required packages
import os, sys, math, datetime
from ij import IJ, ImagePlus, ImageStack
from ij.io import DirectoryChooser
from ij.measure import ResultsTable
from ij.measure import Measurements
from ij.process import ImageProcessor
from ij.process import ImageConverter
from ij.gui import WaitForUserDialog
from ij.gui import GenericDialog
from ij.plugin.frame import RoiManager
from ij.plugin.filter import ParticleAnalyzer
# Set Threshold mode
thresholdMode = False
gd = GenericDialog("Set Threshold Mode")
gd.addChoice("Would you like to enable thresholding mode?", ["No, run the normal macro", "Yes, enable thresholding mode"], "No")
gd.addMessage("Watershed will NOT be run in threshold mode and WILL BE RUN in macro mode.")
gd.showDialog()
if gd.getNextChoice() == "Yes, enable thresholding mode":
thresholdMode = True
# Set default thresholds:
# round_threshold is the minimum roundness a roi must have to be considered an organoid for the isOrganoid column
# area_threshold is the minimum area a roi must have to be considered an organoid for the isorganoid column
# minimum_size is the minimum area to be considered an ROI
gd = GenericDialog("Other Thresholds.")
gd.addMessage("Ajust after you have determined if new thresholds are needed.")
gd.addStringField("Round threshold", "0.62")
gd.addStringField("Area Threshold", "50000")
gd.addStringField("Minimum Size", "3000")
gd.showDialog()
round_threshold = float(gd.getNextString())
area_threshold = float(gd.getNextString())
minimum_size = float(gd.getNextString())
#set pix_width and pix_height to real dimensions per pixel
gd = GenericDialog("Dimension Options")
gd.addMessage("Conversion from pixles to uM :Evos 10X pixle width/height = 0.8777017 uM")
gd.addMessage("Conversion from pixles to uM :Evos 4X pixle width/height = 2.1546047 uM")
gd.addMessage("Conversion from pixles to uM :Calculate for your objective and enter below")
gd.addStringField("Pixel Width:", "0.8777017")
gd.addStringField("Pixel Height:", "0.8777017")
gd.showDialog()
pix_width = gd.getNextString()
pix_height = gd.getNextString()
# Get input and output directories with GUI
dc = DirectoryChooser("Choose an input directory")
inputDirectory = dc.getDirectory()
dc = DirectoryChooser("Choose an output directory")
outputDirectory = dc.getDirectory()
with open(outputDirectory + "output_"+datetime.datetime.now().strftime("%Y-%m-%d-%H-%M")+".csv", "w") as output:
# set the output column names for the csv sheet
output.write("Subfolder, File Name,Feret,MinFeret,Average Feret,Area,Equivalent Circle Diameter, Ellipse Major, Ellipse Minor, Circularity,Roundness,Solidity, MeetsCriteria \n")
subfolders = []
# Finds subfolders in input directory
for subfolder in os.listdir(inputDirectory):
if os.path.isdir(inputDirectory + subfolder):
subfolders.append(subfolder)
# If there are no subfolders, runs on images in input directory instead
if len(subfolders) == 0:
subfolders = [""]
for subfolder in subfolders:
#Opens each image
for filename in os.listdir(inputDirectory + subfolder):
imp = IJ.openImage(inputDirectory + subfolder + '/' + filename)
if imp:
# 10X objective
IJ.run(imp, "Properties...", "channels=1 slices=1 frames=1 unit=um pixel_width=" +str(pix_width)+ " pixel_height=" +str(pix_height)+" voxel_depth=25400.0508001") # Change to a GUI option later?
# Threshold, fills hole and watershed
ic = ImageConverter(imp);
ic.convertToGray8();
IJ.setAutoThreshold(imp, "Default dark")
if thresholdMode:
imp.show()
IJ.run("Threshold...")
WaitForUserDialog("Title", "Adjust threshold").show()
IJ.run(imp, "Convert to Mask", "")
IJ.run(imp, "Invert", "")
IJ.run(imp, "Fill Holes", "")
if not thresholdMode:
IJ.run(imp, "Watershed", "")
#Measure particles
table = ResultsTable()
pa = ParticleAnalyzer(ParticleAnalyzer.EXCLUDE_EDGE_PARTICLES, Measurements.AREA | Measurements.FERET | Measurements.CIRCULARITY | Measurements.SHAPE_DESCRIPTORS | Measurements.CENTROID | Measurements.ELLIPSE, table, minimum_size, 9999999999999999, 0.1, 1.0)
pa.setHideOutputImage(True)
pa.analyze(imp)
index = -1
maxArea = -1
# Check if Column even exists (in case it didn't measure anything)
if table.getColumnIndex("Area") != -1:
# Find the ROI with the largest area
for i, area in enumerate(table.getColumn(table.getColumnIndex("Area"))):
if area > maxArea:
index = i
if thresholdMode:
imp.show()
# Writes everything in the output file
if index != -1:
diameter = 2* math.sqrt( float(table.getValue("Area", index)) / (2* math.pi))
isOrganoid = table.getValue("Area", index) > area_threshold and table.getValue("Area", index) > round_threshold
output.write(str(subfolder) + ',' + filename + ',' + str(table.getValue("Feret", index)) + ',' + str(table.getValue("MinFeret", index)) + ',' + str((table.getValue("MinFeret", index)+table.getValue("Feret", index))/2) + ',' + str(table.getValue("Area", index)) + ',' + str(diameter) + ',' + str(table.getValue("Major", index)) + ','+ str(table.getValue("Minor", index)) + ','+ str(table.getValue("Circ.", index)) + ',' +str(table.getValue("Round", index)) + ',' + str(table.getValue("Solidity", index)) + ',' + str(isOrganoid))
else:
output.write(str(subfolder) + ',' + filename + ",NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA")
output.write('\n')
imp.changes = False
imp.close()
# End of macro
cat = """
\ /\ Macro completed!
) ( ') meow!
( / )
\(__)|"""
print(cat)