Выделение контура лица после детектирования / Прикладные вопросы распознавания образов / Recog.ru - Распознавание образов для программистов


Выделение контура лица после детектирования

Встроенные возможности OpenCV позволяют детектировать лицо, но при распознавании лиц может потребоваться более точное определение его границ. Метод выделения границ лица описан в проекте «Face detection and swap». Там расставлялись точки, называемые «щупами», которые сдвигались к лицу, пока не находился подходящий цвет лица, и движение не останавливалось. Расстановка «щупов» выглядела так:

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

Расстановка «щупов» описанным ниже образом негативно сказалась на выделении лица, также как и критерий проверки цвета. Поэтому «щупы» были расставлены в центре, а движение происходило к краям, пока не находился цвет отличный от цвета лица. Первоначально вычислялся средний цвет:

	uchar* ptr = (uchar*) (Image24->imageData);
	int i,j,all=0;
	CvScalar FaceColor;	
	long r=0,g=0,b=0;
	for(i=3*Image24->height/8;i<=5*Image24->height/8;i++)
		for(j=3*Image24->width/8;j<=5*Image24->width/8;j++)
		{
			r+=ptr[i*Image24->widthStep+j*3];
			g+=ptr[i*Image24->widthStep+j*3+1];
			b+=ptr[i*Image24->widthStep+j*3+2];
			all++;
		}
	FaceColor=CV_RGB(r/all,g/all,b/all);

Затем делалась расстановка точек и их движение, ниже пример для левой половины лица:
const int Contour_Points	=	60;
const float Face_Color_Delta	=	0.3f;
CvPoint p,p_mem,p_begin;	CvPoint * cvpoints =  new CvPoint[Contour_Points*2];
double k1,k2,k3;
for(i=0;i<Contour_Points;i++)
	{
		p.x=0;p.y=int(((float)Image24->height/Contour_Points)*(i+0.5f));
		for(j=3*Image24->width/8;j>0;j--)
		{
			r=ptr[p.y*Image24->widthStep+j*3];
			g=ptr[p.y*Image24->widthStep+j*3+1];
			b=ptr[p.y*Image24->widthStep+j*3+2];
			CvScalar Color=CV_RGB(r,g,b);
			k1=Color.val[0]/FaceColor.val[0];
			k2=Color.val[1]/FaceColor.val[1];
			k3=Color.val[2]/FaceColor.val[2];
			if (abs(1-k1)<Face_Color_Delta || abs(1-k2)<Face_Color_Delta || abs(1-k3)<Face_Color_Delta) p.x=j;
			else break;
		}
		cvpoints[i]=p;
#ifdef _DEBUG
		if (i>0) cvLine(Image24Temp,p,p_mem,CV_RGB(0,255,0),1);
#endif
		p_mem=p;
		if (i==0) p_begin=p;
	}

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

Какой-то результат есть, но надо убрать эти сильные колебания. После удаления точек (считаем, что точки такого рода с сильным отклонение не значимы), у нас уже более лучшая картина:

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

	CvContour contour;   
	CvSeqBlock contour_block;  
	cvMakeSeqHeaderForArray(CV_SEQ_POLYGON, sizeof(CvContour),sizeof(CvPoint),cvpoints, Contour_Points*2, (CvSeq*)&contour, &contour_block); 
	CvSeq* seq = (CvSeq*)&contour;
	seq  = cvApproxPoly( seq , sizeof(CvContour), storage,CV_POLY_APPROX_DP, 5, 1 );

Результат следующий:

С таким результатом можно уже работать по выделению отдельных элементов лица. Ну а мы еще посмотрим, как построится минимальный эллипс для контура:
CvBox2D box=cvFitEllipse2(seq);
cvEllipseBox(Image24Temp4,box,CV_RGB(0,255,0));


Ну, собственно это пока все результаты эксперимента.
  • 0
  • 21 апреля 2012, 19:27
  • vidikon

Комментарии (16)

