第四章 学习OpenCV——细说HighGUI

第四章 学习OpenCV——细说HighGUI

目录

  • 第四章 学习OpenCV细说HighGUI
    • 目录
      • 例4-1 使用鼠标在窗口中画方形
      • 例4-2 使用滚动条实现一个开关功能用户可以选择打开或关闭
      • 例4-3 视频图像读入处理及合并显示
      • 例4-4 读入图像获取对应像素的BGR值坐标值并显示于图片上
      • 例4-5 读入图像鼠标按住画矩形框再次点击时重新绘制生成颜色直方图
      • 例4-6 读入视频文件一个滚动条控制播放位置以10为步长跳进另一滚动条控制停止播放
      • 例4-7 创建一个简单画图程序
      • 例4-8 创建一个简单标签程序
      • 例4-9 透视变换
      • 例4-10 有趣的人脸检测Alpha融合程序
      • 例4-11 图像稳定程序

例4-1 使用鼠标在窗口中画方形

调用cvSetMouseCallback()函数来注册鼠标回调函数,以响应鼠标事件,并根据具体的event来确定给出的响应。具体代码如下:

#include 
#include   
#include   
#include  

using namespace std;

void my_mouse_callback(int event, int x, int y,
    int flags, void* param);        //声明回调函数

CvRect box;                         //所画矩形的角点、宽高
bool drawing_box = false;           //判断左键是否按下,按下为true              

void draw_box(IplImage* img, CvRect rect)           //绘制矩形
{
    cvRectangle(img, 
        cvPoint(box.x,box.y),                       //两个对顶点
        cvPoint(box.x+box.width,box.y+box.height),      
        cvScalar(0x00, 0x00, 0xff));                //BGR 设置为红色
}

int main(int argc, char* argv[])
{
    box = cvRect(-1, -1, 0, 0);             //(x,y,width,height)

    IplImage* image = cvCreateImage(cvSize(200, 200), IPL_DEPTH_8U, 3); //200*200 8位 3通道
    cvZero(image);                          //清零
    IplImage* temp = cvCloneImage(image);   //克隆图像,复制图像随原图像变化
    cvNamedWindow("Box Example");

    cvSetMouseCallback("Box Example", my_mouse_callback, (void*)image); //注册回调函数
    //事件产生窗口、回调函数名、事件产生影响的类

    while (1)
    {
        cvCopyImage(image, temp);           //复制图像,不随原图像变化(清除temp图像上实时显示的那部分矩形)

        if (drawing_box)                    //左键按下,开始绘制(左键按下时动态显示矩形)
            draw_box(temp, box);            //在temp图像上画矩形
        cvShowImage("Box Example", temp);   //显示temp
        if (cvWaitKey(15) == 27)            //15ms刷新一次
            break;
    }
    cvReleaseImage(&image);
    cvReleaseImage(&temp);
    cvDestroyWindow("Box Example");
}

void my_mouse_callback(int event, int x, int y, int flags, void* param)
{
    IplImage* image = (IplImage*)param;
    switch (event)
    {
        case CV_EVENT_MOUSEMOVE:
        {
            if (drawing_box)                //左键已按下标志,开始绘制
            {
                box.width = x - box.x;      //x为当前鼠标的位置 
                box.height = y - box.y;     //box.x为左键按下时的位置
            }
        }
        break;
        case CV_EVENT_LBUTTONDOWN:
        {
            drawing_box = true;
            box = cvRect(x, y, 0, 0);   //记录矩形起始位置
        }
        break;
        case CV_EVENT_LBUTTONUP:
        {
            drawing_box = false;        //左键已松开标志,结束绘制
            if (box.width<0)            //将width转化为正数,保证绘图从左向右进行
            {
                box.x += box.width;     //box.x=box.x+box.width 回移起点位置
                box.width *= -1;        //box.x为左键按下时的位置
            }
            if (box.height<0)           //将height转化为正数,保证绘图从上向下进行
            {
                box.y += box.height;    
                box.height *= -1;
            }
            draw_box(image, box);
        }
        break;
    }
}

运行结果如下图:
第四章 学习OpenCV——细说HighGUI_第1张图片

例4-2 使用滚动条实现一个开关功能,用户可以选择打开或关闭

调用cvCreateTrackbar()函数来创建一个2值滚动条,以实现按钮开关功能。具体代码如下:

#include 
#include   
#include   
#include  

using namespace std;

int g_switch_value = 0;         

void switch_off_function()
{
    cout << "switch off" << endl;
    cout << g_switch_value << endl;
}

void switch_on_function()
{
    cout << "switch on" << endl;
    cout << g_switch_value << endl;
}

void switch_callback(int position)
{
    if (position == 0)
        switch_off_function();
    else
        switch_on_function();
}

int main(int argc, char* argv[])
{
    cvNamedWindow("Demo Window",1);

    cvCreateTrackbar("Switch", "Demo Window", &g_switch_value, 1, switch_callback);    
    //创建滚动条(滚动条名称,所属窗口,当前位置指针【拖动时,替换指针指向的整数】,最大值,回调函数)

    while (1)
    {
        if (cvWaitKey(15) == 27)            //15ms刷新一次
            break;
    }
    cvDestroyWindow("Demo Window");
}

运行结果如下图:
第四章 学习OpenCV——细说HighGUI_第2张图片

例4-3 视频图像读入、处理及合并显示

本例完成的工作如下:

 1. 从视频文件中读入数据;
 2. 将读入的图像转换为灰度图;
 3. 对图像做Canny边缘检测;
 4. 将三个过程的处理结果显示在不同的窗口中;
 5. 将三个步骤显示在一个图像中;
 6. 在图像的三个不同的部分上分别写上合适的文字标签;

具体代码如下:

#include <cv.h>
#include <highgui.h>

using namespace std;

IplImage* doCanny(IplImage *in, double lowThresh, double highThresh, double aperture)
{
    if (in->nChannels != 1)
    {
        cout << in->nChannels << endl;
        cout << "不能输出" << endl;
        return(0);          //Canny只能处理灰度图像
    }
    IplImage* out = cvCreateImage(cvGetSize(in), IPL_DEPTH_8U, 1);   //cvSize(cvGetSize(image)),报错
    cvCanny(in, out, lowThresh, highThresh, aperture);  
    //(输入,输出,控制边缘连接,强边缘初始分割,Sobel算子大内核大小) 
    return(out);
}

