Reputation: 1626
I am new to image processing and I was trying to detect vertical lines using this code-
image=imread('benzene.jpg');
BW = im2bw(image);
w1=[-1 2 -1 ; -1 2 -1 ; -1 2 -1];
g=(imfilter(double(BW),w1));
g=abs(g);
T=max(g(:));
g=g>=T;
imshow(g);
This was my image-
And this is what I got after performming the operations-
So my question is why am I getting this output?There are 10 vertical lines if vertical double bonds are counted as 2 distinct vertical lines.Also what if I want to get horizontal,vertical,45 and -45 all the lines,how can I use all the 4 masks to get one single output?
Upvotes: 9
Views: 9746
Reputation: 104464
One simple suggestion I have is to detect the gradient and determine the orientation of an edge point. Bear in mind that the orientation is in the direction that is perpendicular to the edge. Therefore, if you want to find vertical lines, the direction that is perpendicular to a vertical line is horizontal, which is either 180 degrees or -180 degrees with respect to the Cartesian plane. As such, for each orientation of the edge points that are detected, if the orientation is either -180 degrees or 180 degrees, then set the output of this location to be true
, else false
. To detect the gradient orientations, use imgradient
from the image processing toolbox for that. I'm assuming this is available as you have used both imread
and im2bw
and they are both part of that toolbox:
im = imread('https://i.sstatic.net/bdNOt.png');
tol = 5;
[~,ang] = imgradient(im);
out = (ang >= 180 - tol | ang <= -180 + tol);
imshow(out);
The code uses a variable called tol
to define a tolerance in the angles you want to detect to account for noise or edges that look vertical but when the angle is computed, it may not appear to be. Basically, we are looking for any points whose angles are within 180 degrees or -180 degrees.
This is what we get:
As a means of post-processing, you could use bwareaopen
to filter out pixel regions whose areas fall below a certain amount. Taking advantage of the fact that the vertical lines have a larger area than the other pixels, you could do something like this:
out_filter = bwareaopen(out, 50);
We get:
Now if you want to detect horizontal lines, you should find gradient orientations that are either -90 or 90 degrees. This makes sense because those lines that are horizontal, the direction perpendicular to a horizontal line is indeed vertical, and that's either -90 or 90 degrees. If you want slanted lines, if you want a left leaning line, look for angles of either 45 degrees or -135 degrees and a right leaning line, either -45 degrees or 135 degrees. I'll let you work out why these angles are indeed representative of those kinds of lines.
You don't have any horizontal lines in the image you provided, so I'll just look for slanted lines:
Note: I had to increase the tolerance due to quantization errors.
im = imread('https://i.sstatic.net/bdNOt.png');
tol = 20;
[~,ang] = imgradient(im);
out = (ang >= 45 - tol & ang <= 45 + tol) | (ang >= -135 - tol & ang <= -135 + tol);
out_filter = bwareaopen(out, 50);
imshow(out_filter);
Also had to increase the tolerance here as well:
im = imread('https://i.sstatic.net/bdNOt.png');
tol = 20;
[~,ang] = imgradient(im);
out = (ang >= 135 - tol & ang <= 135 + tol) | (ang >= -45 - tol & ang <= -45 + tol);
out_filter = bwareaopen(out, 50);
imshow(out_filter);
Upvotes: 14
Reputation: 26069
A different approach is to use the fact that all the lines that depict bonds have the same aspect ratio and area. after filtering the image leaving it with only the bonds, we can look at the orientation or at the list of indices that compose them to detect if they are vertical or whatnot. All this can be done using regionprops
.
image=rgb2gray(imread('benzene.png'));
d=abs(255-image); % inverse the image
d=im2bw(d);
stat=regionprops(d,'Area', 'Orientation','PixelIdxList');
areas=[stat.Area];
hist(areas)
Inspecting the histogram shows where to cut for the lines, the lines have smaller areas than the letters, and they should have approximately the same area. So I cut for areas below 1000 pixels:
idx=find(areas<1000);
angs=round([stat(idx).Orientation]);
now you can use the angs
and idx
to get which ever type of line you want. For example lets just plot the 30 deg lines:
d2=zeros(size(d));
d2(vertcat(stat(idx(angs==30)).PixelIdxList))=1;
imagesc(d2)
Note that at the time I started answering this question the image I took was the benzene.png file. Now I realize that you have provided a different image than the original one, such that the lines that depict bonds are not separate, rather you have "rings". I'll see later if I can address that as well if you want me to.
EDIT:
To find the relevant line for the new image, where you have rings, the only difference the lines have is that, well, they are straight "lines" and not curved. So I resort to the beloved Hough transform to pick them up:
image=imread('https://i.sstatic.net/bdNOt.png');
d=abs(1-image); % inverse the image
BW=im2bw(d);
BW = bwmorph(BW,'skel',1);
[H, T, R] = hough(BW,'Theta',-90:10:80);
P = houghpeaks(H, 100,'NHoodSize',[3 3],'threshold',1);
lines = houghlines(BW, T, R, P, 'FillGap',5, 'MinLength', 35);
Let's obtain the angles of the detected lines:
angs=round([lines.theta]);
you'll see that here angs
will generate values of 0,-60 or 60 degrees.
say you want to plot only those that are 0 degrees:
p1=vertcat(lines(angs==0).point1);
p2=vertcat(lines(angs==0).point2);
imshow(BW, 'InitialMag',200, 'Border','tight'), hold on
for k = 1:size(p1,1)
line([p1(k,1) p2(k,1)],[p1(k,2) p2(k,2)], 'LineWidth',4,...
'Color',[1 0 0]); hold on
end
hold off
Upvotes: 7
Reputation: 1453
I am still in the process of doing it. But till now I have got this. I have not used your filter but rather a different one.
I used the first image which you supplied. The filters are described here : image_filters.
image=imread('benzene.png');
BW = im2bw(image);
w1=(1/3)*[1 0 -1;1 0 -1;1 0 -1];
g=(imfilter(double(BW),w1));
g(g<1)=0;
imshow(g);
The output which I got is this : As you can see the result is not yet complete. I can suggest to you to try two things : use morphological erosion operator to remove the small elements. You can also use connected components to do so.
In the meantime try to do what I suggested. If I get the answer I will update it.
Upvotes: 2