opencv——感兴趣区域(ROI)的分析和选取[详细总结]
引⾔
在利⽤OpenCV对图像进⾏处理时,通常会遇到⼀个情况,就是只需要对部分感兴趣区域进⾏处理。因此,如何选取感兴趣区域呢?(其实就是“抠图”)。在学习opencv的掩码运算后,尝试实现⼀个类似halcon的reduce_domain功能,对于实现抠图的过程中,需要掌握的要点就是位运算符和copyTo函数 位运算符的相关API:
void bitwise_and(InputArray src1, InputArray src2, OutputArray dst);  //dst = src1 & src2  “与”操作
void bitwise_or(InputArray src1, InputArray src2, OutputArray dst);  //dst = src1 | src2  “或”操作
void bitwise_xor(InputArray src1, InputArray src2, OutputArray dst);  //dst = src1 ^ src2 “异或”操作
void bitwise_not(InputArray src, OutputArray dst);  //dst = ~src                          “⾮”操作
copyTo函数它的定义
OpenCV中pyTo()有两种形式:
1、pyTo(imageROI),作⽤是把image的内容复制到imageROI;
2、pyTo(imageROI,mask),作⽤是把原图(image)和掩膜(mask)与运算后得到ROI区域(imageROI)。
mask就是位图,如果mask像素的值是⾮0的,我就拷贝它,否则不拷贝。(⾮零的位置就是原图中的那些需要拷贝的部分)
正⽂部分
对于感兴趣区域(Region of Interest, ROI)的选取,⼀般有两种情形:1)已知ROI在图像中的位置;2)ROI在图像中的位置未知。
1)第⼀种情形很简单,根据ROI的坐标直接从原图抠出,不过前提是要知道其坐标,
注意程序中的这两句关于Mask的操作。
mask = Mat::zeros(src.size(), CV_8UC1);
mask(r1).setTo(255);  //r1是设置好的感兴趣区域
解释⼀下上⾯两句的操作。
1. 第⼀步建⽴与原图⼀样⼤⼩的mask图像,并将所有像素初始化为0,因此全图成了⼀张全⿊⾊的⼆值图。
2. 第⼆步将mask图中的r1区域的所有像素值设置为255,也就是整个r1区域变成了⽩⾊。
若要只是提取ROI 区域,这⼀句代码(两种书写⽅法)即可
// 指定感兴趣区域,两种书写⽅法
Mat ROI = src(Rect(80, 80, 200, 200));
Mat ROI2(src, Rect(80, 80, 200, 200));
但是若要提取不规则区域ROI ,该怎么处理呢?
答案:使⽤ contour (轮廓)来指定ROI 区域
int  main(int  argc, char ** argv)
{
Mat src,dst;
src = imread("D:/opencv 练习图⽚/薛之谦.jpg");
Mat ROI = Mat::zeros(src.size(), CV_8UC1);
vector <vector<Point>> contours;//轮廓
vector<Point> pts;//多边形⾓点集合
pts.push_back(Point(30, 45));
pts.push_back(Point(100, 15));//push_back() 在Vector 最后添加⼀个元素(参数为要插⼊的值)
pts.push_back(Point(200, 145));
pts.push_back(Point(300, 240));
pts.push_back(Point(50, 250));
contours.push_back(pts);
drawContours(ROI, contours, 0, Scalar(255), -1);//⽤⽩⾊填充多边形区域
imshow("掩码", ROI);
imshow("img", src);
rectangle函数opencvimshow("dst", dst);
waitKey(0);
return  0;}
2)第⼆种情形当我们不知道感兴趣ROI区域坐标时,我们通过⿏标交互地提取ROI。OpenCV中⿏标操作依赖⿏标的回调函数和响应函数实现。主函数中调⽤⿏标的回调函数,将⿏标操作与程序的窗⼝绑定,产⽣⿏标操作时回调函数调⽤⿏标响应函数执⾏。
回调函数setMouseCallback
void setMouseCallback(const string& winname,
MouseCallback onMouse,
void* userdata=0 )
第⼀个参数,windows视窗名称,对名为winname的视窗进⾏⿏标监控;
第⼆个参数,⿏标响应处理函数,监听⿏标的点击,移动,松开,判断⿏标的操作类型,并进⾏响应的函数处理;
第三个参数,⿏标响应处理函数的ID,与⿏标相应处理函数相匹配就⾏,暂时只⽤到默认为0的情况。
⿏标响应处理函数onMouse
void onMouse(int event,int x,int y,int flags,void *ustc)
第⼀个参数,⿏标操作时间的整数代号,在opencv中,event⿏标事件总共有10中,从0-9依次代表如下:
1EVENT_MOUSEMOVE      =0,    //滑动
2EVENT_LBUTTONDOWN    =1,    //左键点击
3EVENT_RBUTTONDOWN    =2,    //右键点击
4EVENT_MBUTTONDOWN    =3,    //中间点击
5EVENT_LBUTTONUP      =4,    //左键释放
6EVENT_RBUTTONUP      =5,    //右键释放
7EVENT_MBUTTONUP      =6,    //中间释放
8EVENT_LBUTTONDBLCLK  =7,    //左键双击
9EVENT_RBUTTONDBLCLK  =8,    //右键双击
10EVENT_MBUTTONDBLCLK  =9//中间释放
第⼆个参数,代表⿏标位于窗⼝的(x,y)坐标位置,窗⼝左上⾓默认为原点,向右为x轴,向下为y轴;
第三个参数,代表⿏标的拖拽事件,以及键盘⿏标联合事件,总共有32种事件,这⾥不再赘述。
第四个参数,函数参数的编号。
⽤onMouse实现⼿动截取ROI区域,⾃动提取ROI。代码如下:
using namespace std;
using namespace cv;
bool draw;
Mat src;//原始图像
Mat roi;//ROI图像
Point cursor;//初始坐标
Rect rect;//标记ROI的矩形框
void onMouse(int event, int x, int y, int flags, void *param);
int main(int argc, char** argv)
{
src = imread("D:/opencv练习图⽚/薛之谦.jpg");
namedWindow("SrcImage");
imshow("SrcImage", src);
setMouseCallback("SrcImage", onMouse, NULL);
waitKey(0);
return0;
}
void onMouse(int event, int x, int y, int flags, void *param)
{
Mat img = src.clone();
switch (event)
{
//按下⿏标左键
case EVENT_LBUTTONDOWN:
//点击⿏标图像时,清除之前ROI图像的显⽰窗⼝
destroyWindow("ROI");
//存放起始坐标
cursor = Point(x, y);
//初始化起始矩形框
rect = Rect(x, y, 0, 0);
draw = true;
break;
//松开⿏标左键
case EVENT_LBUTTONUP:
if (rect.height > 0 && rect.width > 0)
{
//将img中的矩形区域复制给roi,并显⽰在SignROI窗⼝
roi = img(Rect(rect.x, rect.y, rect.width, rect.height));
rectangle(img, rect, Scalar(0, 0, 255), 2);
namedWindow("SignROI");
imshow("SignROI", img);
/
/将画过矩形框的图像⽤原图像还原
imshow("SrcImage", img);
//显⽰ROI图像
namedWindow("ROI");
imshow("ROI", roi);
waitKey(0);
}
draw = false;
break;
//移动光标
case EVENT_MOUSEMOVE:
if (draw)
{
//⽤MIN得到左上点作为矩形框的起始坐标,如果不加这个,画矩形时只能向⼀个⽅向进⾏            rect.x = MIN(x, cursor.x);
rect.y = MIN(y, cursor.y);
rect.width = abs(cursor.x - x);
rect.height = abs(cursor.y - y);
//防⽌矩形区域超出图像的范围
rect &= Rect(0, 0, ls, ws);
}
break;
}
}
参考链接: