若该文为原创文章,未经允许不得转载
原博主博客地址:https://blog.csdn.net/qq21497936
本文章博客地址:https://blog.csdn.net/qq21497936/article/details/101351444
目录
OpenCV开发专栏
《OpenCV开发笔记(〇):使用mingw530_32编译openCV3.4.1源码,搭建Qt5.9.3的openCV开发环境》
《OpenCV开发笔记(三):OpenCV图像的概念和基本操作》
《OpenCV开发笔记(四):OpenCV图片和视频数据的读取与存储》
《OpenCV开发笔记(五):OpenCV读取与操作摄像头》
《OpenCV开发笔记(六):OpenCV基础数据结构、颜色转换函数和颜色空间》
《OpenCV开发笔记(八):OpenCV常用操作之计时、缩放、旋转、镜像》
《OpenCV开发笔记(九):OpenCV区域图像(ROI)和整体、局部图像混合》
《OpenCV开发笔记(十):OpenCV图像颜色通道分离和图像颜色多通道混合》:待发布
持续补充中…
OpenCV开发笔记(九):OpenCV区域图像(ROI)和整体、局部图像混合
前言
图像操作常常需要获取指定区域的图像,对于OpenCV提供了ROI(感兴趣区域)支持;
图像的叠加和混合也是常用的操作之一。
Demo
感兴趣区域:ROI
在图像处理领域中,专业名词感兴趣的区域其实就是选取指定区域的图像。
选择指定区域
图像中提取感兴趣区域(Region of interest)有两种方法:
- 方法一:使用构造函数
//创建宽度为 320,高度为 240 的 3 通道图像
Mat img(Size(320, 240), CV_8UC3);
//roi 是表示 img 中 Rect(10, 10, 100, 100)区域的对象
Mat roi(img, Rect(10, 10, 100, 100));
- 方法二:使用括号运算符
Mat roi2 = img(Rect(10, 10, 100, 100));
// 用括号运算符
Mat roi3 = img(Range(10, 100), Range(10, 100));
// 用构造函数
Mat roi4(img, Range(10, 100), Range(10, 100));
测试代码
cv::Mat srcMat;
QString fileName = "D:/qtProject/openCVDemo/openCVDemo/modules/openCVManager/images/1.jpg";
srcMat = cv::imread(fileName.toStdString());
if(!srcMat.data)
{
qDebug() << __FILE__ << __LINE__ << "Failed to load image:" << fileName;
return;
}
cv::imshow("orgin mat", srcMat);
cv::Mat roiMat = srcMat(cv::Range(srcMat.rows/2 - 50, srcMat.rows/2 + 50),
cv::Range(srcMat.cols/2 - 30, srcMat.cols/2 + 30));
cv::imshow("roi mat", roiMat);
cv::waitKey(0);
测试效果
图像混合(整体混合)
图像混合有多种方式。
线性混合
线性混合操作是一种典型的二元(两个输入)的像素操作,它的理论公式如下:
G(x) = (1-a)F1(x) + aF2(x)
a代表透明度的值(0.0~1.0)对两幅图像(F1和F2)活两段视频产生时间上的画面叠化效果,前面页面切换至后面页面的一个切换过程。
线性混合过程主要使用addWeighted函数。
计算数组加权和:addWeighted()函数
这个函数的作用是计算两个mat的加权和,当然读者可以自己遍历像素计算。该函数的原型如下:
CV_EXPORTS_W void addWeighted(InputArray src1,
double alpha,
InputArray src2,
double beta,
double gamma,
OutputArray dst,
int dtype = -1);
- 参数一:输入图像1,mat类型
- 参数二:alpha值,表示图像1的权重
- 参数三:输入图像2,mat类型
- 参数四:beta值,表示图像2的权重
- 参数五:gamma值,一个加到权重总和上的标量值
- 参数六:输出图像dst,mat类型,它与输入的两个mat尺寸和通道相同
- 参数七:dtype,输出陈列的可选深度,默认值-1,当两个输入数组具有相同的深度时,这个参数设置为-1(默认值),即等同于src1.depth()。
以上函数的计算公式如下:
dst = src1[I] * alpha + src2[I] * beta + gamma;
其中I为元素位置的索引值,当遇到多通道mat的时候,每个通道都需要独立的进行处理。
注意:当输出mat的深度为CV_32S时,这个函数不使用,会导致内存溢出或者算出的结果错误。
测试代码
// 测试线性混合: 正向 与 x轴翻转后的线性混合
cv::Mat mat1;
cv::Mat mat2;
cv::Mat mat3;
QString fileName1 = "D:/qtProject/openCVDemo/openCVDemo/modules/openCVManager/images/1.jpg";
QString fileName2 = "D:/qtProject/openCVDemo/openCVDemo/modules/openCVManager/images/1.bmp";
mat1 = cv::imread(fileName1.toStdString());
mat2 = cv::imread(fileName2.toStdString());
if(!mat1.data || !mat2.data)
{
qDebug() << __FILE__ << __LINE__
<< "Failed to load image:" << fileName1 << "or" << fileName2;
return;
}
// 对mat2进行缩放,缩放至mat1的大小,否则会报错
cv::resize(mat2, mat2, cv::Size(mat1.cols, mat1.rows));
// 然后进行混合
double a = 0.0;
while(true)
{
cv::addWeighted(mat1, a, mat2, (1.0-a), 0.0, mat3);
cv::imshow("mat3", mat3);
int key = cv::waitKey(0);
if(key == 27)
{
break;
}else if(key == '1')
{
a += 0.05;
if(a >= 1.0)
{
a = 1.0;
}
}else if(key == '2')
{
a -= 0.05;
if(a < 0.0)
{
a = 0.0;
}
}
}
测试效果
图像混合(局部混合)
原理和方法参照图像混合(整体混合)。
局部混合可以理解对感兴趣的区域进行混合,这里特别要注意一下:
- 使用 mat1 = mat2,其实mat1是mat2的副本(同一段数据);
- 使用 mat1 = mat2(roi),其实mat1是mat2上roi区域的副本(同一段数据);
- 使用 mat1 = mat2.clone(),mat1才是mat2的拷贝副本(两段相同数据);
- 使用 mat1 = mat2(roi).clone(),mat1才是mat2上roi区域的的拷贝副本(两段相同数据);
测试代码
// 测试线性混合: 正向 与 x轴翻转后的线性混合
cv::Mat mat1;
cv::Mat mat2;
cv::Mat mat3;
QString fileName1 = "D:/qtProject/openCVDemo/openCVDemo/modules/openCVManager/images/1.jpg";
QString fileName2 = "D:/qtProject/openCVDemo/openCVDemo/modules/openCVManager/images/1.bmp";
mat1 = cv::imread(fileName1.toStdString());
mat2 = cv::imread(fileName2.toStdString());
if(!mat1.data || !mat2.data)
{
qDebug() << __FILE__ << __LINE__
<< "Failed to load image:" << fileName1 << "or" << fileName2;
return;
}
// 对mat2进行缩放,缩放至mat1的1/4的大小
cv::resize(mat2, mat2, cv::Size(mat1.cols/2, mat1.rows/2));
double a = 0.0;
while(true)
{
// mat4只是mat1的副本
// cv::Mat mat4 = mat1;
cv::Mat mat4 = mat1.clone();
qDebug() << __FILE__ << __LINE__
<< mat1.cols/2 - mat1.cols/4
<< mat1.cols/2 + mat1.cols/4
<< mat1.rows/2 - mat1.rows/4
<< mat1.rows/2 + mat1.rows/4;
cv::Mat mat5 = mat4(cv::Range(mat1.rows/2 - mat1.rows/4, mat1.rows/2 + mat1.rows/4),
cv::Range(mat1.cols/2 - mat1.cols/4, mat1.cols/2 + mat1.cols/4));
qDebug() <<__FILE__<<__LINE__;
cv::addWeighted(mat5, a, mat2, (1.0-a), 0.0, mat5);
qDebug() <<__FILE__<<__LINE__;
cv::imshow("mat4", mat4);
int key = cv::waitKey(0);
if(key == 27)
{
break;
}else if(key == '1')
{
a += 0.05;
if(a >= 1.0)
{
a = 1.0;
}
}else if(key == '2')
{
a -= 0.05;
if(a < 0.0)
{
a = 0.0;
}
}
}
测试效果
Demo源码
void OpenCVManager::testROIAndBlend()
{
#define TEST_ROI (0) // roi
#define TEST_BLEND (0) // 图像加权混合(全部区域)
#define TEST_BLEND_ROI (1) // 图像加权混合(roi区域)
#if TEST_ROI
// 测试提取
cv::Mat srcMat;
QString fileName = "D:/qtProject/openCVDemo/openCVDemo/modules/openCVManager/images/1.jpg";
srcMat = cv::imread(fileName.toStdString());
if(!srcMat.data)
{
qDebug() << __FILE__ << __LINE__ << "Failed to load image:" << fileName;
return;
}
cv::imshow("orgin mat", srcMat);
cv::Mat roiMat = srcMat(cv::Range(srcMat.rows/2 - 50, srcMat.rows/2 + 50),
cv::Range(srcMat.cols/2 - 30, srcMat.cols/2 + 30));
cv::imshow("roi mat", roiMat);
cv::waitKey(0);
#endif
#if TEST_BLEND
// 测试线性混合: 正向 与 x轴翻转后的线性混合
cv::Mat mat1;
cv::Mat mat2;
cv::Mat mat3;
QString fileName1 = "D:/qtProject/openCVDemo/openCVDemo/modules/openCVManager/images/1.jpg";
QString fileName2 = "D:/qtProject/openCVDemo/openCVDemo/modules/openCVManager/images/1.bmp";
mat1 = cv::imread(fileName1.toStdString());
mat2 = cv::imread(fileName2.toStdString());
if(!mat1.data || !mat2.data)
{
qDebug() << __FILE__ << __LINE__
<< "Failed to load image:" << fileName1 << "or" << fileName2;
return;
}
// 对mat2进行缩放,缩放至mat1的大小,否则会报错
cv::resize(mat2, mat2, cv::Size(mat1.cols, mat1.rows));
// 然后进行混合
double a = 0.0;
while(true)
{
cv::addWeighted(mat1, a, mat2, (1.0-a), 0.0, mat3);
cv::imshow("mat3", mat3);
int key = cv::waitKey(0);
if(key == 27)
{
break;
}else if(key == '1')
{
a += 0.05;
if(a >= 1.0)
{
a = 1.0;
}
}else if(key == '2')
{
a -= 0.05;
if(a < 0.0)
{
a = 0.0;
}
}
}
#endif
#if TEST_BLEND_ROI
// 测试线性混合: 正向 与 x轴翻转后的线性混合
cv::Mat mat1;
cv::Mat mat2;
cv::Mat mat3;
QString fileName1 = "D:/qtProject/openCVDemo/openCVDemo/modules/openCVManager/images/1.jpg";
QString fileName2 = "D:/qtProject/openCVDemo/openCVDemo/modules/openCVManager/images/1.bmp";
mat1 = cv::imread(fileName1.toStdString());
mat2 = cv::imread(fileName2.toStdString());
if(!mat1.data || !mat2.data)
{
qDebug() << __FILE__ << __LINE__
<< "Failed to load image:" << fileName1 << "or" << fileName2;
return;
}
// 对mat2进行缩放,缩放至mat1的1/4的大小
cv::resize(mat2, mat2, cv::Size(mat1.cols/2, mat1.rows/2));
double a = 0.0;
while(true)
{
// mat4只是mat1的副本
// cv::Mat mat4 = mat1;
cv::Mat mat4 = mat1.clone();
qDebug() << __FILE__ << __LINE__
<< mat1.cols/2 - mat1.cols/4
<< mat1.cols/2 + mat1.cols/4
<< mat1.rows/2 - mat1.rows/4
<< mat1.rows/2 + mat1.rows/4;
cv::Mat mat5 = mat4(cv::Range(mat1.rows/2 - mat1.rows/4, mat1.rows/2 + mat1.rows/4),
cv::Range(mat1.cols/2 - mat1.cols/4, mat1.cols/2 + mat1.cols/4));
qDebug() <<__FILE__<<__LINE__;
cv::addWeighted(mat5, a, mat2, (1.0-a), 0.0, mat5);
qDebug() <<__FILE__<<__LINE__;
cv::imshow("mat4", mat4);
int key = cv::waitKey(0);
if(key == 27)
{
break;
}else if(key == '1')
{
a += 0.05;
if(a >= 1.0)
{
a = 1.0;
}
}else if(key == '2')
{
a -= 0.05;
if(a < 0.0)
{
a = 0.0;
}
}
}
#endif
}
工程模板:对应版本号v1.6.0
对应版本号v1.6.0
原博主博客地址:https://blog.csdn.net/qq21497936
本文章博客地址:https://blog.csdn.net/qq21497936/article/details/101351444
转载:https://blog.csdn.net/qq21497936/article/details/101351444