第四章 学习OpenCV——细说HighGUI

本章深入学习OpenCV的HighGUI模块,包括使用鼠标画图、滚动条控制功能、视频图像处理、像素获取与显示、矩形框绘制、视频播放控制、简单画图和标签程序、透视变换、人脸识别与Alpha融合、图像稳定等应用实例。
摘要由CSDN通过智能技术生成

第四章 学习OpenCV——细说HighGUI

目录

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

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

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

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;
    }
}

运行结果如下图:
鼠标在窗口中画方形

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

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

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

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");
}

运行结果如下图:
滚动条实现按钮开关

例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");
}

运行结果如下图:
视频读入、处理及合并显示
本例的难点在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,究其原因就是未将后两个图像头转化为三通道图像,其结果如下图:
压缩为1/3

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

本例完成的工作如下:

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

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

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

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 = 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值