/*
#include
using namespace cv;
int main()
{
Mat img = imread("1.jpg");
imshow("测试图片",img);
waitKey(50);
return 0;
}*/
/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%% %%%%%%
%%%%%% 欢迎到www.opencvchina.com下载源代码和资料 %%%%%%
%%%%%% %%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
*/
/*
* Code written by Lya (GeckoGeek.fr)
*/
//#include "stdafx.h"
#include "opencv/highgui.h"
#include "opencv/cv.h"
#include
#include
#include
// Maths methods
#define max(a, b) ((a) > (b) ? (a) : (b))
#define min(a, b) ((a) < (b) ? (a) : (b))
#define abs(x) ((x) > 0 ? (x) : -(x))
#define sign(x) ((x) > 0 ? 1 : -1)
// Step mooving for object min & max
#define STEP_MIN 5
#define STEP_MAX 100
//轮廓区域最小面积
int detectarea=100;
IplImage *image;
IplImage *im;
// Position of the object we overlay
CvPoint objectPos = cvPoint(-1, -1); //目标当前位置
// Color tracked and our tolerance towards it
int h = 0, s = 0, v = 0, tolerance = 10;
CvPoint temp ;
int T_HSV[3]={111,111,23};
// int HSV[3]={177,185,120};
int HSV[3]={179,79,81};
int HSV_f[3]={177,185,120};
//int count=0;
int countd=0; //在drawbody函数用到
CvPoint HEAD;//上次的头部坐标
CvPoint HAND[5];//用于存储上次的手部坐标
CvPoint FOOT[5];//用于存储上次的脚坐标
CvPoint point[10];//存储目标位置
CvScalar color=CV_RGB(255,158,97);
// Next position of the object we overlay
CvPoint objectNextPos;
//此函数用于画出image图片中是HSV[]色彩的所有轮廓,HSV[]是感兴趣的三通道色彩
CvPoint ditalcontours(IplImage* image, int *nbPixels,int HSV[],int detectarea) {
void drawcontours(IplImage* mask,IplImage* image);
int x, y;
//CvScalar pixel;
IplImage *hsv, *mask;
IplConvKernel *kernel;
int sommeX = 0, sommeY = 0;
*nbPixels = 0;
// Create the mask &initialize it to white (no color detected)
mask = cvCreateImage(cvGetSize(image), image->depth, 1);
// Create the hsv image
hsv = cvCloneImage(image);
cvCvtColor(image, hsv, CV_BGR2HSV);
// We create the mask
cvInRangeS(hsv, cvScalar(HSV[0] - tolerance -1, HSV[1] - tolerance,0), cvScalar(HSV[0] + tolerance -1, HSV[1] + tolerance, 255), mask);
//cvInRangeS(1,2,3,4)此函数就是从hsv图像中选出在范围在2到3之间的像素点,选出来符合的置为1,否则置为0。
//最后把二值化图像存在mask中
//cvShowImage("mask第一步", mask);//展示mask效果
// Create kernels for the morphological operation
kernel = cvCreateStructuringElementEx(5, 5, 2, 2, CV_SHAPE_CROSS);
//生成膨胀和溶解结构 kernel
// Morphological opening (inverse because we have white pixels on black background)
cvDilate(mask, mask, kernel, 1); //膨胀
cvErode(mask, mask, kernel, 1); //溶解
// We go through the mask to look for the tracked object and get its gravity center
for(x = 0; x < mask->width; x++) {
for(y = 0; y < mask->height; y++) {
// If its a tracked pixel, count it to the center of gravity's calcul
if(((uchar *)(mask->imageData + y*mask->widthStep))[x] == 255) {
sommeX += x; //目标像素点x坐标总和
sommeY += y; //目标像素点y坐标总和
(*nbPixels)++; //目标像素点个数
}
}
}
// Show the result of the mask image
// cvShowImage("Mask第二部", mask);
drawcontours(mask,image);
// We release the memory of kernels
cvReleaseStructuringElement(&kernel);
// We release the memory of the mask
cvReleaseImage(&mask);
// We release the memory of the hsv image
cvReleaseImage(&hsv);
// If there is no pixel, we return a center outside the image, else we return the center of gravity
if(*nbPixels > 0)
return cvPoint((int)(sommeX / (*nbPixels)), (int)(sommeY / (*nbPixels))); //目标像素点的质心
else
return cvPoint(-1, -1);
}
//此函数用于勾画黑白图mask的轮廓
void drawcontours(IplImage* mask,IplImage* image)
{
IplConvKernel *kernel = cvCreateStructuringElementEx(5, 5, 2, 2, CV_SHAPE_ELLIPSE);
IplImage* pyr = cvCreateImage( cvSize((mask->width )/2, (mask->height)/2), 8, 1 );
CvMemStorage *stor;
CvSeq *cont;
// 中值滤波,消除小的噪声
cvSmooth( mask, mask, CV_MEDIAN, 3, 0, 0, 0 );
cvPyrDown( mask, pyr, CV_GAUSSIAN_5x5 );// 向下采样,去掉噪声,图像是原图像的四分之一 ,mask是pyr面积的四倍,要不然会内存溢出
cvDilate( pyr, pyr, 0, 1 ); // 做膨胀操作,消除目标的不连续空洞
cvPyrUp( pyr,mask, CV_GAUSSIAN_5x5 );// 向上采样,恢复图像,图像是原图像的四倍
cvErode(mask, mask, kernel, 1); //溶解
cvShowImage("Mask第三部", mask);
// 下面的程序段用来找到轮廓
// Create dynamic structure and sequence.
stor = cvCreateMemStorage(0);
cont = cvCreateSeq(CV_SEQ_ELTYPE_POINT, sizeof(CvSeq), sizeof(CvPoint) , stor);
// 找到所有轮廓
//cvFindContors( dst, stor, &cont, sizeof(CvContour),
//CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE, cvPoint(0,0));
cvFindContours( mask, stor, &cont, sizeof(CvContour),
CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE, cvPoint(0,0));
// 直接使用CONTOUR中的矩形来画轮廓
/*
double area=0;
CvSeq *contemp=cont;
for(;contemp;contemp = contemp->h_next)
{ CvRect temp = ((CvContour*)cont)->rect;
if(temp.height * temp.width>area)
area=temp.height * temp.width;
}
*/
for(int num=0;cont;cont = cont->h_next)
{
CvRect r = ((CvContour*)cont)->rect;
if(r.height * r.width > detectarea) // 面积小的方形抛弃掉
{
point[num%10].x=(2*r.x + r.width)/2;
point[num%10].y= (2*r.y + r.height)/2;
//cvRectangle( image, cvPoint(r.x,r.y), cvPoint(r.x + r.width, r.y + r.height), CV_RGB(255,0,0), 1, CV_AA,0); //给指定区域画轮廓
cvCircle(im,point[num%10],10,color,CV_FILLED,CV_AA,0);
printf( "轮廓的开始位置 %d.%d ,中心x,y = %d.%d ,个数是%d\n",r.x,r.y ,point[num%10].x,point[num%10].y ,num);
num++;
}
}
cvReleaseStructuringElement(&kernel);
// free memory
cvReleaseMemStorage(&stor);
// We release the memory of the hsv image
cvReleaseImage(&pyr);
}
int SMINY(CvPoint P[])
{ int i=0;
int MAX=1000,c=0;
while(P[i].x&&P[i].y)
{ if(P[i].y0&&P[i].x>0)
{
MAX=P[i].y;
c=i;
}
i++;
}
return c;
}
int SMINX(CvPoint P[])
{
int i=0;
int MAX=1000,c=0;
while(P[i].x&&P[i].y)
{ if(P[i].x0&&P[i].y>0)
{
MAX=P[i].x;
c=i;
}
i++;
}
return c;
}
int SMAXX(CvPoint P[])
{
int i=0;
int MAX=0,c=0;
while(P[i].x&&P[i].y)
{ if(P[i].x>MAX&&P[i].y>0)
{
MAX=P[i].x;
c=i;
}
i++;
}
return c;
}
void drawupbody(CvPoint P[],CvPoint center,CvPoint P1[])
{ int i=0;
int c=0;
CvPoint pt1_Rect;
CvPoint pt2_Rect;
CvPoint head;
CvPoint body;
CvPoint hand[2];
CvPoint foot[2];
char key='e';
int radius=0;
//char str[15]; //CvFont font;
c=SMINY(P);
head.x=P[c].x;
head.y=P[c].y;
c=SMINX(P);
hand[0].x=P[c].x;
hand[0].y=P[c].y;
c=SMAXX(P);
hand[1].x=P[c].x;
hand[1].y=P[c].y;
body.x=center.x;
body.y=center.y;
c=SMINX(P1);
foot[0].x=P1[c].x;
foot[0].y=P1[c].y;
c=SMAXX(P1);
foot[1].x=P1[c].x;
foot[1].y=P1[c].y;
//对头部,手臂点 空间位置跳跃控制。减少乱跳现象
if(countd++<4)
{HEAD.x=head.x;
HEAD.y=head.y;
for(int i=0;i<2;i++)
{ HAND[i].x=hand[i].x;
HAND[i].y=hand[i].y;
FOOT[i].x=foot[i].x;
FOOT[i].y=foot[i].y;
}
}
// printf( "轮廓的中心x,y = %d.%d \n", abs(HEAD.x-head.x),abs(HEAD.y-head.y));
if( abs(HEAD.x-head.x)>40||abs(HEAD.y-head.y)>40)
{head.x=HEAD.x;
head.y=HEAD.y;
}
HEAD.x=head.x;
HEAD.y=head.y;
for(int i=0;i<2;i++)
{
if( abs(HAND[i].x-hand[i].x)>60||abs(HAND[i].y-hand[i].y)>60)
{
hand[i].x=HAND[i].x;
hand[i].y=HAND[i].y;
}
if( abs(FOOT[i].x-foot[i].x)>100||abs(FOOT[i].y-foot[i].y)>100)
{
foot[i].x=FOOT[i].x;
foot[i].y=FOOT[i].y;
}
HAND[i].x=hand[i].x;
HAND[i].y=hand[i].y;
FOOT[i].x=foot[i].x;
FOOT[i].y=foot[i].y;
} //对头部,手臂点 空间位置跳跃控制。减少乱跳现象
// printf("右手坐标是%d,%d",hand[1].x,hand[1].y);
IplImage *img=cvCreateImage(cvGetSize(image),IPL_DEPTH_8U,3);
cvNamedWindow("image",CV_WINDOW_AUTOSIZE);
pt1_Rect.x=0;
pt1_Rect.y=0;
pt2_Rect.x=600;
pt2_Rect.y=600;
color=CV_RGB(97,158,225);
cvRectangle(img,pt1_Rect,pt2_Rect,color,CV_FILLED,CV_AA,0);
color=CV_RGB(255,158,97);
radius=35;
cvCircle(img,head,radius,color,CV_FILLED,CV_AA,0);//头部
pt1_Rect.x=body.x-30;
pt1_Rect.y=body.y-20;
pt2_Rect.x=body.x+30;
pt2_Rect.y=body.y+80;
cvRectangle(img,pt1_Rect,pt2_Rect,color,CV_FILLED,CV_AA,0);//身体
cvLine(img,head,body,color,10,CV_AA,0);//颈部
color=CV_RGB(255,255,0);
pt1_Rect.x=body.x-30;
pt1_Rect.y=body.y-20;
cvLine(img,pt1_Rect,hand[0],color,30,CV_AA,0);//手臂1
pt1_Rect.x=body.x+30;
pt1_Rect.y=body.y-20;
cvLine(img,pt1_Rect,hand[1],color,30,CV_AA,0);//手臂2
color=CV_RGB(255,255,0);
pt1_Rect.x=body.x-30;
pt1_Rect.y=body.y+70;
cvLine(img,pt1_Rect,foot[0],color,30,CV_AA,0);//腿1
pt1_Rect.x=body.x+30;
pt1_Rect.y=body.y+70;
cvLine(img,pt1_Rect,foot[1],color,30,CV_AA,0);//腿1
cvShowImage("image",img);
cvReleaseImage(&img);
}
//此函数截取人物部分,再来跟踪颜色质心对于人物跟踪更加精准
void ditaldeal(IplImage* image, CvPoint t,int *nbPixels,int HSV[])
{
int tem=*nbPixels;
CvPoint h[5],f[5];
im=cvCreateImage(cvGetSize(image),image->depth,image->nChannels);
cvCopy(image,im,NULL);
//cvSetImageROI(im,cvRect(t.x-70,t.y-150,140,400));
detectarea=100;//最小区域面积的设定
temp =ditalcontours(im,nbPixels,HSV,detectarea);
for(int i=0;i<5;i++)
{h[i].x=point[i].x; h[i].y=point[i].y; }//找上身的点
for(int i=0;i<5;i++)
{point[i].x=NULL; point[i].y=NULL; }//清空point
//cvCopy(image,im,NULL);
detectarea=50;
temp =ditalcontours(im,nbPixels,HSV_f,detectarea);
for(int i=0;i<5;i++)
{f[i].x=point[i].x; f[i].y=point[i].y;
//printf("下半身的第%d点是x=%d,y=%d",i,f[i].x,f[i].y);
}//找下身的点
for(int i=0;i<5;i++)
{point[i].x=NULL; point[i].y=NULL; }//注意清空point[],以免下一帧有影响
drawupbody(h,objectNextPos,f);
}
//此函数用于跟踪HSV[]三通道色彩像素点的质心
CvPoint binarisation(IplImage* image, int *nbPixels,int HSV[]) {
int x, y;
//CvScalar pixel;
IplImage *hsv, *mask;
IplConvKernel *kernel;
int sommeX = 0, sommeY = 0;
*nbPixels = 0;
// Create the mask &initialize it to white (no color detected)
mask = cvCreateImage(cvGetSize(image), image->depth, 1);
// Create the hsv image
hsv = cvCloneImage(image);
cvCvtColor(image, hsv, CV_BGR2HSV);
// We create the mask
cvInRangeS(hsv, cvScalar(HSV[0] - tolerance -1, HSV[1]- tolerance, 0), cvScalar(HSV[0] + tolerance -1, HSV[1] + tolerance, 255), mask);
//cvInRangeS(1,2,3,4)此函数就是从hsv图像中选出在范围在2到3之间的像素点,选出来符合的置为1,否则置为0。
//最后把二值化图像存在mask中
//cvShowImage("mask第一步", mask);//展示mask效果
// Create kernels for the morphological operation
kernel = cvCreateStructuringElementEx(5, 5, 2, 2, CV_SHAPE_ELLIPSE);
//生成膨胀和溶解结构 kernel
// Morphological opening (inverse because we have white pixels on black background)
cvDilate(mask, mask, kernel, 1); //膨胀
cvErode(mask, mask, kernel, 1); //溶解
// We go through the mask to look for the tracked object and get its gravity center
for(x = 0; x < mask->width; x++) {
for(y = 0; y < mask->height; y++) {
// If its a tracked pixel, count it to the center of gravity's calcul
if(((uchar *)(mask->imageData + y*mask->widthStep))[x] == 255) {
sommeX += x; //目标像素点x坐标总和
sommeY += y; //目标像素点y坐标总和
(*nbPixels)++; //目标像素点个数
}
}
}
// Show the result of the mask image
//cvShowImage("Mask第二部", mask);
// We release the memory of kernels
cvReleaseStructuringElement(&kernel);
// We release the memory of the mask
cvReleaseImage(&mask);
// We release the memory of the hsv image
cvReleaseImage(&hsv);
// If there is no pixel, we return a center outside the image, else we return the center of gravity
if(*nbPixels > 0)
return cvPoint((int)(sommeX / (*nbPixels)), (int)(sommeY / (*nbPixels))); //目标像素点的质心
else
return cvPoint(-1, -1);
}
//此函数用于给指定点的质心打点
void addObjectToVideo(IplImage* image, CvPoint objectNextPos, int nbPixels) {
int objectNextStepX, objectNextStepY;
CvPoint t;
t.x=objectNextPos.x;
t.y=objectNextPos.y;
// Draw an object (circle) centered on the calculated center of gravity
if (nbPixels > 10)
cvDrawCircle(image, objectNextPos, 10, CV_RGB(255, 0, 0), -1);
cvRectangle( image, cvPoint(t.x-50,t.y-120),
cvPoint(t.x + 50, t.y + 90),
CV_RGB(255,0,0), 1, CV_AA,0);
// We show the image on the window
}
//得到鼠标点击处的HSV[]三通道色彩
void getObjectColor(int event, int x, int y, int flags, void *param = NULL) {
// Vars
CvScalar pixel;
IplImage *hsv;
if(event == CV_EVENT_LBUTTONUP) {
// Get the hsv image
hsv = cvCloneImage(image);
cvCvtColor(image, hsv, CV_BGR2HSV);
// Get the selected pixel
pixel = cvGet2D(hsv, y, x);
// Change the value of the tracked color with the color of the selected pixel
h = (int)pixel.val[0]; //修改全局变量 h通道值
s = (int)pixel.val[1]; //修改全局变量 s通道值
v = (int)pixel.val[2]; //修改全局变量 v通道值
// { HSV[0]=h; HSV[1]=s; HSV[2]=v;}
{ T_HSV[0]=h; T_HSV[1]=s; T_HSV[2]=v;}
// Release the memory of the hsv image
cvReleaseImage(&hsv);
}
}
/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%% %%%%%%
%%%%%% 欢迎到www.opencvchina.com下载源代码和资料 %%%%%%
%%%%%% %%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
*/
int main() {
double t;
// Image & hsvImage
IplImage *hsv;
// Video Capture
CvCapture *capture;
// Key for keyboard event
char key='e';
// Number of tracked pixels
int nbPixels;
// Initialize the video Capture (200 => CV_CAP_V4L2)
// capture = cvCreateCameraCapture(0);
capture = cvCaptureFromAVI("face.mp4" );
// Check if the capture is ok
if (!capture) {
printf("Can't initialize the video capture.\n");
return -1;
}
// Create the windows
cvNamedWindow("GeckoGeek Color Tracking", CV_WINDOW_AUTOSIZE);
cvMoveWindow("GeckoGeek Color Tracking", 0, 100);
// Mouse event to select the tracked color on the original image
cvSetMouseCallback("GeckoGeek Color Tracking", getObjectColor); //设置鼠标监听,修改目标颜色的h,s,v三个通道值
image = cvQueryFrame(capture);
// While we don't want to quit
while(key != 'Q' && key != 'q') {
// We get the current image
image = cvQueryFrame(capture);
// If there is no image, we exit the loop
if(!image)
continue;
objectNextPos = binarisation(image, &nbPixels,T_HSV); //返回 质心坐标,nbPixels是目标像素的个数
// addObjectToVideo(image, objectNextPos, nbPixels); //质心移动处理函数
t = (double)cvGetTickCount();//用来计算算法执行时间
ditaldeal(image,objectNextPos,&nbPixels,HSV);//人物颜色质心跟踪更加精准
t = (double)cvGetTickCount() - t;//相减为算法执行的时间
printf( "detection time = %g ms\n", t/((double)cvGetTickFrequency()*1000.) );
cvShowImage("GeckoGeek Color Tracking", image);//完整框,显示框不能重名
cvShowImage("GeckoGeek Color Track", im);//人物框
// We wait 10 ms
key = cvWaitKey(10);
}
// Destroy the windows we have created
cvDestroyWindow("GeckoGeek Color Tracking");
cvDestroyWindow("GeckoGeek Mask");
// Destroy the capture
cvReleaseCapture(&capture);
cvReleaseImage(&hsv);
return 0;
}