[学习笔记]运动目标跟踪

  http://mxmkeep.blog.163.com/blog/static/10649241520096222427732/

主要是用Camshift算法实现,了解Camshift算法,必须先了解以下东西:
什么是直方图:应该先是一个直方结构体,然后用cvCreateHist()函数创建,再用CalcHist计算出相应的直方图。可能是一个色彩分布图吧.....

再者,反向投影cvCalcBackProject
根据获得的色彩直方图将原始图像转化成色彩概率分布图像,这个过程就被称作"Back Projection"。

下面是说明文档,看不懂,结合着慢慢看:


1、直方图:
CvHistogram  结构定义
多维直方图 
typedef struct CvHistogram
{
int type;
CvArr* bins;
float thresh[CV_MAX_DIM][2]; /* for uniform histograms */
float** thresh2; /* for non-uniform histograms */
CvMatND mat; /* embedded matrix header for array histograms */
}CvHistogram;

 

2、CreateHist
创建直方图

创建直方图,函数说明文档,
CvHistogram* cvCreateHist( int dims, int* sizes, int type,
float** ranges=NULL, int uniform=1 );
dims 
直方图维数的数目 
sizes 
直方图维数尺寸的数组 
type 
直方图的表示格式: CV_HIST_ARRAY 意味着直方图数据表示为多维密集数组 CvMatND; CV_HIST_TREE 意味着直方图数据表示为多维稀疏数组 CvSparseMat. 
ranges 
图中方块范围的数组. 它的内容取决于参数 uniform 的值。这个范围的用处是确定何时计算直方图或决定反向映射(backprojected ),每个方块对应于输入图像的哪个/哪组值。 
uniform 
归一化标识。 如果不为0,则ranges[i](0<=i<cDims,译者注:cDims为直方图的维数,对于灰度图为1,彩色图为3)是包含两个元素的范围数组,包括直方图第i维的上界和下界。在第i维上的整个区域 [lower,upper]被分割成 dims[i] 个相等的块(译者注:dims[i]表示直方图第i维的块数),这些块用来确定输入象素的第 i 个值(译者注:对于彩色图像,i确定R, G,或者B)的对应的块;如果为0,则ranges[i]是包含dims[i]+1个元素的范围数组,包括lower0, upper0, lower1, upper1 == lower2, ..., upperdims[i]-1, 其中lowerj 和upperj分别是直方图第i维上第 j 个方块的上下界(针对输入象素的第 i 个值)。任何情况下,输入值如果超出了一个直方块所指定的范围外,都不会被 cvCalcHist 计数,而且会被函数 cvCalcBackProject 置零。 
函数 cvCreateHist 创建一个指定尺寸的直方图,并且返回创建的直方图的指针。 如果数组的 ranges 是 0, 则直方块的范围必须由函数 cvSetHistBinRanges 稍后指定。虽然 cvCalcHist 和 cvCalcBackProject 可以处理 8-比特图像而无需设置任何直方块的范围,但它们都被假设等分 0..255 之间的空间。

 

3、CalcHist
计算图像image(s) 的直方图 

void cvCalcHist( IplImage** image, CvHistogram* hist,
                 int accumulate=0, const CvArr* mask=NULL );
image 
输入图像s (虽然也可以使用 CvMat** ). 
hist 
直方图指针 
accumulate 
累计标识。如果设置,则直方图在开始时不被清零。这个特征保证可以为多个图像计算一个单独的直方图,或者在线更新直方图。 
mask 
操作 mask, 确定输入图像的哪个象素被计数 
函数 cvCalcHist 计算单通道或多通道图像的直方图。 用来增加直方块的数组元素可从相应输入图像的同样位置提取。 Sample. 计算和显示彩色图像的 2D 色调-饱和度图像 

4、CalcBackProject
计算反向投影 

void cvCalcBackProject( IplImage** image, CvArr* back_project, const CvHistogram* hist );
image 
输入图像 (也可以传递 CvMat** ). 
back_project 
反向投影图像,与输入图像具有同样类型. 
hist 
直方图 
函数 cvCalcBackProject 计算直方图的反向投影. 对于所有输入的单通道图像同一位置的象素数组,该函数根据相应的象素数组(RGB),放置其对应的直方块的值到输出图像中。用统计学术语,输出图像象素点的值是观测数组在某个分布(直方图)下的概率。 例如,为了发现图像中的红色目标,可以这么做: 

