requires("1.54s"); macro "Batch_DAPI_C1_MainNuclei_ManualThreshold_LessAggressive_withBatchReport_andAllAreas" { // ========================================================= // DEFAULT PARAMETERS // ========================================================= rollingBallNuc = 25; gaussianSigmaNuc = 0.8; // threshold iniziale, modificabile a mano per ogni immagine minThresholdNuc = 20; maxThresholdNuc = 255; minNucleusArea = 40; minNucleusCirc = 0.04; excludeEdges = false; // default conservativo doWatershedNuclei = false; saveOverlay = true; fontSizeLabel = 18; // ========================================================= // SELECT FOLDERS // ========================================================= inputDir = getDirectory("Scegli la cartella con le immagini C1"); if (inputDir == "") exit("Nessuna cartella input."); outputDir = getDirectory("Scegli la cartella dove salvare i risultati"); if (outputDir == "") exit("Nessuna cartella output."); if (!endsWith(inputDir, "/")) inputDir = inputDir + "/"; if (!endsWith(outputDir, "/")) outputDir = outputDir + "/"; fileList = getFileList(inputDir); // ========================================================= // PARAMETER DIALOG // ========================================================= Dialog.create("Parametri nuclei batch"); Dialog.addNumber("Rolling ball", rollingBallNuc); Dialog.addNumber("Gaussian sigma", gaussianSigmaNuc); Dialog.addNumber("Min threshold iniziale (8-bit)", minThresholdNuc); Dialog.addNumber("Max threshold iniziale (8-bit)", maxThresholdNuc); Dialog.addNumber("Min nucleus area", minNucleusArea); Dialog.addNumber("Min nucleus circularity", minNucleusCirc); Dialog.addCheckbox("Watershed nuclei", doWatershedNuclei); Dialog.addCheckbox("Exclude edge particles", excludeEdges); Dialog.addCheckbox("Save overlay", saveOverlay); Dialog.addNumber("Overlay font size", fontSizeLabel); Dialog.show(); rollingBallNuc = Dialog.getNumber(); gaussianSigmaNuc = Dialog.getNumber(); minThresholdNuc = Dialog.getNumber(); maxThresholdNuc = Dialog.getNumber(); minNucleusArea = Dialog.getNumber(); minNucleusCirc = Dialog.getNumber(); doWatershedNuclei = Dialog.getCheckbox(); excludeEdges = Dialog.getCheckbox(); saveOverlay = Dialog.getCheckbox(); fontSizeLabel = Dialog.getNumber(); // ========================================================= // BATCH REPORTS // ========================================================= batchReportCsv = outputDir + "Batch_Nuclei_Report.csv"; batchAreaCsv = outputDir + "Batch_Nuclei_All_Areas.csv"; batchHeader = "Sample_ID," + "Count," + "Mean_area," + "SD_area," + "Min_area," + "Max_area," + "Total_area," + "Threshold_mode," + "Watershed_used," + "Exclude_edges\n"; File.saveString(batchHeader, batchReportCsv); batchAreaHeader = "Sample_ID," + "Nucleus_ID," + "Area," + "X," + "Y\n"; File.saveString(batchAreaHeader, batchAreaCsv); // ========================================================= // LOOP FILES // ========================================================= for (f = 0; f < fileList.length; f++) { name = fileList[f]; path = inputDir + name; if (File.isDirectory(path)) continue; if (!(endsWith(name, "C1.tif") || endsWith(name, "C1.tiff") || endsWith(name, "C1.TIF") || endsWith(name, "C1.TIFF"))) continue; open(path); originalTitle = getTitle(); run("Select None"); roiManager("Reset"); // ----------------------------------------------------- // BASE NAME // ----------------------------------------------------- baseName = originalTitle; if (endsWith(baseName, ".tif") || endsWith(baseName, ".TIF")) baseName = substring(baseName, 0, lengthOf(baseName) - 4); else if (endsWith(baseName, ".tiff") || endsWith(baseName, ".TIFF")) baseName = substring(baseName, 0, lengthOf(baseName) - 5); roiZip = outputDir + baseName + "_Nuclei_ROIs.zip"; areaCsv = outputDir + baseName + "_Nuclei_Area.csv"; summaryCsv = outputDir + baseName + "_Nuclei_Summary.csv"; overlayTif = outputDir + baseName + "_Nuclei_Overlay.tif"; // ===================================================== // PREP IMAGE // ===================================================== Stack.getDimensions(w, h, c, z, t); if (z > 1) { run("Z Project...", "projection=[Max Intensity]"); rename("DAPI_MAX"); selectWindow(originalTitle); close(); } else { run("Duplicate...", "title=DAPI_MAX"); selectWindow(originalTitle); close(); } // copia per overlay finale selectWindow("DAPI_MAX"); run("Select None"); run("Duplicate...", "title=DAPI_FOR_OVERLAY"); // ===================================================== // NUCLEI SEGMENTATION // ===================================================== selectWindow("DAPI_MAX"); run("Select None"); run("Duplicate...", "title=NUC_PREP"); selectWindow("NUC_PREP"); run("8-bit"); run("Subtract Background...", "rolling=" + rollingBallNuc); if (gaussianSigmaNuc > 0) run("Gaussian Blur...", "sigma=" + gaussianSigmaNuc); run("Enhance Contrast...", "saturated=0.35 normalize"); // threshold iniziale, modificabile a mano setThreshold(minThresholdNuc, maxThresholdNuc); run("Threshold..."); waitForUser("[" + baseName + "]\nControlla la sogliatura dei nuclei.\nRegola la threshold se necessario.\nNON premere Apply.\nPremi solo OK qui per continuare."); selectWindow("NUC_PREP"); setOption("BlackBackground", false); run("Convert to Mask"); // Pulizia minima per non spezzare nuclei singoli run("Fill Holes"); if (doWatershedNuclei) run("Watershed"); rename("NUCLEI_MASK"); // ===================================================== // ANALYZE PARTICLES // ===================================================== roiManager("Reset"); if (isOpen("Results")) { selectWindow("Results"); run("Clear Results"); } run("Set Measurements...", "area centroid shape decimal=3"); options = "size=" + minNucleusArea + "-Infinity circularity=" + minNucleusCirc + "-1.00 display add"; if (excludeEdges) options = options + " exclude"; run("Analyze Particles...", options); nucleiCount = roiManager("count"); // ===================================================== // NO NUCLEI FOUND // ===================================================== if (nucleiCount == 0) { if (isOpen("Results")) { selectWindow("Results"); run("Clear Results"); } setResult("Image", 0, baseName); setResult("Count", 0, 0); setResult("Mean_area", 0, 0); setResult("SD_area", 0, 0); setResult("Min_area", 0, 0); setResult("Max_area", 0, 0); setResult("Total_area", 0, 0); setResult("Threshold_mode", 0, "manual_per_image"); setResult("Watershed_used", 0, doWatershedNuclei); setResult("Exclude_edges", 0, excludeEdges); updateResults(); saveAs("Results", summaryCsv); batchLine = baseName + "," + "0,0,0,0,0,0," + "manual_per_image," + doWatershedNuclei + "," + excludeEdges + "\n"; File.append(batchLine, batchReportCsv); close("*"); roiManager("Reset"); run("Collect Garbage"); continue; } // ===================================================== // SAVE ROIs // ===================================================== roiManager("Deselect"); roiManager("Save", roiZip); // ===================================================== // MEASURE EACH NUCLEUS // ===================================================== if (isOpen("Results")) { selectWindow("Results"); run("Clear Results"); } nucArea = newArray(nucleiCount); nucX = newArray(nucleiCount); nucY = newArray(nucleiCount); totalArea = 0; run("Set Measurements...", "area centroid shape decimal=3"); for (i = 0; i < nucleiCount; i++) { selectWindow("DAPI_MAX"); roiManager("select", i); run("Measure"); nucArea[i] = getResult("Area", i); nucX[i] = getResult("X", i); nucY[i] = getResult("Y", i); totalArea += nucArea[i]; } if (nucleiCount == 1) { minA = nucArea[0]; maxA = nucArea[0]; meanA = nucArea[0]; sdA = 0; } else { Array.getStatistics(nucArea, minA, maxA, meanA, sdA); if (sdA != sdA) sdA = 0; } // ===================================================== // APPEND ALL SINGLE NUCLEI TO GLOBAL BATCH AREA FILE // ===================================================== for (i = 0; i < nucleiCount; i++) { line = baseName + "," + (i + 1) + "," + d2s(nucArea[i], 6) + "," + d2s(nucX[i], 3) + "," + d2s(nucY[i], 3) + "\n"; File.append(line, batchAreaCsv); } // ===================================================== // SAVE AREA CSV // ===================================================== if (isOpen("Results")) { selectWindow("Results"); run("Clear Results"); } for (i = 0; i < nucleiCount; i++) { setResult("Image", i, baseName); setResult("Nucleus_ID", i, i + 1); setResult("Area", i, nucArea[i]); setResult("X", i, nucX[i]); setResult("Y", i, nucY[i]); } updateResults(); saveAs("Results", areaCsv); // ===================================================== // SAVE SUMMARY CSV // ===================================================== if (isOpen("Results")) { selectWindow("Results"); run("Clear Results"); } setResult("Image", 0, baseName); setResult("Count", 0, nucleiCount); setResult("Mean_area", 0, meanA); setResult("SD_area", 0, sdA); setResult("Min_area", 0, minA); setResult("Max_area", 0, maxA); setResult("Total_area", 0, totalArea); setResult("Threshold_mode", 0, "manual_per_image"); setResult("Watershed_used", 0, doWatershedNuclei); setResult("Exclude_edges", 0, excludeEdges); updateResults(); saveAs("Results", summaryCsv); // ===================================================== // APPEND TO BATCH REPORT // ===================================================== batchLine = baseName + "," + nucleiCount + "," + d2s(meanA, 6) + "," + d2s(sdA, 6) + "," + d2s(minA, 6) + "," + d2s(maxA, 6) + "," + d2s(totalArea, 6) + "," + "manual_per_image," + doWatershedNuclei + "," + excludeEdges + "\n"; File.append(batchLine, batchReportCsv); // ===================================================== // SAVE OVERLAY IMAGE // ===================================================== if (saveOverlay) { selectWindow("DAPI_FOR_OVERLAY"); run("RGB Color"); rename("OVERLAY_RGB"); setForegroundColor(255, 255, 0); setLineWidth(1); for (i = 0; i < roiManager("count"); i++) { selectWindow("OVERLAY_RGB"); roiManager("select", i); run("Draw"); } setForegroundColor(255, 255, 0); setFont("SansSerif", fontSizeLabel, "antialiased"); for (i = 0; i < nucleiCount; i++) { drawString("" + (i + 1), nucX[i] + 4, nucY[i] - 4); } drawString("Nuclei = " + nucleiCount, 20, 30); drawString("Threshold = manual", 20, 55); saveAs("Tiff", overlayTif); close(); } // ===================================================== // CLEANUP // ===================================================== close("*"); roiManager("Reset"); run("Collect Garbage"); } print("=== Batch nuclei principali completato ==="); print("Batch report salvato in: " + batchReportCsv); print("Batch aree singoli nuclei salvato in: " + batchAreaCsv); }