Jump to content
Compvision.ru
Sign in to follow this  
bugmenot

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

Recommended Posts

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

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

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

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

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

post-1486-217954_thumb.jpg

Share this post


Link to post
Share on other sites

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

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

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

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

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

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

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

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

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

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

Share this post


Link to post
Share on other sites

Спасибо, Smorodov.

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

Share this post


Link to post
Share on other sites

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

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

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

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

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

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]

Share this post


Link to post
Share on other sites

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

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

Share this post


Link to post
Share on other sites

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

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

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

Share this post


Link to post
Share on other sites

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

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

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

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

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

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

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

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

Share this post


Link to post
Share on other sites

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

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

Share this post


Link to post
Share on other sites

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

Share this post


Link to post
Share on other sites

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

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

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

post-1486-12419_thumb.jpg

Share this post


Link to post
Share on other sites

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

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

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

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

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

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

Share this post


Link to post
Share on other sites

После бинаризации с параметром 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

Share this post


Link to post
Share on other sites

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

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

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

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

Share this post


Link to post
Share on other sites

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

Итак, некоторые прямоугольники найдены. Моя функция 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

Share this post


Link to post
Share on other sites

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

Share this post


Link to post
Share on other sites

Можно попробовать достать все контуры по точкам и рисовать их при помощи 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

Share this post


Link to post
Share on other sites

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

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

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

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this  

  • Recently Browsing   0 members

    No registered users viewing this page.

×