[学习笔记]BCB下实现彩色目标跟踪
实现彩色目标跟踪,就调用opencv中的camshift算法。
个人对camshift算法理解是:搜索目标的直方图 与 被搜索目标的直方图,通过某种迭代运算。
使得圈出的范围越来越逼近想要搜寻的目标对象。详细请看camshift算法的具体说明。
程序来自:opencv教程 例题 5 -22 (加上自己的注释)
书中的在VC6下控制台实现,我把它改到C++ builder 6下实现。
运行后效果图:
![]()
个人对camshift算法理解是:搜索目标的直方图 与 被搜索目标的直方图,通过某种迭代运算。
使得圈出的范围越来越逼近想要搜寻的目标对象。详细请看camshift算法的具体说明。
程序来自:opencv教程 例题 5 -22 (加上自己的注释)
书中的在VC6下控制台实现,我把它改到C++ builder 6下实现。
运行后效果图:

程序代码:
//---------------------------------------------------------------------------
//--------------头文件---------------
#ifndef camshiftH
#define camshiftH
//---------------------------------------------------------------------------
#include <Classes.hpp>
#include <Controls.hpp>
#include <StdCtrls.hpp>
#include <Forms.hpp>
#include "cv.h"
#include "highgui.h"
//---------------------------------------------------------------------------
//==================全局变量=====================
IplImage *image = 0,*hsv = 0,* hue = 0;
IplImage *mask = 0, *backproject = 0, *histimg = 0;
CvHistogram* hist = 0; //直方
CvCapture* capture = 0;
int backproject_mode = 0; //表示反向投影窗体是否打开
int select_object = 0; //表示是否已开始选
int track_object = 0; //表示是否已选好区域
int show_hist = 0; //表示是否显示直方图窗体
CvPoint origin; //选中区域的原始点
CvRect selection; //选中的矩形区域
CvRect track_window; //
CvBox2D track_box; //tracking 返回的区域box,带角度
CvConnectedComp track_comp;
int hdims = 48; //划分HIST的个数,越高越精确
float hranges_arr[] = {0,180}; //定义直方图内方块的范围,第i维上下界
float* hranges = hranges_arr;
int vmin = 10, vmax = 256, smin = 30; //三个滚动条的初始值
bool closeCAM = false;
//------------全局函数--------------
//定义鼠标回调函数,此处设为全局函数,便于回调
//若想设为类成员函数,参考回调函数说明
//后面加了个指针参数,不加出错,vc中确不会错,不知道为什么。
//查看opencv中Callback函数定义,确实要5个参数
void on_mouse(int event,int x,int y,int flags,void* t = NULL)
{
if(!image)return;
if(image->origin) //windows的bitmap标准
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; //选中区域有效,则表示范围已选好
}
}
//---------------------------------------------------------------------------
CvScalar hsv2rgb(float hue) //直立方H值转为RGB,为什么是这样算的,不清楚
{
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.033333333333333333333f;
sector = cvFloor(hue); //cvFloor 返回不大于参数的最大整数值
p = cvRound(255 * (hue - sector)); //cvRound 返回和参数最接近的整数值
p ^= sector & 1 ? 255 : 0;
rgb[sector_data[sector][0]] = 255;
rgb[sector_data[sector][1]] = 0;
rgb[sector_data[sector][2]] = p;
return cvScalar(rgb[2],rgb[1],rgb[0],0);
}
//---------------------------------------------------------------------------
class TForm1 : public TForm
{
__published: // IDE-managed Components
TButton *Button1;
TButton *Button2;
TButton *Button3;
TButton *Button4;
TButton *Button5;
void __fastcall Button1Click(TObject *Sender);
void __fastcall Button3Click(TObject *Sender);
void __fastcall Button4Click(TObject *Sender);
void __fastcall Button5Click(TObject *Sender);
void __fastcall Button2Click(TObject *Sender);
private: // User declarations
public: // User declarations
__fastcall TForm1(TComponent* Owner);
};
//---------------------------------------------------------------------------
extern PACKAGE TForm1 *Form1;
//---------------------------------------------------------------------------
#endif
//---------------------------------------------------------------------------
//--------------头文件---------------
#ifndef camshiftH
#define camshiftH
//---------------------------------------------------------------------------
#include <Classes.hpp>
#include <Controls.hpp>
#include <StdCtrls.hpp>
#include <Forms.hpp>
#include "cv.h"
#include "highgui.h"
//---------------------------------------------------------------------------
//==================全局变量=====================
IplImage *image = 0,*hsv = 0,* hue = 0;
IplImage *mask = 0, *backproject = 0, *histimg = 0;
CvHistogram* hist = 0; //直方
CvCapture* capture = 0;
int backproject_mode = 0; //表示反向投影窗体是否打开
int select_object = 0; //表示是否已开始选
int track_object = 0; //表示是否已选好区域
int show_hist = 0; //表示是否显示直方图窗体
CvPoint origin; //选中区域的原始点
CvRect selection; //选中的矩形区域
CvRect track_window; //
CvBox2D track_box; //tracking 返回的区域box,带角度
CvConnectedComp track_comp;
int hdims = 48; //划分HIST的个数,越高越精确
float hranges_arr[] = {0,180}; //定义直方图内方块的范围,第i维上下界
float* hranges = hranges_arr;
int vmin = 10, vmax = 256, smin = 30; //三个滚动条的初始值
bool closeCAM = false;
//------------全局函数--------------
//定义鼠标回调函数,此处设为全局函数,便于回调
//若想设为类成员函数,参考回调函数说明
//后面加了个指针参数,不加出错,vc中确不会错,不知道为什么。
//查看opencv中Callback函数定义,确实要5个参数
void on_mouse(int event,int x,int y,int flags,void* t = NULL)
{
if(!image)return;
if(image->origin) //windows的bitmap标准
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; //选中区域有效,则表示范围已选好
}
}
//---------------------------------------------------------------------------
CvScalar hsv2rgb(float hue) //直立方H值转为RGB,为什么是这样算的,不清楚
{
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.033333333333333333333f;
sector = cvFloor(hue); //cvFloor 返回不大于参数的最大整数值
p = cvRound(255 * (hue - sector)); //cvRound 返回和参数最接近的整数值
p ^= sector & 1 ? 255 : 0;
rgb[sector_data[sector][0]] = 255;
rgb[sector_data[sector][1]] = 0;
rgb[sector_data[sector][2]] = p;
return cvScalar(rgb[2],rgb[1],rgb[0],0);
}
//---------------------------------------------------------------------------
class TForm1 : public TForm
{
__published: // IDE-managed Components
TButton *Button1;
TButton *Button2;
TButton *Button3;
TButton *Button4;
TButton *Button5;
void __fastcall Button1Click(TObject *Sender);
void __fastcall Button3Click(TObject *Sender);
void __fastcall Button4Click(TObject *Sender);
void __fastcall Button5Click(TObject *Sender);
void __fastcall Button2Click(TObject *Sender);
private: // User declarations
public: // User declarations
__fastcall TForm1(TComponent* Owner);
};
//---------------------------------------------------------------------------
extern PACKAGE TForm1 *Form1;
//---------------------------------------------------------------------------
#endif
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
//----------------实现文件-----------------------
#include <vcl.h>
#pragma hdrstop
#include "camshift.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
IplImage* frame = 0;
capture = cvCaptureFromCAM(0);
if(!capture)
{
ShowMessage("启动摄像头失败!");
return;
}
cvNamedWindow("目标捕获",1);
//设定鼠标回调事件
cvSetMouseCallback("目标捕获",on_mouse,NULL);
//创建滚动条
cvCreateTrackbar("掩码范围A","目标捕获",&vmin,256,0);
cvCreateTrackbar("掩码范围B","目标捕获",&vmax,256,0);
cvCreateTrackbar("Smin","目标捕获",&smin,256,0);
while(frame = cvQueryFrame(capture))
{
int i,bin_w,c;
if(!image)
{
//分配内存
image = cvCreateImage(cvSize(frame->width,frame->height),8,3);
image->origin = frame->origin;
hsv = cvCreateImage(cvSize(frame->width,frame->height),8,3);
hue = cvCreateImage(cvSize(frame->width,frame->height),8,1);
mask = cvCreateImage(cvSize(frame->width,frame->height),8,1);
backproject = cvCreateImage(cvSize(frame->width,frame->height),8,1);
hist = cvCreateHist(1,&hdims,CV_HIST_ARRAY,&hranges,1);
histimg = cvCreateImage(cvSize(320,200),8,3);
cvZero(histimg);
}
cvCopy(frame,image,0);
cvCvtColor(image,hsv,CV_BGR2HSV); //彩色空间转换,从BGR到HSV
if(track_object)
{
int _vmin = vmin, _vmax = vmax;
//对色彩空间在一定范围内二值化,得出掩码mask
cvInRangeS(hsv,cvScalar(0,smin,MIN(_vmin,_vmax),0),
cvScalar(180,256,MAX(_vmin,_vmax),0),mask);
cvSplit(hsv,hue,0,0,0); //只提取HUE分量
if(track_object < 0) //区域已选择完成
{
float max_val = 0.f;
cvSetImageROI(hue,selection); //设置感兴趣区域
cvSetImageROI(mask,selection);
cvCalcHist(&hue,hist,0,mask); //用掩码计算出直方图
cvGetMinMaxHistValue(hist,0,&max_val,0,0); //找最大值
//线性变换数组,缩放bin到[0,255]区间
cvConvertScale(hist->bins,hist->bins,
max_val ? 255./max_val : 0.,0);
cvResetImageROI(hue); //取消感兴趣区域
cvResetImageROI(mask);
track_window = selection;
track_object = 1;
cvZero(histimg);
bin_w = histimg->width /hdims; //直方图中每条柱子的宽度
//画直方图
for(i = 0;i < hdims;i++)
{
//GetReal*D 返回单通道数组的指定元素
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);
}
}
//计算直方图的反向投影,backproject是想要的结果
cvCalcBackProject(&hue,backproject,hist);
//逻辑相与,用掩码选出相应区域
cvAnd(backproject,mask,backproject,0);
//调用Camshift算法模块
cvCamShift(backproject,track_window,cvTermCriteria(CV_TERMCRIT_EPS |
CV_TERMCRIT_ITER,10,1),&track_comp,&track_box);
track_window = track_comp.rect;
if(backproject_mode)
cvCvtColor(backproject,image,CV_GRAY2BGR); //使用backproject灰度图像
if(image->origin)
track_box.angle = -track_box.angle;
cvEllipseBox(image,track_box,CV_RGB(255,0,0),3,CV_AA,0);
}
if(select_object && selection.width>0 && selection.height>0)
{
cvSetImageROI(image,selection);
cvXorS(image,cvScalarAll(255),image,0);
cvResetImageROI(image);
}
cvShowImage("目标捕获",image);
cvShowImage("直方图",histimg);
if(cvWaitKey(10) == 27 || closeCAM)break;
}
cvReleaseCapture(&capture);
cvDestroyWindow("目标捕获");
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button3Click(TObject *Sender)
{
if(capture)
backproject_mode ^= 1;
else
ShowMessage("您还未启动摄像头");
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button4Click(TObject *Sender)
{
if(!capture)
{
ShowMessage("您还未启动摄像头");
return;
}
track_object = 0;
cvZero(histimg);
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button5Click(TObject *Sender)
{
if(!capture)
{
ShowMessage("您还未启动摄像头");
return;
}
show_hist ^= 1;
if(!show_hist)
{
cvDestroyWindow("直方图");
this->Button5->Caption = "打开直方图窗体";
}
else
{
cvNamedWindow("直方图",1);
this->Button5->Caption = "关闭直方图窗体";
}
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button2Click(TObject *Sender)
{
if(!capture)
{
ShowMessage("您还未启动摄像头");
return;
}
closeCAM = true;
}
//---------------------------------------------------------------------------
//----------------实现文件-----------------------
#include <vcl.h>
#pragma hdrstop
#include "camshift.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
IplImage* frame = 0;
capture = cvCaptureFromCAM(0);
if(!capture)
{
ShowMessage("启动摄像头失败!");
return;
}
cvNamedWindow("目标捕获",1);
//设定鼠标回调事件
cvSetMouseCallback("目标捕获",on_mouse,NULL);
//创建滚动条
cvCreateTrackbar("掩码范围A","目标捕获",&vmin,256,0);
cvCreateTrackbar("掩码范围B","目标捕获",&vmax,256,0);
cvCreateTrackbar("Smin","目标捕获",&smin,256,0);
while(frame = cvQueryFrame(capture))
{
int i,bin_w,c;
if(!image)
{
//分配内存
image = cvCreateImage(cvSize(frame->width,frame->height),8,3);
image->origin = frame->origin;
hsv = cvCreateImage(cvSize(frame->width,frame->height),8,3);
hue = cvCreateImage(cvSize(frame->width,frame->height),8,1);
mask = cvCreateImage(cvSize(frame->width,frame->height),8,1);
backproject = cvCreateImage(cvSize(frame->width,frame->height),8,1);
hist = cvCreateHist(1,&hdims,CV_HIST_ARRAY,&hranges,1);
histimg = cvCreateImage(cvSize(320,200),8,3);
cvZero(histimg);
}
cvCopy(frame,image,0);
cvCvtColor(image,hsv,CV_BGR2HSV); //彩色空间转换,从BGR到HSV
if(track_object)
{
int _vmin = vmin, _vmax = vmax;
//对色彩空间在一定范围内二值化,得出掩码mask
cvInRangeS(hsv,cvScalar(0,smin,MIN(_vmin,_vmax),0),
cvScalar(180,256,MAX(_vmin,_vmax),0),mask);
cvSplit(hsv,hue,0,0,0); //只提取HUE分量
if(track_object < 0) //区域已选择完成
{
float max_val = 0.f;
cvSetImageROI(hue,selection); //设置感兴趣区域
cvSetImageROI(mask,selection);
cvCalcHist(&hue,hist,0,mask); //用掩码计算出直方图
cvGetMinMaxHistValue(hist,0,&max_val,0,0); //找最大值
//线性变换数组,缩放bin到[0,255]区间
cvConvertScale(hist->bins,hist->bins,
max_val ? 255./max_val : 0.,0);
cvResetImageROI(hue); //取消感兴趣区域
cvResetImageROI(mask);
track_window = selection;
track_object = 1;
cvZero(histimg);
bin_w = histimg->width /hdims; //直方图中每条柱子的宽度
//画直方图
for(i = 0;i < hdims;i++)
{
//GetReal*D 返回单通道数组的指定元素
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);
}
}
//计算直方图的反向投影,backproject是想要的结果
cvCalcBackProject(&hue,backproject,hist);
//逻辑相与,用掩码选出相应区域
cvAnd(backproject,mask,backproject,0);
//调用Camshift算法模块
cvCamShift(backproject,track_window,cvTermCriteria(CV_TERMCRIT_EPS |
CV_TERMCRIT_ITER,10,1),&track_comp,&track_box);
track_window = track_comp.rect;
if(backproject_mode)
cvCvtColor(backproject,image,CV_GRAY2BGR); //使用backproject灰度图像
if(image->origin)
track_box.angle = -track_box.angle;
cvEllipseBox(image,track_box,CV_RGB(255,0,0),3,CV_AA,0);
}
if(select_object && selection.width>0 && selection.height>0)
{
cvSetImageROI(image,selection);
cvXorS(image,cvScalarAll(255),image,0);
cvResetImageROI(image);
}
cvShowImage("目标捕获",image);
cvShowImage("直方图",histimg);
if(cvWaitKey(10) == 27 || closeCAM)break;
}
cvReleaseCapture(&capture);
cvDestroyWindow("目标捕获");
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button3Click(TObject *Sender)
{
if(capture)
backproject_mode ^= 1;
else
ShowMessage("您还未启动摄像头");
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button4Click(TObject *Sender)
{
if(!capture)
{
ShowMessage("您还未启动摄像头");
return;
}
track_object = 0;
cvZero(histimg);
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button5Click(TObject *Sender)
{
if(!capture)
{
ShowMessage("您还未启动摄像头");
return;
}
show_hist ^= 1;
if(!show_hist)
{
cvDestroyWindow("直方图");
this->Button5->Caption = "打开直方图窗体";
}
else
{
cvNamedWindow("直方图",1);
this->Button5->Caption = "关闭直方图窗体";
}
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button2Click(TObject *Sender)
{
if(!capture)
{
ShowMessage("您还未启动摄像头");
return;
}
closeCAM = true;
}
//---------------------------------------------------------------------------
浙公网安备 33010602011771号