Guest User

carandraug

a guest
Apr 7th, 2011
280
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 12.20 KB | None | 0 0
  1. // "Macros to count things from nuclei masks"
  2. // Copyright (c) 2011 Carnë Draug <[email protected]>
  3.  
  4. // This program is free software; you can redistribute it and/or modify
  5. // it under the terms of the GNU General Public License as published by
  6. // the Free Software Foundation; either version 3 of the License, or
  7. // (at your option) any later version.
  8. //
  9. // This program is distributed in the hope that it will be useful,
  10. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. // GNU General Public License for more details.
  13. //
  14. // You should have received a copy of the GNU General Public License
  15. // along with this program; If not, see <http://www.gnu.org/licenses/>.
  16.  
  17. macro "Count foci" {
  18.  
  19. // Set variables
  20. extension = ".tif"; // extension of files to analyze (don't forget the dot)
  21.  
  22. //////////////////////////////////////////////////////////////////////////////
  23. // Options menu
  24. //////////////////////////////////////////////////////////////////////////////
  25. Dialog.create("Options to count foci");
  26.  
  27. Dialog.addChoice("Channel for nuclei", newArray("blue", "red", "green"));
  28. Dialog.addNumber("Threshold value for nuclei", 30);
  29. Dialog.addNumber("Minimum particle size", 5000);
  30. Dialog.addCheckbox("Watershed cell nuclei", true);
  31.  
  32. Dialog.addMessage("Aside the channel used to find the nuclei, two other\nare left. Both can be used to count foci if so desired.");
  33. Dialog.addCheckbox("Count foci on other channel #1", true);
  34. Dialog.addCheckbox("Count foci on other channel #2", true);
  35. Dialog.addString("Name for other channel #1", "Channel #1");
  36. Dialog.addString("Name for other channel #2", "Channel #2");
  37. Dialog.addChoice("Channel for foci #1", newArray("red", "green", "blue"));
  38. Dialog.addChoice("Channel for foci #2", newArray("green", "red", "blue"));
  39. Dialog.addNumber("Threshold value for channel #1", 30);
  40. Dialog.addNumber("Threshold value for channel #2", 30);
  41. Dialog.addNumber("Minimum particle size", 2);
  42. Dialog.addNumber("Minimum particle size", 2);
  43. Dialog.addCheckbox("Watershed channel #1", true);
  44. Dialog.addCheckbox("Watershed channel #2", true);
  45.  
  46. Dialog.show();
  47.  
  48. // get options for nuclei
  49. nuclei_channel = get_channel_number (Dialog.getChoice());
  50. nuclei_thres = Dialog.getNumber();
  51. nuclei_min_size = Dialog.getNumber();
  52. nuclei_water = Dialog.getCheckbox();
  53.  
  54. use_1 = Dialog.getCheckbox();
  55. use_2 = Dialog.getCheckbox();
  56. // return error if none of the 2 other channels is selected
  57. if (!use_1 && !use_2) {
  58. exit("None of the other two channels was selected to count foci. You must select at least one. Exiting macro now");
  59. }
  60.  
  61. // create array for each option. If there's only 1 channel selected
  62. // they'll be removed later
  63. // FUCKING limited macro language forces me to make this gymnastic...
  64. extra_name = new_array_from (Dialog.getString(), Dialog.getString());
  65. extra_channel = new_array_from (get_channel_number(Dialog.getChoice()), get_channel_number(Dialog.getChoice()));
  66. extra_thres = new_array_from (Dialog.getNumber(), Dialog.getNumber());
  67. extra_min_size = new_array_from (Dialog.getNumber(), Dialog.getNumber());
  68. extra_water = new_array_from (Dialog.getCheckbox(), Dialog.getCheckbox());
  69. // remove values if one of the channels was not selected
  70. if (use_1 && !use_2) {
  71. extra_name = remove_from_array (extra_name, 1);
  72. extra_channel = remove_from_array (extra_channel, 1);
  73. extra_thres = remove_from_array (extra_thres, 1);
  74. extra_min_size = remove_from_array (extra_min_size, 1);
  75. extra_water = remove_from_array (extra_water, 1);
  76. } else if (!use_1 && use_2) {
  77. extra_name = remove_from_array (extra_name, 0);
  78. extra_channel = remove_from_array (extra_channel, 0);
  79. extra_thres = remove_from_array (extra_thres, 0);
  80. extra_min_size = remove_from_array (extra_min_size, 0);
  81. extra_water = remove_from_array (extra_water, 0);
  82. }
  83.  
  84. // return error if one of the 2 other channels is the same as nuclei but allow
  85. // for channel #1 and #2 to be the same to test different threshold values or
  86. // watershed
  87. if (nuclei_channel == extra_channel[0]) {
  88. exit("A selected channel is the same as channel for nuclei");
  89. // only if both channels are selected does the array extends to index 1 so
  90. // that must be checked first (short circuit comparison operators, GO!!!)
  91. // FUCK ImageJ macro language. Short circuit operators don't work
  92. } else if (use_1 && use_2) {
  93. if (nuclei_channel == extra_channel[1]) {
  94. exit("Selected channel #2 is the same as channel for nuclei");
  95. }
  96. }
  97.  
  98. // Get list of files
  99. file_dir = getDirectory("Choose a Directory ");
  100. file_list = get_cleansed_file_list (file_dir, extension);
  101.  
  102. // Go light speed fast in batch mode
  103. // setBatchMode (true);
  104. if (lengthOf(file_list) == 1 && file_list[0] == 0) {
  105. exit("No file was found in the selected directory with extension " + extension);
  106. }
  107.  
  108. // Start analyzing images, one file at a time
  109. for (file_i = 0; file_i < lengthOf(file_list); file_i++) {
  110. filename = file_list[file_i];
  111. // be nice and tell people where we are
  112. showStatus("Analyzing image " + (file_i+1) + " of " + lengthOf(file_list));
  113. showProgress((file_i+1)/lengthOf(file_list));
  114.  
  115. print ("Taking care of file "+filename);
  116. // if file no longer exists, skip and warn user
  117. if (!File.exists(filename)) {
  118. print (" File " + filename + " seems to no longer exist. Skipped.");
  119. } else {
  120. prepare_environment();
  121. image_ID = prepare_image(filename);
  122. nuclei_count = nuclei_processing (nuclei_channel, nuclei_thres, nuclei_min_size, nuclei_water);
  123. roiManager("save", filename+"nuclei_mask.zip");
  124. print (" Found "+nuclei_count+" nuclei");
  125.  
  126. selectImage(image_ID);
  127. foci_1_mask_ID = foci_processing (extra_channel[0], extra_thres[0], extra_water[0]);
  128. if (lengthOf(extra_name) == 2) {
  129. selectImage(image_ID);
  130. foci_2_mask_ID = foci_processing (extra_channel[1], extra_thres[1], extra_water[1]);
  131. }
  132.  
  133. for (nuclei_i = 0; nuclei_i < nuclei_count; nuclei_i++) {
  134. selectImage(foci_1_mask_ID);
  135. foci_count = foci_per_nuclei (nuclei_i, nuclei_count, extra_min_size[0]);
  136. roiManager("save", filename+"_foci_of_nuclei_"+nuclei_i+"_channel"+extra_name[0]+".zip");
  137. print (" Found "+foci_count+" foci in nuclei "+nuclei_i+" in channel "+extra_name[0]);
  138. run("Clear Results");
  139.  
  140. for (foci_i = 0; foci_i < foci_count; foci_i++) {
  141. selectImage(image_ID);
  142. roi_index = nuclei_count + foci_i;
  143. measure_foci (roi_index, foci_i, get_channel_name(extra_channel[0]));
  144. }
  145. saveAs("results", filename+"_foci_"+extra_name[0]+"_nuclei_"+nuclei_i+".xls");
  146. run("Clear Results");
  147. remove_foci_rois (nuclei_count);
  148.  
  149. if (lengthOf(extra_name) == 2) {
  150. selectImage(foci_2_mask_ID);
  151. foci_count = foci_per_nuclei (nuclei_i, nuclei_count, extra_min_size[1]);
  152. roiManager("save", filename+"_foci_of_nuclei_"+nuclei_i+"_channel"+extra_name[1]+".zip");
  153. print (" Found "+foci_count+" foci in nuclei "+nuclei_i+" in channel "+extra_name[1]);
  154. run("Clear Results");
  155.  
  156. for (foci_i = 0; foci_i < foci_count; foci_i++) {
  157. selectImage(image_ID);
  158. roi_index = nuclei_count + foci_i;
  159. measure_foci (roi_index, foci_i, get_channel_name(extra_channel[1]));
  160. }
  161. saveAs("results", filename+"_foci_"+extra_name[1]+"_nuclei_"+nuclei_i+".xls");
  162. run("Clear Results");
  163. remove_foci_rois (nuclei_count);
  164. }
  165.  
  166. }
  167. close_by_ID(foci_1_mask_ID);
  168. if (lengthOf(extra_name) == 2) {
  169. close_by_ID(foci_2_mask_ID);
  170. }
  171.  
  172. }
  173. }
  174. showStatus("All done captain!");
  175. }
  176.  
  177. ////////////////////////////////////////////////////////////////////////////////
  178. // Functions from this point on
  179. ////////////////////////////////////////////////////////////////////////////////
  180.  
  181. // takes the name of a channel (blue, red or green) and converts to the channel
  182. // number on composite images
  183. function get_channel_number (name) {
  184. if (name == "red"){
  185. number = 1;
  186. } else if (name == "green") {
  187. number = 2;
  188. } else if (name == "blue") {
  189. number = 3;
  190. } else {
  191. exit ("Some error occurred when trying to guess the number of the channel");
  192. }
  193. return number;
  194. }
  195.  
  196. function get_channel_name (number) {
  197. if (number == 1){
  198. name = "red";
  199. } else if (number == 2) {
  200. name = "green";
  201. } else if (number == 3) {
  202. name = "blue";
  203. } else {
  204. exit ("Some error occurred when trying to guess the name of the channel");
  205. }
  206. return name;
  207. }
  208.  
  209. // because the newArray function can't accept the return value of fucntions such
  210. // as Dialog.getString as values for the array, this new function should be able
  211. // to do it
  212. function new_array_from (value1, value2) {
  213. array = newArray(2);
  214. array[0] = value1;
  215. array[1] = value2;
  216. return array;
  217. }
  218.  
  219. // this function prepares the environment for the analysis. Closes other windows
  220. // inclusive the ROI manager and results windows. It also removes all other ROI
  221. function prepare_environment() {
  222. if (isOpen("ROI Manager")) {
  223. selectWindow("ROI Manager");
  224. run("Close");
  225. }
  226. roiManager("reset");
  227. }
  228.  
  229. // Prepares the image for everything and returns the image_ID. Opens it, makes it
  230. // composite and may make any adjustement necessary
  231. function prepare_image (filepath) {
  232. open(filepath);
  233. run("Make Composite");
  234. Stack.setDisplayMode("color");
  235. // composite creates a new image, the ID is different
  236. image_ID = getImageID();
  237. return image_ID;
  238. }
  239.  
  240. // Makes all the morphological processing of the image to get boundaries of the
  241. // cell nuclei.
  242. function nuclei_processing (channel, thres, min_size, water) {
  243. // morphological processing of the mask must end in a dilate or the borders will
  244. // be black and "exclude on edges" won't work
  245. Stack.setChannel(channel);
  246. run("Gaussian Blur...", "sigma=5 slice");
  247. setThreshold(thres, 255);
  248. run("Create Mask");
  249. ID = getImageID();
  250. run("Erode");
  251. run("Dilate");
  252. run("Fill Holes");
  253. run("Erode");
  254. run("Erode");
  255. run("Dilate");
  256. run("Dilate");
  257. run("Fill Holes");
  258. if (water) {
  259. run("Watershed");
  260. }
  261. // Run analyze particles. Consider particles larger than min-size until infinity
  262. // and circularity between 0 and 1 (all). It doesn't show anything, clears
  263. // the results window, excludes on edges, includes holes, and add selections
  264. // to ROI manager
  265. roiManager("reset")
  266. run("Analyze Particles...", "size="+min_size+"-Infinity circularity=0.00-1.00 show=Nothing exclude clear include add");
  267. count = roiManager("count");
  268. close_by_ID(ID);
  269. return count;
  270. }
  271.  
  272. function foci_processing (channel, thres, water) {
  273. Stack.setChannel(channel);
  274. setThreshold(thres, 255);
  275. run("Create Mask");
  276. ID = getImageID();
  277. if (water) {
  278. run("Watershed");
  279. }
  280. return ID;
  281. }
  282.  
  283. function foci_per_nuclei (nuclei_i, nuclei_count, min_size) {
  284. roiManager("Select", nuclei_i);
  285. // do NOT clear the roi manager now
  286. run("Analyze Particles...", "size="+min_size+"-Infinity circularity=0.00-1.00 show=Nothing exclude add");
  287. count = roiManager("count");
  288. count = count - nuclei_count;
  289. return count;
  290. }
  291.  
  292. function measure_foci (roi_index, foci_i, channel_name) {
  293. roiManager('select', roi_index);
  294. getStatistics(area, mean, min, max);
  295. sum = get_intensity_sum();
  296.  
  297. setResult("Area", foci_i, area);
  298. setResult("Mean", foci_i, mean);
  299. setResult("Min", foci_i, min);
  300. setResult("Max", foci_i, max);
  301. setResult("Sum", foci_i, sum);
  302. }
  303.  
  304. function remove_foci_rois (nuclei_count) {
  305. count = roiManager("count");
  306. for (i=(count-1); i>=nuclei_count; i--) {
  307. roiManager("Select", i);
  308. roiManager("Delete");
  309. }
  310. }
  311.  
  312. // Calculates the sum of the intensities of each pixel in the selection
  313. function get_intensity_sum() {
  314. nBins = 256;
  315. total = 0;
  316. getHistogram(values, counts, nBins);
  317. for (i=values[0]; i<values[nBins-1]; i++) {
  318. total = total + (values[i]*counts[i]);
  319. }
  320. return total;
  321. }
Advertisement
Add Comment
Please, Sign In to add comment