Сообщение отредактировал erbol: 04.10.2013, 14:45:57
Пусть надо найти контуры ящика на рисунке. Ясно что выбор инструмента будет зависеть от ситуации
То есть необходимо попробовать несколько способов и выбрать из них самый эффективный.
Для начала попробуем выполнить "бинаризацию" картинки
Алгоритм
1. Загружаем картинку
2. Разбиваем на каналы
3. Пороговое преобразование
4. Склеиваем полученные после фильтрации слои
5. Отображаем полученную картинку
#include <cv.h> #include <highgui.h> #include <stdlib.h> #include <stdio.h> int main(int argc, char* argv[]) { IplImage *image=0, *gray=0, *dst=0, *dst2=0; IplImage* r=0, *g=0, *b=0; // для хранения отдельных слоёв RGB-изображения // имя картинки задаётся первым параметром char* filename = argc >= 2 ? argv[1] : "1Xz1PR_adIw.jpg"; // получаем картинку image = cvLoadImage(filename, 1); printf("[i] image: %s\n", filename); assert( image != 0 ); int minB = 0, maxB = 255; int minG = 0, maxG = 255; int minR = 0, maxR = 255; cvNamedWindow( "original", 1 ); cvCreateTrackbar("B max", "original", &maxB, 255); cvCreateTrackbar("B min", "original", &minB, 255); cvCreateTrackbar("G max", "original", &maxG, 255); cvCreateTrackbar("G min", "original", &minG, 255); cvCreateTrackbar("R max", "original", &maxR, 255); cvCreateTrackbar("R min", "original", &minR, 255); // покажем изображение cvShowImage( "original", image ); r = cvCreateImage(cvGetSize(image), IPL_DEPTH_8U, 1); g = cvCreateImage(cvGetSize(image), IPL_DEPTH_8U, 1); b = cvCreateImage(cvGetSize(image), IPL_DEPTH_8U, 1); // разбиваем на отдельные слои cvSplit(image, b, g, r, 0); // // попробуем пороговое преобразование над отдельными слоями // while (1) { IplImage* t1, *t2, *t3; // для промежуточного хранения t1 = cvCreateImage(cvGetSize(image), IPL_DEPTH_8U, 1); t2 = cvCreateImage(cvGetSize(image), IPL_DEPTH_8U, 1); t3 = cvCreateImage(cvGetSize(image), IPL_DEPTH_8U, 1); // выполняем пороговое преобразование cvThreshold(r, t1, minR, maxR, CV_THRESH_BINARY); cvThreshold(g, t2, minG, maxG, CV_THRESH_BINARY); cvThreshold(b, t3, minB, maxB, CV_THRESH_BINARY); // складываем результаты cvMerge(t3, t2, t1, 0, image); //cvMerge(b, g, r, 0, image); cvNamedWindow( "RGB cvThreshold", 1 ); cvShowImage( "RGB cvThreshold", image); cvReleaseImage(&t1); cvReleaseImage(&t2); cvReleaseImage(&t3); cvWaitKey(100); } // освобождаем ресурсы cvReleaseImage(& image); cvReleaseImage(&r); cvReleaseImage(&g); cvReleaseImage(&b); // удаляем окна cvDestroyAllWindows(); return 0; }
Некоторые замечания по поводу поиска контуров предметов на рисунке
1. Если использовать сглаживание, то обязательно cvSmooth(src, dst, CV_BILATERAL, kernel, kernel, smoothness, smoothnes)
2. Если использовать пороговую бинаризацию, то обязательно cvAdaptiveThreshold
Неожиданно для меня в качестве источника и получателя функции cvSmooth могут быть цветные картинки
Вот исходная картинка
а это картинка после сглаживания в режиме CV_BILATERAL, kernel = 15, smoothness_color = 50.0, smoothness_space = 300.0
cvSmooth(src, dst, CV_BILATERAL, kernel , kernel , smoothness_color, smoothness_space);
Видно что края объектов на рисунке не размыты, хотя удалось избавиться от ненужных подробностей на полу и на стенке полигона
Пример использования функции cvMorphologyEx (морфологические преобразования). с типом преобразования CV_MOP_CLOSE. Это преобразование позволяет избавиться от узких участков разъединяющих широкие сравнительо однородные по цвету области
Видно, что линии на стене стали менее заметными
В статье описан хороший фильтр яркости
http://habrahabr.ru/post/172651/
http://www.ipol.im/p.../2011/lmps_rpe/
http://demo.ipol.im/...E320C2C0D7B7642
Исходная картинка
После обработки фильтром яркости Retinex Poisson Equation с параметром t=10.0
Сообщение отредактировал erbol: 29.10.2013, 15:52:44
Для коррекции баланса белого применяется алгоритм нормализации гистограммы (равномерное растяжение гистограммы от минимальных до максимальных значений яркости, с «обрезанием» неинформативных краев). Для обработки яркости изображения его гистограмма сдвигается в центр.
Третий аргумент функции cvHoughLines2 целое число которое определяет метод трансформации Хафа. Их три.
Если выбрать метод CV_HOUGH_PROBABILISTIC то в результате получим для каждого отрезка прямой на рисунке пару точек, которые определяют концы отрезка
Этот метод хорошо работает в случае изображений с несколькими длинными линейными сегментами. Для коротких отрезков он делает ошибки. Точки отрезков не соответствуют линиям на картинке, они задают более короткие отрезки
Вот пример работы функции для такой картинки
Для таких параметров функции
lines = cvHoughLines2( dst, storage, CV_HOUGH_PROBABILISTIC, 1, CV_PI/180, 1, 1, 1 );
После фильтрации различных "шумов" (тени, засветки, ненужные для анализа картинки объекты на изображении) получаем бинарную картинку содержащую так называемые "краевые точки", то есть точки которые лежат на контурах предметов окружающих робота
Краевые точки (пикселы) это геометрические абстракции нижнего уровня. Из них можно получить абстракции более высокого уровня - отрезки прямых и далее анализируя отрезки можно получить абстракции высшего уровня - "стенки коридора лабиринта" и "препятствия"
Желательно чтобы методы используемые для получения отрезков прямых и далее информации о перегородках и стенках лабиринта имели высокую "устойчивость и качество"
image=cvLoadImage("vanish.png"); IplImage *gray_out = cvCreateImage(cvGetSize(image), IPL_DEPTH_8U, 1); IplImage *canny_out = cvCreateImage(cvGetSize(image), IPL_DEPTH_8U, 1); // Convert the image to grayscale cvCvtColor(image , gray_out, CV_RGB2GRAY); // Denoising cvSmooth(gray_out, gray_out, CV_GAUSSIAN, 3, 3);
// Detect edges cvCanny(gray_out, canny_out, th1, th2, 3);
// Detect lines CvMemStorage* storage = cvCreateMemStorage(0); CvSeq *lines = cvHoughLines2(canny_out, storage, CV_HOUGH_STANDARD, 1, CV_PI/180, 50);
// Hough standard transform std::vector<float> m1, c1, xvc, yvc; for (int i = 0; i < MIN(lines->total,100); i++ ) { float *line = (float*)cvGetSeqElem(lines, i); float rho = line[0]; float theta = line[1]; // Находим координаты точки пересечения перпендикуляра // из центра координат и отрезка double a = cos(theta), b = sin(theta); // х0 , у0 - координаты точки пересечения перпендикуляра и отрезка double x0 = a * rho, y0 = b * rho; // Line points CvPoint pt1, pt2; // Находим координаты точек лежащих на //растоянии 1000 пикселов от точки х0 и у0 в //обе стороны pt1.x = cvRound(x0 + 1000 * (-B)); pt1.y = cvRound(y0 + 1000 * ( a)); pt2.x = cvRound(x0 - 1000 * (-B)); pt2.y = cvRound(y0 - 1000 * ( a)); // Save angled line // Сохраняем нужные значения в вектора // Для каждой линии определяем две величины if (fabs(theta) > 0.0) { float m = -cos(theta) / sin(theta); float c = rho * (1.0 / sin(theta)); if (0.5 < fabs(m) && fabs(m) < 5.0) { m1.push_back(m); c1.push_back(c); cvLine(image, pt1, pt2, CV_RGB(255,0,0)); } } }
// Detect vanishing points //Если точка пересечения двух произвольных линий из //набора находится в окне, то сохраняем координаты //этой точки for (int k = 0; k < (int)m1.size(); k++) { for(int k1 = k+1; k1 < (int)m1.size(); k1++) { if (fabs(m1[k] - m1[k1]) > 1.0) { int xv = fabs((c1[k1] - c1[k]) / (m1[k] - m1[k1])); int yv = fabs((m1[k1] * xv) + c1[k1]); if(yv > 0 && yv < image->height && xv > 0 && xv < image->width) { xvc.push_back(xv); yvc.push_back(yv); } } } }
// Detected // Находим взвешенное значение всех точек пересеченя - //это точка лежит на линии горизонта if (!xvc.empty()) { double sum_x = 0, sum_y = 0, sum_w = 0; for (int i = 0; i < (int)xvc.size(); i++) { //http://www.realcoding.net/articles/opisanie-funktsii-c-si-c-hypot.html // sqrt(x*x + y*y); double w = exp(-hypot(xvc[i] - canny_out->width/2, yvc[i] - canny_out->height/2)); sum_x += w * xvc[i]; sum_y += w * yvc[i]; sum_w += w; } // Vanishing point CvPoint vanishing_point = cvPoint(sum_x/sum_w, sum_y/sum_w); // Show result cvCircle(image, vanishing_point, 5, cvScalar(255,0,0), 2); }
#include <cv.h> #include <highgui.h> #include <math.h> // Vanishing point detection sample based on // http://dasl.mem.drexel.edu/wiki/index.php/Vanishing_point_detection_for_corridors_and_hallways // -------------------------------------------------------------------------- // main(Number of arguments, Argument values) // Description : This is the entry point of the program. // Return value : SUCCESS:0 ERROR:-1 // -------------------------------------------------------------------------- int main(int argc, char** argv) { //IplImage* dst = cvCreateImage( cvGetSize(src), 8, 1 ); //IplImage* color_dst = cvCreateImage( cvGetSize(src), 8, 3 ); // Thresholds. Создаем переменные для бегунков int th1 = 0, th2 = 255; // Create a window cvNamedWindow("canny"); cvCreateTrackbar("th1", "canny", &th1, 255); cvCreateTrackbar("th2", "canny", &th2, 255); IplImage *image = 0; // Main loop while (1) { // Key input int key = cvWaitKey(1); if (key == 0x1b) break; image = cvLoadImage("vanish.png");; // Фильтруем шумы IplImage *gray_out = cvCreateImage(cvGetSize(image), IPL_DEPTH_8U, 1); IplImage *canny_out = cvCreateImage(cvGetSize(image), IPL_DEPTH_8U, 1); // Convert the image to grayscale cvCvtColor(image , gray_out, CV_RGB2GRAY); // Denoising cvSmooth(gray_out, gray_out, CV_GAUSSIAN, 3, 3); // Ищем границы // Detect edges cvCanny(gray_out, canny_out, th1, th2, 3); // Detect lines Ищем отрезки прямых линий CvMemStorage* storage = cvCreateMemStorage(0); CvSeq *lines = cvHoughLines2(canny_out, storage, CV_HOUGH_STANDARD, 1, CV_PI/180, 50); // Hough standard transform std::vector<float> m1, c1, xvc, yvc; for (int i = 0; i < MIN(lines->total,100); i++ ) { float *line = (float*)cvGetSeqElem(lines, i); float rho = line[0]; float theta = line[1]; // Находим координаты точки пересечения перпендикуляра // из центра координат и отрезка double a = cos(theta), b = sin(theta); // х0 , у0 - координаты точки пересечения перпендикуляра и отрезка double x0 = a * rho, y0 = b * rho; // Line points CvPoint pt1, pt2; // Находим координаты точек лежащих на //растоянии 1000 пикселов от точки х0 и у0 в //обе стороны pt1.x = cvRound(x0 + 1000 * (-b)); pt1.y = cvRound(y0 + 1000 * ( a)); pt2.x = cvRound(x0 - 1000 * (-b)); pt2.y = cvRound(y0 - 1000 * ( a)); // Save angled line // Сохраняем нужные значения в вектора // Для каждой линии определяем две величины if (fabs(theta) > 0.0) { float m = -cos(theta) / sin(theta); float c = rho * (1.0 / sin(theta)); if (0.5 < fabs(m) && fabs(m) < 5.0) { m1.push_back(m); c1.push_back(c); cvLine(image, pt1, pt2, CV_RGB(255,0,0)); } } } // Detect vanishing points //Если точка пересечения двух произвольных линий из //набора находится в окне, то сохраняем координаты //этой точки for (int k = 0; k < (int)m1.size(); k++) { for(int k1 = k+1; k1 < (int)m1.size(); k1++) { if (fabs(m1[k] - m1[k1]) > 1.0) { int xv = fabs((c1[k1] - c1[k]) / (m1[k] - m1[k1])); int yv = fabs((m1[k1] * xv) + c1[k1]); if(yv > 0 && yv < image->height && xv > 0 && xv < image->width) { xvc.push_back(xv); yvc.push_back(yv); } } } } // Detected // Находим взвешенное значение всех точек пересеченя - //это точка лежит на линии горизонта if (!xvc.empty()) { double sum_x = 0, sum_y = 0, sum_w = 0; for (int i = 0; i < (int)xvc.size(); i++) { //http://www.realcoding.net/articles/opisanie-funktsii-c-si-c-hypot.html // sqrt(x*x + y*y); double w = exp(-hypot(xvc[i] - canny_out->width/2, yvc[i] - canny_out->height/2)); sum_x += w * xvc[i]; sum_y += w * yvc[i]; sum_w += w; } // Vanishing point CvPoint vanishing_point = cvPoint(sum_x/sum_w, sum_y/sum_w); // Show result cvCircle(image, vanishing_point, 5, cvScalar(255,0,0), 2); } // Display the image cvShowImage("canny", canny_out); cvShowImage("camera", image); // Release memories cvReleaseImage(&gray_out); cvReleaseImage(&image); cvReleaseImage(&canny_out); cvReleaseMemStorage(&storage); } return 0; }
Информативная часть картинки в алгоритме "точка схода" это контур основания лабиринта по которому движется робот. Для поиска "точки схода" используются две линии контура "параллельные" оси камеры по обе стороны от нее. Заметим, что чем больше значение порогового параметра в функции cvHoughLines2 тем точнее вычисляется положение "точки схода". Поэтому значение параметра лучше динамически подстраивать под конкретную картинку полученную от робота. Выбирая его таким образом, чтобы отрезки из которых строятся прямые имели длину не менее 20 пикселов. Если это условие выполняется увеличиваем значение параметра, до тех пор пока число "точек схода" прямых полученных от функции cvHoughLines2 не станет минимальным, например меньше четырех
if ((int)xvc.size() > limit) { printf("threshold(+) %d\n", th3); printf("xvc %d\n", (int)xvc.size()); if ((int)xvc.size() > 10) { th3 = th3 + 4; } else { th3++; } } else { // (int)xvc.size() <= limit && (int)xvc.size() > 0 if ((int)xvc.size() > 0) { if (!xvc.empty()) { for (int j = 0; j < (int)xvc.size(); j++) { xvc1.push_back(xvc[j]); yvc1.push_back(yvc[j]); } } flag = false; } else { // (int)xvc.size() == 0 printf("threshold(-) %d\n", th3); printf("xvc %d\n", (int)xvc.size()); th3--; // Выходим из цикла, так как не обнаруженно прямых линий на картинке if (th3 < low_th3) flag = false; } }
Я покупал на ebay. Доставка где то 50 долларов
Вот , например
http://www.ebay.com/...=item51b582d6c1
Сообщение отредактировал erbol: 18.12.2013, 10:05:42
#include <cv.h> #include <highgui.h> #include <math.h> // Vanishing point detection sample based on // http://dasl.mem.drexel.edu/wiki/index.php/Vanishing_point_detection_for_corridors_and_hallways // -------------------------------------------------------------------------- // main(Number of arguments, Argument values) // Description : This is the entry point of the program. // Return value : SUCCESS:0 ERROR:-1 // -------------------------------------------------------------------------- // Thresholds. Создаем переменные для бегунков //IplImage *canny_out=0; int th1 = 255, th2 = 255, th3 = 20; void findTh1(IplImage* src) { bool flag = true, flag1 = true; while (flag) { IplImage *canny_out = cvCreateImage(cvGetSize(src), IPL_DEPTH_8U, 1); // Detect edges cvCanny(src, canny_out, th1, th2, 3); // Detect lines Ищем отрезки прямых линий CvMemStorage* storage = cvCreateMemStorage(0); CvSeq *lines = cvHoughLines2(canny_out, storage, CV_HOUGH_STANDARD, 1, CV_PI/180, th3); // Hough standard transform std::vector<float> m1, c1, xvc, yvc; for (int i = 0; i < MIN(lines->total,100); i++ ) { float *line = (float*)cvGetSeqElem(lines, i); float rho = line[0]; float theta = line[1]; // Находим координаты точки пересечения перпендикуляра // из центра координат и отрезка double a = cos(theta), b = sin(theta); // х0 , у0 - координаты точки пересечения перпендикуляра и отрезка double x0 = a * rho, y0 = b * rho; // Line points CvPoint pt1, pt2; // Находим координаты точек лежащих на //растоянии 1000 пикселов от точки х0 и у0 в //обе стороны pt1.x = cvRound(x0 + 1000 * (-B)); pt1.y = cvRound(y0 + 1000 * ( a)); pt2.x = cvRound(x0 - 1000 * (-B)); pt2.y = cvRound(y0 - 1000 * ( a)); // Save angled line // Сохраняем нужные значения в вектора // Для каждой линии определяем две величины if (fabs(theta) > 0.0) { float m = -cos(theta) / sin(theta); float c = rho * (1.0 / sin(theta)); if (0.4 < fabs(m) && fabs(m) < 5.0) { m1.push_back(m); c1.push_back(c); //cvLine(image, pt1, pt2, CV_RGB(255,0,0)); } } } // Detect vanishing points //Если точка пересечения двух произвольных линий из //набора находится в окне, то сохраняем координаты //этой точки for (int k = 0; k < (int)m1.size(); k++) { for(int k1 = k+1; k1 < (int)m1.size(); k1++) { if (fabs(m1[k] - m1[k1]) > 1.0) { int xv = fabs((c1[k1] - c1[k]) / (m1[k] - m1[k1])); int yv = fabs((m1[k1] * xv) + c1[k1]); if(yv > 0 && yv < src->height && xv > 0 && xv < src->width) { xvc.push_back(xv); yvc.push_back(yv); } } } } if ((int)xvc.size() == 0) { if (th1 > 5) { th1 = th1 - 5; } else { flag = false; } } else { if (th3 < 100 && (int)xvc.size() > 2) th3 = th3 + 2; else { flag = false; } } cvReleaseMemStorage(&storage); } } int main(int argc, char** argv) { // Create a window cvNamedWindow("canny"); cvCreateTrackbar("th1", "canny", &th1, 255); cvCreateTrackbar("th2", "canny", &th2, 255); IplImage *image = 0; // Main loop while (1) { // Key input int key = cvWaitKey(1); if (key == 0x1b) break; image = cvLoadImage("srcgray.png"); int width = (int) image->width; int height = (int) image->height; // Очищаем верхнюю половину рисунка, заливаем ее белым цветом // устанавливаем ROI cvSetImageROI(image, cvRect(0,0,image->width,image->height/2)); // обнулим изображение cvZero(image); // Инвертируем cvAddS(image, cvScalar(255,255,255), image); // сбрасываем ROI cvResetImageROI(image); // Фильтруем шумы IplImage *gray_out = cvCreateImage(cvGetSize(image), IPL_DEPTH_8U, 1); IplImage *canny_out = cvCreateImage(cvGetSize(image), IPL_DEPTH_8U, 1); // Convert the image to grayscale cvCvtColor(image , gray_out, CV_RGB2GRAY); // Denoising cvSmooth(gray_out, gray_out, CV_GAUSSIAN, 3, 3); findTh1(gray_out); // Ищем границы // Detect edges cvCanny(gray_out, canny_out, th1, th2, 3); // Detect lines Ищем отрезки прямых линий CvMemStorage* storage = cvCreateMemStorage(0); CvSeq *lines = cvHoughLines2(canny_out, storage, CV_HOUGH_STANDARD, 1, CV_PI/180, th3); // Hough standard transform std::vector<float> m1, c1, xvc, yvc; for (int i = 0; i < MIN(lines->total,100); i++ ) { float *line = (float*)cvGetSeqElem(lines, i); float rho = line[0]; float theta = line[1]; // Находим координаты точки пересечения перпендикуляра // из центра координат и отрезка double a = cos(theta), b = sin(theta); // х0 , у0 - координаты точки пересечения перпендикуляра и отрезка double x0 = a * rho, y0 = b * rho; // Line points CvPoint pt1, pt2; // Находим координаты точек лежащих на //растоянии 1000 пикселов от точки х0 и у0 в //обе стороны pt1.x = cvRound(x0 + 1000 * (-B)); pt1.y = cvRound(y0 + 1000 * ( a)); pt2.x = cvRound(x0 - 1000 * (-B)); pt2.y = cvRound(y0 - 1000 * ( a)); // Save angled line // Сохраняем нужные значения в вектора // Для каждой линии определяем две величины if (fabs(theta) > 0.0) { float m = -cos(theta) / sin(theta); float c = rho * (1.0 / sin(theta)); if (0.4 < fabs(m) && fabs(m) < 5.0) { m1.push_back(m); c1.push_back(c); cvLine(image, pt1, pt2, CV_RGB(255,0,0)); } } } // Detect vanishing points //Если точка пересечения двух произвольных линий из //набора находится в окне, то сохраняем координаты //этой точки for (int k = 0; k < (int)m1.size(); k++) { for(int k1 = k+1; k1 < (int)m1.size(); k1++) { if (fabs(m1[k] - m1[k1]) > 1.0) { int xv = fabs((c1[k1] - c1[k]) / (m1[k] - m1[k1])); int yv = fabs((m1[k1] * xv) + c1[k1]); if(yv > 0 && yv < image->height && xv > 0 && xv < image->width) { xvc.push_back(xv); yvc.push_back(yv); } } } } // Detected // Находим взвешенное значение всех точек пересеченя - //это точка лежит на линии горизонта if (!xvc.empty()) { double sum_x = 0, sum_y = 0, sum_w = 0; for (int i = 0; i < (int)xvc.size(); i++) { //http://www.realcoding.net/articles/opisanie-funktsii-c-si-c-hypot.html // sqrt(x*x + y*y); double w = exp(-hypot(xvc[i] - canny_out->width/2, yvc[i] - canny_out->height/2)); sum_x += w * xvc[i]; sum_y += w * yvc[i]; sum_w += w; } // Vanishing point CvPoint vanishing_point = cvPoint(sum_x/sum_w, sum_y/sum_w); // Show result cvCircle(image, vanishing_point, 5, cvScalar(255,0,0), 2); } // Display the image cvShowImage("canny", canny_out); cvShowImage("camera", image); // Release memories cvReleaseImage(&gray_out); cvReleaseImage(&image); cvReleaseImage(&canny_out); cvReleaseMemStorage(&storage); } printf("th1 %d\n", th1); printf("th3 %d\n", th3); return 0; }
Вот результаты работы обработки программой картинок
Для этой картинки программа выдала следующие оптимальные значения
для параметра th1 - 255, для th3 - 86
для параметра th1 - 255, для th3 - 100
для параметра th1 - 210, для th3 - 48
для параметра th1 - 255, для th3 - 48
пользователей: 0, неизвестных прохожих: 0, скрытых пользователей: 0
Размещение рекламы на сайте Предложения о сотрудничестве Служба поддержки пользователей
© 2011-2022 vse.kz. При любом использовании материалов Форума ссылка на vse.kz обязательна.