int main(int argc, char** argv)
{
    cvNamedWindow("Natural", CV_WINDOW_AUTOSIZE);
    cvNamedWindow("Gray", CV_WINDOW_AUTOSIZE);
    cvNamedWindow("Canny", CV_WINDOW_AUTOSIZE);
    cvNamedWindow("All", CV_WINDOW_AUTOSIZE);
    CvCapture* capture = cvCreateFileCapture("D:\\Template\\OpenCV\\Template21_AVI2Gray_Canny_Text\\ConsoleApplication1\\好想告诉你.avi");

    IplImage* frame1=cvQueryFrame(capture);

    CvFont font;                //字体变量

    int width = (int)cvGetCaptureProperty(capture, CV_CAP_PROP_FRAME_WIDTH);
    int height = (int)cvGetCaptureProperty(capture, CV_CAP_PROP_FRAME_HEIGHT);
    CvSize size = cvSize(width,height);
    CvSize size1 = cvSize(width * 3, height);

    IplImage* frame2 = cvCreateImage(size, IPL_DEPTH_8U, 1);                //单通道,8位,相同大小,灰度
    IplImage* frame3 = cvCreateImage(size, IPL_DEPTH_8U, 1);                //Canny
    IplImage* frame_gray = cvCreateImage(size, IPL_DEPTH_8U, 3);            //三通道
    IplImage* frame_canny = cvCreateImage(size, IPL_DEPTH_8U, 3);
    IplImage* frame4 = cvCreateImage(size1, IPL_DEPTH_8U, frame1->nChannels);   //All

    cvZero(frame4);

    //创建三个新的图像头,通道、深度与原图相同,分别指向开始处、1/3处、2/3处
    IplImage* imgheader1 = cvCreateImageHeader(size, frame1->depth, frame1->nChannels);   
    //IplImage* imgheader2 = cvCreateImageHeader(size, frame2->depth, frame2->nChannels);//错 压缩为1/3
    //IplImage* imgheader3 = cvCreateImageHeader(size, frame3->depth, frame3->nChannels);     
    IplImage* imgheader2 = cvCreateImageHeader(size, frame2->depth, frame1->nChannels);      //正确
    IplImage* imgheader3 = cvCreateImageHeader(size, frame3->depth, frame1->nChannels);  

    imgheader1->widthStep = frame4->widthStep;          //设置widthStep与原图像相同
    imgheader2->widthStep = frame4->widthStep;
    imgheader3->widthStep = frame4->widthStep;

    imgheader1->origin = frame4->origin;                //设置原点与原图像相同 左上
    imgheader2->origin = frame4->origin;
    imgheader3->origin = frame4->origin;

    imgheader1->imageData = frame4->imageData;                                      //指向开始像素位置
    imgheader2->imageData = frame4->imageData + 1 * 3 * width * frame2->nChannels;  //指向1/3像素位置
    imgheader3->imageData = frame4->imageData + 2 * 3 * width * frame3->nChannels;  //指向2/3像素位置

    cvInitFont(&font, CV_FONT_HERSHEY_COMPLEX, 1.0, 1.0, 1.0);

    while (1)
    {
        frame1 = cvQueryFrame(capture);
        if (!frame1) break;

        cvConvertImage(frame1, frame2, CV_BGR2GRAY);        //图像强制转化为gray  ****

        frame3 = doCanny(frame2, 50, 150, 3);               //灰度图做Canny边缘检测

        //cvCopy(frame1, imgheader1);           //错误,此时显示后两幅图像会被压缩为1/3
        //cvCopy(frame2, imgheader2);
        //cvCopy(frame3, imgheader3);

        cvCopy(frame1, imgheader1);                         //正确
        cvConvertImage(frame2, frame_gray, CV_GRAY2BGR);
        cvCopy(frame_gray, imgheader2);
        cvConvertImage(frame3, frame_canny, CV_GRAY2BGR);
        cvCopy(frame_canny, imgheader3);

        cvPutText(frame4, "Origin", cvPoint(100, 25), &font, cvScalar(255, 255, 255));  //文字标签
        cvPutText(frame4, "Gray", cvPoint(450, 25), &font, cvScalar(255, 255, 255));
        cvPutText(frame4, "Canny", cvPoint(750, 25), &font, cvScalar(255, 255, 255));

        cvShowImage("Natural", frame1);
        cvShowImage("Gray", frame2);
        cvShowImage("Canny", frame3);
        cvShowImage("All", frame4);

        char c = cvWaitKey(32);
        if (c == 27) break;
    }

    cvReleaseCapture(&capture);
    cvDestroyWindow("Natural");
    cvDestroyWindow("Gray");
    cvDestroyWindow("Canny");
    cvDestroyWindow("All");
}

运行结果如下图:
第四章 学习OpenCV——细说HighGUI_第3张图片
本例的难点在b中,即实现将所有三个步骤显示在一个图像中,书中提示创建三个图像头,使图像头分别指向图像数据的开始处,1/3处,2/3处,然后使用cvCopy()复制。此时,在创建总的显示图像和三个图像头时,图像的通道数设置很关键,因为三张图像同时显示在一张图片中,但三个图像的通道是不同的,图1位三通道,其后两个为单通道。如果总的显示图像创建为三通道,而三个图像头为一个三通道,两个单通道,上述代码中两部分代码做如下修改:
Part1:

    IplImage* imgheader2 = cvCreateImageHeader(size, frame2->depth, frame2->nChannels);   
    IplImage* imgheader3 = cvCreateImageHeader(size, frame3->depth, frame3->nChannels); 

Part2:

        cvCopy(frame1, imgheader1);
        cvCopy(frame2, imgheader2);
        cvCopy(frame3, imgheader3);

此时的显示的整个图像,后两个图像头对应的图像区域会被压缩为1/3,究其原因就是未将后两个图像头转化为三通道图像,其结果如下图:
第四章 学习OpenCV——细说HighGUI_第4张图片

例4-4 读入图像,获取对应像素的BGR值、坐标值,并显示于图片上

本例完成的工作如下:

 1. 读入并显示一张图片;
 2. 鼠标点击图像时获取对应像素的颜色值(BGR)和对应像素的坐标;
 3. 用文本将颜色值和对应像素的坐标显示在点击处;

本题a中要求将坐标显示到上例图像中,方法类似,具体代码如下:

#include 
#include 

using namespace std;

void my_mouse_callback(int event, int x, int y, int flags, void* param);    //声明回调函数

void Get_Show_BGR(IplImage* img, CvPoint point)         //获取、显示像素点处的RGB
{
    CvFont font;                //字体变量
    CvScalar s;                 //BGR值变量
    char str[40] = { 0 };

    cvInitFont(&font, CV_FONT_HERSHEY_COMPLEX, 1.0, 1.0, 1.0);          //初始化字体相关参数
    s = cvGet2D(img, point.x, point.y);
    cout << " B: " << s.val[0] << " G: " << s.val[1] << " R: " << s.val[2];
    //字符串格式化命令,格式化的数据写入某个字符串中
    sprintf(str, "(%d,%d)(%.0f,%.0f,%.0f)", point.x, point.y, s.val[2], s.val[1], s.val[0]);   
    cvPutText(img, str, point, &font, cvScalar(255, 255, 255));         //输出相应的文字标签
}

int main(int argc, char** argv)    //带参数的mian函数
{

    IplImage *img = cvLoadImage("D:\\Template\\OpenCV\\Template22_Trigger_Show_BGR_Coordinate\\Debug\\3.jpg");
    cvNamedWindow("Example1", CV_WINDOW_AUTOSIZE);

    cvSetMouseCallback("Example1", my_mouse_callback, (void*)img);      //注册回调函数
    //事件产生窗口、回调函数名、事件产生影响的类
    while (1)
    {
        cvShowImage("Example1", img);
        char c = cvWaitKey(32);
        if (c == 27) break;
    }

    cvReleaseImage(&img);
    cvDestroyWindow("Example1");
}

void my_mouse_callback(int event, int x, int y, int flags, void* param)
{
    IplImage* image = (IplImage*)param;
    CvPoint point;                      //鼠标按下的坐标点  
    switch (event)
    {
        case CV_EVENT_LBUTTONDOWN:
        {
            point = cvPoint(x, y);      //记录左键按下的位置
            Get_Show_BGR(image, point);
        }
        break;
    }
}

运行结果如下图:
第四章 学习OpenCV——细说HighGUI_第5张图片

例4-5 读入图像,鼠标按住画矩形框,再次点击时重新绘制,生成颜色直方图

本例完成的工作如下:

 1. 读入显示图像,鼠标左键按住画矩形框;
 2. 下一次按键时,恢复为原始图像,并重新开始绘制矩形;
 3. 在独立窗口中画颜色直方图,显示选定区域内各像素范围对应的每个通道像素点的数量;

具体代码如下:

#include 
#include   
#include   
#include  

using namespace std;

void my_mouse_callback(int event, int x, int y, int flags, void* param);    //声明回调函数

CvRect box;                         //所画矩形的角点、宽高
bool drawing_box = false;           //判断左键是否按下,按下为true  
bool drawing_col = false;               //判断是否重绘图像
int R=0;            //R像素点个数
int G=0;            //G像素点个数



void draw_box(IplImage* img, CvRect rect)           //绘制矩形
{
    cvRectangle(img, 
        cvPoint(box.x,box.y),                       //两个对顶点
        cvPoint(box.x+box.width,box.y+box.height),      
        cvScalar(0x00, 0x00, 0xff));                //BGR 设置为红色
}

IplImage* createCoordinate(IplImage* img)
{
    int x_grid=0;
    int y_grid = 0;
    int x_interval=90;
    int y_interval = 560;
    CvFont font;                //字体变量
    char str[40] = { 0 };

    cvInitFont(&font, CV_FONT_HERSHEY_PLAIN, 1.0, 1.0, 1.0);    //初始化字体相关参数

    cvSet(img, CV_RGB(255, 255, 255));      //白色背景

    cvLine(img, cvPoint(40, 560), cvPoint(980, 560), CV_RGB(100, 100, 100), 1);     //X轴
    cvLine(img, cvPoint(965, 550), cvPoint(980, 560), CV_RGB(100, 100, 100), 1);    //X轴箭头
    cvLine(img, cvPoint(965, 570), cvPoint(980, 560), CV_RGB(100, 100, 100), 1);    //X轴箭头

    cvLine(img, cvPoint(40, 560), cvPoint(40, 40), CV_RGB(100, 100, 100), 1);       //Y轴
    cvLine(img, cvPoint(30, 55), cvPoint(40, 40), CV_RGB(100, 100, 100), 1);        //Y轴箭头
    cvLine(img, cvPoint(50, 55), cvPoint(40, 40), CV_RGB(100, 100, 100), 1);        //Y轴箭头

    for (int i = 0; i < 8; i++)         //X坐标
    {
        sprintf(str, "(%d,%d) ", x_grid, x_grid + 31);    //字符串格式化命令,格式化的数据写入某个字符串中
        cvPutText(img, str, cvPoint(x_interval, 580), &font, cvScalar(0, 0, 0));    //输出相应的文字标签
        x_grid += 32;
        x_interval += 110;
    }
    for (int j = 0; j < 10; j++)        //Y坐标
    {
        sprintf(str, "%d", y_grid);   //字符串格式化命令,格式化的数据写入某个字符串中
        cvPutText(img, str, cvPoint(10, y_interval), &font, cvScalar(0, 0, 0)); //输出相应的文字标签
        y_grid += 50;
        y_interval -= 50;
    }
    return img;
}

