How to evenly place points along a segmented line?

Sample image and/or code

image1

image2

image3

getSelectionCoordinates(x, y); 				//returns array of x and y coordinates for selection (in this case a straight line has only two points and four coords)

ylength = y[0] - y[1]; 						//finds the length of the line in the y axis by taking the first y coordinate from the second
xlength = x[0] - x[1];						//finds the length in the x axis	the same way
n = 4										//tell the program you will break the line into 4 segments	
yinc = (ylength/n); 						//the increment in the y axis between each point's y coordinate
xinc = (xlength/n); 						//the increment in the x axis between each point's x coordinate
for (j=0; j<=n; j++) { 						//a for loop to plot points, repeats for the total number of segments + 1 (as 0 is counted)
	xcoord = x[0] - (xinc*j); 				//calculates the x coordinate of the point to be plotted by subtracting the increment value (multiplied by the current segment number defined by 'j') from the first x coord of the line
	ycoord = y[0] - (yinc*j); 				//calculates the y coordinate as above from the top-most point on the line
	setKeyDown("shift"); 					//required to plot multiple points on the same selection (otherwise will only plot one point and overwrite them every time it loops)
	makePoint(xcoord, ycoord); 				//makes a point selection at the x and y coords calculated above
}

Background

I am working with images of seed pods. and counting the number of seeds in each pod.

Analysis goals

I need to visually divide the seed pod into four segments of equal length so that I can count the number of seeds in each quarter of the pod.

The program works well for this when using the straight line tool. I simply use the straight line tool to draw a line along the length of the pod, then run the above program (Image1). It creates 5 dots equally spaced thus allowing me to count the number of seeds between each dot (Image2).

However, some seed pods are curved rather than straight (Image3). This makes the straight line tool ineffective for dividing the seed pod into equal segments. What I would like to do is alter the program such that it can be used on segmented lines, not just on straight lines. That way I can draw a segmented line such as the one shown in Image3 and have the program create dots to divide the segmented line into four equal segments.

Challenges

I can’t figure out how to get this to work for a segmented line. Conceptiually I think i’d have to first sum up the total length of all line segments, divide that by 4, figure out which line segment each of those points are in, and then get the program to make a point there? I’m not really sure how to do that though. Any help would be benificial. I am still new to coding.

I got the code originally from this post, and modified it slightly:

Hi,

Have you tried this: Change your line width to 60 (Edit>Options>Line Width…), trace a segmented line over the seed pod, and then use Edit>Selection>Straighten.

From the curved pod in your third image, you end up with this:

Sincerely,

Matthieu

Thanks Matthieu! While this does work it increases the analysis time for each image. Since the data set I am working with includes thousands of images, the extra time will really add up. Maybe if I could incorporate the straightening process into the program and then automatically add the points to the new image… Thanks again for your help, it gives me some more things to think about.

I wrote a macro a while ago for a facility user that needed something similar. Not sure if this is exactly what you would be looking for, but it might help you a bit.

	getPixelSize(unit, xh, yh);

	if(xh != yh){
		print("Pixel aspect ratio of the image is not 1, this macro will not work appropriately.");
		return;
	}

	Dialog.create("Draw options");

	Dialog.addNumber("Segment distance (scaled):", 25);
	Dialog.addNumber("Circle diameter (pixels):", 10);
	Dialog.addChoice("Circle color:", newArray("white", "black", "red", "blue", "green", "magenta", "yellow", "cyan", "gray"), "white");
	Dialog.show();

	dist = Dialog.getNumber();
	diam = Dialog.getNumber();
	setColor(Dialog.getChoice());
	
	run("Interpolate", "interval=" + dist/xh + " smooth");

	run("Properties... ", "name= position=1 stroke=none width=1 list");

	xs = Table.getColumn("X");
	ys = Table.getColumn("Y");

	run("Close");

	for (i = 0; i < xs.length; ++i) {
		xs[i] /= xh;
		ys[i]/= yh;
		
		fillOval(xs[i]-diam/2, ys[i]-diam/2, diam, diam);
	}

	run("Select None");

I found the interpolate function worked really well for this sort of thing. Hope this helps.

1 Like

Hi
@Tyler_Mendes
Macro that works for me for your image n ° 3.
She takes up the idea of @amccall

