Перейти к содержимому
Compvision.ru
bugmenot

Сегментация изображения паллеты с досками.

Recommended Posts

Здравствуйте.

Я junior в программировании. Застряла на распознавании, но не могу так просто признать поражение. Помогите немного пожалуйста.

Надо распознать и измерить доски на большой полноцветной фотографии. Некоторые доски сливаются в один объект и их надо разделить или измерить приблизительно. Также сложно полностью отделить доски от фона, так как тени на досках и окружающий фон имеют одинаковый цвет и освещенность.

Сегментировать тоже не получилось cvPyrMeanShiftFiltering вылетает, cvPyrSegmentation не подходит - цвета слишком близки, и лишь немного лучше cvWatershed. Разные фильтры на этом этапе только ухудшают результат. Здесь приводили несколько интересных библиотек, но они все на си, а я пишу только на питоне.

Картинка черно-белая уже после предварительной обработки. Цветная (оригинал), к сожалению, не приаттачилась.

post-1486-217954_thumb.jpg

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Здравствуйте.

Я junior в программировании. Застряла на распознавании, но не могу так просто признать поражение. Помогите немного пожалуйста.

Надо распознать и измерить доски на большой полноцветной фотографии. Некоторые доски сливаются в один объект и их надо разделить или измерить приблизительно. Также сложно полностью отделить доски от фона, так как тени на досках и окружающий фон имеют одинаковый цвет и освещенность.

Сегментировать тоже не получилось cvPyrMeanShiftFiltering вылетает, cvPyrSegmentation не подходит - цвета слишком близки, и лишь немного лучше cvWatershed. Разные фильтры на этом этапе только ухудшают результат. Здесь приводили несколько интересных библиотек, но они все на си, а я пишу только на питоне.

Картинка черно-белая уже после предварительной обработки. Цветная (оригинал), к сожалению, не приаттачилась.

Если только количество, то можно сделать так:

0. бинаризировать (cvThreshold)

1. посчитать количество ненулевых пикселей (см. cvCountNonZero).

2. поделить полученное значение на среднее количество пикселей в сечении доски.

Если сегментация, то надо подумать.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Спасибо, Smorodov.

Нужно измерить ширину и высоту каждой доски. То черно-белое изображение вырезано маской после бинаризации. Качественно его сегментировать сложно, даже после обработки. Пробовала заливку cvFloodFill, тени всеравно не захватывает или заливает вместе с фоном. Пробовала преобразование Хафа для линий cvHoughLines2, угол 1.57 радиан . Получилось слишком много разбросанных линий и непонятно что с ними можно сделать.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Да, структура не очень-то прямоугольная.

Мы глазами и мозгом разделяем доски с учетем текстуры, а это задачка не на один вечер.

Иногда, в сегментации текста, применяют классификаторы (типа каскадов Хаара, или нейронные сети, и т.п.) обученные на промежутки между букв.

Может здесь что то подобное попробовать? (по времени затратно, и результат не гарантирован)

Можно еще структурными элементами попробовать помучить эту картинку, у меня вот что вышло:

post-1-0-70246400-1342124614_thumb.png


#include <iostream>
#include <vector>
#include <stdio.h>
#include "opencv2/core/core.hpp"
#include "opencv2/core/gpumat.hpp"
#include "opencv2/core/opengl_interop.hpp"
#include "opencv2/gpu/gpu.hpp"
#include "opencv2/ml/ml.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/contrib/contrib.hpp"
#include "opencv2/video/tracking.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/nonfree/nonfree.hpp"
#include "fstream"
#include "iostream"
using namespace std;
using namespace cv;