void draw_column(IplImage* img1, CvRect rect, IplImage* img2)           //绘制矩形
{
    CvScalar s;                                         //BGR值变量
    double blue;
    double green;
    double red;
    int Grid=0;
    int Interval=90;
    int number=0;

    CvFont font;                //字体变量
    char str1[10] = { 0 };
    char str2[10] = { 0 };
    char str3[10] = { 0 };

    int B[8] = { 0 };           //B像素点个数
    int G[8] = { 0 };           //B像素点个数
    int R[8] = { 0 };           //B像素点个数

    cvInitFont(&font, CV_FONT_HERSHEY_PLAIN, 1.0, 1.0, 1.0);    //初始化字体相关参数

    /******************图像上制定区域内RGB像素值,使用cvPtr2D算法********************/
        for (int i = box.x; i < (box.x + box.width); i++)       //矩阵指针行寻址
        {
            for (int j = box.y; j < (box.y + box.height); j++)  //矩阵指针列寻址
            {
                Grid = 0;   //搜索范围置0
                s = cvGet2D(img1, i, j);   //获取对应像素点RGB值

                blue = s.val[0];                  //分别获取获取BGR的值
                green = s.val[1];
                red = s.val[2];
                cout << " B: " << blue << " G: " << green << " R: " << red<for (int k = 0; k < 8; k++)             //像素值所处范围
                {
                    if ((blue >= Grid) && (blue < (Grid + 32)))
                    {
                        B[k]++;
                        cout << " 区域下限: " << Grid << " B像素点总数: "<if ((green >= Grid) && (green < (Grid + 32)))
                    {
                        G[k]++;
                        cout << " 区域下限: " << Grid << " G像素点总数: " << G[k] << endl;
                    }
                    if ((red >= Grid) && (red < (Grid + 32)))
                    {
                        R[k]++;
                        cout << " 区域下限: " << Grid << " R像素点总数: " << R[k] << endl;
                    }
                    Grid += 32;     //跳转到下一块区域
                }
                number++;
        }
    }
        for (int l = 0; l < 8; l++)
        {
            sprintf(str1, "%d", B[l]);    //字符串格式化命令,格式化的数据写入某个字符串中
            sprintf(str2, "%d", G[l]);    //字符串格式化命令,格式化的数据写入某个字符串中
            sprintf(str3, "%d", R[l]);    //字符串格式化命令,格式化的数据写入某个字符串中
            cvRectangle(img2,
                cvPoint(Interval, 560),                 //两个对顶点
                cvPoint(Interval + 10, 560-B[l]),
                cvScalar(0xff, 0x00, 0x00),             //BGR 设置为蓝色
                CV_FILLED);                             //实心填充
            cvRectangle(img2,
                cvPoint(Interval+30, 560),              //两个对顶点
                cvPoint(Interval + 40, 560 - G[l]),
                cvScalar(0x00, 0xff, 0x00),             //BGR 设置为绿色
                CV_FILLED);                             //实心填充
            cvRectangle(img2,
                cvPoint(Interval+60, 560),              //两个对顶点
                cvPoint(Interval + 70, 560 - R[l]),
                cvScalar(0x00, 0x00, 0xff),             //BGR 设置为红色
                CV_FILLED);                             //实心填充

            //输出相应的个数标签
            cvPutText(img2, str1, cvPoint(Interval-5, 555-B[l]), &font, cvScalar(0, 0, 0));     
            cvPutText(img2, str2, cvPoint(Interval +25, 555 - G[l]), &font, cvScalar(0, 0, 0)); 
            cvPutText(img2, str3, cvPoint(Interval +55, 555 - R[l]), &font, cvScalar(0, 0, 0));
            Interval += 110;
        }
    cout << " 区域像素点总数: " << number;
}

int main(int argc, char* argv[])
{
    box = cvRect(-1, -1, 0, 0);             //(x,y,width,height)

    IplImage* image = cvLoadImage("D:\\Template\\OpenCV\\Template23_Event_Trigger_Rect\\Debug\\4.jpg"); 
    IplImage* temp = cvCloneImage(image);   //克隆图像,复制图像随原图像变化
    IplImage* temp1 = cvCloneImage(image);  //克隆图像,复制图像随原图像变化
    IplImage* histogram = cvCreateImage(cvSize(1000, 600), 8, 3);
    IplImage* histogram_temp = cvCloneImage(histogram);

    cvNamedWindow("Box Example");
    cvNamedWindow("Box Example1");
    cvNamedWindow("Box Histogram");

    cvSetMouseCallback("Box Example", my_mouse_callback, (void*)temp); //注册回调函数
    //事件产生窗口、回调函数名、事件产生影响的类
    createCoordinate(histogram);

    while (1)
    {
        if (drawing_box)                    //左键按下,开始绘制(左键按下时动态显示矩形)
        {
            cvCopyImage(image, temp1);      //画之前恢复为原始图像
            draw_box(temp1, box);           //在temp1图像上画矩形
        }
        if (drawing_col)                    //左键按下,开始绘制(左键按下时动态显示矩形)
        {
            cvCopyImage(histogram, histogram_temp);     //画之前恢复为原始图像
            draw_column(temp1, box, histogram_temp);
            drawing_col = false;
        }

        cvShowImage("Box Example", temp1);              //显示temp1
        cvShowImage("Box Example1", temp);              //显示temp
        cvShowImage("Box Histogram", histogram_temp);   //显示柱状图
        if (cvWaitKey(15) == 27)                        //15ms刷新
            break;
    }
    cvReleaseImage(&image);
    cvReleaseImage(&temp);
    cvReleaseImage(&temp1);
    cvReleaseImage(&histogram);
    cvReleaseImage(&histogram_temp);
    cvDestroyWindow("Box Example");
    cvDestroyWindow("Box Example1");
    cvDestroyWindow("Box Histogram");   
}

void my_mouse_callback(int event, int x, int y, int flags, void* param)
{
    IplImage* image = (IplImage*)param;
    switch (event)
    {
        case CV_EVENT_MOUSEMOVE:
        {
            if (drawing_box)                //左键已按下标志,开始绘制
            {
                box.width = x - box.x;      //x为当前鼠标的位置 
                box.height = y - box.y;     //box.x为左键按下时的位置
            }
        }
        break;
        case CV_EVENT_LBUTTONDOWN:
        {
            drawing_box = true;
            box = cvRect(x, y, 0, 0);   //记录矩形起始位置
        }
        break;
        case CV_EVENT_LBUTTONUP:
        {
            drawing_box = false;        //左键已松开标志,结束绘制
            drawing_col = true;
            if (box.width<0)            //将width转化为正数,保证绘图从左向右进行
            {
                box.x += box.width;     //box.x=box.x+box.width 回移起点位置
                box.width *= -1;        //box.x为左键按下时的位置
            }
            if (box.height<0)           //将height转化为正数,保证绘图从上向下进行
            {
                box.y += box.height;    
                box.height *= -1;
            }
            draw_box(image, box);       //在temp上画矩形
        }
        break;
    }
}

程序中Box Example中图像可重绘,Box Example1中图像能反映所有画过的矩形,运行结果如下图(图2为灰度图是生成的颜色直方图,图3为RGB图像生成的颜色直方图):
第四章 学习OpenCV——细说HighGUI_第6张图片
第四章 学习OpenCV——细说HighGUI_第7张图片
第四章 学习OpenCV——细说HighGUI_第8张图片

例4-6 读入视频文件,一个滚动条控制播放位置,以10为步长跳进,另一滚动条控制停止/播放

本例完成的工作如下:

 1. 读入并显示视频文件;
 2. 滚动条控制播放位置,以10为步长跳进;
 3. 另一滚动条控制停止/播放;

具体代码如下:

#include 
#include 

using namespace std;

int g_slider_position = 0;      //滚动条位置全局变量
int slider_position = 0;        //快进全局变量

CvCapture* g_capture = NULL;    //确定要读入AVI文件的指针

int g_switch_value = 1;

void switch_off_function()
{
    cout << "switch off" << endl;
    cout << g_switch_value << endl;
}

void switch_on_function()
{
    cout << "switch on" << endl;
    cout << g_switch_value << endl;
}

void switch_callback(int pos)
{
    if (pos == 0)
        switch_off_function();
    else
        switch_on_function();
}

void onTrackbarSlide(int pos)
{
    slider_position += 10;          //以10为步长快进,点击一次滚动条,滚动条当前位置进10
    g_slider_position = slider_position;
    //cvSetCaptureProperty(g_capture, CV_CAP_PROP_POS_FRAMES, pos);  //根据点击位置,以帧数设置读入
    cvSetCaptureProperty(g_capture, CV_CAP_PROP_POS_FRAMES, g_slider_position);  //以帧数设置读入
}       //回调函数,滚动条被拖动时调用,位置以32位整型返回

int main(int argc, char** argv)
{
    IplImage* frame;
    g_capture = cvCreateFileCapture("D:\\Template\\OpenCV\\Template24_ShowAVI_Switch_Trackbar\\Debug\\好想告诉你.avi");  
    int frames = (int)cvGetCaptureProperty(g_capture, CV_CAP_PROP_FRAME_COUNT);   //获取总帧数以设定滚动条

    cvNamedWindow("Example3");      //创建窗口

    if (frames != 0)
    {
        cvCreateTrackbar("Position","Example3",&g_slider_position,frames,onTrackbarSlide);  //创建滚动条
        cvCreateTrackbar("Switch", "Example3", &g_switch_value, 1, switch_callback);
    }           //将变量绑定到滚动条表征最大值和回调函数

    while (1)
    {
        if (g_switch_value == 1)
        {
            frame = cvQueryFrame(g_capture);
            if (!frame) break;
            cvShowImage("Example3", frame);
            g_slider_position++;            //滚动条当前位置变量
            slider_position++;              //以十为步长快进变量
            //滚动条随视频移动
            cvCreateTrackbar("Position", "Example3", &g_slider_position, frames, onTrackbarSlide);    
            //char c = cvWaitKey(32);       //在此处设置waitkey,开关会导致程序卡死
            //if (c == 27) break;
        }
        char c = cvWaitKey(32);
        if (c == 27) break;
    }
    cvReleaseCapture(&g_capture);
    cvDestroyWindow("Example3");
    return(0);
}

运行结果如下图:
第四章 学习OpenCV——细说HighGUI_第9张图片

例4-7 创建一个简单画图程序

本例完成的工作如下:

 1. 可以新建一个图像,并将图像中所有像素值设置为0,然后显示出来;
 2. 鼠标左键画线、圆、椭圆、矩形,鼠标右键橡皮擦功能;
 3. “逻辑画图”,滚动条选择AND,只显示原始像素数据为100的数据(由于1接近背景色不容易查看所以用100);

具体代码如下:

#include 
#include   
#include   
#include  
#include 

using namespace std; 

void my_mouse_callback(int event, int x, int y, int flags, void* param);    //声明回调函数

IplImage* image;                    //新建的图像
CvRect box;                         //所画矩形的角点、宽高
CvRect erase;                       //所画矩形的角点、宽高
bool drawing_box = false;           //判断左键是否按下,按下为true  
bool drawing_erase = false;         //判断右键是否按下
bool enAND = false;                 //判断AND功能是否使能
int g_switch_value = 0;
int g_logic_value = 0;

void create_image()
{   
    cout << "Create Image" << endl; 
    cout << g_switch_value << endl; 
    cvZero(image);                          //清零
}

void draw_line(IplImage* img, CvRect rect)
{   
    cvLine(img,             
        cvPoint(rect.x,rect.y),
        cvPoint(rect.x + rect.width, rect.y + rect.height),
        cvScalar(0x00, 0x00, 0x64));    //BGR 设置为R值设置为100,用于验证逻辑绘图中的AND
}

void draw_circle(IplImage* img, CvRect rect)
{
    int radius = 0;
    radius = (int)sqrt(rect.width*rect.width + rect.height*rect.height);
    cvCircle(img,
        cvPoint(rect.x, rect.y),
        radius,
        cvScalar(0x00, 0x00, 0xff));    //BGR 设置为红色
}

void draw_ellipse(IplImage* img, CvRect rect)
{
    cvEllipse(img, 
        cvPoint(rect.x, rect.y), 
        cvSize(rect.width, rect.height),
        0,0,360,
        cvScalar(0x00, 0x00, 0xff));
}

void draw_rect(IplImage* img, CvRect rect)
{
    cvRectangle(img, 
        cvPoint(rect.x, rect.y),                        //两个对顶点
        cvPoint(rect.x + rect.width, rect.y + rect.height),
        cvScalar(0x00, 0x00, 0xff));                    //BGR 设置为红色
}

void Draw(IplImage* img, CvRect rect)
{
    switch (g_switch_value)
    {
        case 0:
            break;
        case 1:
            break;
        case 2:
            draw_line(img, rect);
            break;
        case 3:
            draw_circle(img, rect);
            break;
        case 4:
            draw_ellipse(img, rect);
            break;
        case 5:
            draw_rect(img, rect);
            break;
    }
}

void eraser(IplImage* img, CvRect rect)
{
    cvLine(img,
        cvPoint(rect.x, rect.y),
        cvPoint(rect.x + rect.width, rect.y + rect.height),
        cvScalar(0x00, 0x00, 0x00),10); //BGR 设置为红色
}

void switch_callback(int pos)
{
    if (g_switch_value==1)
        create_image();
}

void Show_AND(IplImage* img)
{
    int num;
    for (int i = 0; i<400; i++)         //矩阵指针行寻址
    {
        for (int j = 0; j<400; j++)     //矩阵指针列寻址
        {
            uchar *ptr = cvPtr2D(img, i, j);   //index1 行 index2 列
            for (int k = 0; k < 3; k++)
            {
                if (ptr[k] == 0x64)
                {
                    ptr[k] = 0x64;
                }
                else
                    ptr[k] = 0;
                //num = ptr[k];
                //cout << num << endl;          //换行
            }   
        }
    }
}

void logic_callback(int pos)
{
    switch (g_logic_value)
    {
        case 1:
            enAND = true;
            break;
        default:
            enAND = false;
            break;
    }
}

int main(int argc, char* argv[])
{
    box = cvRect(-1, -1, 0, 0);             //(x,y,width,height)
    image = cvCreateImage(cvSize(400, 400), IPL_DEPTH_8U, 3); //200*200 8位 3通道
    cvZero(image);                          //清零

    IplImage* temp = cvCloneImage(image);   //克隆图像,复制图像随原图像变化
    cvNamedWindow("Box Example");
    cvCreateTrackbar("Switch", "Box Example", &g_switch_value, 5, switch_callback); //创建绘图模式滚动条
    cvCreateTrackbar("Logic", "Box Example", &g_logic_value, 1, logic_callback);    //创建逻辑绘图滚动条
    cvSetMouseCallback("Box Example", my_mouse_callback, (void*)image);             //注册回调函数
    //事件产生窗口、回调函数名、事件产生影响的类
    while (1)
    {
            cvCopyImage(image, temp);   //复制图像,不随原图像变化,(清除temp图像上左键按住时,实时显示的矩形)
        if (drawing_box)                    //左键按下,开始绘制(左键按下时动态显示矩形)
            Draw(temp, box);                //在temp图像上画矩形(动态显示)
        if (drawing_erase)                  //左键按下,开始绘制(左键按下时动态显示矩形)
            eraser(temp, erase);            //在temp图像上画矩形
        if (enAND == true)
        {
            Show_AND(image);
            enAND = false;
        }
        cvShowImage("Box Example", temp);   //显示temp
        if (cvWaitKey(15) == 27)            //15ms刷新一次
            break;
    }
    cvReleaseImage(&image);
    cvReleaseImage(&temp);
    cvDestroyWindow("Box Example");
}

void my_mouse_callback(int event, int x, int y, int flags, void* param)
{
    IplImage* image = (IplImage*)param;
    switch (event)
    {       
        case CV_EVENT_MOUSEMOVE:
        {
            if (drawing_box)                //左键已按下标志,开始绘制
            {
            box.width = x - box.x;      //x为当前鼠标的位置 
            box.height = y - box.y;     //box.x为左键按下时的位置
            }
            if (drawing_erase)              //左键已按下标志,开始绘制
            {
                erase.width = x - erase.x;      //x为当前鼠标的位置 
                erase.height = y - erase.y;     //box.x为右键按下时的位置
            }
        }
            break;
        case CV_EVENT_LBUTTONDOWN:
        {
            drawing_box = true;
            box = cvRect(x, y, 0, 0);   //记录矩形起始位置
        }
            break;
        case CV_EVENT_LBUTTONUP:
        {
            drawing_box = false;        //左键已松开标志,结束绘制
            Draw(image, box);
        }
            break;
        case CV_EVENT_RBUTTONDOWN:

        {
            drawing_erase = true;       //右键已松开标志,结束绘制
            erase = cvRect(x, y, 0, 0); //记录矩形起始位置      
        }
            break;
        case CV_EVENT_RBUTTONUP:
        {
            drawing_erase = false;      //右键已松开标志,结束绘制
            eraser(image, erase);
        }
            break;
    }
}

运行结果如下图(直线对应像素为100,用于表示逻辑画图,图1右上方有橡皮擦痕迹):
第四章 学习OpenCV——细说HighGUI_第10张图片
第四章 学习OpenCV——细说HighGUI_第11张图片

例4-8 创建一个简单标签程序

本例完成的工作如下:

 1. 可以新建一个图像,并将图像中所有像素值设置为0,然后显示出来;
 2. 用户点击一个位置,在此位置输出一个标签;
 3. 允许使用Enter键进行编辑;
 4. 正在编辑的标签可以使用backspace键修改原有内容;

具体代码如下:

#include 
#include   
#include   
#include  
#include 

using namespace std;

void my_mouse_callback(int event, int x, int y, int flags, void* param);    //声明回调函数

//IplImage* image;                  //新建的图像
CvRect box;                         //所画矩形的角点、宽高
bool drawing_tag = false;           //判断左键是否按下,按下为true      
bool drawing_next = false;          //判断是否为新的一点
CvFont font;                        //字体变量

char display_text[40] = "|";

int main(int argc, char* argv[])
{
    box = cvRect(-1, -1, 0, 0);             //(x,y,width,height)

    IplImage* image = cvCreateImage(cvSize(512, 512), IPL_DEPTH_8U, 3); //200*200 8位 3通道
    cvZero(image);                          //清零

    IplImage* temp = cvCloneImage(image);   //克隆图像,复制图像随原图像变化
    IplImage* temp1 = cvCloneImage(image);  //克隆图像,复制图像随原图像变化
    cvNamedWindow("Box Example");

    cvSetMouseCallback("Box Example", my_mouse_callback, (void*)image); //注册回调函数
    //事件产生窗口、回调函数名、事件产生影响的类
    cvInitFont(&font, CV_FONT_HERSHEY_PLAIN, 1.0, 1.0, 1.0);    //初始化字体相关参数

    while (1)
    {
        cvCopyImage(temp, temp1);   //复制图像,不随原图像变化,(清除temp图像上左键按住时,实时显示的矩形)
        if (drawing_tag)            //左键按下,开始绘制(左键按下时动态显示矩形)
            cvPutText(temp1, display_text, cvPoint(box.x, box.y), &font, cvScalarAll(255)); 
            //实时显示标签内容
        cvShowImage("Box Example", temp1);  //显示temp1

        char c = cvWaitKey(15);
        //判断是否为新的一点
        if (drawing_next == true)
        {
            drawing_next = false;
            cvCopyImage(temp,image);        //存储上一标签的图像,(用于修改当前位置标签)
        }
        // 32 -- 126 可显示字符 AscII
        if (drawing_tag == true && c < 126 && c > 32)
        {
            char tempstr[2] = { c };
            if (display_text == "|")
            {
                strcpy(display_text, tempstr);  //将字符串tempstr复制到diaplay_text
            }
            else
            {
                strcat(display_text, tempstr);  //将字符串tempstr连接到diaplay_text结尾
            }
        }
        // 13 -- Enter键  AscII
        else if (c == 13)
        {
            if (drawing_tag == true)        //设置标签
            {
                cout<<"完成标签"<255));
            }
            else                            //修改标签
            {
                cout << "修改标签" << endl;
                cvCopyImage(image, temp);   //清除图像上内容(用上一个标签的图像覆盖)
                drawing_tag = true;
            }
        }
        // 8 -- 退格键  AscII
        else if (c == 8)
        {
            int len = strlen(display_text);     //计算数组长度
            printf("剩余字节数:%d\n", len-2);
            display_text[len - 1] = '\0';       //NULL
        }
        else if (c == 27) break;
    }
    cvReleaseImage(&image);
    cvReleaseImage(&temp);
    cvReleaseImage(&temp1);
    cvDestroyWindow("Box Example");
}

