Commit 379780f9 authored by Karel Štěpka's avatar Karel Štěpka
Browse files

Update file ScratchAssay.ijm

parent a05eaf11
Loading
Loading
Loading
Loading
+110 −15
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ open("d:/Scratch Assay/Images/2025-03-05 KER/25_1_t24.JPG");




*/

/*
@@ -55,6 +56,7 @@ doScalingDuringRegistration = false; // When registering ROIs between different
defaultFiducialPlacementOption = 1;  // 0 if the fiducials are placed along the gap. 1 if they are placed to the sides of it.
defaultGapOrientationOption = 0;  // 0 if the gap is vertical. 1 if the gap is horizontal. 2 if the gap orientation is unknown or varies from series to series.
fiducialBlurFactor = 1/46;  // The higher fiducialBlurFactor is, the more blurred the fiducial detection image will be. (Gaussian blur sigma will be equal to image width * fiducialBlurFactor.)
defaultContrastEnhancementPercentage = 20;

selectionColor = "red";

@@ -69,6 +71,8 @@ leewayFractionToUse = 0.9; // How far should we actually expand the rectangle?
roiManagerWindowPadding = 10;  // Padding between the ROI Manager window and the edge of the screen (pixels).
roiManagerWindowWidth = 500;  // Width of the ROI Manager window (pixels).
roiManagerWindowHeight = 600;  // Height of the ROI Manager window (pixels).
logWindowPaddding = 100;  // Padding between the Log window and the edge of the screen (pixels).
resultsWindowPaddding = 200;  // Padding between the Results window and the edge of the screen (pixels).

intermediateDebugImagePrefix = "__";
intermediateSignificantImagePrefix = "_";
@@ -76,6 +80,9 @@ summaryTitle = "Summary";
fiducialPlacementOptions = newArray("along the gap", "across the gap");
gapOrientationOptions = newArray("vertical", "horizontal", "unknown / varies");

minContrastEnhancementPercentage = 0;
maxContrastEnhancementPercentage = 90;

////////////////////////////////////////////////////////////////////////////////


@@ -118,9 +125,17 @@ function cleanup() {
		close(intermediateDebugImagePrefix + "*");
		close(intermediateSignificantImagePrefix + "*");
	}
	roiManager("reset");
	close("ROI Manager");
	close(summaryTitle);

	// Set the size and position of the Log window.
	selectIfExists("Log");
	Table.setLocationAndSize(logWindowPaddding, logWindowPaddding, screenWidth - logWindowPaddding * 2, screenHeight - logWindowPaddding * 2, "Log");

	// Set the size and position of the ROI manager window.
	roiManager("reset");
	run("ROI Manager...");
	selectIfExists("ROI Manager");
	Table.setLocationAndSize(screenWidth - roiManagerWindowPadding - roiManagerWindowWidth, roiManagerWindowPadding, roiManagerWindowWidth, roiManagerWindowHeight, "ROI Manager");
}


@@ -223,6 +238,10 @@ function createSetupDialog() {

  	Dialog.addRadioButtonGroup("Fiducial Placement", fiducialPlacementOptions, 1, 2, fiducialPlacementOptions[defaultFiducialPlacementOption]);
		
	Dialog.setInsets(10, 10, 0);
	Dialog.addNumber("Contrast enhancement (" + minContrastEnhancementPercentage + ".." + maxContrastEnhancementPercentage + ", default " + defaultContrastEnhancementPercentage + "):", defaultContrastEnhancementPercentage, 1, 5, "");
		
		
	Dialog.setInsets(10, 0, 0);
	Dialog.addMessage("------------------------------------------------------------------------------------------------------------------------------------------------");
	
@@ -292,7 +311,12 @@ function readSettingsFromSetupDialog() {
	fiducialsAreAlongTheGap = (Dialog.getRadioButton() == fiducialPlacementOptions[0]);
	print("fiducialsAreAlongTheGap: " + fiducialsAreAlongTheGap);
	
	// Read the saturation percentage for contrast enhancement.
	contrastEnhancementPercentage = Math.max(Math.min(Dialog.getNumber(), maxContrastEnhancementPercentage), minContrastEnhancementPercentage);
	print("contrastEnhancementPercentage: " + contrastEnhancementPercentage);
	

	// Read the debugging options.
	useBatchModeForComputations = Dialog.getCheckbox();

	// Close older versions of the intermediate images (unless they should be kept).
@@ -358,6 +382,8 @@ function finalize() {
	if (Table.columnExists("Group")) {
		Table.deleteColumn("Group");
	}	
	// Set the size and position of the Results window.
	Table.setLocationAndSize(resultsWindowPaddding, resultsWindowPaddding, screenWidth - resultsWindowPaddding * 2, screenHeight - resultsWindowPaddding * 2, "Results");
	
	/*
	Table.renameColumn("Area", "Area (" + unit + "^2)");
@@ -578,6 +604,20 @@ function findFiducials(imageIndex) {
	}
	setSelectionName(getImageNameForStage(imageIndex, "rgb_input") + " Line Across Gap");
	roiManager("Add");	
	
	
	
	// "(Thick) horizontal line" selection marking the area between the fiducials. Will not correspont to the "rotated rectangle" selection directly, but may allow for better gap threshold finding.
	if (fiducialsAreAlongTheGap) {
		makeLine((xStart + xEnd) / 2 - (xDirectionToLeftSide) * minLeeway * leewayFractionToUse, (yStart + yEnd) / 2,
		         (xStart + xEnd) / 2 + (xDirectionToLeftSide) * minLeeway * leewayFractionToUse, (yStart + yEnd) / 2,
		         distanceFromStartToEnd);
	} else {
		makeLine(xStart, (yStart + yEnd) / 2, xEnd, (yStart + yEnd) / 2, interFiducialSpaceWidth);
	}
	setSelectionName(getImageNameForStage(imageIndex, "rgb_input") + " Horizontal Line Across Gap");
	roiManager("Add");	
	
}


@@ -672,10 +712,7 @@ function prepareContrastEnhancedImage(imageIndex) {
	run("Duplicate...", "title=[" + getImageNameForStage(imageIndex, "enhanced") + "]");

	// Try to enhance the contrast of the cells while also smoothing out the noise.
	//run("Enhance Contrast...", "saturated=0 normalize");
	//run("Enhance Contrast...", "saturated=5 normalize");
	//run("Enhance Contrast...", "saturated=10 normalize");
	run("Enhance Contrast...", "saturated=20 normalize");  // High values of "saturated" (up to 20 was tested) seem to help with low-contrast images.
	run("Enhance Contrast...", "saturated=" + contrastEnhancementPercentage +" normalize");  // High values of "saturated" (up to 20 was tested) seem to help with low-contrast images.
	run("Median...", "radius=5");  // TODO: Median radius should be smaller than the cell size. (About one third of the SE size used for morphological gradient/opening seems to work.)
}

@@ -753,27 +790,33 @@ function findEmptyRegionsInAllImages(emptyRegionThreshold) {


function determineEmptyRegionThreshold() {
	thresholds = newArray();
	print("\nFinding empty region threshold in the first image, \"" + getImageNameForStage(0, "rgb_input") + "\".");	
	selectWindow(getImageNameForStage(0, "gradient"));
	run("Select None");



	// Find the threshold from all pixels of getImageNameForStage(0, "rgb_input") + " Rectangle Between Fiducials".
	// (Duplicating the rotated-rectangle selection creates a full rectangular image covering the same area.)
	RoiManager.selectByName(getImageNameForStage(0, "rgb_input") + " Rectangle Between Fiducials");	
	run("Duplicate...", "title=[" + getImageNameForStage(0, "temp_for_thresholding") + "]");
	setAutoThreshold(thresholdingMethodToUse);
	getThreshold(lowerRectangleThreshold, upperRectangleThreshold);  // Anything that is <= upperRectangleThreshold will be considered to be the gap.
	print("Threshold calculated using \"" + thresholdingMethodToUse + "\" method from all pixels of \"" + getImageNameForStage(0, "rgb_input") + " Rectangle Between Fiducials\": " + upperRectangleThreshold);
	thresholds = Array.concat(thresholds, upperRectangleThreshold);
	close(getImageNameForStage(0, "temp_for_thresholding"));
	selectWindow(getImageNameForStage(0, "gradient"));
	print("Threshold calculated using \"" + thresholdingMethodToUse + "\" method from all pixels of \"" + getImageNameForStage(0, "rgb_input") + " Rectangle Between Fiducials\": " + upperRectangleThreshold);
	
	
	
	// Find the threshold across the gap (along the "thick line" crossing the gap).
	// Careful: if the line is at an angle other than 90°, the threshold may be too high,
	// because it may happen that none of the columns align with the gap, and so each one's
	// intensity is higher than the intensity of the actual empty background.	
	
	selectWindow(getImageNameForStage(0, "gradient"));
	RoiManager.selectByName(getImageNameForStage(0, "rgb_input") + " Line Across Gap");	
print("selected image before computation of line profile: \"" + getTitle() + "\"");
print("selected ROI before computation of line profile: \"" + Roi.getName + "\"");
	profile = getProfile();
	// TODO: Maybe the statistics can indicate whether there is any gap present or not? 
	//       But careful -- to test this, use images with a large gap, with no gap at all,
@@ -782,6 +825,8 @@ function determineEmptyRegionThreshold() {
	print("Profile min: " + min + ", max: " + max + ", mean: " + mean + ", stdDev: " + stdDev);

	// Show the profile across the gap.
print("selected image before plotting of line profile: \"" + getTitle() + "\"");
print("selected ROI before plotting of line profile: \"" + Roi.getName + "\"");
	run("Plot Profile");
	rename(intermediateDebugImagePrefix + getTitle());

@@ -793,14 +838,65 @@ function determineEmptyRegionThreshold() {
			setPixel(i, j, profile[i]);
		}
	}
	
	setAutoThreshold(thresholdingMethodToUse);
	getThreshold(lowerLineThreshold, upperLineThreshold);  // Anything that is <= upperLineThreshold will be considered to be the gap.
	thresholds = Array.concat(thresholds, upperLineThreshold);
	close(getImageNameForStage(0, "temp_for_plot"));
	print("Threshold calculated using \"" + thresholdingMethodToUse + "\" method from plot along \"" + getImageNameForStage(0, "rgb_input") + " Line Across Gap\": " + upperLineThreshold);

	result = Math.min(upperLineThreshold, upperRectangleThreshold);
	print("Returning threshold = min(" + upperLineThreshold + ", " + upperRectangleThreshold + ") = " + result);  // This is only a redundant debugging output.

// TODO: BUG: Sometimes , an error may occur because no linear selection is active when calling run("Plot Profile"). It is unclear when this can happen (seems almost non-deterministic). This has been so far only observed when the horizontal line threshold computation was enabled.
//showMessage("a");
/**/
	// Find the threshold across the gap, perfectly horizontally (along the "thick line" crossing the gap horizontally).
	selectWindow(getImageNameForStage(0, "gradient"));
	RoiManager.selectByName(getImageNameForStage(0, "rgb_input") + " Horizontal Line Across Gap");	
