23. OpenCV шаг за шагом. Поиск объекта по цвету. Цветовое пространство HSV


1. OpenCV шаг за шагом. Введение
2. Установка
3. Hello World
4. Загрузка картинки.
5. Вывод видео
6. Ползунок
7. Захват видео с камеры
8. Запись видео
9. События от мышки
10. Обработка изображения — сглаживание
11. Обработка изображения — изменение размеров
12. ROI — интересующая область изображения
13. Типы данных OpenCV
14. Матрица
15. Сохранение данных в XML
16. Генерация случайных чисел
17. Обработка изображения — морфологические преобразования
18. Обработка изображения — морфологические преобразования 2
19. Обработка изображения — заливка части изображения
20. Обработка изображения — альфа-смешивание
21. Обработка изображения — пороговое преобразование
22. Поиск объекта по цвету — RGB
23. Поиск объекта по цвету. Цветовое пространство HSV.

Успели соскучиться? Всё суета-суета 🙂 А ещё и Новый Год приближается 🙂
Как понятно из названия, продолжим рассмотрение выделения объектов по цвету. И в конце, в качестве бонуса, рассмотрим пример детектирования кожи 🙂

На прошлом шаге было рассмотрено выделение объекта по его RGB-цвету, но использовать модель HSV намного удобнее, т.к. координаты этой цветовой модели выбраны с учетом человеческого восприятия.

HSV (англ. Hue, Saturation, Value — тон, насыщенность, значение) — цветовая модель, в которой координатами цвета являются:
* Hue — цветовой тон, (например, красный, зелёный или сине-голубой). Варьируется в пределах 0—360°, однако иногда приводится к диапазону 0—100 или 0—1.
* Saturation — насыщенность. Варьируется в пределах 0—100 или 0—1. Чем больше этот параметр, тем «чище» цвет, поэтому этот параметр иногда называют чистотой цвета. А чем ближе этот параметр к нулю, тем ближе цвет к нейтральному серому.
* Value (значение цвета) или Brightness — яркость. Также задаётся в пределах 0—100 и 0—1.

Модель HSV была создана Элви Реем Смитом, одним из основателей Pixar, в 1978 году. Она является нелинейным преобразованием модели RGB.

Эта модель очень удобна для поиска на изображении объектов по цвету (и яркости). Например, пятно от лазерной указки, цветовые маркеры или просто объекты с выделяющимся цветом.
Преобразовать изображение из RGB в HSV очень просто:

// создаём изображения
IplImage* hsv = cvCreateImage( cvGetSize(src), 8, 3 );
IplImage* h_plane = cvCreateImage( cvGetSize(src), 8, 1 );
IplImage* s_plane = cvCreateImage( cvGetSize(src), 8, 1 );
IplImage* v_plane = cvCreateImage( cvGetSize(src), 8, 1 );
//  конвертируем в HSV 
cvCvtColor( src, hsv, CV_BGR2HSV );
// разбиваем на каналы
cvCvtPixToPlane( hsv, h_plane, s_plane, v_plane, 0 );

Т.е. сначала просто создаются 4 изображения: одно для хранения изображения в формате HSV и три для последующего разделения изображения на отдельные каналы H, S и V.

Ниже приводится код полезной утилиты, которая может быть полезна для определения минимальных и максимальных значений каналов HSV для выделения объекта на изображении.
Программа преобразует изображение в формат HSV и затем используя ползунки и функцию cvInRangeS() формирует в дополнительных оконах «H range» бинарное изображение, которое получается из тех пикселей канала, значение которых больше Hmin и меньше Hmax.
Далее для всех трёх получившихся пороговых изображений выполняется операция логического И (т.е. в результирующем окне «hsv and» белыми будут только те пиксели, которые есть у всех трёх пороговых изображений).

//
// конвертирует изображение в HSV
// и позволяет подобрать параметры
// Hmin, Hmax, Smin, Smax, Vmin, Vmax
// для выделения нужного объекта
//
// robocraft.ru
//
#include <cv.h>
#include <highgui.h>
#include <stdlib.h>
#include <stdio.h>

IplImage* image = 0;
IplImage* dst = 0;

// для хранения каналов HSV
IplImage* hsv = 0;
IplImage* h_plane = 0;
IplImage* s_plane = 0;
IplImage* v_plane = 0;
// для хранения каналов HSV после преобразования
IplImage* h_range = 0;
IplImage* s_range = 0;
IplImage* v_range = 0;
// для хранения суммарной картинки
IplImage* hsv_and = 0;

int Hmin = 0;
int Hmax = 256;

int Smin = 0;
int Smax = 256;

int Vmin = 0;
int Vmax = 256;

int HSVmax = 256;

//
// функции-обработчики ползунков
//
void myTrackbarHmin(int pos) {
	Hmin = pos;
	cvInRangeS(h_plane, cvScalar(Hmin), cvScalar(Hmax), h_range);
}

void myTrackbarHmax(int pos) {
	Hmax = pos;
	cvInRangeS(h_plane, cvScalar(Hmin), cvScalar(Hmax), h_range);
}

void myTrackbarSmin(int pos) {
	Smin = pos;
	cvInRangeS(s_plane, cvScalar(Smin), cvScalar(Smax), s_range);
}

void myTrackbarSmax(int pos) {
	Smax = pos;
	cvInRangeS(s_plane, cvScalar(Smin), cvScalar(Smax), s_range);
}

void myTrackbarVmin(int pos) {
	Vmin = pos;
	cvInRangeS(v_plane, cvScalar(Vmin), cvScalar(Vmax), v_range);
}

void myTrackbarVmax(int pos) {
	Vmax = pos;
	cvInRangeS(v_plane, cvScalar(Vmin), cvScalar(Vmax), v_range);
}

int main(int argc, char* argv[])
{
	// имя картинки задаётся первым параметром
	char* filename = argc == 2 ? argv[1] : "Image0.jpg";
	// получаем картинку
	image = cvLoadImage(filename,1);

	printf("[i] image: %s\n", filename);
	assert( image != 0 );

	// создаём картинки
	hsv = cvCreateImage( cvGetSize(image), IPL_DEPTH_8U, 3 );
	h_plane = cvCreateImage( cvGetSize(image), IPL_DEPTH_8U, 1 );
	s_plane = cvCreateImage( cvGetSize(image), IPL_DEPTH_8U, 1 );
	v_plane = cvCreateImage( cvGetSize(image), IPL_DEPTH_8U, 1 );
	h_range = cvCreateImage( cvGetSize(image), IPL_DEPTH_8U, 1 );
	s_range = cvCreateImage( cvGetSize(image), IPL_DEPTH_8U, 1 );
	v_range = cvCreateImage( cvGetSize(image), IPL_DEPTH_8U, 1 );
	hsv_and = cvCreateImage( cvGetSize(image), IPL_DEPTH_8U, 1 );
	//  конвертируем в HSV 
	cvCvtColor( image, hsv, CV_BGR2HSV ); 
	// разбиваем на отельные каналы
	cvCvtPixToPlane( hsv, h_plane, s_plane, v_plane, 0 );

	//
	// определяем минимальное и максимальное значение
	// у каналов HSV
	double framemin=0;
	double framemax=0;

	cvMinMaxLoc(h_plane, &framemin, &framemax);
	printf("[H] %f x %f\n", framemin, framemax );
	Hmin = framemin;
	Hmax = framemax;
	cvMinMaxLoc(s_plane, &framemin, &framemax);
	printf("[S] %f x %f\n", framemin, framemax );
	Smin = framemin;
	Smax = framemax;
	cvMinMaxLoc(v_plane, &framemin, &framemax);
	printf("[V] %f x %f\n", framemin, framemax );
	Vmin = framemin;
	Vmax = framemax;

	// окна для отображения картинки
	cvNamedWindow("original",CV_WINDOW_AUTOSIZE);
	cvNamedWindow("H",CV_WINDOW_AUTOSIZE);
	cvNamedWindow("S",CV_WINDOW_AUTOSIZE);
	cvNamedWindow("V",CV_WINDOW_AUTOSIZE);
	cvNamedWindow("H range",CV_WINDOW_AUTOSIZE);
	cvNamedWindow("S range",CV_WINDOW_AUTOSIZE);
	cvNamedWindow("V range",CV_WINDOW_AUTOSIZE);
	cvNamedWindow("hsv and",CV_WINDOW_AUTOSIZE);

	cvCreateTrackbar("Hmin", "H range", &Hmin, HSVmax, myTrackbarHmin);
	cvCreateTrackbar("Hmax", "H range", &Hmax, HSVmax, myTrackbarHmax);
	cvCreateTrackbar("Smin", "S range", &Smin, HSVmax, myTrackbarSmin);
	cvCreateTrackbar("Smax", "S range", &Smax, HSVmax, myTrackbarSmax);
	cvCreateTrackbar("Vmin", "V range", &Vmin, HSVmax, myTrackbarVmin);
	cvCreateTrackbar("Vmax", "V range", &Vmax, HSVmax, myTrackbarVmax);

	//
	// разместим окна по рабочему столу
	//
	if(image->width <1920/4 && image->height<1080/2){
		cvMoveWindow("original", 0, 0);
		cvMoveWindow("H", image->width+10, 0);
		cvMoveWindow("S", (image->width+10)*2, 0);
		cvMoveWindow("V", (image->width+10)*3, 0);
		cvMoveWindow("hsv and", 0, image->height+30);
		cvMoveWindow("H range", image->width+10, image->height+30);
		cvMoveWindow("S range", (image->width+10)*2, image->height+30);
		cvMoveWindow("V range", (image->width+10)*3, image->height+30);
	}

	while(true){

		// показываем картинку
		cvShowImage("original",image);

		cvShowImage( "H", h_plane );
		cvShowImage( "S", s_plane );
		cvShowImage( "V", v_plane );

		cvShowImage( "H range", h_range );
		cvShowImage( "S range", s_range );
		cvShowImage( "V range", v_range );

		// складываем 
		cvAnd(h_range, s_range, hsv_and);
		cvAnd(hsv_and, v_range, hsv_and);

		cvShowImage( "hsv and", hsv_and );

		char c = cvWaitKey(33);
		if (c == 27) { // если нажата ESC - выходим
			break;
		}
	}
	printf("\n[i] Results:\n" );
	printf("[H] %d x %d\n", Hmin, Hmax );
	printf("[S] %d x %d\n", Smin, Smax );
	printf("[V] %d x %d\n", Vmin, Vmax );

	// освобождаем ресурсы
	cvReleaseImage(&image);
	cvReleaseImage(&hsv);
	cvReleaseImage(&h_plane);
	cvReleaseImage(&s_plane);
	cvReleaseImage(&v_plane);
	cvReleaseImage(&h_range);
	cvReleaseImage(&s_range);
	cvReleaseImage(&v_range);
	cvReleaseImage(&hsv_and);
	// удаляем окна
	cvDestroyAllWindows();
	return 0;
}