void my_mouse_callback(int event, int x, int y, int flags, void* param)
{
    IplImage* image = (IplImage*)param;
    switch (event)
    {
        case CV_EVENT_LBUTTONDOWN:
        {
            drawing_tag = true;
            drawing_next = true;
            box = cvRect(x, y, 0, 0);       //记录矩形起始位置
            strcpy(display_text, "|");      //将字符串 "|"复制到diaplay_text,每次重新设置标签
            cvPutText(image, display_text, cvPoint(box.x, box.y), &font, cvScalarAll(255));
        }
        break;
    }
}

运行结果如下图:
第四章 学习OpenCV——细说HighGUI_第12张图片

例4-9 透视变换

本例完成的工作如下:

 1. 读入一幅图像,并使用数字键1-8控制变换矩阵;
 2. 按下数字键1-8任意一个键,透视变换数据会变大,同时按下Shift键,对应数据减小;
 3. 添加放大、缩小功能;
 4. 添加旋转功能;

具体代码如下:

#include 
#include   
#include   
#include  
#include 

using namespace std;

int g_slider_position = 1;
int g_position = 0;

bool already = false;
bool already_rotate = false;

void onTrackbarSlide(int pos)
{
    already = true;             //标志是否进行过一次缩放
}

void onRotateSlide(int pos)
{
    already_rotate = true;      //标志是否进行过一次旋转
}