//-----------------------------------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------------------------------
int main( int argc, char** argv )
{
int width;
int height;

Mat Img=imread("C:\\ImagesForTest\\doski.jpg",0);
Mat img;
cv::resize(Img,img,Size(Img.cols/4,Img.rows/4));

Mat Edges;
cv::Canny(img,Edges,200,200,3,true);
namedWindow("Edges");
imshow("Edges",Edges);

Mat rect_1 = getStructuringElement(CV_SHAPE_CROSS, Size(3,3));
erode(img, img, rect_1,Point(),3);

Mat rect_2 = getStructuringElement(CV_SHAPE_RECT, Size(3,3));
dilate(img,img,rect_2,Point(),1);
cv::threshold(img,img,150,255,0);

rect_1 = getStructuringElement(CV_SHAPE_RECT, Size(5,1));
erode(img, img, rect_12,Point(),1);

namedWindow("Image");
imshow("Image",img);

waitKey(0);

return 0;
}
[/code]

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Структурные элементы пробовала, они здесь только мешают. erode dilate без них получше выглядит, но не все доски разделяет. В любом случае стопроцентного распознавания быть и не может.

А как сегментировать эти отдельные части, и как потом их перебрать по одному?

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

хмм проблема в том, что доски не всегда прямоугольные, может попробовать какой то более мягкий критерий для прямоугольников?

п.с. перенесите тему в отдельную тему.

post-701-0-13783300-1342160508_thumb.png

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Некоторые доски и глазом-то не разделить (выглядит как сплошной брус с тонкой трещиной).

Нужно какую-нибудь дополнительную информацию использовать, например, постоянную толщину досок.

Прямоугольник-то из нее можно сделать, но сильно-ли это поможет?

Метод можно использовать такой:

1. Структурными элементами получить сплошую область.

2. получить профиль. (сканировать линией по вертикали и фиксировать высоту области в каждой x-координате)

3. аналогичным образом (по одной линии) отмасштабировать исходное изображение. (берем вертикальную линию пикселей (cvSampleLine, или через ROI),cvResize до максимальной высоты области).

Что получится не знаю, может хоть слои досок разделить поможет.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

что если взять бинаризованную картинку как была выше в посте №4

и начать вписывать внутрь только горизонтально-вертикально ортогональные прямоугольники с примерным ограничением на размер? хотя тут всё таки остается дилема типа один большой или 2 маленьких слипшихся прямоугольника, но такие вещи можно отфильтровать статистически.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Интересно было бы все-таки на цветную картинку (оригинал) поглядеть.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

2. получить профиль. (сканировать линией по вертикали и фиксировать высоту области в каждой x-координате)

Мама-мия, можно где-нибудь глянуть как это в коде должно выглядеть? В OpenCV есть такая функция?

Интересно было бы все-таки на цветную картинку (оригинал) поглядеть.

post-1486-12419_thumb.jpg

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Например cvSampleLine выдаст массив яркостей точек лежащих на отрезке (хотя здесь можно просто циклом точки считывать).

Бежим в цикле по точкам, нашли ненулевую - записали, бежим дальше, нашли нулевую - записали.

Маловероятно что этот метод сильно поможет.

Есть еще одно предложение.

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

справа и слева это позволит исключить взаимное перекрытие и уменьшить эффект теней.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

После бинаризации с параметром CV_THRESH_OTSU и Erode-Dilate получаются более-менее разделенные доски.

cvThreshold(pallet, pallet, 1, 255, CV_THRESH_OTSU)

cvErode(pallet, pallet, None, 13)

cvDilate(pallet, pallet, None, 10)

Может будет проще как-то проанализировать контура?