macro "mymacro"
{
requires("1.53g");
setBackgroundColor(0,0,0);
setOption("BlackBackground",true);
img=getImageID();
// Start batch mode
setBatchMode(true);
selectImage(img);
run("Duplicate...", "title=1");
close("\\Others");
run("Duplicate...", "title=2");
run("Flip Vertically");
run("HSB Stack");
wait(100);
run("Stack to Images");
close("Brightness");
close("Hue");
selectWindow("Saturation");
setAutoThreshold("Yen dark");
//run("Threshold...");
setOption("BlackBackground", false);
run("Convert to Mask");
run("Fill Holes");
run("Create Selection");
run("Enlarge...", "enlarge=-15");
setBackgroundColor(255, 255, 255);
run("Clear Outside");
run("Select None");
run("Skeletonize");
histogram();
n=getResult("Count",255) / 4;

run("Analyze Line Graph");
Plot.showValues();
//----------------------------
// Complete one table for the values ​​of x and another for the values ​​of y
//------------------------
x = newArray();
// loop the table rows
for ( i=0; i<nResults; i++ ) 
x=Array.concat(x,getResult("X",i));
//Array.show (x);
//---------------------------
y = newArray();
// loop the table rows
for ( i=0; i<nResults; i++ ) 
y=Array.concat(y,getResult("Y",i));
//Array.show (y);
//----------------------------------
selectImage("1");
 makeSelection("freeline", x, y);
run("Interpolate", "interval=n   adjust");
close("Results");
close("Line Graph");
close("Saturation"); 
close("Log");

// End of batch mode
setBatchMode(false);
exit("All is done !");

//-----------------------------------------
function histogram()
{
  nBins = 256;
  run("Clear Results");
  row = 0;
  getHistogram(values, counts, nBins);
  for (i=0; i<nBins; i++) {
      setResult("Value", row, values[i]);
      setResult("Count", row, counts[i]);
      row++;
   }
  updateResults();
}

Please let me know if it is suitable.
Thank you

1 Like

Thanks for the feedback everyone! I’ve found a solution that works well. I was able to write the following code:

//This macro works to measure the length of a silique using the segmented or straight line tool; it
//automatically measures the silique once your line is drawn, and also straightens the silique for ease
//of seed counting. 
macro "Seed Count Tool -Cff0D81D72C07bD23D33D43Cff0D63D83C07bD24D54Cff0D94C07bD25C000D55C07bD65Cff0Da5C07bD36C000D46D66C07bD76Cff0Db6C07bD47C000D57D77C07bD87Cff0Dc7C07bD58C000D68D88C07bD98Cff0Dd8Df8C07bD69C000D79D99C07bDa9Cff0De9C07bD7aC000D8aDaaC07bDbaCff0DdaC07bD8bC000D9bDbbC07bDcbD9cC000DacDccC07bDdcDadC000DbdC07bDcdDbeDde"{
	if (isOpen("straight.png")){
		selectWindow("straight.png");
		close();
	}
	title = getTitle();
	selectWindow(title);
	run("Straighten...", "title=[straight.png] line=70");
		ylength = 0	;					//finds the length of the line in the y axis by taking the first y coordinate from the second
		xlength = getWidth();					//finds the length in the x axis	the same way
		n = 4			;							//tell the program you will break the line into 4 segments	
		yinc = (ylength/n); 						//the increment in the y axis between each point's y coordinate
		xinc = (xlength/n); 						//the increment in the x axis between each point's x coordinate
		for (j=0; j<=n; j++) { 						//a for loop to plot points, repeats for the total number of segments + 1 (as 0 is counted)
			xcoord = xlength - (xinc*j); 				//calculates the x coordinate of the point to be plotted by subtracting the increment value (multiplied by the current segment number defined by 'j') from the first x coord of the line
			ycoord = (getHeight()/2) - (yinc*j); 				//calculates the y coordinate as above from the top-most point on the line
			setKeyDown("shift"); 					//required to plot multiple points on the same selection (otherwise will only plot one point and overwrite them every time it loops)
			makePoint(xcoord, ycoord); 				//makes a point selection at the x and y coords calculated above
		}
	
	selectWindow(title);
	run("Measure");
	selectWindow("straight.png");

	setTool("multipoint");
}
1 Like