int main(int argc, char* argv[])
{
    CvPoint2D32f srcQuad[4], dstQuad[4];                    //变换的两个点集
    CvMat* warp_matrix = cvCreateMat(3, 3, CV_32FC1);       //变换矩阵
    IplImage* src, *dst;                                    //变换图像与原图像
    cvNamedWindow("Original", 1);                       
    cvNamedWindow("Perspective_Warp", 1);

    src = cvLoadImage("D:\\Template\\OpenCV\\Template27_Perspective_Transformation\\Debug\\4.jpg");
    if (src != 0)
    {
        cvCreateTrackbar("Position", "Original", &g_slider_position, 2, onTrackbarSlide);//缩放条
        cvCreateTrackbar("Rotate", "Original", &g_position, 1, onRotateSlide);           //旋转条
    }

    dst = cvCloneImage(src);        //克隆图像
    dst->origin = src->origin;      //设置相同原点
    cvZero(dst);                    //清零

    srcQuad[0].x = 0;                   //左上
    srcQuad[0].y = 0;
    srcQuad[1].x = src->width-1;        //右上
    srcQuad[1].y = 0;               
    srcQuad[2].x = 0;                   //左下
    srcQuad[2].y = src->height - 1;
    srcQuad[3].x = src->width - 1;      //右下
    srcQuad[3].y = src->height - 1;

    dstQuad[0].x = src->width*0.05;     //左上
    dstQuad[0].y = src->height*0.33;
    dstQuad[1].x = src->width*0.9;      //右上
    dstQuad[1].y = src->height*0.25;
    dstQuad[2].x = src->width*0.2;      //左下
    dstQuad[2].y = src->height*0.7;
    dstQuad[3].x = src->width*0.8;      //右下
    dstQuad[3].y = src->height*0.9;

    while (1)
    {
        char c=cvWaitKey(15);
//      printf("当前字符:%d\n", c); //可用于打印测试shift+数字的值
        switch (c)
        {
            case 49:
                if (dstQuad[0].x < src->width)
                    dstQuad[0].x++;
                else
                    dstQuad[0].x = src->width;
                break;
            case 50:
                if (dstQuad[0].y < src->height)
                    dstQuad[0].y++;
                else
                    dstQuad[0].y = src->height;
                break;
            case 51:
                if (dstQuad[1].x < src->width)
                    dstQuad[1].x++;
                else
                    dstQuad[1].x = src->width;
                break;
            case 52:
                if (dstQuad[1].y < src->height)
                    dstQuad[1].y++;
                else
                    dstQuad[1].x = src->height;
                break;
            case 53:
                if (dstQuad[2].x < src->width)
                    dstQuad[2].x++;
                else
                    dstQuad[2].x = src->width;
                break;
            case 54:
                if (dstQuad[2].y < src->height)
                    dstQuad[2].y++;
                else
                    dstQuad[2].y = src->height;
                break;
            case 55:
                if (dstQuad[3].x < src->width)
                    dstQuad[3].x++;
                else
                    dstQuad[3].x = src->width;
                break;
            case 56:
                if (dstQuad[3].y < src->height)
                    dstQuad[3].y++;
                else
                    dstQuad[3].y = src->height;
                break;
            /*****************************Shift+Num*********************************/
            case 33:
                if (dstQuad[0].x > 0)
                    dstQuad[0].x--;
                else
                    dstQuad[0].x = 0;
                break;
            case 64:
                if (dstQuad[0].y > 0)
                    dstQuad[0].y--;
                else
                    dstQuad[0].y = 0;
                break;
            case 35:
                if (dstQuad[1].x > 0)
                    dstQuad[1].x--;
                else
                    dstQuad[1].x = 0;
                break;
            case 36:
                if (dstQuad[1].y > 0)
                    dstQuad[1].y--;
                else
                    dstQuad[1].x = 0;
                break;
            case 37:
                if (dstQuad[2].x > 0)
                    dstQuad[2].x--;
                else
                    dstQuad[2].x = 0;
                break;
            case 94:
                if (dstQuad[2].y > 0)
                    dstQuad[2].y--;
                else
                    dstQuad[2].y = 0;
                break;
            case 38:
                if (dstQuad[3].x > 0)
                    dstQuad[3].x--;
                else
                    dstQuad[3].x = 0;
                break;
            case 42:
                if (dstQuad[3].y > 0)
                    dstQuad[3].y--;
                else
                    dstQuad[3].y = 0;
                break;
            default:
                break;
        }
        if (c == 27) break;
        if ((g_slider_position == 0) && already)    //缩小
        {
            dstQuad[0].x = srcQuad[0].x * 2;        //左上
            dstQuad[0].y = srcQuad[0].y * 2;
            dstQuad[1].x = srcQuad[1].x*0.5;        //右上
            dstQuad[1].y = srcQuad[1].y*0.5;
            dstQuad[2].x = srcQuad[2].x * 2;        //左下
            dstQuad[2].y = srcQuad[2].y*0.5;
            dstQuad[3].x = srcQuad[3].x*0.5;        //右下
            dstQuad[3].y = srcQuad[3].y*0.5;
            already = false;
        }
        if ((g_slider_position == 1) && already)    //回复正常大小
        {
            dstQuad[0].x = srcQuad[0].x;            //左上
            dstQuad[0].y = srcQuad[0].y;
            dstQuad[1].x = srcQuad[1].x;            //右上
            dstQuad[1].y = srcQuad[1].y;
            dstQuad[2].x = srcQuad[2].x;            //左下
            dstQuad[2].y = srcQuad[2].y;
            dstQuad[3].x = srcQuad[3].x;            //右下
            dstQuad[3].y = srcQuad[3].y;
            already = false;
        }
        if ((g_slider_position == 2) && already)    //放大
        {
            dstQuad[0].x = srcQuad[0].x * 0.5;      //左上
            dstQuad[0].y = srcQuad[0].y*0.5;
            dstQuad[1].x = srcQuad[1].x * 2;        //右上
            dstQuad[1].y = srcQuad[1].y * 2;
            dstQuad[2].x = srcQuad[2].x*0.5;        //左下
            dstQuad[2].y = srcQuad[2].y * 2;
            dstQuad[3].x = srcQuad[3].x * 2;        //右下
            dstQuad[3].y = srcQuad[3].y * 2;
            already = false;
        }
        if ((g_position == 0) && already_rotate)    //逆时针旋转90°
        {
            dstQuad[0].x = srcQuad[0].x;        //左上
            dstQuad[0].y = srcQuad[0].y;
            dstQuad[1].x = srcQuad[1].x;        //右上
            dstQuad[1].y = srcQuad[1].y;
            dstQuad[2].x = srcQuad[2].x;        //左下
            dstQuad[2].y = srcQuad[2].y;
            dstQuad[3].x = srcQuad[3].x;        //右下
            dstQuad[3].y = srcQuad[3].y;
            already_rotate = false;
        }
        if ((g_position == 1) && already_rotate)    //逆时针旋转90°
        {
            dstQuad[0].x = srcQuad[2].x;        //左上
            dstQuad[0].y = srcQuad[2].y;
            dstQuad[1].x = srcQuad[0].x;        //右上
            dstQuad[1].y = srcQuad[0].y;
            dstQuad[2].x = srcQuad[3].x;        //左下
            dstQuad[2].y = srcQuad[3].y;
            dstQuad[3].x = srcQuad[1].x;        //右下
            dstQuad[3].y = srcQuad[1].y;
            already_rotate = false;
        }

        cvGetPerspectiveTransform(srcQuad,
            dstQuad,
            warp_matrix );                          //计算透视变换矩阵

        cvWarpPerspective(src, dst, warp_matrix);   //进行透视变换
        cvShowImage("Original", src);
        cvShowImage("Perspective_Warp", dst);

    }
    cvReleaseImage(&src);
    cvReleaseImage(&dst);
    cvReleaseMat(&warp_matrix);
    cvDestroyWindow("Original");
    cvDestroyWindow("Perspective_Warp");
}

