本系列文章由@浅墨_毛星云 出品,转载请注明出处。
文章链接: http://blog.csdn.net/poem_qianmo/article/details/20911629
作者:毛星云(浅墨) 邮箱: happylifemxy@163.com
写作当前博文时配套使用的OpenCV版本: 2.4.8
在这篇文章里,我们一起学习了在OpenCV中如何定义感兴趣区域ROI,如何使用addWeighted函数进行图像混合操作,以及将ROI和addWeighted函数结合起来使用,对指定区域进行图像混合操作。
PS:文章末尾提供了博文配套程序源代码的下载。
文章开头,依旧是先放一张截图:
一、设定感兴趣区域——ROI(region of interest)
在图像处理领域,我们常常需要设置感兴趣区域(ROI,region of interest),来专注或者简化我们的工作过程 。也就是从图像中选择的一个图像区域,这个区域是我们图像分析所关注的重点。我们圈定这个区域,以便进行进一步处理。而且,使用ROI指定我们想读入的目标,可以减少处理时间,增加精度,给图像处理来带不小的便利。
ROI区域定义的两种方法
定义ROI区域有两种方法,第一种是使用cv:Rect.顾名思义,cv::Rect表示一个矩形区域。指定矩形的左上角坐标(构造函数的前两个参数)和矩形的长宽(构造函数的后两个参数)就可以定义一个矩形区域。
- //定义一个Mat类型并给其设定ROI区域
- Mat imageROI;
- //方法一
- imageROI=image(Rect(500,250,logo.cols,logo.rows));
另一种定义ROI的方式是指定感兴趣行或列的范围(Range)。Range是指从起始索引到终止索引(不包括终止索引)的一连段连续序列。cv::Range可以用来定义Range。如果使用cv::Range来定义ROI,那么前例中定义ROI的代码可以重写为:
- //方法二
- imageROI=srcImage3(Range(250,250+logoImage.rows),Range(200,200+logoImage.cols));
好了,下面我们来看一个实例,显示如何利用ROI将一幅图加到另一幅图的指定位置。大家如果需要拷贝如下的函数中的代码直接运行的话,自己建一个基于console的程序,然后把函数体中的内容拷贝到main函数中,然后找两幅大小合适的图片,加入到工程目录下,并和代码中读取的文件名一致即可。
在下面的代码中,我们通过一个图像掩膜(mask),直接将插入处的像素设置为logo图像的像素值,这样效果会很赞很逼真:
- //----------------------------------【ROI_AddImage( )函数】----------------------------------
- // 函数名:ROI_AddImage()
- // 描述:利用感兴趣区域ROI实现图像叠加
- //----------------------------------------------------------------------------------------------
- bool ROI_AddImage()
- {
- //【1】读入图像
- Mat srcImage1= imread("dota_pa.jpg");
- Mat logoImage= imread("dota_logo.jpg");
- if(!srcImage1.data ) { printf("你妹,读取srcImage1错误~! \n"); return false; }
- if(!logoImage.data ) { printf("你妹,读取logoImage错误~! \n"); return false; }
- //【2】定义一个Mat类型并给其设定ROI区域
- Mat imageROI= srcImage1(Rect(200,250,logoImage.cols,logoImage.rows));
- //【3】加载掩模(必须是灰度图)
- Mat mask= imread("dota_logo.jpg",0);
- //【4】将掩膜拷贝到ROI
- logoImage.copyTo(imageROI,mask);
- //【5】显示结果
- namedWindow("<1>利用ROI实现图像叠加示例窗口");
- imshow("<1>利用ROI实现图像叠加示例窗口",srcImage1);
- return true;
- }
这个函数首先是载入了两张jpg图片到srcImage1和logoImage中,然后定义了一个Mat类型的imageROI,并使用cv::Rect设置其感兴趣区域为srcImage1中的一块区域,将imageROI和srcImage1关联起来。接着定义了一个Mat类型的的mask并读入dota_logo.jpg,顺势使用Mat:: copyTo把mask中的内容拷贝到imageROI中,于是就得到了最终的效果图,namedWindow和imshow配合使用,显示出最终的结果。
运行结果如下:
这里白色的dota2 logo,就是通过操作之后加上去的图像。
二、初级图像混合——线性混合操作
线性混合操作是一种典型的二元(两个输入)的像素操作,它的理论公式是这样的:
如果看过我之前写的游戏编程Alpha混合那篇文章的朋友们应该有些熟悉,其实他们是差不多的:
【Visual C++】游戏开发五十五浅墨 DirectX教程二十二的美学:alpha混合技术
我们通过在范围0到1之间改变alpha值,来对两幅图像(f0(x)和f1(x))或两段视频(同样为(f0(x)和f1(x))产生时间上的画面叠化(cross-dissolve)效果,就像幻灯片放映和电影制作中的那样。即在幻灯片翻页时设置的前后页缓慢过渡叠加效果,以及电影情节过渡时经常出现的画面叠加效果。
实现方面,我们主要运用了OpenCV中addWeighted函数,我们来全面的了解一下它:
addWeighted函数
这个函数的作用是,计算两个数组(图像阵列)的加权和。原型如下:
- void addWeighted(InputArray src1, double alpha, InputArray src2, double beta, double gamma, OutputArray dst, int dtype=-1);
- 第一个参数,InputArray类型的src1,表示需要加权的第一个数组,常常填一个Mat。
- 第二个参数,alpha,表示第一个数组的权重
- 第三个参数,src2,表示第二个数组,它需要和第一个数组拥有相同的尺寸和通道数。
- 第四个参数,beta,表示第二个数组的权重值。
- 第五个参数,dst,输出的数组,它和输入的两个数组拥有相同的尺寸和通道数。
- 第六个参数,gamma,一个加到权重总和上的标量值。看下面的式子自然会理解。
- 第七个参数,dtype,输出阵列的可选深度,有默认值-1。;当两个输入数组具有相同的深度时,这个参数设置为-1(默认值),即等同于src1.depth()。
如果用数学公式来表达,addWeighted函数计算如下两个数组(src1和src2)的加权和,得到结果输出给第四个参数。即addWeighted函数的作用可以被表示为为如下的矩阵表达式为:
dst = src1[I]*alpha+ src2[I]*beta + gamma;
其中的I,是多维数组元素的索引值。而且,在遇到多通道数组的时候,每个通道都需要独立地进行处理。另外需要注意的是,当输出数组的深度为CV_32S时,这个函数就不适用了,这时候就会内存溢出或者算出的结果压根不对。
理论和函数的讲解就是上面这些,接着我们来看代码实例,以融会贯通。
- //---------------------------------【LinearBlending()函数】-------------------------------------
- // 函数名:LinearBlending()
- // 描述:利用cv::addWeighted()函数实现图像线性混合
- //--------------------------------------------------------------------------------------------
- bool LinearBlending()
- {
- //【0】定义一些局部变量
- double alphaValue = 0.5;
- double betaValue;
- Mat srcImage2, srcImage3, dstImage;
- //【1】读取图像 ( 两幅图片需为同样的类型和尺寸 )
- srcImage2= imread("mogu.jpg");
- srcImage3= imread("rain.jpg");
- if(!srcImage2.data ) { printf("你妹,读取srcImage2错误~! \n"); return false; }
- if(!srcImage3.data ) { printf("你妹,读取srcImage3错误~! \n"); return false; }
- //【2】做图像混合加权操作
- betaValue= ( 1.0 - alphaValue );
- addWeighted(srcImage2, alphaValue, srcImage3, betaValue, 0.0, dstImage);
- //【3】创建并显示原图窗口
- namedWindow("<2>线性混合示例窗口【原图】 by浅墨", 1);
- imshow("<2>线性混合示例窗口【原图】 by浅墨", srcImage2 );
- namedWindow("<3>线性混合示例窗口【效果图】 by浅墨", 1);
- imshow("<3>线性混合示例窗口【效果图】 by浅墨", dstImage );
- return true;
- }
代码解析:
<0>首先当然是定义一些局部变量,alpha值beta值,三个Mat类型的变量。
- //【0】定义一些局部变量
- double alphaValue = 0.5;
- double betaValue;
- Mat srcImage2, srcImage3, dstImage;
在这里我们设置alpha值为0.5。
<1>读取两幅图像并作错误处理
这步很简单,直接上代码:
- //读取图像 ( 两幅图片需为同样的类型和尺寸 )
- srcImage2= imread("mogu.jpg");
- srcImage3= imread("rain.jpg");
- if(!srcImage2.data ) { printf("你妹,读取srcImage2错误~! \n"); return false; }
- if(!srcImage3.data ) { printf("你妹,读取srcImage3错误~! \n"); return false; }
在这里需要注意的是,因为我们是对 srcImage1和srcImage2求和,所以它们必须要有相同的尺寸(宽度和高度)和类型,不然多余的部分没有对应的“伴”,肯定会出问题。
<2> 进行图像混合加权操作
载入图像后,我们就可以来生成混合图像,也就是之前公式中的g(x)。为此目的,使用函数 addWeighted 可以很方便地实现,也就是因为 addWeighted 进行了如下计算:
这里的对应于addWeighted的第2个参数alpha
这里的对应于addWeighted的第4个参数beta
这里的对应于addWeighted的第5个参数,在上面代码中被我们设为0.0。
代码其实很简单,就是这样:
- //【2】进行图像混合加权操作
- betaValue = ( 1.0 - alphaValue );
- addWeighted( srcImage2, alphaValue, srcImage3,betaValue, 0.0, dstImage);
- 其中beta值为1-alpha,gamma值为0。
<3>创建显示窗口,显示图像。
- // 【3】创建并显示原图窗口
- namedWindow("<2>线性混合示例窗口【原图】 by浅墨", 1);
- imshow("<2>线性混合示例窗口【原图】 by浅墨", srcImage2 );
- namedWindow("<3>线性混合示例窗口【效果图】 by浅墨", 1);
- imshow("<3>线性混合示例窗口【效果图】 by浅墨", dstImage );
接着来看一下运行效果图,首先是原图:
然后是效果图:
三、综合示例
在前面分别介绍的设定感兴趣区域ROI和使用addWeighted函数进行图像线性混合的基础上,我们还将他们两者中和起来使用,也就是先指定ROI,并用addWeighted函数对我们指定的ROI区域的图像进行混合操作,我们将其封装在了一个名为ROI_LinearBlending的函数中,方便大家分块学习。
- //---------------------------------【ROI_LinearBlending()】-------------------------------------
- // 函数名:ROI_LinearBlending()
- // 描述:线性混合实现函数,指定区域线性图像混合.利用cv::addWeighted()函数结合定义
- // 感兴趣区域ROI,实现自定义区域的线性混合
- //--------------------------------------------------------------------------------------------
- bool ROI_LinearBlending()
- {
- //【1】读取图像
- Mat srcImage4= imread("dota_pa.jpg",1);
- Mat logoImage= imread("dota_logo.jpg");
- if(!srcImage4.data ) { printf("你妹,读取srcImage4错误~! \n"); return false; }
- if(!logoImage.data ) { printf("你妹,读取logoImage错误~! \n"); return false; }
- //【2】定义一个Mat类型并给其设定ROI区域
- Mat imageROI;
- //方法一
- imageROI=srcImage4(Rect(200,250,logoImage.cols,logoImage.rows));
- //方法二
- //imageROI=srcImage4(Range(250,250+logoImage.rows),Range(200,200+logoImage.cols));
- //【3】将logo加到原图上
- addWeighted(imageROI,0.5,logoImage,0.3,0.,imageROI);
- //【4】显示结果
- namedWindow("<4>区域线性图像混合示例窗口 by浅墨");
- imshow("<4>区域线性图像混合示例窗口 by浅墨",srcImage4);
- return true;
- }
从这篇文章开始,如果不出意外的话,为了方便大家分块各个击破学习,每讲一个部分,示例代码都将封装在一个函数中,免得大家像学习各种不是特别地道的OpenCV教程时一样,看到代码全放在main函数中,心都碎了。
好了,下面放出详细注释的本篇文章的全部示例源代码:
- //-----------------------------------【程序说明】----------------------------------------------
- // 程序名称::【OpenCV入门教程之四】 ROI区域图像叠加&初级图像混合 全剖析 配套源码
- // VS2010版 OpenCV版本:2.4.8
- // 2014年3月10日 Create by 浅墨
- // 图片素材出处:dota2原画 dota2logo
- // 浅墨的微博:@浅墨_毛星云
- //------------------------------------------------------------------------------------------------
- //-----------------------------------【头文件包含部分】---------------------------------------
- // 描述:包含程序所依赖的头文件
- //----------------------------------------------------------------------------------------------
- #include <cv.h>
- #include <highgui.h>
- #include <iostream>
- //-----------------------------------【命名空间声明部分】---------------------------------------
- // 描述:包含程序所使用的命名空间
- //-----------------------------------------------------------------------------------------------
- using namespace cv;
- using namespace std;
- //-----------------------------------【全局函数声明部分】--------------------------------------
- // 描述:全局函数声明
- //-----------------------------------------------------------------------------------------------
- bool ROI_AddImage();
- bool LinearBlending();
- bool ROI_LinearBlending();
- //-----------------------------------【main( )函数】--------------------------------------------
- // 描述:控制台应用程序的入口函数,我们的程序从这里开始
- //-----------------------------------------------------------------------------------------------
- int main( )
- {
- system("color 5E");
- if(ROI_AddImage()&& LinearBlending( )&&ROI_LinearBlending( ))
- {
- cout<<endl<<"嗯。好了,得出了你需要的图像~! : )";
- }
- waitKey(0);
- return 0;
- }
- //----------------------------------【ROI_AddImage( )函数】----------------------------------
- // 函数名:ROI_AddImage()
- // 描述:利用感兴趣区域ROI实现图像叠加
- //----------------------------------------------------------------------------------------------
- bool ROI_AddImage()
- {
- //【1】读入图像
- Mat srcImage1= imread("dota_pa.jpg");
- Mat logoImage= imread("dota_logo.jpg");
- if(!srcImage1.data ) { printf("你妹,读取srcImage1错误~! \n"); return false; }
- if(!logoImage.data ) { printf("你妹,读取logoImage错误~! \n"); return false; }
- //【2】定义一个Mat类型并给其设定ROI区域
- Mat imageROI= srcImage1(Rect(200,250,logoImage.cols,logoImage.rows));
- //【3】加载掩模(必须是灰度图)
- Mat mask= imread("dota_logo.jpg",0);
- //【4】将掩膜拷贝到ROI
- logoImage.copyTo(imageROI,mask);
- //【5】显示结果
- namedWindow("<1>利用ROI实现图像叠加示例窗口");
- imshow("<1>利用ROI实现图像叠加示例窗口",srcImage1);
- return true;
- }
- //---------------------------------【LinearBlending()函数】-------------------------------------
- // 函数名:LinearBlending()
- // 描述:利用cv::addWeighted()函数实现图像线性混合
- //--------------------------------------------------------------------------------------------
- bool LinearBlending()
- {
- //【0】定义一些局部变量
- double alphaValue = 0.5;
- double betaValue;
- Mat srcImage2, srcImage3, dstImage;
- //【1】读取图像 ( 两幅图片需为同样的类型和尺寸 )
- srcImage2= imread("mogu.jpg");
- srcImage3= imread("rain.jpg");
- if(!srcImage2.data ) { printf("你妹,读取srcImage2错误~! \n"); return false; }
- if(!srcImage3.data ) { printf("你妹,读取srcImage3错误~! \n"); return false; }
- //【2】进行图像混合加权操作
- betaValue= ( 1.0 - alphaValue );
- addWeighted(srcImage2, alphaValue, srcImage3, betaValue, 0.0, dstImage);
- //【3】创建并显示原图窗口
- namedWindow("<2>线性混合示例窗口【原图】 by浅墨", 1);
- imshow("<2>线性混合示例窗口【原图】 by浅墨", srcImage2 );
- namedWindow("<3>线性混合示例窗口【效果图】 by浅墨", 1);
- imshow("<3>线性混合示例窗口【效果图】 by浅墨", dstImage );
- return true;
- }
- //---------------------------------【ROI_LinearBlending()】-------------------------------------
- // 函数名:ROI_LinearBlending()
- // 描述:线性混合实现函数,指定区域线性图像混合.利用cv::addWeighted()函数结合定义
- // 感兴趣区域ROI,实现自定义区域的线性混合
- //--------------------------------------------------------------------------------------------
- bool ROI_LinearBlending()
- {
- //【1】读取图像
- Mat srcImage4= imread("dota_pa.jpg",1);
- Mat logoImage= imread("dota_logo.jpg");
- if(!srcImage4.data ) { printf("你妹,读取srcImage4错误~! \n"); return false; }
- if(!logoImage.data ) { printf("你妹,读取logoImage错误~! \n"); return false; }
- //【2】定义一个Mat类型并给其设定ROI区域
- Mat imageROI;
- //方法一
- imageROI=srcImage4(Rect(200,250,logoImage.cols,logoImage.rows));
- //方法二
- //imageROI=srcImage4(Range(250,250+logoImage.rows),Range(200,200+logoImage.cols));
- //【3】将logo加到原图上
- addWeighted(imageROI,0.5,logoImage,0.3,0.,imageROI);
- //【4】显示结果
- namedWindow("<4>区域线性图像混合示例窗口 by浅墨");
- imshow("<4>区域线性图像混合示例窗口 by浅墨",srcImage4);
- return true;
- }
最后看一下整体的运行效果图。
首先是经过背景颜色修改的console窗口:
然后依次是四张效果图:
嗯,本篇文章到这里就基本结束了,最后放出本篇文章配套示例程序的下载地址。
本篇文章的配套源代码请点击这里下载:
OK,本节的内容大概就是这些,我们下篇文章见:)
相关推荐
【OpenCV入门教程之四】主要讲解了在OpenCV中如何操作感兴趣区域(ROI)以及如何进行初级图像混合,这是图像处理中常见的基础操作。本文将深入解析这两个概念及其应用场景。 1. ROI区域图像(Region of Interest) ...
博文《【OpenCV入门教程之四】 ROI区域图像叠加&初级图像混合 全剖析》的配套详细注释源代码。 博文链接: http://blog.csdn.net/poem_qianmo/article/details/20911629 1.Release文件夹下的exe可以直接看到运行...
本文将深入探讨“OpenCV-ROI区域图像叠加”这一主题,结合提供的代码和图片,我们将理解如何在特定的感兴趣区域(ROI, Region of Interest)上叠加图像。 ROI是图像处理中的一个重要概念,它指的是图像中我们特别...
ROI图像叠加是一种常用的技术,用于突出显示图像中的特定部分,这在分析、处理或可视化图像时非常有用。OpenCV是一个强大的开源计算机视觉库,它提供了丰富的功能来处理这种任务。 在OpenCV中,实现ROI的基本步骤...
2. **图像融合**:将两个图像混合在一起,通常需要考虑权重和叠加方式,以实现更加自然的融合效果。 3. **区域选择和操作**:在特定的图像区域上进行操作,如ROI(Region of Interest)选择,可以实现只对图像的一...
在OpenCV库中,图像处理是一项重要的功能,其中包括图像叠加、图像融合以及按位操作等。这些技术在图像分析、视觉特效以及图像合成等领域有着广泛的应用。以下将详细阐述这三个概念及其实现方法。 一、图像叠加 ...
在OpenCV中,实现位图叠加可以通过简单的像素复制操作完成,也可以通过复杂的图像混合算法来实现更加自然的效果。 #### 三、OpenCV中的位图叠加实现 在给定的代码示例中,展示了如何在OpenCV中实现位图叠加到视频流...
- **图像ROI(感兴趣区域):** 提供了对图像局部区域进行操作的方法。 - **拆分及合并图像通道:** 讲解了如何分离或组合图像的不同颜色通道。 - **图像填充:** 介绍了如何为图像边缘添加颜色或图案。 **3.2 图像...
在OpenCV中,将两张图片显示到一幅图片上通常是为了比较或分析图像处理的效果。这个问题在上述描述中有所提及,作者希望通过将处理前后的图片并排放置在一个图像中,直观地展示它们之间的差异。在尝试这个任务时,...
ROI允许用户定义一个特定的图像区域进行处理,例如分析、裁剪或增强。在视频流中,这可能用于追踪特定对象或仅关注画面的某个部分。在OpenCV中,可以通过定义矩形、多边形或者其他形状来选取ROI,并对这个区域进行...
C# EmguCV应用,是.NET平台下OpenCV图像处理库的封装,为C#等.NET兼容编程语言提供了图像处理的能力。EmguCV的优势在于其友好的.NET界面,为用户操作提供了直观的感觉。该技术可以在Mono环境下编译,并且能在Linux或...
3. 区域筛选:在进行边缘检测后,我们会设定一个感兴趣的区域(ROI),只关注图像中可能包含车道线的部分,从而减少计算量和误检。 4. 基于霍夫变换的直线检测:使用霍夫变换找到图像中的直线,这是识别车道线的...