本文实例为大家分享了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.webp";// 文件名字串
// 载入视频流
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, ".webp");
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()