//showMessage("b");
print("selected image before computation of horizontal line profile: \"" + getTitle() + "\"");
print("selected ROI before computation of horizontal line profile: \"" + Roi.getName + "\"");
	profile = getProfile();
	// TODO: Maybe the statistics can indicate whether there is any gap present or not? 
	//       But careful -- to test this, use images with a large gap, with no gap at all,
	//       with a thin gap, and with only a few empty spots left.
	Array.getStatistics(profile, min, max, mean, stdDev);
	print("Profile min: " + min + ", max: " + max + ", mean: " + mean + ", stdDev: " + stdDev);
//showMessage("c");

	// Show the profile across the gap.
print("selected image before plotting of horizontal line profile: \"" + getTitle() + "\"");
print("selected ROI before plotting of horizontal line profile: \"" + Roi.getName + "\"");
	run("Plot Profile");
	rename(intermediateDebugImagePrefix + getTitle() + " (horizontal)");
//showMessage("d");

	// Create a small image containing the plot's values, so that it can be thresholded with one of the built-in thresholding functions.
	plotImageHeight = 1;  // A height greater than 1 wuld be used mostly for debugging/viewing purposes. For most of the thresholding methods, the height of 1 should be enough.
	newImage(getImageNameForStage(0, "temp_for_plot"), bitDepthToImageTypeString(bitDepth()), profile.length, plotImageHeight, 1);
	for (i = 0; i < profile.length; i++) {
		for (j = 0; j < plotImageHeight; j++) {
			setPixel(i, j, profile[i]);
		}
	}
	setAutoThreshold(thresholdingMethodToUse);
	getThreshold(lowerHorizontalLineThreshold, upperHorizontalLineThreshold);  // Anything that is <= upperHorizontalLineThreshold will be considered to be the gap.
	thresholds = Array.concat(thresholds, upperHorizontalLineThreshold);
	close(getImageNameForStage(0, "temp_for_plot"));
	print("Threshold calculated using \"" + thresholdingMethodToUse + "\" method from plot along \"" + getImageNameForStage(0, "rgb_input") + " Horizontal Line Across Gap\": " + upperHorizontalLineThreshold);
