引言

在利用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中image.copyTo()有两种形式:

1、image.copyTo(imageROI),作用是把image的内容复制到imageROI;

2、image.copyTo(imageROI,mask),作用是把原图(image)和掩膜(mask)与运算后得到ROI区域(imageROI)。

mask就是位图,如果mask像素的值是非0的,我就拷贝它,否则不拷贝。(非零的位置就是原图中的那些需要拷贝的部分)


正文部分

对于感兴趣区域(Region of Interest, ROI)的选取,一般有两种情形:1)已知ROI在图像中的位置;2)ROI在图像中的位置未知。

 1)第一种情形  很简单,根据ROI的坐标直接从原图抠出,不过前提是要知道其坐标,

  •  矩形ROI区域提取(将ROI存放于一张新的图像里)
int main(int argc, char** argv)
{
Mat src, mask,dst;
Rect r1(80, 80, 200, 200);//创建矩形ROI区域
src = imread("D:/opencv练习图片/薛之谦.jpg");
mask = Mat::zeros(src.size(), CV_8UC1);//创建纯黑色二值图像
mask(r1).setTo(255);//构建掩膜(将矩形ROI涂白)
src.copyTo(dst, mask);//掩膜运算
imshow("ROI区域", dst);
imshow("掩膜", mask );
waitKey(0);
return 0;
}

注意程序中的这两句关于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);//用白色填充多边形区域
src.copyTo(dst, ROI);//掩码运算
imshow("掩码", ROI);
imshow("img", src);
imshow("dst", dst);
waitKey(0);
return 0;
}

  •  若是只想要一个圆形区域呢?
int main(int argc, char** argv)
{
Mat src;
src = imread("D:/opencv练习图片/薛之谦.jpg");
Mat dst = Mat::zeros(src.size(), src.type());//创建三通道图像
Mat mask = Mat::zeros(src.size(), CV_8U);//创建单通道掩码图像
//最小内接圆算法
Point circleCenter(mask.cols / 2, mask.rows / 2);//获取图像中心点(圆心)
int radius = min(mask.cols, mask.rows) / 2;//获取半径
// 画圆
circle(mask, circleCenter, radius, Scalar(255), -1);//构建掩码图像
src.copyTo(dst, mask);//掩码运算
imshow("掩码", mask);
imshow("原图像", src);
imshow("ROI区域", 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);
return 0;
}
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); //将画过矩形框的图像用原图像还原
src.copyTo(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, src.cols, src.rows);
}
break;
}
}

参考链接:(8条消息) OpenCV中感兴趣区域的选取与检测(一)_鼹鼠的胡须 的博客-CSDN博客_opencv感兴趣区域

(8条消息) OpenCV探索之路(十三):详解掩膜mask_weixin_34221773的博客-CSDN博客

