OpenCV实现抠图工具

 更新时间:2022年01月19日 11:29:28   作者:幸福de小阳  
这篇文章主要为大家详细介绍了OpenCV实现抠图工具,文中示例代码介绍的非常详细,具有一定为大家详细的参考价值,感兴趣的小伙伴们可以参考一下

本文实例为大家分享了OpenCV实现抠图工具的具体代码,供大家参考,具体内容如下

在计算机图像领域,我们经常需要做一些抠图的工作,将图像中的目标感兴趣区域提取出来,剔除其他冗余的背景元素,以实现计算机视觉的各项功能(如车辆检测、人脸检测等)。如果纯粹使用美图秀秀等工具类软件的话,由于工具类软件将图像处理中各种可能用到的功能都集成在了一起,所以纯粹做抠图的话效率很低。现在我们就用 OpenCV 来实现一段简易的抠图程序,只需要在画面上选定目标的感兴趣区域,该目标就会被自动按序号保存。

代码如下,同时包含有通俗易懂的注释:

#include <io.h>
#include <conio.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <opencv.hpp>
 
// 抠图是单目标还是多目标,若为单目标请将下面这行文字取消注释,反之请注释这段文字。
// #define SINGLE_OBJECT
 
#define TRUE 1     // 逻辑真
#define FALSE 0   // 逻辑假
 
#define CODE_ESC 27     // ESC 键的编码
#define CODE_SPC 32    // 空格键的编码
 
#define STATUS_WAIT 0  // 抠图等待状态
#define STATUS_PROC 1     // 抠图进行状态
#define STATUS_DONE 2    // 抠图完成状态
 
#define VIDEO_FILENAME "capture-1.mp4"          // 视频流文件名
 
static int m_x1     = 0;    // 鼠标指针坐标(起点 x)
static int m_x2     = 0;    // 鼠标指针坐标(终点 x)
static int m_y1     = 0;    // 鼠标指针坐标(起点 y)
static int m_y2     = 0;     // 鼠标指针坐标(终点 y)
static int m_status = STATUS_WAIT;              // 当前抠图状态指示
 
static void on_mouse(int, int, int, int, void*);// 鼠标回调
 
// 主程序
int main(void)
{
    int        end    = 0;         // 指示是否结束程序
    int        next   = 0;         // 指示是否切换到下一张图片
    int        code   = 0;        // 存储按键编码
    int        count  = 0;        // 存储目标计数
    int        frame  = 0;         // 视频帧号(用于间隔采样)
    int        maxCol = 0;             // 图像最大列数(= 图像宽度 - 1)
    int        maxRow = 0;                      // 图像最大行数(= 图像高度 - 1)
    CvCapture* pVideo = NULL;          // 视频流对象
    IplImage*  pFrame = NULL;           // 视频帧图像(用于样本存储)
    IplImage*  pFrmCp = NULL;           // 视频帧图像(用于屏幕显示)
    CvPoint    pt1    = cvPoint(0, 0);          // 矩形框对角坐标点 1
    CvPoint    pt2    = cvPoint(0, 0);          // 矩形框对角坐标点 2
    CvRect     r      = cvRect(0, 0, 0, 0);     // 感兴趣区域矩形框
    char       seq[]  = "-2147483648";          // 目标计数的字串形式
    char       fil[]  = "data\\-2147483648.jpg";// 文件名字串
 
    // 载入视频流
    pVideo = cvCreateFileCapture(VIDEO_FILENAME);
    if (!pVideo)
    {
        return -1;
    } // if (!pVideo)
 
    // 创建数据存储目录
    if (_access("data", 0) != 0)
    {
        system("md data");
    } // if (_access())
 
    // 获取首帧图像,并创建拷贝,同时得到最大列数和行数,方便之后使用
    pFrame = cvQueryFrame(pVideo);
    if (pFrame)
    {
        pFrmCp = cvCreateImage(cvGetSize(pFrame), 8, pFrame->nChannels);
        maxCol = pFrmCp->width - 1;
        maxRow = pFrmCp->height - 1;
    } // if (pFrame)
    else
    {
        cvReleaseCapture(&pVideo);
        return -1;
    } // else
 
    // 设置显示窗口,并设置鼠标回调
    cvNamedWindow("Monitor", CV_WINDOW_AUTOSIZE);
    cvSetMouseCallback("Monitor", on_mouse, NULL);
 
    // 其他初始化
    end = FALSE;
    count = 0;
    frame = 0;
 
    while (!end && pFrame)
    {
        next = FALSE;
        while (!next && !end)
        {
            // 将原始视频图像复制到拷贝区域中(清除已将图像进行污染的线条、矩形框等)
            cvCopy(pFrame, pFrmCp, NULL);
            if (STATUS_WAIT == m_status)
            {
                // 等待抠图状态。画出横向和纵向的参考线
                cvLine(pFrmCp, cvPoint(m_x1, 0), cvPoint(m_x1, maxRow), CV_RGB(0, 255, 0));
                cvLine(pFrmCp, cvPoint(0, m_y1), cvPoint(maxCol, m_y1), CV_RGB(0, 255, 0));
            } // if (STATUS_WAIT)
            else if (STATUS_PROC == m_status)
            {
                // 抠图过程中。画出当前选定的感兴趣区域
                pt1 = cvPoint(m_x1, m_y1);
                pt2 = cvPoint(m_x2, m_y2);
                cvRectangle(pFrmCp, pt1, pt2, CV_RGB(0, 255, 0));
            } // else if (STATUS_PROC)
            else if (STATUS_DONE == m_status)
            {
                // 抠图完毕,获得感兴趣区域并按编号保存样本
                r = cvRect(
                    m_x1,
                    m_y1,
                    m_x2 - m_x1 + 1,
                    m_y2 - m_y1 + 1
                    ); // 矩形感兴趣区域
                if (r.width > 30 && r.height > 30)
                {
                    // 区域达到了一定大小,抠图有效,保存感兴趣区域样本
                    ++count;
                    cvSetImageROI   (pFrame, r);
                    sprintf_s       (seq, "%d", count);
                    strcpy_s        (fil, "data\\");
                    strcat_s        (fil, seq);
                    strcat_s        (fil, ".jpg");
                    cvSaveImage     (fil, pFrame, 0);
                    cvResetImageROI (pFrame);
 
#ifdef SINGLE_OBJECT
                    m_next = TRUE;
#endif
                } // if (r.width)
                
                // 恢复抠图等待状态
                m_status = STATUS_WAIT;
            } // else if (STATUS_DONE)
 
            cvShowImage("Monitor", pFrmCp);
            code = cvWaitKey(10);
            if (CODE_SPC == code)
            {
                next = TRUE;
            } // if (CODE_SPC)
            else if (CODE_ESC == code)
            {
                end = TRUE;
            } // else if (CODE_ESC)
        } // while (!next)
 
        if (next)
        {
            do
            {
                pFrame = cvQueryFrame(pVideo);
                ++frame;
            } while (pFrame && frame % 60 != 0); // do...while
        } // if (next)
    } // while (!end)
 
    cvDestroyAllWindows();
    cvReleaseImage(&pFrmCp);
    cvReleaseCapture(&pVideo);
 
    return 0;
} // main()
 
 
// 鼠标事件回调
void on_mouse(int event, int x, int y, int flags, void* param)
{
    switch (flags)
    {
    case CV_EVENT_MOUSEMOVE:
        if (STATUS_WAIT == m_status)
        {
            // 等待状态,确定感兴趣区域起点
            m_x1 = x, m_y1 = y;
        } // if (STATUS_WAIT)
        else if (STATUS_PROC == m_status)
        {
            // 捕捉状态,确定感兴趣区域终点
            m_x2 = x, m_y2 = y;
        } // else if (STATUS_PROC)
        break;
 
    case CV_EVENT_LBUTTONDOWN:
        if (STATUS_WAIT == m_status)
        {
            // 等待状态按下鼠标,进入捕捉状态,固定起点
            m_x1 = x, m_y1 = y;
            m_status = STATUS_PROC;
        } // if (STATUS_WAIT)
        else if (STATUS_PROC == m_status)
        {
            // 捕捉状态按下鼠标,捕捉完成,固定终点
            m_x2 = x, m_y2 = y;
            m_status = STATUS_DONE;
        } // else if (STATUS_PROC)
        break;
    } // switch
} // on_mouse()

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

