Обнаружение линий на изображении

Я новичок в обработке изображений, и я пытался обнаружить вертикальные линии, используя этот код:

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);

Это был мой образ-

введите здесь описание изображения

А вот что у меня получилось после выполнения операций- введите здесь описание изображения

Итак, мой вопрос: почему я получаю этот вывод? Есть 10 вертикальных линий, если вертикальные двойные связи считаются как 2 отдельные вертикальные линии. А что, если я хочу получить горизонтальные, вертикальные, 45 и -45 все линии, как я могу использовать все 4 маски, чтобы получить один выход?


person Noober    schedule 19.06.2015    source источник
comment
а можно онлайн ссылку на это изображение? не могу найти в нете   -  person roni    schedule 19.06.2015
comment
Я отредактировал свой вопрос. Я понял свою ошибку, но теперь снова получаю неожиданный результат.   -  person Noober    schedule 19.06.2015
comment
@roni Вы можете сохранить это изображение отсюда, так как сейчас у меня нет ссылки на изображение.   -  person Noober    schedule 19.06.2015
comment
Проблема в вашем фильтре, конечно. Ознакомьтесь с некоторыми базовыми фильтрами, чтобы получить представление: en.wikipedia.org/wiki/Prewitt_operator en.wikipedia.org/wiki/Sobel_operator   -  person Ander Biguri    schedule 19.06.2015


Ответы (3)


У меня есть одно простое предложение: определить градиент и определить ориентацию граничной точки. Имейте в виду, что ориентация осуществляется в направлении, которое перпендикулярно краю. Следовательно, если вы хотите найти вертикальные линии, направление, перпендикулярное вертикальной линии, является горизонтальным, что составляет либо 180 градусов, либо -180 градусов по отношению к декартовой плоскости. Таким образом, для каждой ориентации обнаруженных краевых точек, если ориентация равна -180 градусов или 180 градусов, установите выход этого местоположения как true, иначе false. Чтобы определить ориентацию градиента, используйте imgradient из набора инструментов для обработки изображений. . Я предполагаю, что это доступно, поскольку вы использовали как imread, так и im2bw, и они оба являются частью этого набора инструментов:

im = imread('http://i.stack.imgur.com/bdNOt.png');
tol = 5;
[~,ang] = imgradient(im);
out = (ang >= 180 - tol | ang <= -180 + tol);
imshow(out);

В коде используется переменная с именем tol для определения допуска в углах, которые вы хотите обнаружить для учета шума или краев, которые выглядят вертикальными, но при вычислении угла может показаться, что это не так. По сути, мы ищем любые точки, углы которых находятся в пределах 180 градусов или -180 градусов.

Вот что мы получаем:

введите здесь описание изображения

В качестве средства постобработки вы можете использовать bwareaopen для фильтрации пиксельные области, площадь которых падает ниже определенного значения. Воспользовавшись тем, что вертикальные линии имеют большую площадь, чем другие пиксели, вы можете сделать что-то вроде этого:

out_filter = bwareaopen(out, 50);

Мы получили:

введите здесь описание изображения


Теперь, если вы хотите обнаружить горизонтальные линии, вы должны найти ориентацию градиента, которая составляет либо -90, либо 90 градусов. Это имеет смысл, потому что те линии, которые горизонтальны, направление, перпендикулярное горизонтальной линии, действительно является вертикальным, и это либо -90, либо 90 градусов. Если вам нужны наклонные линии, если вам нужна линия с наклоном влево, ищите углы либо 45 градусов, либо -135 градусов, а линия с наклоном вправо - либо -45 градусов, либо 135 градусов. Я позволю вам понять, почему эти углы действительно представляют такие линии.

На изображении, которое вы предоставили, нет горизонтальных линий, поэтому я просто поищу наклонные линии:

Линии наклона влево

Примечание. Мне пришлось увеличить допуск из-за ошибок квантования.