RSS свернуть / развернуть
+
0
здравствуйте. в программе где «расстановка точек и их движение»
for(j=3*Image24->width/8;j>0;j--) — почему именно такое j начальное?
и для правой половины лица будет отличие — p.x = image->width?
avatar

gora

  • 14 мая 2012, 09:09
+
0
j начальное по той причине, что мы идем не от самого центра лица (все-таки знаем, что это лицо, а значит можно не от центра смотреть, а чуть с краю), чтобы исключить тень от носа ( к п примеру). Для правой:
for(i=0;i<Contour_Points;i++)
	{
		p.x=Image24->width-1;p.y=int(((float)Image24->height/Contour_Points)*((Contour_Points-1-i)+0.5f));
		for(j=5*Image24->width/8;j<Image24->width;j++)
		{
...
avatar

vidikon

  • 14 мая 2012, 10:19
+
0
спасибо, немного ясней стало)) при аппроксимации контура, он вообще никак не сглаживается у меня)
avatar

gora

  • 15 мая 2012, 11:10
+
0
Не понял, а это делалось «Какой-то результат есть, но надо убрать эти сильные колебания. После удаления точек (считаем, что точки такого рода с сильным отклонение не значимы), » Я код по этому не приводил.
cvApproxPoly сгладит только слабый шум.
avatar

vidikon

  • 15 мая 2012, 18:07
+
0
еще вопрос
r+=ptr[i*Image24->widthStep+j*3];
g+=ptr[i*Image24->widthStep+j*3+1];
b+=ptr[i*Image24->widthStep+j*3+2];
как я понимаю ptr массив пикселей изображения, в котором строки располагаются друг за другом, widthStep длина каждой строки, по приведенной формуле обращаемся к (i,j)элементу, а j*3, j*3+1, j*3+2 — это для каждого пикселя обращаемся к компонентам b,g,r. почему то значения r, g, b -получаются < 0 — тут и запутался(
avatar

gora

  • 16 мая 2012, 08:34
+
0
<0 только одна идея, что ptr unsigned char у вас. Посмотрите в отладке, как изменяются значения.
avatar

vidikon

  • 16 мая 2012, 13:19
+
0
Т.е наоборот char, а не unsigned
avatar

vidikon

  • 16 мая 2012, 15:29
+
0
ага, понял свою ошибку) спс))
image->widthStep = 536(число байт в строке) image->width = 178, 178*3 = 534 не совпадает с widthStep. по какой причине они не совпадают?))
avatar

gora

  • 17 мая 2012, 09:34
+
0
Это унаследовалось от формата BMP — выравнивание на границу 4 байта, 534 на 4 не делится, поэтому добавляется еще 2 байта.
avatar

vidikon

  • 17 мая 2012, 09:51
+
0
понятно)спс
uchar* ptr = (uchar*) image->imageData;
long r = ptr[0]; ///<----это будет r-компонента в левом нижнем углу, или в верхнем углу?)) просто немного запутался как строки располагаются в верхние пиксели как бы в начале массива ptr, или в конце
avatar

gora

  • 18 мая 2012, 19:04
+
0
В левом верхнем.
avatar

vidikon

  • 19 мая 2012, 07:35
+
0
спс)
avatar

gora

  • 20 мая 2012, 15:27
+
0
а можно через ptr присвоить цвет компоненте, например
ptr[0] = 255;
ptr[1] = 255;
ptr[2] = 255;
avatar

gora

  • 07 июня 2012, 19:05
+
0
да. Почему нет?
avatar

vidikon

  • 07 июня 2012, 21:42
+
0
Подскажите, пожалуйста, каким образом можно удалить сильно отклоняющиеся точки контура?
avatar

somebody

  • 11 апреля 2013, 21:37
+
0
Просто отслеживаем dx (сильное изменение) и если dx то предполагаем, что это ошибка — в последующих элементах смотрим, есть ли точки с похожими x, если есть то считаем линию между двумя этими точками, а остальные с большим dx заменяем на те, которые подходят по формуле линии
avatar

vidikon

  • 12 апреля 2013, 08:58

Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.