/**/



	//result = Math.min(upperLineThreshold, upperRectangleThreshold);
	//print("Returning threshold = min(" + upperLineThreshold + ", " + upperRectangleThreshold + ") = " + result);  // This is only a redundant debugging output.
	
	//result = Math.min(Math.min(upperLineThreshold, upperRectangleThreshold), upperHorizontalLineThreshold);
	//print("Returning threshold = min(" + upperLineThreshold + ", " + upperRectangleThreshold + ", " + upperHorizontalLineThreshold + ") = " + result);  // This is only a redundant debugging output.
	
	Array.getStatistics(thresholds, min, max, mean, stdDev);
	result = min;
	print("Candidate thresholds:");  // This is only a redundant debugging output.
	Array.print(thresholds);
	print("Returning min threshold = " + result);

	return result;
}
@@ -947,8 +1043,6 @@ function verifyAllImagesHaveIdenticalSizesAndUnits() {


function processImages() {
	selectIfExists("Log");

	// Check if all images in imagesToAnalyze have the same size, units, and pixel size. If not, abort.
	setBatchMode(true);  // Before testing the image sizes, enter the batch mode in any case to prevent too much flickering. 
	verifyAllImagesHaveIdenticalSizesAndUnits();
@@ -1065,6 +1159,7 @@ var imagesToAnalyze = newArray();

var fiducialsAreAlongTheGap;  // True if the fiducials are placed along the gap. False if they are placed to the sides of it.
var gapOrientation;
var contrastEnhancementPercentage;

var closeIntermediateImages;
var useBatchModeForComputations;