opencv——感兴趣区域(ROI)的分析和选取[详细总结]的更多相关文章

  1. opencv感兴趣区域ROI

    addWeighted //显示原图 Mat src = imread("data/img/1.jpg"); imshow("src",src); //显示lo ...

  2. opencv探索之路(十二):感兴趣区域ROI和logo添加技术

    在图像处理领域,有一个非常重要的名词ROI. 什么是ROI? 它的英文全称是Region Of Interest,对应的中文解释就是感兴趣区域. 感兴趣区域,就是我们从图像中选择一个图像区域,这个区域 ...

  3. opencv —— copyTo 设置与操作感兴趣区域(ROI)

    感兴趣区域:ROI 对感兴趣区域进行的一系列操作,相当于直接在原图相应部分进行操作. Mat imageROI = srcImage(Rect(0,0,dstImage.cols, dstImage. ...

  4. 例3-12opencv设置ROI感兴趣区域

    前面说了一堆,也不知道啥用,感觉也没说清楚,可能确实需要一些例子来显性表示一下,或者他们在当初出版书籍针对的人群已经有了对图像的基本认识,然而自己还是没有建立起来,往后看看吧,希望能比较清楚的自己处理 ...

  5. OpenCV3编程入门笔记(2)计时函数、感兴趣区域RIO、分离/混合通道

    11     绘制直线的line函数 DrawLine(Mat img, Pont start, Point end); 绘制椭圆的ellipse函数 DrawEllipse(Mat img, dou ...

  6. 获取图片中感兴趣区域的信息(Matlab实现)

    内容提要 如果一幅图中只有一小部分图像你感兴趣(你想研究的部分),那么截图工具就可以了,但是如果你想知道这个区域在原图像中的坐标位置呢? 这可是截图工具所办不到的,前段时间我就需要这个功能,于是将其用 ...

  7. [zt] ROI (Region of Interest) 感兴趣区域 OpenCV

    在以前介绍IplImage结构的时候,有一个重要的参数——ROI.ROI全称是”Region Of Interest”,即感兴趣的区域.实际上,它是IPL/IPP(这两个是Inter的库)结构IplR ...

  8. opencv读写视频,对感兴趣区域进行裁剪

    作为小码农,本人最近想对一段视频的某个区域进行处理,因此要将该段视频区域裁剪出来,搜搜网上,发现没有痕迹,是故自己琢磨一下,左右借鉴,编了如下代码,目标得以实现,希望对你有用. #include &q ...

  9. 基于OpenCV实现对图片及视频中感兴趣区域颜色识别

    基于OpenCV实现图片及视频中选定区域颜色识别 近期,需要实现检测摄像头中指定坐标区域内的主体颜色,通过查阅大量相关的内容,最终实现代码及效果如下,具体的实现步骤在代码中都详细注释,代码还可以进一步 ...

随机推荐

  1. IPFS挖矿靠谱吗?

    IPFS是一个旨在创建持久且分布式存储和共享文件的网络传输协议,前景广阔且实用意义大,为区块链行业提供了一种新的可能.而IPFS挖矿挖出的FIL,则是在IPFS技术的基础上,对维护IPFS网络的用户的 ...

  2. c++ 反汇编 除法优化

    接上篇:<C++反汇编与逆向分析技术揭秘>--算术运算和赋值 printf("argc / 4 = %d\n", argc / 4); printf("arg ...

  3. 策略枚举:消除在项目里大批量使用if-else的正确姿势

    文/朱季谦 想起刚开始接触JAVA编程的时候,若遇到大量流程判断语句,几乎满屏都是if-else语句,多得让自己都忘了哪里是头,哪里是尾,但是,纵然满屏是if-else,但彼时也没有觉得多别扭.等到编 ...

  4. 如何配置Nginx,实现http访问重定向到https?

    现在越来越多的网站,当我们输入域名时,会自动重定向到https,我们只需要简单修改下Nginx配置文件/usr/local/nginx/conf/nginx.conf(根据个人的实际存储路径)即可. ...

  5. QT项目-Chart Themes Example学习(一)

    1.main.cpp #include "themewidget.h" #include <QtWidgets/QApplication> #include <Q ...

  6. 开源一个比雪花算法更好用的ID生成算法(雪花漂移)

    比雪花算法更好用的ID生成算法(单机或分布式唯一ID) 转载及版权声明 本人从未在博客园之外的网站,发表过本算法长文,其它网站所现文章,均属他人拷贝之作. 所有拷贝之作,均须保留项目开源链接,否则禁止 ...

  7. PAT (Advanced Level) Practice 1008 Elevator (20 分) 凌宸1642

    PAT (Advanced Level) Practice 1008 Elevator (20 分) 凌宸1642 题目描述: The highest building in our city has ...

  8. 【DB宝46】NoSQL数据库之CouchBase简介、集群搭建、XDCR同步及备份恢复

    目录 一. CouchBase概述 1.1.简述 1.2.CouchDB和CouchBase比对 1.2.1.CouchDB和CouchBase的相同之处 1.2.2.CouchDB和CouchBas ...

  9. Sql Server Report Service访问服务页面503解决方法

    这个问题可能性比较多,也有多个方案去解决,可以从如下方法里逐个测试 1.打最新的数据库补丁. 2.删除报表服务配置的密钥,重启报表服务. 3.修改报表服务器配置的用户账户为域管理员 4.找到报表服务器 ...

  10. Spring Cloud微服务限流之Sentinel+Apollo生产实践

    Sentinel概述 在基于Spring Cloud构建的微服务体系中,服务之间的调用链路会随着系统的演进变得越来越长,这无疑会增加了整个系统的不可靠因素.在并发流量比较高的情况下,由于网络调用之间存 ...