How to quantify line shape with ImageJ macro - macros

I would like to quantify the shape of a line on the wings of butterflies which can vary from quite straight to squiggly similar to the horizon in a landscape, or similar to a graph (per each x value there is only 1 y value), although overall orientation varies. My idea is to use the free hand tool to trace the line of interest and then let an ImageJ macro quantify it (automating this may be tricky because there are many line-like structures). Two traits seem useful to me;
the proportion between the length of the drawn line and the straight line between the end points.
'Dispersion' of the line such as calculated in the Directionality plugin.
Other traits such as what proportion of the line is below or under the straight line that connects the extremes may also be useful.
How can this be coded? I am building an interactive macro that prompts the measuring of various traits for an open image.
Hopefully the below (non-functional) code will convey what I am trying to do.
//line shape analysis
run("Select None");
setTool("free hand");
waitForUser("Trace the line between point A and B");
length= measure();
String command = "Directionality";
new PlugInFilterRunner(da, command, "nbins=60, start=-90, method=gradient");
get data...
//to get distance between points A and B
run("Select None");
setTool("multipoint");
waitForUser("Distances","Click on points A and B \nAfter they appear, you can click and drag them if you need to readjust.");
getSelectionCoordinates(xCoordinates, yCoordinates);
xcoordsp = xCoordinates;
ycoordsp = yCoordinates;
makeLine(xcoordsp[0], ycoordsp[0], xcoordsp[1], ycoordsp[1]);
List.setMeasurements;
StrLength = List.getValue("Length");
I have looked online for solutions but found surprisingly little about this relatively simple issue.
warm regards,
Freerk

Here is a simple solution to determine to what extent the line deviates from a straight line between pint A and B. The 'straightness' is the proportion between the two measures.
// To meausure line length to compare to length of straight line aka Euclidean distance
run("Select None");
setTool("polyline");
waitForUser("Trace the line between point V5 and V3 by clickinmg at each corner finish by double click"); // Points V5 and V3 refer to point A and B It can be adjusted
run("Measure");
getStatistics(Perim);
FLR=Perim; // FLR For forewing lenght real
// to get Euclidian distance between points A and B
run("Select None");
setTool("multipoint");
waitForUser("Distances","Click on points A and B \nAfter they appear, you can click and drag them if you need to readjust.");
getSelectionCoordinates(xCoordinates, yCoordinates);
xcoordsp = xCoordinates;
ycoordsp = yCoordinates;
makeLine(xcoordsp[0], ycoordsp[0], xcoordsp[1], ycoordsp[1]);
List.setMeasurements;
FLS = List.getValue("Length"); // FLS For forewing length straight
}
I would still be grateful for more sophistcated line parameters

Related

Using the Overlay feature in panel but showing only an averaged line

I have created an overlay of 100 curves. I thought the image looked impressive but one of my reviewers stated that I should only show the averaged line. Meaning, only show a line that is the average of the 100 curves. Is this possible using the xtline feature, or do I need to get deeper into programming code to produce the graphic? Alternatively, it would be great if I could show both (the 100 curves and the averaged curve) in the same graphic.
You don't need anything complicated here. Just calculate the mean across the panel and show it directly. Here is some technique. I am guessing that in your real example with 100 curves a legend is pointless. Note the c(L) and read the help to find what it does.
webuse grunfeld, clear
set scheme s1color
gen log_invest = log(invest)
egen mean_log_invest = mean(log_invest), by(year)
line log_invest year, legend(off) lc(gs12) c(L) || line mean_log_invest year, c(L) scheme(s1color) ytitle(something sensible)

Expand line to a polygon

I would like to expand a line to a wider polygon. Add 10 meter on both sides of the line for example.
Here is an example of what I would like
Take this line
And expand it to a wider polygon, like this
I did this manually, is there a way to do this automaticly?
Changing the KML or using a program?
Thanks
Vincent
Depending on how accurate you need this – this is not trivial.
One possible algorithm could be:
for each segment do
expand segment to rectangle with width 2r
targetShape.join(rectangle)
next
for each point do
expand point to circle with radius r
targetShape.join(circle)
next
targetShape.outerHull(precision)
Each single line in this routine is tricky and depends on your expectations.
You could leave out the circles and instead make the rectangles longer, but this wil not work on sharp turns.
All of this involves ugly calculations to figure orthogonal lines etc.
You could try it in an graphic tool, gimp or inkskape :-)

Location based segmentation of objects in an image (in Matlab)

I've been working on an image segmentation problem and can't seem to get a good idea for my most recent problem.
This is what I have at the moment:
Click here for image. (This is only a generic example.)
Is there a robust algorithm that can automatically discard the right square as not belonging to the group of the other four squares (that I know should always be stacked more or less on top of each other) ?
It can sometimes be the case, that one of the stacked boxes is not found, so there's a gap or that the bogus box is on the left side.
Your input is greatly appreciated.
If you have a way of producing BW images like your example:
s = regionprops(BW, 'centroid');
centroids = cat(1, s.Centroid);
xpos = centroids(:,1); should then be the x-positions of the boxes.
From here you have multiple ways to go, depending on whether you always have just one separated box and one set of grouped boxes or not. For the "one bogus box far away, rest closely grouped" case (away from Matlab, so this is unchecked) you could even do something as simple as:
d = abs(xpos-median(xpos));
bogusbox = centroids(d==max(d),:);
imshow(BW);
hold on;
plot(bogusbox(1),bogusbox(2),'r*');
Making something that's robust for your actual use case which I am assuming doesn't consist of neat boxes is another matter; as suggested in comments, you need some idea of how close together the positioning of your good boxes is, and how separate the bogus box(es) will be.
For example, you could use other regionprops measurements such as 'BoundingBox' or 'Extrema' and define some sort of measurement of how much the boxes overlap in x relative to each other, then group using that (this could be made to work even if you have multiple stacks in an image).

Get length of irregular object in a BW or RGB picture and draw it into picture for control

I face a well known problem which I am not able to solve.
I have the picture of a root (http://cl.ly/image/2W3C0a3X0a3Y). From this picture, I would like to know the length of the longest root (1st problem), the portion of the big roots and the small roots in % (say the diameter as an orientation which is the second problem). It is important that I can distinguish between fine and big roots since this is more or less the aim of the study (portion of them compared between different species). The last thing, I would like to draw a line along the measured longest root to check if everything was measured right.
For the length of the longest root, I tried to use regionprops(), which is not optimal since this assumes an oval as basic shape if I got this right.
However, the things I could really need support with are in fact:
How can I get the length of the longest root (start point should be the place where the longest root leaves the main root with the biggest diameter)?
Is it possible to distinguish between fine and big roots and can I get the portion of them? (the coin, the round object in the image is the reference)
Can I draw properties like length and diameter into the picture?
I found out how to draw the centriods of ovals and stuff, but I just dont understand how to do it with the proposed values.
I hope this is no double post and this question does not exists like this somewhere else, if yes, I am sorry for that.
I would like to thank the people on this forum, you do a great job and everybody with a question can be lucky to have you here.
Thank you for the help,
Phillip
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
EDIT
I followed the proposed solution, the code until now is as followed:
clc
clear all
close all
img=imread('root_test.jpg');
labTransformation = makecform('srgb2lab');
labI = applycform(img,labTransformation);
%seperate l,a,b
l = labI(:,:,1);
a = labI(:,:,2);
b = labI(:,:,3);
level = graythresh(l);
bw = im2bw(l);
bw = ~bw;
bw = bwareaopen(bw, 200);
se = strel('disk', 5);
bw2=imdilate(bw, se);
bw2 = imfill(bw2, 'holes');
bw3 =bwmorph(bw2, 'thin', 5);
bw3=double(bw3);
I4 = bwmorph(bw3, 'skel', 200);
%se = strel('disk', 10);%this step is for better visibility of the line
%bw4=imdilate(I4, se);
D = bwdist(I4);
This leads my in the skeleton picture - which is a great progress, thank you for that!!!
I am a little bit out at the point where I have to calculate the distances. How can I explain MatLab that it has to calculate the distance from all the small roots to the main root (how to define this?)? For this I have to work with the diameters first, right?
Could you maybe give the one or the other hint more how to accomplish the distance/length problem?
Thank you for the great help till here!
Phillip
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
EDIT2
Ok, I managed to separate the single root parts. This is not what your edit proposed, but at least something. I have the summed length of all roots as well - not too bad. But even with the (I assume) super easy step by step explanation I have never seen such a tree. I stopped at the point at which I have to select an invisible point - the rest is too advanced for me.
I dont want to waste more of the time and I am very thankful for the help you gave me already. But I suppose I am too MatLab-stupid to accomplish this :)
Thanks! Keep going like this, it is really helpful.
Phillip
For a pre-starting point, I don't see the need for a resolution of 3439x2439 for that image, it doesn't seem to add anything important to the problem, so I simply worked with a resized version of 800x567 (although there should be (nearly) no problem to apply this answer to the larger version). Also, you mention regionprops but I didn't see any description of how you got your binary image, so let us start from the beginning.
I considered your image in the LAB colorspace, then binarized the L channel by Otsu, applied a dilation on this result considering the foreground as black (the same could be done by applying an erosion instead), and finally removed small components. The L channel gives a better representation of your image than the more direct luma formula, leading to an easier segmentation. The dilation (or erosion) is done to join minor features, since there are quite a bit of ramifications that appear to be irrelevant. This produced the following image:
At this point we could attempt using the distance transform combined with grey tone anchored skeleton (see Soille's book on morphology, and/or "Order Independent Homotopic Thinning for Binary and Grey Tone Anchored Skeletons" by Ranwez and Soille). But, since the later is not easily available I will consider something simpler here. If we perform hole filling in the image above followed by thinning and pruning, we get a rough sketch of the connections between the many roots. The following image shows the result of this step composed with the original image (and dilated for better visualization):
As expected, the thinned image takes "shortcuts" due to the hole filling. But, if such step wasn't performed, then we would end up with cycles in this image -- something I want to avoid here. Nevertheless, it seems to provide a decent approximation to the size of the actual roots.
Now we need to calculate the sizes of the branches (or roots). The first thing is deciding where the main root is. This can be done by using the above binary image before the dilation and considering the distance transform, but this will not be done here -- my interest is only showing the feasibility of calculating those lengths. Supposing you know where your main root is, we need to find a path from a given root to it, and then the size of this path is the size of this root. Observe that if we eliminate the branch points from the thinned image, we get a nice set of connected components:
Assuming each end point is the end of a root, then the size of a root is the shortest path to the main root, and the path is composed by a set of connected components in the just shown image. Now you can find the largest one, the second largest, and all the others that can be calculated by this process.
EDIT:
In order to make the last step clear, first let us label all the branches found (open the image in a new tab for better visualization):
Now, the "digital" length of each branch is simply the amount of pixels in the component. You can later translate this value to a "real-world" length by considering the object added to the image. Note that at this point there is no need to depend on Image Processing algorithms at all, we can construct a tree from this representation and work there. The tree is built in the following manner: 1) find the branching point in the skeleton that belongs to the main root (this is the "invisible point" between the labels 15, 16, and 17 in the above image); 2) create an edge from that point to each branch connected to it; 3) assign a weight to the edge according to the amount of pixels needed to travel till the start of the other branch; 4) repeat with the new starting branches. For instance, at the initial point, it takes 0 pixels to reach the beginning of the branches 15, 16, and 17. Then, to reach from the beginning of the branch 15 till its end, it takes the size (number of pixels) of the branch 15. At this point we have nothing else to visit in this path, so we create a leaf node. The same process is repeated for all the other branches. For instance, here is the complete tree for this labeling (the dual representation of the following tree is much more space-efficient):
Now you find the largest weighted path -- which corresponds to the size of the largest root -- and so on.

Morphological separation of two connected boundaries

I've got a question regarding the following scenario.
As I post-process an image, I gained a contour, which is unfortunately twice connected as you can see at the bottom line. To make it obvious what I want is just the outter line.
Therefore I zoomed in and marked the line, i want of the large image.
What I want from this selection is only the outter part, which I've marked as green in the next picture. Sorry for my bad drawing skills. ;)
I am using MatLab with the IPT. So I also tried to make out with bwmorph and the hbreak option, but it threw an error.
How do I solve that problem?
If you were successful could you please tell me a bit more about it?
Thank you in advance!
Sincerely
It seems your input image is a bit different than the one you posted, since I couldn't directly collect the branch points (there were too many of them). So, to start handling your problem I considering a thinning followed by branch point detection. I also dilate them and remove from the thinned image, this guarantees that in fact there is no connection (4 or 8) between the different segments in the initial image.
f = im2bw(imread('http://i.imgur.com/yeFyF.png'), 0);
g = bwmorph(f, 'thin', 'Inf');
h = g & ~bwmorph(bwmorph(g, 'branchpoints'), 'dilate');
Since h holds disconnected segments, the following operation collects the end points of all the segments:
u = bwmorph(h, 'endpoints');
Now to actually solve your problem I did some quick analysis on what you want to discard. Consider two distinct segments, a and b, in h. We say a and b overlap if the end points of one is contained in the other. By contained I simply mean if the starting x point of one is smaller or equal to the other, and the ending x point is greater or equal too. In your case, the "mountain" overlaps with the segment that you wish to remove. To determine each of them you remove, consider their area. But, since these are segments, area is a meaningless term. To handle that, I connected the end points of a segment, and used as area simply the interior points. As you can clearly notice, the area of the overlapped segment at bottom is very small, so we say it is basically a line and discard it while keeping the "mountain" segment. To do this step the image u is of fundamental importance, since with it you have a clear indication of where to start and stop tracking a contour. If you used the image has is , you would have trouble determining where to start and stop collecting the points of a contour (i.e., the raster order would give you incorrect overlapping indication).
To reconstruct the segment as a single one (currently you have three of them), consider the points you discarded from g in h, and use those that doesn't belong to the now removed bottom segment.
I'd also use bwmorph
%# find the branch point
branchImg = bwmorph(img,'branchpoints');
%# grow the pixel to 3x3
branchImg = imdilate(branchImg,ones(3));
%# hide the branch point
noBranchImg = img & ~branchImg;
%# label the three lines
lblImg = bwlabel(noBranchImg);
%# in the original image, mask label #3
%# note that it may not always be #3 that you want to mask
finalImg = img;
finalImg(lblImg==3) = 0;
%# show the result
imshow(finalImg)