bugmenot 0 Жалоба Опубликовано July 12, 2012 Здравствуйте. Я junior в программировании. Застряла на распознавании, но не могу так просто признать поражение. Помогите немного пожалуйста. Надо распознать и измерить доски на большой полноцветной фотографии. Некоторые доски сливаются в один объект и их надо разделить или измерить приблизительно. Также сложно полностью отделить доски от фона, так как тени на досках и окружающий фон имеют одинаковый цвет и освещенность. Сегментировать тоже не получилось cvPyrMeanShiftFiltering вылетает, cvPyrSegmentation не подходит - цвета слишком близки, и лишь немного лучше cvWatershed. Разные фильтры на этом этапе только ухудшают результат. Здесь приводили несколько интересных библиотек, но они все на си, а я пишу только на питоне. Картинка черно-белая уже после предварительной обработки. Цветная (оригинал), к сожалению, не приаттачилась. Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
Smorodov 579 Жалоба Опубликовано July 12, 2012 Здравствуйте. Я junior в программировании. Застряла на распознавании, но не могу так просто признать поражение. Помогите немного пожалуйста. Надо распознать и измерить доски на большой полноцветной фотографии. Некоторые доски сливаются в один объект и их надо разделить или измерить приблизительно. Также сложно полностью отделить доски от фона, так как тени на досках и окружающий фон имеют одинаковый цвет и освещенность. Сегментировать тоже не получилось cvPyrMeanShiftFiltering вылетает, cvPyrSegmentation не подходит - цвета слишком близки, и лишь немного лучше cvWatershed. Разные фильтры на этом этапе только ухудшают результат. Здесь приводили несколько интересных библиотек, но они все на си, а я пишу только на питоне. Картинка черно-белая уже после предварительной обработки. Цветная (оригинал), к сожалению, не приаттачилась. Если только количество, то можно сделать так: 0. бинаризировать (cvThreshold) 1. посчитать количество ненулевых пикселей (см. cvCountNonZero). 2. поделить полученное значение на среднее количество пикселей в сечении доски. Если сегментация, то надо подумать. Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
bugmenot 0 Жалоба Опубликовано July 12, 2012 Спасибо, Smorodov. Нужно измерить ширину и высоту каждой доски. То черно-белое изображение вырезано маской после бинаризации. Качественно его сегментировать сложно, даже после обработки. Пробовала заливку cvFloodFill, тени всеравно не захватывает или заливает вместе с фоном. Пробовала преобразование Хафа для линий cvHoughLines2, угол 1.57 радиан . Получилось слишком много разбросанных линий и непонятно что с ними можно сделать. Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
Smorodov 579 Жалоба Опубликовано July 12, 2012 Да, структура не очень-то прямоугольная. Мы глазами и мозгом разделяем доски с учетем текстуры, а это задачка не на один вечер. Иногда, в сегментации текста, применяют классификаторы (типа каскадов Хаара, или нейронные сети, и т.п.) обученные на промежутки между букв. Может здесь что то подобное попробовать? (по времени затратно, и результат не гарантирован) Можно еще структурными элементами попробовать помучить эту картинку, у меня вот что вышло: #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] Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
bugmenot 0 Жалоба Опубликовано July 12, 2012 Структурные элементы пробовала, они здесь только мешают. erode dilate без них получше выглядит, но не все доски разделяет. В любом случае стопроцентного распознавания быть и не может. А как сегментировать эти отдельные части, и как потом их перебрать по одному? Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
mrgloom 242 Жалоба Опубликовано July 13, 2012 хмм проблема в том, что доски не всегда прямоугольные, может попробовать какой то более мягкий критерий для прямоугольников? п.с. перенесите тему в отдельную тему. Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
Smorodov 579 Жалоба Опубликовано July 13, 2012 Некоторые доски и глазом-то не разделить (выглядит как сплошной брус с тонкой трещиной). Нужно какую-нибудь дополнительную информацию использовать, например, постоянную толщину досок. Прямоугольник-то из нее можно сделать, но сильно-ли это поможет? Метод можно использовать такой: 1. Структурными элементами получить сплошую область. 2. получить профиль. (сканировать линией по вертикали и фиксировать высоту области в каждой x-координате) 3. аналогичным образом (по одной линии) отмасштабировать исходное изображение. (берем вертикальную линию пикселей (cvSampleLine, или через ROI),cvResize до максимальной высоты области). Что получится не знаю, может хоть слои досок разделить поможет. Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
mrgloom 242 Жалоба Опубликовано July 13, 2012 что если взять бинаризованную картинку как была выше в посте №4 и начать вписывать внутрь только горизонтально-вертикально ортогональные прямоугольники с примерным ограничением на размер? хотя тут всё таки остается дилема типа один большой или 2 маленьких слипшихся прямоугольника, но такие вещи можно отфильтровать статистически. Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
oma 0 Жалоба Опубликовано July 13, 2012 Интересно было бы все-таки на цветную картинку (оригинал) поглядеть. Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
bugmenot 0 Жалоба Опубликовано July 14, 2012 2. получить профиль. (сканировать линией по вертикали и фиксировать высоту области в каждой x-координате) Мама-мия, можно где-нибудь глянуть как это в коде должно выглядеть? В OpenCV есть такая функция? Интересно было бы все-таки на цветную картинку (оригинал) поглядеть. Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
Smorodov 579 Жалоба Опубликовано July 14, 2012 Например cvSampleLine выдаст массив яркостей точек лежащих на отрезке (хотя здесь можно просто циклом точки считывать). Бежим в цикле по точкам, нашли ненулевую - записали, бежим дальше, нашли нулевую - записали. Маловероятно что этот метод сильно поможет. Есть еще одно предложение. Чтобы получить более пригодную для сегментации картинку, я думаю можно делать два снимка на каждую координату: с высокой точки и с низкой. справа и слева это позволит исключить взаимное перекрытие и уменьшить эффект теней. Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
bugmenot 0 Жалоба Опубликовано July 14, 2012 После бинаризации с параметром CV_THRESH_OTSU и Erode-Dilate получаются более-менее разделенные доски. cvThreshold(pallet, pallet, 1, 255, CV_THRESH_OTSU) cvErode(pallet, pallet, None, 13) cvDilate(pallet, pallet, None, 10) Может будет проще как-то проанализировать контура? Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
Smorodov 579 Жалоба Опубликовано July 14, 2012 Теперь, думается надо искать углы (GoodFeaturesToTrack, или просто по шаблону (задать 8 штук, 4 наружних и 4 внутренних угла) http://opencv.itseez.com/doc/tutorials/imgproc/histograms/template_matching/template_matching.html ). После этого находить прямоугольные треугольники. По ним восстанавливать прямоугольники. Перед этой процедурой может быть полезно убрать все четырехугольные контуры (которые и так хорошо распознались). Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
bugmenot 0 Жалоба Опубликовано July 15, 2012 Спасибо всем, вы мне здорово помогли. Итак, некоторые прямоугольники найдены. Моя функция 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 контуры отлично заливаются. А закрасить найденные прямоугольники у меня не вышло, никак не пойму что передать в полигон. Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
Smorodov 579 Жалоба Опубликовано July 15, 2012 Можно попробовать достать все контуры по точкам и рисовать их при помощи fillPoly. Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
bugmenot 0 Жалоба Опубликовано July 16, 2012 Можно попробовать достать все контуры по точкам и рисовать их при помощи 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. Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
Smorodov 579 Жалоба Опубликовано July 16, 2012 Я думал может искать углы прямоугольников на картинке (по шаблонам, см. картинку ниже, нарисовал как смог )? А затем восстановить по ним прямоугольники (3 точки можно выделить практически у всех, а четвертая легко восстанавливается). Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах