Успели соскучиться? Всё суета-суета :) А ещё и Новый Год приближается :)
Как понятно из названия, продолжим рассмотрение выделения объектов по цвету. И в конце, в качестве бонуса, рассмотрим пример детектирования кожи :)
На прошлом шаге было рассмотрено выделение объекта по его 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 очень просто:
Т.е. сначала просто создаются 4 изображения: одно для хранения изображения в формате HSV и три для последующего разделения изображения на отдельные каналы H, S и V.
Ниже приводится код полезной утилиты, которая может быть полезна для определения минимальных и максимальных значений каналов HSV для выделения объекта на изображении.
Программа преобразует изображение в формат HSV и затем используя ползунки и функцию cvInRangeS() формирует в дополнительных оконах «H range» бинарное изображение, которое получается из тех пикселей канала, значение которых больше Hmin и меньше Hmax.
Далее для всех трёх получившихся пороговых изображений выполняется операция логического И (т.е. в результирующем окне «hsv and» белыми будут только те пиксели, которые есть у всех трёх пороговых изображений).
Замечательно! Теперь используя более привычные параметры можно выделять различные объекты на изображении.
Наглядный пример — это детектирование кожи человека на фотографиях :)
Например, для детектированя наготы ;)
См. статью: Популярное изложение алгоритма распознавания наготы на цифровых фотографиях
в OpenCV уже есть класс, CvAdaptiveSkinDetector
в 2.0 и 2.1 он объявлен в cvaux.hpp,
а а 2.2 в modules\contrib\include\opencv2\contrib\contrib.hpp
UPD 2012-11-17
Для получения координат полученного объекта, нужно, всего-лишь, найти центр масс фигуры. Для этого, обходим все пиксели картинки и для ненулевых пикселей складываем координаты и подсчитываем их количество.
Пример:
//
// находим центр масс
//
int Xc = 0;
int Yc = 0;
int counter = 0; // счётчик числа белых пикселей
// пробегаемся по пикселям изображения
for(y=0; y<result->height; y++)
{
uchar* ptr = (uchar*) (result->imageData + y * result->widthStep);
for(x=0; x<result->width; x++)
{
if( ptr[x]>0 )
{
Xc += x;
Yc += y;
counter++;
}
}
}
if(counter!=0)
{
center.x = float(Xc)/counter;
center.y = float(Yc)/counter;
}
Доброго времени суток!
Попробовал приспособить данный код под захват изображения с веб-камеры и столкнулся со следующей проблемой: в окнах X_range и hsv_and при запуске из под VS 2010 серо-черные вертикальные полоски, при запуске.ехе просто черный экран и белая полоска, смахивающая на шум. Хотелось бы узнать, это настолько сильное влияние оказывают шумы на видео или это все же технические проблемы?
Код прилагаю, спасибо заранее ) pastebin.com/gxV6sPab
С Наступившим!
1. у вас должно показывать целых 8 картинок :) оригинальную то картинку показывает?
2. мой код писался для обработки одной картинки — поэтому освобождение ресурсов делается в самом конце программы — в вашем случае у Вас идут просто нереальные утечки памяти — в конце цикла нужно удалять все картинки, которые в нём же создаются.
Но если внести удаление картинок в цикл, то ползунки работать не будут — поэтому лучше вынести из цикла создание картинок — это и по производительности получше — размер кадра ведь остаётся постоянным — поэтому можно создать все картинки всего один раз, а далее просто копировать в них новые данные :)
Ну и чтобы заставить работать ползунки — пороговое преобразование нужно из функций ползунков внести в код цикла.
ну и собственно код: 23-hsv-camera.cpp
Благодарю за обстоятельный и скорый ответ)Оригинал и плейны показывало адекватно, а вот память ело да, как от нечего делать. Буду дальше изучать и допиливать код)
Я не совсем понимаю, как именно работает выделение красного пятна на детекторе луча, например.
Вы не могли бы пояснить более развернуто?
И можно ли где-то прочитать про фильтры: какие они есть, как применяются?
Спасибо :)
т.е. как выделяется улыбка Чеширского кота вам ясно, а лазерный луч — нет? :)
Поиск ведётся в цветовом пространстве 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;
}
}
}
Диапазон значений можно подобрать, используя вышеприведённую утилиту, и затем использовать для нахождения данного объекта на видео или других схожих картинках.
Добрый день
При компиляции в убунту 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
Доброго времени суток!
Есть ли какой-либо метод конвертации цвета, как параметра рисования фигур, в другое цветовое пространство?
Хотелось бы что-нибудь вида
convert_HSV_TO_RGB(cvScalar(15,255,255)).
Просто встает вопрос с выделением объектов рамочками разных цветов, а вбивать руками 20 цветов в RGB не хочется. Да и добавлять потом руками двадцать первый — тоже.
спасибо большое )
я попробовал сделать немного другим способом — с использованием функций cvInRangeS и cvHoughCircles. но, увы, у меня не вышло вывести координаты :(
посмотрите, пожалуйста, мой код и, если сможете, подскажите как:
в общем после долгих мучений добился чтобы показывал координаты, единственное не понятно откуда он начинает считать толи справа на лево сверху вниз, толи как, мож кто разберется, было бы замечательно,
////
// пример работы оператора Собеля - 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;
}
Добрый день,
А у меня такой вопрос, с помошью данного алгоритма я нашел выделил все интересующие меня облости на изображении, как мне подсчитать количество выделенных областей? Буду очень признателен за помощь
Пробовал пользоваться тем методом, получается не совсем корректное кличество из за разомкнутости некоторых контуров, сложной формы исходного контура а так же из за нахождения внутреннего внутреннего и внешнего контуров некоторые области считаются по 2 раза.
А какая функция, кстати, возвращает количество найденных контуров? а то я реализовал через простой счетчик внутри цикла обхода всех контуров?
Сижу гадаю, почему у меня не получается найти так же улыбку и коды значений часто не совпадают… И тут заметил, что оригинальная картинка в этом примере гораздо насыщеннее, чем я скачал из статьи robocraft.ru/blog/computervision/268.html!!!
Чем отличаются 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);
}
:)
Комментарии (22)
RSS свернуть / развернутьПопробовал приспособить данный код под захват изображения с веб-камеры и столкнулся со следующей проблемой: в окнах X_range и hsv_and при запуске из под VS 2010 серо-черные вертикальные полоски, при запуске.ехе просто черный экран и белая полоска, смахивающая на шум. Хотелось бы узнать, это настолько сильное влияние оказывают шумы на видео или это все же технические проблемы?
Код прилагаю, спасибо заранее )
Crady
1. у вас должно показывать целых 8 картинок :) оригинальную то картинку показывает?
2. мой код писался для обработки одной картинки — поэтому освобождение ресурсов делается в самом конце программы — в вашем случае у Вас идут просто нереальные утечки памяти — в конце цикла нужно удалять все картинки, которые в нём же создаются.
Но если внести удаление картинок в цикл, то ползунки работать не будут — поэтому лучше вынести из цикла создание картинок — это и по производительности получше — размер кадра ведь остаётся постоянным — поэтому можно создать все картинки всего один раз, а далее просто копировать в них новые данные :)
Ну и чтобы заставить работать ползунки — пороговое преобразование нужно из функций ползунков внести в код цикла.
ну и собственно код:
noonv
Crady
Вы не могли бы пояснить более развернуто?
И можно ли где-то прочитать про фильтры: какие они есть, как применяются?
Спасибо :)
Dreddik
Поиск ведётся в цветовом пространстве HSV: для каждой плоскости мы задаём свой диапазон значений, в котором и выбираем подходящие пиксели плоскостей, а потом складываем плоскости через логическое И. Т.е. на получившейся бинарной картинке белым цветом будет выделен наш искомый объект.
Можно реализовать такой поиск самостоятельно — обходя пиксели слоёв и проверяя, что они лежат в нужных пределах:
Диапазон значений можно подобрать, используя вышеприведённую утилиту, и затем использовать для нахождения данного объекта на видео или других схожих картинках.
noonv
При компиляции в убунту 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
что делать? спасибо.
vovagorodok
давайте посмотрим ошибку — её возвращает линкер и он говорит,
что не может найти реализацию методов. Значит, вы просто забыли указать необходимый
lib-файл библиотеки.
В статье я специально указал, что:
Попробуйте добавить к параметрам сборки: Успехов!
noonv
Есть ли какой-либо метод конвертации цвета, как параметра рисования фигур, в другое цветовое пространство?
Хотелось бы что-нибудь вида
convert_HSV_TO_RGB(cvScalar(15,255,255)).
Просто встает вопрос с выделением объектов рамочками разных цветов, а вбивать руками 20 цветов в RGB не хочется. Да и добавлять потом руками двадцать первый — тоже.
Altivolus
Скажите пожалуйста, как можно получить координаты распознанной точки (от лазерной указки)?
crawter
пример добавил в конец статьи.
noonv
я попробовал сделать немного другим способом — с использованием функций 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;
}
хотя бы подскажите в какую сторону копать )
crawter
noonv
У меня возникла такая проблема. Использовал ваш алгоритм для нахождение центра масс. По 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;
}
Gunter
winston23
А у меня такой вопрос, с помошью данного алгоритма я нашел выделил все интересующие меня облости на изображении, как мне подсчитать количество выделенных областей? Буду очень признателен за помощь
MixON_VC
noonv
А какая функция, кстати, возвращает количество найденных контуров? а то я реализовал через простой счетчик внутри цикла обхода всех контуров?
MixON_VC
JohnJ
При использовании Split результат больше подходит на тот, что приведён в статье. При использовании CtvPixToPlane H и S изображения получились одинаковыми у меня.
JohnJ
Если сделать всё идентично, что CvtPixToPlane и Split сработали у меня идентично. В чём же разница?
JohnJ
public void CvtPixToPlane(CvArr dst0, CvArr dst1, CvArr dst2, CvArr dst3)
{
Cv.Split(this, dst0, dst1, dst2, dst3);
}
:)
JohnJ
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.