相关文章

  • C++11并发编程关于原子操作atomic的代码示例

    C++11并发编程关于原子操作atomic的代码示例

    今天小编就为大家分享一篇关于C++11并发编程关于原子操作atomic的代码示例,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧
    2018-12-12
  • C++的数据类型你真的了解吗

    C++的数据类型你真的了解吗

    这篇文章主要为大家详细介绍了C++的数据类型,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下,希望能够给你带来帮助
    2022-02-02
  • C++使用join拼接字符串的技巧

    C++使用join拼接字符串的技巧

    在C++中,经常需要将多个字符串拼接成一个大字符串,这个过程很容易出错,但有一些技巧可以帮助我们轻松地实现这个目标,五个C++中join字符串的技巧:使用stringstream、使用字符串迭代器、使用字符串的加法运算符、使用std::accumulate函数和使用boost库的join方法
    2023-11-11
  • Qt自定义图形实现拖拽效果

    Qt自定义图形实现拖拽效果

    这篇文章主要为大家详细介绍了Qt自定义图形实现拖拽效果,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2020-08-08
  • C++中的while循环和for循环语句学习教程

    C++中的while循环和for循环语句学习教程

    这篇文章主要介绍了C++中的while循环和for循环语句学习教程,是C++入门学习中的基础知识,需要的朋友可以参考下
    2015-09-09
  • C++入门指南之贪吃蛇游戏的实现

    C++入门指南之贪吃蛇游戏的实现

    这篇文章主要给大家介绍了关于C++入门指南之贪吃蛇游戏实现的相关资料,文章通过示例代码介绍的非常详细,可以让大家能短时间内写出一个贪吃蛇,需要的朋友可以参考下
    2021-10-10
  • Qt写入Json文件的方法详解(含源码+注释)

    Qt写入Json文件的方法详解(含源码+注释)

    在Qt库中,为JSON的相关操作提供了完整的类支持,下面这篇文章主要给大家介绍了关于Qt写入Json文件(含源码+注释)的相关资料,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2022-10-10
  • C++11 constexpr使用详解

    C++11 constexpr使用详解

    constexpr是一种比const 更严格的束缚, 它修饰的表达式本身在编译期间可知, 并且编译器会尽可能的 evaluate at compile time,本文重点给大家介绍C++11 constexpr使用,需要的朋友可以参考下
    2021-12-12
  • c读取一行字符串,以及c++读取一行字符串的实例

    c读取一行字符串,以及c++读取一行字符串的实例

    今天小编就为大家分享一篇c读取一行字符串,以及c++读取一行字符串的实例,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2018-07-07
  • 详解C++何时需要拷贝构造函数

    详解C++何时需要拷贝构造函数

    拷贝构造函数是一个特殊的构造函数,用于创建一个新对象,该对象与另一个同类对象具有相同的属性和值,在 C++ 中,拷贝构造函数通常采用另一个同类对象作为参数,并使用该对象初始化新对象,本文给大家讲讲何时需要拷贝函数,需要的朋友可以参考下
    2023-09-09

最新评论