скачать иcходник (23-hsv.cpp)

примеры работы:

,а вот пример поиска знаменитой улыбки Чеширского кота:

и, наконец, пример выделения пятна от лазерной указки:

Большинство функций знакомы по предыдущему шагу — осталось рассмотреть только функцию:

CVAPI(void)  cvCvtColor( const CvArr* src, CvArr* dst, int code );

— конвертирует изображение из одного цветового пространства в другое.

src — исходное изображение
dst — получаемое изображение

code — параметр, указывающий тип конвертации:

#define  CV_BGR2BGRA    0
#define  CV_RGB2RGBA    CV_BGR2BGRA

#define  CV_BGRA2BGR    1
#define  CV_RGBA2RGB    CV_BGRA2BGR

#define  CV_BGR2RGBA    2
#define  CV_RGB2BGRA    CV_BGR2RGBA

#define  CV_RGBA2BGR    3
#define  CV_BGRA2RGB    CV_RGBA2BGR

#define  CV_BGR2RGB     4
#define  CV_RGB2BGR     CV_BGR2RGB

#define  CV_BGRA2RGBA   5
#define  CV_RGBA2BGRA   CV_BGRA2RGBA

#define  CV_BGR2GRAY    6
#define  CV_RGB2GRAY    7
#define  CV_GRAY2BGR    8
#define  CV_GRAY2RGB    CV_GRAY2BGR
#define  CV_GRAY2BGRA   9
#define  CV_GRAY2RGBA   CV_GRAY2BGRA
#define  CV_BGRA2GRAY   10
#define  CV_RGBA2GRAY   11

#define  CV_BGR2BGR565  12
#define  CV_RGB2BGR565  13
#define  CV_BGR5652BGR  14
#define  CV_BGR5652RGB  15
#define  CV_BGRA2BGR565 16
#define  CV_RGBA2BGR565 17
#define  CV_BGR5652BGRA 18
#define  CV_BGR5652RGBA 19

#define  CV_GRAY2BGR565 20
#define  CV_BGR5652GRAY 21