对红色物体计算色调直方图,假设图像仅仅包含该物体。则直方图有可能有极值,对应着红颜色。 
对将要搜索目标的输入图像,使用直方图计算其色调平面的反向投影,然后对图像做阈值操作。 
在产生的图像中发现连通部分,然后使用某种附加准则选择正确的部分,比如最大的连同部分。 
这是 Camshift 彩色目标跟踪器中的一个逼进算法,除了第三步,CAMSHIFT 算法使用了上一次目标位置来定位反向投影中的目标。 

opencv之camshift(转自http://blog.csdn.net/houdy/category/56104.aspx
看了几遍都不是很明白  -_-!!

1--Back Projection

CamShift算法,即"Continuously Apative Mean-Shift"算法,是一种运动跟踪算法。它主要通过视频图像中运动物体的颜色信息来达到跟踪的目的。我把这个算法分解成三个部分,便于理解:
1) Back Projection计算
2) Mean Shift算法
3) CamShift算法
在这里主要讨论Back Projection,在随后的文章中继续讨论后面两个算法。

Back Projection
计算Back Projection的步骤是这样的:
1. 计算被跟踪目标的色彩直方图。在各种色彩空间中,只有HSI空间(或与HSI类似的色彩空间)中的H分量可以表示颜色信息。所以在具体的计算过程中,首先将其他的色彩空间的值转化到HSI空间,然后会其中的H分量做1D直方图计算。
2. 根据获得的色彩直方图将原始图像转化成色彩概率分布图像,这个过程就被称作"Back Projection"。
在OpenCV中的直方图函数中,包含Back Projection的函数,函数原型是:
void cvCalcBackProject(IplImage** img, CvArr** backproject, const CvHistogram* hist);
传递给这个函数的参数有三个:
1. IplImage** img:存放原始图像,输入。
2. CvArr** backproject:存放Back Projection结果,输出。
3. CvHistogram* hist:存放直方图,输入

下面就给出计算Back Projection的OpenCV代码。
1.准备一张只包含被跟踪目标的图片,将色彩空间转化到HSI空间,获得其中的H分量:
IplImage* target=cvLoadImage("target.bmp",-1); //装载图片
IplImage* target_hsv=cvCreateImage( cvGetSize(target), IPL_DEPTH_8U, 3 );
IplImage* target_hue=cvCreateImage( cvGetSize(target), IPL_DEPTH_8U, 3 );
cvCvtColor(target,target_hsv,CV_BGR2HSV); //转化到HSV空间
cvSplit( target_hsv, target_hue, NULL, NULL, NULL ); //获得H分量
2.计算H分量的直方图,即1D直方图:
IplImage* h_plane=cvCreateImage( cvGetSize(target_hsv),IPL_DEPTH_8U,1 );
int hist_size[]={255}; //将H分量的值量化到[0,255]
float* ranges[]={ {0,360} }; //H分量的取值范围是[0,360)
CvHistogram* hist=cvCreateHist(1, hist_size, ranges, 1);
cvCalcHist(&target_hue, hist, 0, NULL);
在这里需要考虑H分量的取值范围的问题,H分量的取值范围是[0,360),这个取值范围的值不能用一个byte来表示,为了能用一个byte表示,需要将H值做适当的量化处理,在这里我们将H分量的范围量化到[0,255].
4.计算Back Projection:
IplImage* rawImage;
//----------------------------------------------
//get from video frame,unsigned byte,one channel
//----------------------------------------------
IplImage* result=cvCreateImage(cvGetSize(rawImage),IPL_DEPTH_8U,1);
cvCalcBackProject(&rawImage,result,hist);
5.结果:result即为我们需要的.

2--Mean Shift算法
这里来到了CamShift算法,OpenCV实现的第二部分,这一次重点讨论Mean Shift算法。
在讨论Mean Shift算法之前,首先讨论在2D概率分布图像中,如何计算某个区域的重心(Mass Center)的问题,重心可以通过以下公式来计算:
1.计算区域内0阶矩
for(int i=0;i< height;i++)
for(int j=0;j< width;j++)
M00+=I(i,j)
2.区域内1阶矩:
for(int i=0;i< height;i++)
for(int j=0;j< width;j++)
{
M10+=i*I(i,j);
M01+=j*I(i,j);
}
3.则Mass Center为:
Xc=M10/M00; Yc=M01/M00
接下来,讨论Mean Shift算法的具体步骤,Mean Shift算法可以分为以下4步:
1.选择窗的大小和初始位置.
2.计算此时窗口内的Mass Center.
3.调整窗口的中心到Mass Center.
4.重复2和3,直到窗口中心"会聚",即每次窗口移动的距离小于一定的阈值。

在OpenCV中,提供Mean Shift算法的函数,函数的原型是:
int cvMeanShift(IplImage* imgprob,CvRect windowIn,
CvTermCriteria criteria,CvConnectedComp* out);

需要的参数为:
1.IplImage* imgprob:2D概率分布图像,传入;
2.CvRect windowIn:初始的窗口,传入;
3.CvTermCriteria criteria:停止迭代的标准,传入;
4.CvConnectedComp* out:查询结果,传出。
(注:构造CvTermCriteria变量需要三个参数,一个是类型,另一个是迭代的最大次数,最后一个表示特定的阈值。例如可以这样构造criteria:criteria=cvTermCriteria(CV_TERMCRIT_ITER|CV_TERMCRIT_EPS,10,0.1)。)

返回的参数:
1.int:迭代的次数。

3--CamShift算法

1.原理
在了解了MeanShift算法以后,我们将MeanShift算法扩展到连续图像序列(一般都是指视频图像序列),这样就形成了CamShift算法。CamShift算法的全称是"Continuously Apaptive Mean-SHIFT",它的基本思想是视频图像的所有帧作MeanShift运算,并将上一帧的结果(即Search Window的中心和大小)作为下一帧MeanShift算法的Search Window的初始值,如此迭代下去,就可以实现对目标的跟踪。整个算法的具体步骤分5步:
Step 1:将整个图像设为搜寻区域。
Step 2:初始话Search Window的大小和位置。
Step 3:计算Search Window内的彩色概率分布,此区域的大小比Search Window要稍微大一点。
Step 4:运行MeanShift。获得Search Window新的位置和大小。
Step 5:在下一帧视频图像中,用Step 3获得的值初始化Search Window的位置和大小。跳转到Step 3继续运行。

2.实现
在OpenCV中,有实现CamShift算法的函数,此函数的原型是:
cvCamShift(IplImage* imgprob, CvRect windowIn,
CvTermCriteria criteria,
CvConnectedComp* out, CvBox2D* box=0);
其中:
imgprob:色彩概率分布图像。
windowIn:Search Window的初始值。
Criteria:用来判断搜寻是否停止的一个标准。
out:保存运算结果,包括新的Search Window的位置和面积。
box:包含被跟踪物体的最小矩形。

说明:
1.在OpenCV 4.0 beta的目录中,有CamShift的例子。遗憾的是这个例子目标的跟踪是半自动的,即需要人手工选定一个目标。我正在努力尝试全自动的目标跟踪,希望可以和大家能在这方面与大家交流。

5.
运动目标跟踪与检测的源代码(CAMSHIFT 算法)
From http://blog.csdn.net/hunnish/archive/2004/09/07/97049.aspx

采用 CAMSHIFT 算法快速跟踪和检测运动目标的 C/C++ 源代码,OPENCV BETA 4.0 版本在其 SAMPLE 中给出了这个例子。算法的简单描述如下(英文):

This application demonstrates a fast, simple color tracking algorithm that can be used to track faces, hands . The CAMSHIFT algorithm is a modification of the Meanshift algorithm which is a robust statistical method of finding the mode (top) of a probability distribution. Both CAMSHIFT and Meanshift algorithms exist in the library. While it is a very fast and simple method of tracking, because CAMSHIFT tracks the center and size of the probability distribution of an object, it is only as good as the probability distribution that you produce for the object. Typically the probability distribution is derived from color via a histogram, although it could be produced from correlation, recognition scores or bolstered by frame differencing or motion detection schemes, or joint probabilities of different colors/motions etc.

In this application, we use only the most simplistic approach: A 1-D Hue histogram is sampled from the object in an HSV color space version of the image. To produce the probability image to track, histogram "back projection" (we replace image pixels by their histogram hue value) is used.

算法的详细情况,请看论文:

http://www.assuredigit.com/incoming/camshift.pdf


实现彩色目标跟踪,就调用opencv中的camshift算法。
个人对camshift算法理解是:直方图,应该是颜色概率分布图,然后去概率小的颜色。
把直方图 与 被搜索目标的图像 进行反向投影运算,剩下颜色与直方图中概率颜色近似的像素
然后用camshift进行迭代运算,把目标框框不断逼近剩下的颜色区域
使得圈出的范围越来越逼近想要搜寻的目标对象。详细请看上两篇camshift算法的具体说明。
程序来自:opencv教程 例题 5 -22 (加上自己的注释)
书中的在VC6下控制台实现,我把它改到C++ builder 6下实现。
运行后效果图:

 

程序代码:


Co de highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/

--> // ---------------------------------------------------------------------------
// --------------头文件---------------
#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
// ---------------------------------------------------------------------------


 


Co de highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/

--> // ---------------------------------------------------------------------------
// ----------------实现文件-----------------------
#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 ;
}
// ---------------------------------------------------------------------------

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值