im = imread('http://i.stack.imgur.com/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);

введите здесь описание изображения

Линии наклона вправо:

Также пришлось увеличить допуск и здесь:

im = imread('http://i.stack.imgur.com/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);

введите здесь описание изображения

person rayryeng    schedule 19.06.2015
comment
Следуйте за этим с out=bwareaopen(out,50); (возможно, придется немного адаптировать порог), чтобы удалить строки, связанные с буквами. - person Jonas; 19.06.2015
comment
@Jonas - Ты читаешь мои мысли... Я на самом деле только что сделал это, лол. Еще очень забавно, что мы с вами выбрали точно такие же пороги. - person rayryeng; 19.06.2015
comment
отличный ответ сам не мог понять! - person roni; 20.06.2015
comment
Большое спасибо. Для горизонтальных или других линий, какой должен быть диапазон углов? - person Noober; 21.06.2015
comment
Горизонтальный будет либо 0, либо 90. Наклонный должен быть +/- 45 и +/- 135. Я обновлю свой пост, чтобы показать вам больше строк с тем же изображением. - person rayryeng; 21.06.2015
comment
@Noober - обновлю через пару часов. К сожалению, нужно уйти прямо сейчас! - person rayryeng; 21.06.2015
comment
@rayryeng Не проблема. Большое спасибо. - person Noober; 21.06.2015
comment
@Noober - Готово. Взглянуть. - person rayryeng; 22.06.2015
comment
@rayryeng Большое спасибо. - person Noober; 22.06.2015

Другой подход заключается в использовании того факта, что все линии, изображающие связи, имеют одинаковое соотношение сторон и площадь. после фильтрации изображения, оставив в нем только связи, мы можем посмотреть на ориентацию или на список индексов, которые их составляют, чтобы определить, являются ли они вертикальными или что-то еще. Все это можно сделать с помощью 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)

введите здесь описание изображения

Просмотр гистограммы показывает, где нужно обрезать линии, линии имеют меньшую площадь, чем буквы, и они должны иметь примерно одинаковую площадь. Поэтому я обрезал области ниже 1000 пикселей:

idx=find(areas<1000);
angs=round([stat(idx).Orientation]);

теперь вы можете использовать angs и idx, чтобы получить нужный тип строки. Например, давайте просто построим 30-градусные линии:

d2=zeros(size(d));
d2(vertcat(stat(idx(angs==30)).PixelIdxList))=1;
imagesc(d2)

введите здесь описание изображения

Обратите внимание, что в то время, когда я начал отвечать на этот вопрос, изображение, которое я сделал, было файлом benzene.png. Теперь я понимаю, что вы предоставили изображение, отличное от исходного, так что линии, изображающие связи, не являются отдельными, а у вас есть «кольца». Я посмотрю позже, смогу ли я решить и это, если вы хотите.

ИЗМЕНИТЬ:

Чтобы найти соответствующую линию для нового изображения, где у вас есть кольца, единственное отличие линий состоит в том, что они прямые, а не изогнутые. Поэтому я прибегаю к любимому преобразованию Хафа, чтобы подобрать их:

image=imread('http://i.stack.imgur.com/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);

Получим углы обнаруженных линий:

angs=round([lines.theta]);

вы увидите, что здесь angs будет генерировать значения 0, -60 или 60 градусов.

скажем, вы хотите построить только те, которые равны 0 градусов:

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

введите здесь описание изображения

person bla    schedule 19.06.2015
comment
Спасибо большое. А как вы решили резать только за меньше 1000? - person Noober; 21.06.2015
comment
Также как я могу использовать angles и idx? Не могли бы вы привести пример. - person Noober; 21.06.2015
comment
Большое спасибо. На самом деле да, мне нужно разобраться с кольцами. - person Noober; 23.06.2015
comment
Я добавил версию, которая также отвечает на кольца - person bla; 24.06.2015
comment
Большое спасибо. Ответ, который я принял, достаточно хорош, но я надеялся найти альтернативные методы для получения того же результата. Еще раз спасибо, это действительно помогло. - person Noober; 24.06.2015

Я все еще в процессе. Но до сих пор у меня есть это. Я не использовал ваш фильтр, а другой.

Я использовал первое изображение, которое вы предоставили. Фильтры описаны здесь: 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);

Результат, который я получил, таков: Как видите, результат еще не завершен. Я могу предложить вам попробовать две вещи: использовать оператор морфологической эрозии для удаления мелких элементов. Для этого также можно использовать подключенные компоненты.

output

А пока попробуйте сделать то, что я предложил. Если я получу ответ, я обновлю его.

person roni    schedule 19.06.2015
comment
Вместо морфологических операций есть более простой способ обнаружения этих прямых (после фильтрации). Просто берите их один за другим (например, по imlabel) и вычисляйте ширину каждой высоты. Если он постоянный, то это прямая линия, иначе это какой-то шум, которого вы хотите избежать/ - person Ander Biguri; 19.06.2015
comment
Я больше думал о преобразованиях Хафа. Но я не мог применить его - person roni; 19.06.2015