#define  CV_BGR2BGR555  22
#define  CV_RGB2BGR555  23
#define  CV_BGR5552BGR  24
#define  CV_BGR5552RGB  25
#define  CV_BGRA2BGR555 26
#define  CV_RGBA2BGR555 27
#define  CV_BGR5552BGRA 28
#define  CV_BGR5552RGBA 29

#define  CV_GRAY2BGR555 30
#define  CV_BGR5552GRAY 31

#define  CV_BGR2XYZ     32
#define  CV_RGB2XYZ     33
#define  CV_XYZ2BGR     34
#define  CV_XYZ2RGB     35

#define  CV_BGR2YCrCb   36
#define  CV_RGB2YCrCb   37
#define  CV_YCrCb2BGR   38
#define  CV_YCrCb2RGB   39

#define  CV_BGR2HSV     40
#define  CV_RGB2HSV     41

#define  CV_BGR2Lab     44
#define  CV_RGB2Lab     45

#define  CV_BayerBG2BGR 46
#define  CV_BayerGB2BGR 47
#define  CV_BayerRG2BGR 48
#define  CV_BayerGR2BGR 49

#define  CV_BayerBG2RGB CV_BayerRG2BGR
#define  CV_BayerGB2RGB CV_BayerGR2BGR
#define  CV_BayerRG2RGB CV_BayerBG2BGR
#define  CV_BayerGR2RGB CV_BayerGB2BGR

#define  CV_BGR2Luv     50
#define  CV_RGB2Luv     51
#define  CV_BGR2HLS     52
#define  CV_RGB2HLS     53

#define  CV_HSV2BGR     54
#define  CV_HSV2RGB     55

#define  CV_Lab2BGR     56
#define  CV_Lab2RGB     57
#define  CV_Luv2BGR     58
#define  CV_Luv2RGB     59
#define  CV_HLS2BGR     60
#define  CV_HLS2RGB     61

#define  CV_COLORCVT_MAX  100

Замечательно! Теперь используя более привычные параметры можно выделять различные объекты на изображении.
Наглядный пример — это детектирование кожи человека на фотографиях 🙂
Например, для детектированя наготы 😉
См. статью: Популярное изложение алгоритма распознавания наготы на цифровых фотографиях

в OpenCV уже есть класс,
CvAdaptiveSkinDetector

в 2.0 и 2.1 он объявлен в cvaux.hpp,
а а 2.2 в modules\contrib\include\opencv2\contrib\contrib.hpp

см. пример

samples\c\adaptiveskindetector.cpp

реализация класса находится в:

OpenCV-2.2.0\modules\contrib\src\adaptiveskindetector.cpp

Если вы его откроете, то обнаружите, что детектирование кожи проводится в HSV-пространстве с параметрами:
H 3-33
V 15-250

//
// пример использования CvAdaptiveSkinDetector
//
// robocraft.ru
//

#include <cv.h>
#include <highgui.h>
#include <cvaux.h> // для CvAdaptiveSkinDetector

#include <stdlib.h>
#include <stdio.h>

int main(int argc, char* argv[])
{
	IplImage* image=0, *dst=0, *mask=0;

	// имя картинки задаётся первым параметром
	char* filename = argc >= 2 ? argv[1] : "Image0.jpg";
	// получаем картинку
	image = cvLoadImage(filename, 1);

	printf("[i] image: %s\n", filename);
	assert( image != 0 );

	// покажем изображение
	cvNamedWindow( "original");
	cvShowImage( "original", image );

	// картинка для хранения изображения
	dst = cvCloneImage(image);

	// детектор кожи
	CvAdaptiveSkinDetector filter(1, CvAdaptiveSkinDetector::MORPHING_METHOD_ERODE); // MORPHING_METHOD_ERODE_DILATE

	// картинка для хранения результата (маски)
	mask = cvCreateImage( cvGetSize(image), IPL_DEPTH_8U, 1);

	// обработка
	filter.process(image, mask);

	// покажем маску
	cvNamedWindow( "mask");
	cvShowImage( "mask", mask);

	// пробегаемся по всем пикселям изображения
	// и увеличиваем у пикселей картинки, где установлена маска
	// зелёную компоненту до максимума
	for( int y=0; yheight; y++ ) {
		uchar* ptr = (uchar*) (dst->imageData + y * dst->widthStep); // изображение
		uchar* ptrM = (uchar*) (mask->imageData + y * mask->widthStep); // маска
		for( int x=0; xwidth; x++ ) {
			if(ptrM[x]){ // маска установлена
				// 3 канала 
				//ptr[3*x] = ; // B
				ptr[3*x+1] = 255; // G
				//ptr[3*x+2] = ; // R
			}
		}
	}

	// покажем картинку
	cvNamedWindow( "dst");
	cvShowImage( "dst", dst );

	// ждём нажатия клавиши
	cvWaitKey(0);

	// освобождаем ресурсы
	cvReleaseImage(& image);
	cvReleaseImage(&dst);
	cvReleaseImage(&mask);

	// удаляем окна
	cvDestroyAllWindows();
	return 0;
}