运行结果如下图:
第四章 学习OpenCV——细说HighGUI_第13张图片
第四章 学习OpenCV——细说HighGUI_第14张图片
第四章 学习OpenCV——细说HighGUI_第15张图片
第四章 学习OpenCV——细说HighGUI_第16张图片

例4-10 有趣的人脸检测+Alpha融合程序

本例完成的工作如下:

 1. 编译一个facedetect.cpp代码;
 2. 检测到人脸,将人脸矩形区域内的图像用目标图像代替;
 3. 加入一个滑动条,在范围0.0~1.0之间设置10个位置,使用滚动条来调节矩形区域内图像与目标图像的Alpha融合;
 4. 人脸识别部分程序参考 ID:未语愁眸 ;

blog:未语愁眸

源代码包含多个文件,笔者不在文中赘述,上传了加入Alpha融合的人脸识别代码,下载地址:源代码

运行结果如下图:
第四章 学习OpenCV——细说HighGUI_第17张图片

例4-11 图像稳定程序

本例完成的工作如下:

 1. 编译lkdemo代码(运动追踪或光流法代码);
 2. 在一个更大窗口中创建并显示视频图像;
 3. 轻轻移动摄像机,并用光流法向量将图像显示在大窗口中,且保持画面稳定;
 4. 部分程序参考 ID:Peak_Ding;     

blog:Peak_Ding
具体代码如下:

#include 
#include 
#include 
#include 

#include 
#include 

using namespace std;
using namespace cv;

