Detect the width of a valley in an array: Analyzing line profiles

Dear helpers,

I’m not quite sure if this is the right place to ask this, but since I want to use FiJi for analyzing an image I start here.

As stated in the thread topic, I want to locate the position of an valley within a line selection profile of an image.

You can clearly see a dark shadow in the OCT picture (optical coherence tomography). I want to know the approximate location of the “shadow” area in order to crop the picture to the bounds of this area.

My approach was to extract a profile of an horizontal line and then analyze the profile.

run("Median...", "radius=10"); // reduce noise
makeLine(0, 0.40*getHeight(), getWidth(), 0.40*getHeight()) /
profile = getProfile();
Plot.create("Line Profile", "Pixel", "Grey-Value", profile)
maxLocs = Array.findMaxima(profile, 10);
Array.show(maxLocs);

But I have trouble to detect the right maxima (or minima for that matter) to get the positions.

Do you have any suggestions how I might be able to detect my desired area? I have to do this on a couple of hundreds of pictures, hence the approach with a macro code.

Any help would be appreciated.
If you have any questions, don’t hesitate to ask :slight_smile:

Best

TMC

btw. the image shows a bovine enamel/dentin block with an artificial lesion, which I want to analyze further

Hi @TMC!

You can detect a long valley using some simple logic: it is actually a valley (below some threshold), and it’s the longest one in the profile:

thres=55;

run("Duplicate...", "title=temp");
run("Median...", "radius=10"); // reduce noise
makeLine(0, 0.40*getHeight(), getWidth(), 0.40*getHeight());
profile = getProfile();
close();

//---------just for visualization----------
	makeLine(0, 0.40*getHeight(), getWidth(), 0.40*getHeight());
	Overlay.remove;
	Roi.setStrokeColor("green");
	Overlay.addSelection;
	Overlay.show;
//-----------------------------------------

longest_run=0;
start=-1;
longest_start=-1;
longest_end=-1;
last_down=false;
for (i = 0; i < profile.length; i++) {
	down=(profile[i]<thres);
	if(down){
		if(!last_down){
			start=i;
			}		
		} else {
			if(last_down){
					if(i-start>longest_run){
						longest_run=i-start;
						longest_start=start;
						longest_end=i;
						}
				}
			}

	last_down=down;
	}


makeLine(longest_start, 0.40*getHeight(), longest_end, 0.40*getHeight());

//---------just for visualization----------
	Roi.setStrokeColor("red");
	Overlay.addSelection;
	Overlay.show;
	run("Select None");
	
	Plot.create("Line Profile", "Pixel", "Grey-Value", profile);
	Plot.setColor("red");
	Plot.drawLine(longest_start,thres, longest_end, thres);
	Plot.setColor("black");
	Plot.show();
//--------------------------------------------

Depending on your set of images, the tricky part might be choosing reasonable values for the height (plus, maybe width) of the line where you take the profile, and the actual value of the threshold.

Cheers,
Nico

2 Likes

Hey Nico ,

thank you very much for your quick reply! Awesome!

Yesterday night a had a very similar idea. I know that in every image / data set the valley is almost every time in the same area. So I started using the Array.findMinima macro function and I checked if two of the found minima correspond to my area of interst.

This works as well :slight_smile:

But your solution is more robust I’d say. I’m going to test both approaches for various data sets.

Many many thanks!

Thresholds are always tricky with OCT Images, but since we new this from the start we tried to minimize the risk of having different reflectivity values in every data set.

Best TMC

1 Like