#include "cv.h"
#include "highgui.h"
#include <stdio.h>
#include <ctype.h>
#include <iostream>
#include <windows.h>
CvPoint2D32f pt[4];//自己定义的点作为椭圆方框图的基准点的数组
CvHistogram *hist = 0;
int backproject_mode = 0;
int select_object = 0;
int track_object = 0;
int show_hist = 1;
CvPoint origin;
CvRect selection;
CvRect track_window;
/*CvRect矩形框的偏移和大小
typedef struct CvRect
{ int x; /* 方形的最左角的x-坐标
int y; /* 方形的最上或者最下角的y-坐标
int width; /* 宽
int height; /* 高 *}*/
CvBox2D track_box; // tracking 返回的区域 box带角度 .CvBox2D旋转的二维盒子
CvConnectedComp track_comp;
int hdims = 48; // 划分HIST的个数越高越精确
float hranges_arr[] = {0,180};
float* hranges = hranges_arr;
int vmin = 10, vmax = 256, smin = 30;
IplImage *image=NULL;
static void on_mouse( int event, int x, int y, int flags,void *param)
{
if( !image )
return ;
if( image->origin )
y = image->height - y;
if( select_object )
{
selection.x = MIN(x,origin.x);
selection.y = MIN(y,origin.y);
selection.width = selection.x + CV_IABS(x - origin.x);
selection.height = selection.y + CV_IABS(y - origin.y);
selection.x = MAX( selection.x, 0 );
selection.y = MAX( selection.y, 0 );
selection.width = MIN( selection.width, image->width );
selection.height = MIN( selection.height, image->height );
selection.width -= selection.x;
selection.height -= selection.y;
}
switch( event )
{
case CV_EVENT_LBUTTONDOWN:
origin = cvPoint(x,y);
selection = cvRect(x,y,0,0);
select_object = 1;
break;
case CV_EVENT_LBUTTONUP:
select_object = 0;
if( selection.width > 0 && selection.height > 0 )
track_object = -1;
#ifdef _DEBUG
printf("\n # 鼠标的选择区域");
printf("\n X = %d, Y = %d, Width = %d, Height = %d",
selection.x, selection.y, selection.width, selection.height);
#endif
break;
}
}
CvScalar hsv2rgb( float hue )
{
int rgb[3], p, sector;
static const int sector_data[][3]=
{{0,2,1}, {1,2,0}, {1,0,2}, {2,0,1}, {2,1,0}, {0,1,2}};
hue *= 0.033333333333333333333333333333333f;
sector = cvFloor(hue);
p = cvRound(255*(hue - sector));
p ^= sector & 1 ? 255 : 0;
rgb[sector_data[sector][0]] = 255;
rgb[sector_data[sector][1]] = 0;
rgb[sector_data[sector][2]] = p;
#ifdef _DEBUG
printf("\n # Convert HSV to RGB");
printf("\n?? HUE = %f", hue);
printf("\n?? R = %d, G = %d, B = %d", rgb[0],rgb[1],rgb[2]);
#endif
return cvScalar(rgb[2], rgb[1], rgb[0],0);
}
int main( int argc, char** argv )
{
char chishu=0;
int yzhi,yyzhi,jishu;
IplImage* hsv,*backproject,*mask,*hue,*histimg;
CvCapture* capture = 0;//CvCapture 视频获取结构
IplImage* frame = 0;
capture = cvCaptureFromCAM( 0 );//cvCaptureFromCAM 初始化从摄像头中获取视频
if( !capture )
{
fprintf(stderr,"Could not initialize capturing...\n");
return -1;
}
printf( "Hot keys: \n"
"\tESC - quit the program\n"
"\tc - stop the tracking\n"
"\tb - switch to/from backprojection view\n"
"\th - show/hide object histogram\n"
"To initialize tracking, select the object with mouse\n");
//cvNamedWindow( "Histogram", 1 );
cvNamedWindow( "CamShiftDemo", 1 );
cvSetMouseCallback( "CamShiftDemo", on_mouse, NULL ); // on_mouse 自定义事件
cvCreateTrackbar( "Vmin", "CamShiftDemo", &vmin, 256,
0 );//cvCreateTrackbar 创建横拉条
cvCreateTrackbar( "Vmax", "CamShiftDemo", &vmax, 256, 0 );
cvCreateTrackbar( "Smin", "CamShiftDemo", &smin, 256, 0 );
for(;;)
{
int i, bin_w, c;
frame = cvQueryFrame( capture );//vQueryFrame 从摄像头或者文件中抓取并返回一帧
if( !frame )
break;
if( !image )
{
/* allocate all the buffers */
image = cvCreateImage( cvGetSize(frame), 8, 3 );
image->origin = frame->origin;
hsv = cvCreateImage( cvGetSize(frame), 8, 3 );//CreateImage创建头并分配数据
//IplImage* cvCreateImage( CvSize size, int depth, int channels );
hue = cvCreateImage( cvGetSize(frame), 8, 1 );
mask = cvCreateImage( cvGetSize(frame), 8, 1 );
backproject = cvCreateImage( cvGetSize(frame), 8, 1 );
hist = cvCreateHist( 1, &hdims, CV_HIST_ARRAY, &hranges, 1 );
/*CreateHist创建直方图
CvHistogram* cvCreateHist( int dims, int* sizes, int type,
float** ranges=NULL, int uniform=1 );dims直方图维数的数目*/
// 计算直方图
histimg = cvCreateImage( cvSize(320,200), 8, 3 );
/*CvSize
矩形框大小以像素为精度
typedef struct CvSize
{int width; / 矩形宽 /int height; / 矩形高 /}*/
cvZero( histimg );
/*对于密集型号数组(CvMat, CvMatND or IplImage)cvZero(array)
就相当于 cvSet(array,cvScalarAll(0),0), 对于稀疏数组所有的元
素都将被删除.*/
}
cvCopy( frame, image, 0 );
cvCvtColor( image, hsv, CV_BGR2HSV ); // 彩色空间转换 BGR to HSV
if( track_object )
{
int _vmin = vmin, _vmax = vmax;
cvInRangeS( hsv, cvScalar(0,smin,MIN(_vmin,_vmax),0),
/*CvScalar可存放在1-2-3-4-TUPLE类型的捆绑数据的容
器
typedef struct CvScalar
{ double val[4]
}
CvScalar;
/* 构造函数用val0初始化val[0]用val1初始化val[1]等等*/
cvScalar(180,256,MAX(_vmin,_vmax),0), mask );
/*InRangeS检查数组元素是否在两个数量之间
void cvInRangeS( const CvArr* src, CvScalar lower, CvScalar upper,
CvArr* dst );
src第一个原数组
lower包括进的下边界.
upper 不包括进的上边界
dst 输出数组必须是 8u 或 8s 类型.*/
// 得到二值的MASK
cvSplit( hsv, hue, 0, 0, 0 ); // 只提取 HUE 分量
/*Split分割多通道数组成几个单通道数组或者从数组中提取一
个通道
void cvSplit( const CvArr* src, CvArr* dst0, CvArr* dst1,CvArr*
dst2, CvArr* dst3 );
#define cvCvtPixToPlane cvSplit
src 原数组.
dst0...dst3目标通道*/
if( track_object < 0 )
{
float max_val = 0.f;
cvSetImageROI( hue, selection ); // 得到选择区域 for ROI
/*SetImageROI基于给定的矩形设置感兴趣区域
void cvSetImageROI( IplImage* image, CvRect rect );
image 图像头.rect ROI矩形. */
cvSetImageROI( mask, selection ); // 得到选择区域 for mask
cvCalcHist( &hue, hist, 0, mask ); // 计算直方图
/*CalcHist计算图像image(s) 的直方图
void cvCalcHist( IplImage** image, CvHistogram* hist,int
accumulate=0, const CvArr* mask=NULL );
image输入图像s (虽然也可以使用 CvMat** ).
hist直方图指针
accumulate累计标识。如果设置则直方图在开始时不被清零。
这个特征保证可以为多个图像计算一个单独的直方图或者在线更新
直方图。
mask 操作 mask, 确定输入图像的哪个象素被计数*/
cvGetMinMaxHistValue( hist, 0, &max_val, 0, 0 ); // 只找最大值
/*void cvGetMinMaxHistValue( const CvHistogram* hist,float*
min_value,
float* max_value,int* min_idx=NULL, int* max_idx=NULL );
hist直方图
min_value直方图最小值的指针
max_value直方图最大值的指针
min_idx数组中最小坐标的指针
max_idx数组中最大坐标的指针
函数 cvGetMinMaxHistValue 发现最大和最小直方块以及它们
的位置。任何输出变量都是可选的。
在具有同样值几个极值中返回具有最小下标索引以字母排
列顺序定的那一个。*/
cvConvertScale( hist->bins, hist->bins, max_val ? 255. / max_val :
0., 0 ); // 缩放 bin 到区间 [0,255]
/*ConvertScale使用线性变换转换数组
void cvConvertScale( const CvArr* src, CvArr* dst, double scale=1,
doubleshift=0 );
#define cvCvtScale cvConvertScale
#define cvScale cvConvertScale
#define cvConvert( src, dst ) cvConvertScale( (src), (dst), 1, 0 )
src原数组.
dst输出数组
scale比例因子.
shift原数组元素按比例缩放后添加的值。*/
cvResetImageROI( hue ); // remove ROI
/*ResetImageROI释放图偈的ROI
void cvResetImageROI( IplImage* image );
image图像头.函数 cvResetImageROI 释放图像 ROI. 释放之后
整个图像被认为是全部被选中的*/
cvResetImageROI( mask );
track_window = selection;
track_object = 1;
cvZero( histimg );
bin_w = histimg->width / hdims; // hdims: 条的个数则 bin_w 为条的宽度
// 画直方图
for( i = 0; i < hdims; i++ )
{
int val =
cvRound( cvGetReal1D(hist->bins,i)*histimg->height/255 );
CvScalar color = hsv2rgb(i*180.f/hdims);
cvRectangle( histimg,
cvPoint(i*bin_w,histimg->height),cvPoint((i+1)*bin_w,histimg->height -
val),color, -1, 8, 0 );
}
}
cvCalcBackProject( &hue, backproject, hist ); // 使用 back project 方法
/*CalcBackProject计算反向投影
void cvCalcBackProject( IplImage** image, CvArr* back_project,
constCvHistogram* hist );
image输入图像 (也可以传递 CvMat** ).
back_project反向投影图像与输入图像具有同样类型.
hist直方图*/
cvAnd( backproject, mask, backproject, 0 );
/*And计算两个数组的每个元素的按位与
void cvAnd( const CvArr* src1, const CvArr* src2, CvArr* dst,
const CvArr*mask=NULL );
src1第一个原数组
src2第二个原数组.
dst输出数组
mask操作覆盖面 8-bit 单通道数组; 只有覆盖面指定的输出
数组被修改*/
// calling CAMSHIFT 算法模块
cvCamShift( backproject, track_window,
cvTermCriteria( CV_TERMCRIT_EPS | CV_TERMCRIT_ITER, 10, 1 ),
&track_comp, &track_box );
/*CamShift发现目标中心尺寸和方向
int cvCamShift( const CvArr* prob_image, CvRect window,
CvTermCriteriacriteria,
CvConnectedComp* comp, CvBox2D* box=NULL );
prob_image目标直方图的反向投影 (见 cvCalcBackProject).
window初始搜索窗口
criteria确定窗口搜索停止的准则
comp生成的结构包含收敛的搜索窗口坐标 (comp->rect 字段)
与窗口内部所有象素点的和(comp->area 字段).
box目标的带边界盒子。如果非 NULL, 则包含目标的尺寸和方
向。
*/
track_window = track_comp.rect;
if( backproject_mode )
cvCvtColor( backproject, image, CV_GRAY2BGR ); // 使用backproject灰度图像
/*CvtColor色彩空间转换
void cvCvtColor( const CvArr* src, CvArr* dst, int code );
src输入的 8-比特 或浮点图像.
dst输出的 8-比特 或浮点图像.
code色彩空间转换*/
if( image->origin )
track_box.angle = -track_box.angle;
cvEllipseBox( image, track_box, CV_RGB(255,0,0), 3, CV_AA,
0 );//绘制椭圆圆弧和椭圆扇形
/*void cvEllipseBox( CvArr* img, CvBox2D box, CvScalar
color,int thickness=1, int line_type=8, int shift=0 );
img 图像。
box 绘制椭圆圆弧所需要的外界矩形.
thickness 分界线线条的粗细程度。
line_type 分界线线条的类型,见CVLINE的描述。
shift 椭圆框顶点坐标的精度。 */
//图形处理
cvBoxPoints( track_box, &pt[0] );
if(chishu==10)///处理图像每十副图检测一次椭圆方框的基准坐标左下角是基准点0,0
{
printf(" x= %d,y= %d ",(int)pt[0].x,(int)pt[0].y);//强制转换变成整形数值
chishu=0;
}
chishu++;
}
if( select_object && selection.width > 0 && selection.height > 0 )
{
cvSetImageROI( image, selection );
cvXorS( image, cvScalarAll(255), image, 0 );
cvResetImageROI( image );
}
cvShowImage( "CamShiftDemo", image );
cvShowImage( "Histogram", histimg );
c = cvWaitKey(10);
if( c == 27 )
break; // exit from for-loop
switch( c )
{
case 'b':
backproject_mode ^= 1;
break;
case 'c':
track_object = 0;
cvZero( histimg );
break;
case 'h':
show_hist ^= 1;
if( !show_hist )
cvDestroyWindow( "Histogram" );
else
cvNamedWindow( "Histogram", 1 );
break ;
default: ;
}
}
cvReleaseCapture( &capture );
cvDestroyWindow("CamShiftDemo");
return 0;
}