Mat frame;                  //原始图像
Mat prevImg;                //灰度图像1(光流金字塔第一个输入)
Mat nextImg;                //灰度图像2(光流金字塔第二个输入)
Mat warp_matrix;            //透视变换矩阵
Mat perspecttive_frame;     //透视变换后显示的图像

vector prevPts;        //跟踪标定点(定义point2f类型的向量),始终为nextPts的上一次值
vector nextPts;        //光流追踪输出的点向量,第二幅图像中计算的输入特征的新点位置
vector initial;        //标定跟踪点副本,用于画图显示最初点击的标定点位置

vector status;   //输出状态向量(无符号char);相应的流特征被发现,向量的每个元素被设置为1,否则,被置为0.
vector<float> err;      //输出错误向量;向量每个元素设为相应特征的一个错误,误差测量的类型可以在flags参数中设置
                        //如果无法找到流则会出现the error is not defined,此时使用status(状态)参数找出此情况

bool detect_station=false;      //完成跟踪点标定标志

vector<int> test;       

static void help()
{
    // print a welcome message, and the OpenCV version
    cout << "\nThis is a demo of Lukas-Kanade optical flow lkdemo(),\n"
            "Using OpenCV version " << CV_VERSION << endl;
    cout << "\nIt uses camera by default, but you can provide a path to video as an argument.\n";
    cout << "\nHot keys: \n"
            "\tESC once - delete all the points\n"
            "\tESC twice - quit the program\n"
            "\tTo add a feature point click it(LBUTTONDOWN)\n" 
            "\tRBUTTONDOWN - auto-initialize tracking\n" << endl;
}

void my_mouse_callback(int event, int x, int y, int flags, void* param)
{
    switch (event){
    case CV_EVENT_LBUTTONDOWN:              //点击左键标定跟踪点
    {
        prevPts.push_back(cvPoint(x, y));   //标定跟踪点,向量尾部插入点元素
        initial.push_back(cvPoint(x, y));   //标定跟踪点副本

    }
        break;
    case CV_EVENT_RBUTTONDOWN:
    {
        detect_station = true;              //点击右键确认完成跟踪点标定
    }
        break;
    }
}

int main()
{
    help();     //帮助信息

    cvNamedWindow("OpticalFlow", 0);                //0:可手动调整大小
    cvNamedWindow("Perspective_Image", 1);          //1:自动调整大小
    cvSetMouseCallback("OpticalFlow", my_mouse_callback);   //注册鼠标回调函数

    CvCapture* capture = cvCreateCameraCapture(0);  //读入摄像机视频流,0:随机选择一个
    cvWaitKey(100);

    while (capture)
    {
        frame = cvQueryFrame(capture);          //下一帧图像载入内存填充或更新

        imshow("OpticalFlow", frame);           //显示图像矩阵
        cvWaitKey(1);

        if (detect_station == true)             //判断是否完成标定
        {
            cvtColor(frame, nextImg, CV_BGR2GRAY);  //原始图像BGR转化为灰度图,第二个光流金字塔输入图像
            if (prevImg.empty())                    //判断prevImg是否为空,为空则真
            {
                nextImg.copyTo(prevImg);    //第一个光流金字塔输入图像,初次运行,光流金字塔两输入图像相同
            }
        }
        while (detect_station == true)      //判断是否完成标定
        {
            if (prevPts.size() == 0)        //判断是否有标定点,标定点全部丢失则溢出
            {
                break;
            }
            //光流金字塔,计算稀疏光流向量
            calcOpticalFlowPyrLK(   
                prevImg,    //第一个8位输入图像,或通过buildOpticalFlowPyramid()建立的金字塔
                nextImg,    //第二个输入图像或者和prevImg相同尺寸和类型的金字塔
                prevPts,    //标定跟踪点,二维点向量存储找到的光流;(单精度浮点数)
                nextPts,    //输出二维点向量(用单精度浮点坐标),第二幅图像中计算的输入特征的新点位置
                status,     //输出状态向量(uchar);若对应流特征被发现,向量的每个元素被设置为1,否则,被置为0
                err);       //输出错误向量;向量每个元素设为相应特征的错误,误差测量的类型可在flags参数中设置
                            //若无法找到流则出现the error is not defined,此时使用status(状态)参数找出此情况

            int k = 0;                                  //追踪输出点存在的有效点数
            for (int i = 0; i//轮询光流追踪输出点
            {
                if (status[i])                      //输出状态向量,判断每个点向量所对应特征是否被发现,0:丢失
                {
                    //prevPts[k++] = prevPts[i];    //标定跟踪点
                    initial[k] = initial[i];        //标定跟踪点副本(去除已丢失的追踪输出点对应的标定点)
                    /****************注意k++和++k的区别,k++是先执行再加1**********************/
                    nextPts[k++] = nextPts[i];      //光流追踪输出点,第一次赋值是给nextPts[0]

                }   
            }
            nextPts.resize(k);      //重新设置光流追踪输出点长度(部分点可能已丢失)
            //nextPts.resize(k);
            initial.resize(k);      //重新设置标定跟踪点长度

            for (int i = 0; i//轮询光流追踪输出点,显示标定点与输出点之间的偏移
            {
                line(frame, initial[i], nextPts[i], Scalar(0, 0, 255)); //画线,由标定点指向输出点
                circle(frame, nextPts[i], 3, Scalar(255, 0, 0));        //画圆,输出点为圆心,半径3
            }

            swap(prevPts, nextPts);     //交换标定点和光流输出点,标定点prevPts始终是输出点nextPts的前一个值
            swap(prevImg, nextImg);     //交换两幅光流追踪输入图像,使prevImg始终是视频流中nextImg的前一帧图像

            imshow("OpticalFlow", frame);   //显示图像 跟踪点移动
                                            //图像稳定(透视变换,基于标定点(4个)将新图像投影到原始图像的平面)
            if (prevPts.size() == 4)
            {
                warp_matrix = getPerspectiveTransform(nextPts, prevPts);   //(src,dst)计算透视变换矩阵
                                                                           //之前swap()所以src、dst反转
                warpPerspective(nextImg,                    //输入图像
                    perspecttive_frame,                     //输出图像
                    warp_matrix,                            //变换矩阵
                    cvSize(frame.cols, frame.rows));        //稀疏透视变换
                imshow("Perspective_Image", perspecttive_frame);            //显示变换的图像
            }
            //继续下一帧的处理
            frame = cvQueryFrame(capture);          //下一帧图像载入内存填充或更新
            cvtColor(frame, nextImg, CV_BGR2GRAY);  //nextImg第二个光流金字塔输入图像,装入新的一帧

            char c = cvWaitKey(10);             //第一次按下esc,清空现有跟踪标定点
            if (c == 27)
            {
                detect_station = false;         //跟踪标定完成失能
                initial.resize(0);              //清空跟踪标定点
                prevPts.resize(0);
                break;
            }
        }
        char d = cvWaitKey(10);     //第二次esc退出程序
        if (d == 27) break;
    }
    cvDestroyWindow("OpticalFlow");
    cvReleaseCapture(&capture);
    return 0;
}

运行结果如下图:
第四章 学习OpenCV——细说HighGUI_第18张图片

你可能感兴趣的:(OpenCV,opencv)