скачать иcходник (23-CvAdaptiveSkinDetector.cpp)
результат:

UPD 2012-11-17
Для получения координат полученного объекта, нужно, всего-лишь, найти центр масс фигуры. Для этого, обходим все пиксели картинки и для ненулевых пикселей складываем координаты и подсчитываем их количество.
Пример:

	//
	// находим центр масс
	//

	int Xc = 0;
	int Yc = 0;
	int counter = 0; // счётчик числа белых пикселей

	// пробегаемся по пикселям изображения
	for(y=0; yheight; y++)
	{
		uchar* ptr = (uchar*) (result->imageData + y * result->widthStep);
		for(x=0; xwidth; x++)
		{
			if( ptr[x]>0 )
			{
				Xc += x;
				Yc += y;
				counter++; 
			}
		}
	}
	
	if(counter!=0)
	{
		center.x = float(Xc)/counter;
		center.y = float(Yc)/counter;
	}

далее: 24. Работа с камерой через библиотеку videoInput.

Ссылки
http://ru.wikipedia.org/wiki/Цветовая_модель
http://ru.wikipedia.org/wiki/HSV_(цветовая_модель)
Популярное изложение алгоритма распознавания наготы на цифровых фотографиях

С Наступающим!


24 комментария на «“23. OpenCV шаг за шагом. Поиск объекта по цвету. Цветовое пространство HSV”»

  1. Доброго времени суток!
    Попробовал приспособить данный код под захват изображения с веб-камеры и столкнулся со следующей проблемой: в окнах X_range и hsv_and при запуске из под VS 2010 серо-черные вертикальные полоски, при запуске.ехе просто черный экран и белая полоска, смахивающая на шум. Хотелось бы узнать, это настолько сильное влияние оказывают шумы на видео или это все же технические проблемы?
    Код прилагаю, спасибо заранее ) pastebin.com/gxV6sPab

    • С Наступившим!
      1. у вас должно показывать целых 8 картинок 🙂 оригинальную то картинку показывает?
      2. мой код писался для обработки одной картинки — поэтому освобождение ресурсов делается в самом конце программы — в вашем случае у Вас идут просто нереальные утечки памяти — в конце цикла нужно удалять все картинки, которые в нём же создаются.
      Но если внести удаление картинок в цикл, то ползунки работать не будут — поэтому лучше вынести из цикла создание картинок — это и по производительности получше — размер кадра ведь остаётся постоянным — поэтому можно создать все картинки всего один раз, а далее просто копировать в них новые данные 🙂
      Ну и чтобы заставить работать ползунки — пороговое преобразование нужно из функций ползунков внести в код цикла.
      ну и собственно код:
      23-hsv-camera.cpp

    • Благодарю за обстоятельный и скорый ответ)Оригинал и плейны показывало адекватно, а вот память ело да, как от нечего делать. Буду дальше изучать и допиливать код)

  2. Я не совсем понимаю, как именно работает выделение красного пятна на детекторе луча, например.
    Вы не могли бы пояснить более развернуто?
    И можно ли где-то прочитать про фильтры: какие они есть, как применяются?
    Спасибо 🙂

    • т.е. как выделяется улыбка Чеширского кота вам ясно, а лазерный луч — нет? 🙂
      Поиск ведётся в цветовом пространстве HSV: для каждой плоскости мы задаём свой диапазон значений, в котором и выбираем подходящие пиксели плоскостей, а потом складываем плоскости через логическое И. Т.е. на получившейся бинарной картинке белым цветом будет выделен наш искомый объект.
      Можно реализовать такой поиск самостоятельно — обходя пиксели слоёв и проверяя, что они лежат в нужных пределах:

      // пробегаемся по пикселям изображения (H и V)
      	for(y=0; y<hsv->height; y++){
      		uchar* ptrH = (uchar*) (h_plane->imageData + y * h_plane->widthStep);
      		uchar* ptrV = (uchar*) (v_plane->imageData + y * v_plane->widthStep);
      		uchar* ptrAND = (uchar*) (and->imageData + y * and->widthStep);
      		for(x=0; x<hsv->width; x++){
      			if( (ptrH[x]>=Hmin && ptrH[x]<=Hmax) && (ptrV[x]>=Vmin && ptrV[x]<=Vmax) ){
      				ptrAND[x] = 255;
      			}
      		}
      	}

      Диапазон значений можно подобрать, используя вышеприведённую утилиту, и затем использовать для нахождения данного объекта на видео или других схожих картинках.

  3. Добрый день
    При компиляции в убунту 11:
    user@Admin:~/testCV$ make
    g++ -I/usr/local/include/opencv -L/usr/local/lib -o test.o test.cpp -lopencv_core -lopencv_imgproc -lopencv_highgui
    test.cpp: In function ‘int main(int, char**)’:
    /tmp/ccSPBM74.o: In function `main’:
    test.cpp:(.text+0xcb): undefined reference to `CvAdaptiveSkinDetector::CvAdaptiveSkinDetector(int, int)’
    test.cpp:(.text+0x107): undefined reference to `CvAdaptiveSkinDetector::process(_IplImage*, _IplImage*)’
    test.cpp:(.text+0x237): undefined reference to `CvAdaptiveSkinDetector::~CvAdaptiveSkinDetector()’
    test.cpp:(.text+0x255): undefined reference to `CvAdaptiveSkinDetector::~CvAdaptiveSkinDetector()’
    collect2: ld returned 1 exit status
    make: *** [test] Ошибка 1
    что делать? спасибо.

    • Добрый!
      давайте посмотрим ошибку — её возвращает линкер и он говорит,
      что не может найти реализацию методов. Значит, вы просто забыли указать необходимый
      lib-файл библиотеки.
      В статье я специально указал, что:

      в OpenCV уже есть класс,
      CvAdaptiveSkinDetector

      в 2.0 и 2.1 он объявлен в cvaux.hpp,
      а а 2.2 в modules\contrib\include\opencv2\contrib\contrib.hpp

      Попробуйте добавить к параметрам сборки:

      -lopencv_contrib

      Успехов!

  4. Доброго времени суток!
    Есть ли какой-либо метод конвертации цвета, как параметра рисования фигур, в другое цветовое пространство?
    Хотелось бы что-нибудь вида
    convert_HSV_TO_RGB(cvScalar(15,255,255)).
    Просто встает вопрос с выделением объектов рамочками разных цветов, а вбивать руками 20 цветов в RGB не хочется. Да и добавлять потом руками двадцать первый — тоже.

    • т.к. объект на картинке один, то можно поступить просто — найти его центр масс.
      пример добавил в конец статьи.

  5. спасибо большое )
    я попробовал сделать немного другим способом — с использованием функций cvInRangeS и cvHoughCircles. но, увы, у меня не вышло вывести координаты 🙁
    посмотрите, пожалуйста, мой код и, если сможете, подскажите как:

    #include «stdafx.h»
    #include <cv.h>
    #include <highgui.h>
    #include <stdlib.h>
    #include <stdio.h>

    IplImage* frame=0; // картинка, которую я получаю с видеокамеры
    IplImage* new_frame = 0;

    int main(int argc, char* argv[])
    {
    CvCapture* capture = cvCreateCameraCapture(CV_CAP_ANY);
    assert( capture );

    double width = cvGetCaptureProperty(capture, CV_CAP_PROP_FRAME_WIDTH);
    double height = cvGetCaptureProperty(capture, CV_CAP_PROP_FRAME_HEIGHT);
    printf("[i] %.0f x %.0f\n", width, height );

    CvScalar min = cvScalar(150, 84, 130, 0);
    CvScalar max = cvScalar(358, 256, 255, 0);
    CvMemStorage* storage = cvCreateMemStorage(0);
    cvNamedWindow(«capture», CV_WINDOW_AUTOSIZE);
    cvNamedWindow(«MyPoint»,CV_WINDOW_AUTOSIZE);

    while(true)
    {

    frame = cvQueryFrame( capture );
    new_frame = cvCreateImage( cvSize(frame->width, frame->height), IPL_DEPTH_8U, 1);
    cvInRangeS(frame, min, max, new_frame);
    cvLine(frame, cvPoint(width/2,height), cvPoint(width/2,1), cvScalar(20, 100, 100),5);

    cvShowImage(«capture», frame);
    cvShowImage(«MyPoint», new_frame);
    CvSeq* results = cvHoughCircles(new_frame, storage, CV_HOUGH_GRADIENT, 2, new_frame->width/10);
    for( int i = 0; i < results->total; i++ )
    {
    float* p = (float*) cvGetSeqElem( results, i );
    CvPoint pt = cvPoint( cvRound( p[0] ), cvRound( p[1] )); // по идее это центр круга
    cvCircle(new_frame,pt,cvRound( p[2] ),CV_RGB(0xff,0xff,0xff));
    printf(«X position = %d Y position = %d», pt.x, pt.y); // ничего не выводится (
    }
    char c = cvWaitKey(33);
    if (c == 27)
    {
    break;
    }
    }
    cvReleaseCapture( &capture );
    cvDestroyWindow(«capture»);
    return 0;
    }

    хотя бы подскажите в какую сторону копать )

  6. Доброго времени суток!
    У меня возникла такая проблема. Использовал ваш алгоритм для нахождение центра масс. По Y координаты находятся нормально, а вот с Х проблемы: программа выдает какие то странные значения.
    #include «stdafx.h»
    #include «highgui.h»
    #include «cv.h»
    #include «highgui.h»
    #include «math.h»
    int _tmain(int argc, _TCHAR* argv[])
    {
    IplImage* img = cvLoadImage(«E://result.jpg»);
    cvNamedWindow(«Screenshot», CV_WINDOW_AUTOSIZE);
    cvShowImage(«Screenshot», img);
    cvMoveWindow(«Screenshot», 0, 0);
    printf(«height = %d width = %d », img->height,img->width);
    int Xc = 0;
    int Yc = 0;
    int centerx = 0;
    int centery = 0;
    int counter = 0;
    for(int y=0; y<img->height; y++)
    {
    int* ptr = (int*) (img->imageData + y * img->widthStep);
    for(int x=0; x<img->width; x++)
    {
    if( ptr[x]>0 )
    {
    Xc += x;
    Yc += y;
    counter++;
    //printf(«xc = %d yc = %d », Xc,Yc);
    }
    }
    }
    if(counter!=0)
    {
    centerx = (Xc)/counter;
    centery = (Yc)/counter;
    }
    printf(«X position = %d Y position = %d », centerx,centery);
    cvWaitKey(0);
    cvDestroyAllWindows();
    return 0;
    }

  7. Добрый день,
    А у меня такой вопрос, с помошью данного алгоритма я нашел выделил все интересующие меня облости на изображении, как мне подсчитать количество выделенных областей? Буду очень признателен за помощь

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

  8. Чем отличаются CvtPixToPlane от Split?
    При использовании Split результат больше подходит на тот, что приведён в статье. При использовании CtvPixToPlane H и S изображения получились одинаковыми у меня.

    • А нет, в Cvt не преобразованное RGB изображение оригинала подставлял просто…
      Если сделать всё идентично, что CvtPixToPlane и Split сработали у меня идентично. В чём же разница?

    • А, примерно понятно. Вот декомпилированный участок OpenCVSharp для функции CvtPixToPlane:
      public void CvtPixToPlane(CvArr dst0, CvArr dst1, CvArr dst2, CvArr dst3)
      {
      Cv.Split(this, dst0, dst1, dst2, dst3);
      }
      🙂

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

    ////
    // пример работы оператора Собеля - cvSobel()
    //
    // robocraft.ru
    //
    
    #include <cv.h>
    #include <highgui.h>
    #include <stdlib.h>
    #include <stdio.h>
    
    //#include <stdafx.h>
    #include <math.h>
    
    
    IplImage* image = 0;
    IplImage* dst = 0;
    IplImage* dst2 = 0;
    
    
    
    int main(int argc, char* argv[])
    {
            // имя картинки задаётся первым параметром
            const char* filename = argc >= 2 ? argv[1] : "2.jpg";
            // получаем картинку
            image = cvLoadImage(filename, 1);
            // создаём картинки
            dst = cvCreateImage( cvSize(image->width, image->height), IPL_DEPTH_16S, image->nChannels);
            dst2 = cvCreateImage( cvSize(image->width, image->height), image->depth, image->nChannels);
    
            printf("[i] image: %s\n", filename);
            assert( image != 0 );
    
            // окно для отображения картинки
            cvNamedWindow("original", CV_WINDOW_AUTOSIZE);
            cvNamedWindow("sobel", CV_WINDOW_AUTOSIZE);
    
            printf("height = %d width = %d ", image->height,image->width);
        		int Xc = 0;
                int Yc = 0;
                int counter = 0; // счётчик числа белых пикселей
    
                int centerx = 0;
                int centery = 0;
    
            int aperture = argc == 3 ? atoi(argv[2]) : 3;
    
            while(1){
    
    
    
    
                            // применяем оператор Собеля
                            cvSobel(image, dst, 2, 2, aperture);
                            // преобразуем изображение к 8-битному
                            cvConvertScale(dst, dst2);
    
    
                    cvShowImage("original", image);
                    cvShowImage("sobel", dst2);
    
                    // пробегаемся по пикселям изображения
                       for(int y=0; y<dst2->height; y++)
                       {
                    	   uchar* ptr = (uchar*) (dst2->imageData + y * dst2->widthStep);
                    	   for(int x=0; x<dst2->width; x++)
                       {
                    		   if( ptr[x]>0 )
                       {
                    			   Xc += x;
                    			   Yc += y;
                    			   counter++;
                       }
                       }
                       }
                       	   if(counter!=0)
                       {
                       		   int centerx = float(Xc)/counter;
                       		   int centery = float(Yc)/counter;
                       }
            //       printf("X position = %d Y position = %d ", centerx,centery);
    
                    char c = cvWaitKey(33);
                    if (c == 27) {  break;   }
                            }
            int Xpoz= float(Xc)/counter;
            int Ypoz=float(Yc)/counter;
            //printf("X position = %d Y position = %d ", centerx,centery);
            printf("X position = %d Y position = %d ", Xpoz,Ypoz);
    
            // освобождаем ресурсы
            cvReleaseImage(& image);
            cvReleaseImage(&dst);
            cvReleaseImage(&dst2);
            // удаляем окна
            cvDestroyAllWindows();
            return 0;
    }
    
    
  10. Здравствуйте. Понимаю что тема старая, но…
    В общем в последней версии OpenCV я не нашел метода: cvCvtPixToPlane
    Подскажите если не трудно, чем его можно заменить, нужна реализация вашего:

    cvCvtPixToPlane( hsv, h_plane, s_plane, v_plane, 0 );
  11. Ага, пока ждал ответа сам разобрался, а выглядеть будет примерно так:
    (код под OpenCVSharp — C# реализацию…)

    Mat[] mats = new Mat[3]{h_plane, s_plane, v_plane};
    Cv2.Split(hsvMat, out mats);

    Теперь встал вопрос в другом…
    При попытке сохранения, или вывода окна с h_plane, s_plane, v_plane программа вываливается с ошибкой:
    Source image must have 1, 3 or 4 channels
    То-есть, изображение должно иметь 1, 3, или 4 канала.
    Проверяю свойство h_plane.Channels(), получаю кол-во каналов = 2
    Как то я запутался, а что делать что-бы получить 1 канал, почему Split возвращает изображение с 2 каналами?

Добавить комментарий

Arduino

Что такое Arduino?
Зачем мне Arduino?
Начало работы с Arduino
Для начинающих ардуинщиков
Радиодетали (точка входа для начинающих ардуинщиков)
Первые шаги с Arduino

Разделы

  1. Преимуществ нет, за исключением читабельности: тип bool обычно имеет размер 1 байт, как и uint8_t. Думаю, компилятор в обоих случаях…

  2. Добрый день! Я недавно начал изучать программирование под STM32 и ваши уроки просто бесценны! Хотел узнать зачем использовать переменную типа…

3D-печать AI Arduino Bluetooth CraftDuino DIY Google IDE iRobot Kinect LEGO OpenCV Open Source Python Raspberry Pi RoboCraft ROS swarm ИК автоматизация андроид балансировать бионика версия видео военный датчик дрон интерфейс камера кибервесна манипулятор машинное обучение наше нейронная сеть подводный пылесос работа распознавание робот робототехника светодиод сервомашинка собака управление ходить шаг за шагом шаговый двигатель шилд юмор

OpenCV
Робототехника
Будущее за бионическими роботами?
Нейронная сеть - введение