post-1486-8730_thumb.jpg

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Теперь, думается надо искать углы (GoodFeaturesToTrack, или просто по шаблону (задать 8 штук, 4 наружних и 4 внутренних угла) http://opencv.itseez.com/doc/tutorials/imgproc/histograms/template_matching/template_matching.html ).

После этого находить прямоугольные треугольники.

По ним восстанавливать прямоугольники.

Перед этой процедурой может быть полезно убрать все четырехугольные контуры (которые и так хорошо распознались).

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Спасибо всем, вы мне здорово помогли.

Итак, некоторые прямоугольники найдены. Моя функция FindFiltContours находит и сортирует контура. Каждый подходящий контур вписывает в

прямоугольник MinAreaRect, после чего контур удаляет. Код еще немного кривоват.


def FindFiltContours(img):
storage = cvCreateMemStorage(0)
contour = cvCreateSeq( 0, sizeof_CvSeq, sizeof_CvPoint, storage )
contour = CvSeq_CvPoint.cast( contour )
rects = []

scanner = cvStartFindContours( img, storage )
while contour != None :
contour = cvFindNextContour( scanner )
if (contour != None) :
if (cvBoundingRect(contour).height < 70) and (cvBoundingRect(contour).width < 270) :
rects.append(cvMinAreaRect2(contour))
cvSubstituteContour( scanner, None)
contours = cvEndFindContours(scanner)

cvZero( img )
cvDrawContours(img, contours, cvScalarAll(255), cvScalarAll(255), 2, -1)

return img, rects
[/php]

Возникает следующий вопрос, теперь уже найденные прямоугольники нужно закрасить в черний цвет или перенести слипшиеся на новое изображение для дальнейшей обработки. Сложность в том, что полученные контуры не заливаются, хоть в cvDrawContours и указана отрицательная толщина линий. Странно, ведь полученные с помощью cvFindContours контуры отлично заливаются.

А закрасить найденные прямоугольники у меня не вышло, никак не пойму что передать в полигон.

post-1486-150299_thumb.jpg

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Можно попробовать достать все контуры по точкам и рисовать их при помощи fillPoly.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Можно попробовать достать все контуры по точкам и рисовать их при помощи fillPoly.

У меня в питоне рисование полигонов и полилиний не работает, похоже на баг.

Контуры без разрывов можно залить cvDrawContours если указать отрицательную толщину линии.

Прямоугольники нарисованы поверх контуров только для наглядности, как нарисовать объект cvMinAreaRect уже пролетало на этом форуме.

Я нарисовала так:


for rect in rects:
pt = cvBoxPoints(rect)
cvLine(image, (cvRound(pt[0].x), cvRound(pt[0].y)), (cvRound(pt[1].x), cvRound(pt[1].y)), cvScalarAll(255))
cvLine(image, (cvRound(pt[1].x), cvRound(pt[1].y)), (cvRound(pt[2].x), cvRound(pt[2].y)), cvScalarAll(255))
cvLine(image, (cvRound(pt[2].x), cvRound(pt[2].y)), (cvRound(pt[3].x), cvRound(pt[3].y)), cvScalarAll(255))
cvLine(image, (cvRound(pt[3].x), cvRound(pt[3].y)), (cvRound(pt[0].x), cvRound(pt[0].y)), cvScalarAll(255))
[/php]

Основной код программы пока довольно примитивен:

[php]
cvThreshold(image, image, 1, 255, CV_THRESH_OTSU)

cvErode(image, image, None, 13)
cvDilate(image, image, None, 10)
image, rects = FindFiltContours(image, rects)

cvErode(image, image, None, 14)
cvDilate(image, image, None, 12)
image, rects = FindFiltContours(image, rects)
cvDilate(image, image, None, 5)

В первой строке бинаризация изображения из первого поста.

Функция FindFiltContours - двумя постами выше. Двойное присваивание - особенность питона. Мне так писать удобнее если функция возвращает несколько переменных.

Последний cvDilate добавлен просто для равновесия, чтобы полностью восстановить все что покусано cvErode.

Залитые контуры - готовая маска. Возможно стоит попробовать ею вырезать часть необработанного изображения и натравить что-то типа cvCanny.

post-1486-105425_thumb.jpg

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Я думал может искать углы прямоугольников на картинке (по шаблонам, см. картинку ниже, нарисовал как смог :) )?

post-1-0-34937500-1342476234_thumb.png

А затем восстановить по ним прямоугольники (3 точки можно выделить практически у всех, а четвертая легко восстанавливается).

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Создайте учётную запись или войдите для комментирования

Вы должны быть пользователем, чтобы оставить комментарий

Создать учётную запись

Зарегистрируйтесь для создания учётной записи. Это просто!

Зарегистрировать учётную запись

Войти

Уже зарегистрированы? Войдите здесь.

Войти сейчас


  • Сейчас на странице   0 пользователей

    Нет пользователей, просматривающих эту страницу

×