opencv的core组件——像素,ROI,图像混合(3)
ROI区域图像叠加&图像混合
- ROI区域图像叠加
ROI(region of interest):感兴趣区域:利用感兴趣区域ROI实现图像叠加。
1.1 使用cv::Retc
定义一个矩形区域。指定矩形左上角坐标和矩阵的长宽就可以定义一个矩形区域
//定义一个Mat 类型并给其设定ROI区域
Mat imageROI;
//方法一
imageROI = image(Rect(500, 250, log.cols, log.rows));
1.2 使用cv::Range
另一种定义ROI的方式是指定感兴趣区域的行或列的范围。Range是指从起始索引到终止索引(不包括)的一连串序列。
Mat imageROI;
imageROI = image(Range(250, 250 + log.rows), Range(200, 200 + log.cols));
1.3 实例:如何将一幅图像加到另一幅图的指定位置
函数名:ROI_AddImage()
描述:利用感兴趣区域实现图像的叠加
bool ROI_AddImge()
{//[1]载入图像Mat srcImage1 = imread("../1.jpg");Mat logImage2 = imread("../2.jpg");if(!srcImage1.data) {printf("读取srcImage1 false!");return false;}if(!logImage2.data) {printf("读取logImage2 false!");return false;}//[2]定义一个Mat类型,并设定ROI区域Mat imageROI = srcImage1(Rect(0, 0, logImage2.cols, logImage2.rows));//[3]加载掩模(必需是灰度图)Mat mask = imread("../2.jpg", 0);//[4]将掩模复制到ROIlogImage2.copyTo(imageROI, mask);//[5]显示结果namedWindow("<1>利用ROI实现图像的叠加显示窗口");imshow("<1>利用ROI实现图像叠加显示窗口", srcImage1);return true;
}
这个函数首先载入了两张图片到srcImage和logImage中,然后定义也Mat类型的imageROI,并在srcImage中设定一块矩形的感兴趣区域,将imageROI和srcImage关联起来。然后定义了一个Mat类的mask,读入2.jpg,然后把mask中的内容复制到imageROI中,据得到最终的效果。
注意:一定要保证nameWindow()中的窗口字符串要和imshow()中的一致,否则,会多处一个窗口
- 图像混合
2.1 线性混合操作
线性混合操作是一种典型的二元(两个图像)的像素操作,它的理论公式为:
g(x) = (1-a)f2f_2f2(x) + af1f_1f1(x)
我们通过在0~1之间改变alpha的值,来实现两张图片或两段视频产生的时间上的画面的叠加效果,图片间的过渡叠加效果,视频的画面叠加效果——addWeighted
2.2 addWeighted函数
(1), 计算两个数组(图像阵列)的加权和。因为要对两个图像进行加权求和,所以图像尺寸一定要相同。
原型:
void addWeighted(InputArray src1, double alpha, InputArray src2, double beta, double gamma, OutputArray dst, int dtype = -1);
参数:
- 参数一:InputArray src1, 表示加权的第一个数组, InputArray相当于Mat.
- 参数二:alpha,第一个数组的权重。
- 参数三:src2,第二个数组,它需要和第一个数组有相同的尺寸和通道数字(颜色)。
- 参数四:beta,表示第二个数组的权重值。
- 参数五:dst, 输出的数组,和输入的两个数组有相同的尺寸和通道数。
- 参数六:gamma,一个加权到权重总和上的标量值。
- 参数七:dtype, 输出阵列的可选深度,默认-1.当两个输入数组具有相同的深度时,等同于src1.depth()
dst = src1[I] * alpha + src2[I] * beta + gamma;
其中I是多维数组元素的索引值。遇到多通道时,每个通道都需要单独处理。当输出数组的深度为CV_32S时,这个函数就不适用了,内存会益处或值不对。
2.3 代码实例
函数名:LinearBlending()
描述:图像混合加权操作,叠加效果图
bool LinearBlending()
{//[0]定义一些局部变量double alphaValue = 0.5;double betaValue;Mat srcImage2, srcImage3, dstImage;//[1]读取图片(两幅图同尺度和类型)Mat srcImage2 = imread("../3.jpg");Mat srcImage3 = imread("../4.jpg");if(!srcImage2.data) {cout <<"读取srcImage2 错误!" << endl;return false;}if(!srcImage3.data) {cout << "读取srcImage3 错误!" << endl;return false;}//[3]作图像混合加权操作betaValue = (1.0 - alphaValue);addWeighted(srcImage2, alphaValue, srcImage3, betaValue, 0.0, dstImage);//[3]创建并显示原窗口namedWindow("<2>线性混合窗口原图>", 1);imshow("<2>线性混合窗口原图【窗口】", srcImage2);namedWindow("<2>线性混合窗口【效果图】");imshow("<2>线性混合窗口【效果图】", dstImage);return true;}
3.初级图像混合
3.1 将感兴趣区域和addWeightd进行结合。
先指定ROI区域,然后用addWeighted函数对指定的ROI区域的图像进行混合操作。
函数名:ROI_LinearBlending()
描述:线性混合实现函数,指定区域线性图像混合;
bool ROI_LiearBlending()
{//[1]读取图像Mat srcImage4 = imread("../1.jpg", 1);Mat logsrcImage = imread("../2.jpg");if(!srcImage4.data) {cout <<"读取srcImage2 错误!" << endl;return false;}if(!logsrcImage.data) {cout << "读取srcImage3 错误!" << endl;return false;}//[2]定义一个Mat类的ROI,并设定区域Mat imageROI;//方法一imageROI = srcImage4(Rect(0, 0 , logsrcImage.cols, logsrcImage.rows));//方法二imageROI = srcImage4(Range(250, 250 + logsrcImage.rows), Range(200, 200 + logsrcImage.cols));//[3]将logo加到原图上addWeighted(imageROI, 0.5, logsrcImage, 0.3, 0.0, imageROI);//[4]显示结果namedWindow("<4>区域线性混合图实例");imshow("<4>区域线性混合实例图", srcImage4);return 0;}
4 综合实例
#include "opencv2/opencv.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include <iostream>
using namespace std;
using namespace cv;bool ROI_AddImage();
bool LinearBlending();
bool ROI_LiearBlending();int main()
{system("color SE");if(ROI_AddImage() && LinearBlending() && ROI_LiearBlending()) {cout << "运行成功!" << endl;}waitKey(0);return 0;
}bool ROI_AddImge()
{//[1]载入图像Mat srcImage1 = imread("../1.jpg");Mat logImage2 = imread("../2.jpg");if(!srcImage1.data) {printf("读取srcImage1 false!");return false;}if(!logImage2.data) {printf("读取logImage2 false!");return false;}//[2]定义一个Mat类型,并设定ROI区域Mat imageROI = srcImage1(Rect(0, 0, logImage2.cols, logImage2.rows));//[3]加载掩模(必需是灰度图)Mat mask = imread("../2.jpg", 0);//[4]将掩模复制到ROIlogImage2.copyTo(imageROI, mask);//[5]显示结果namedWindow("<1>利用ROI实现图像的叠加显示窗口");imshow("<1> 利用ROI实现图像叠加显示窗口", srcImage1);return true;
}bool LinearBlending()
{//[0]定义一些局部变量double alphaValue = 0.5;double betaValue;Mat srcImage2, srcImage3, dstImage;//[1]读取图片(两幅图同尺度和类型)Mat srcImage2 = imread("../3.jpg");Mat srcImage3 = imread("../4.jpg");if(!srcImage2.data) {cout <<"读取srcImage2 错误!" << endl;return false;}if(!srcImage3.data) {cout << "读取srcImage3 错误!" << endl;return false;}//[3]作图像混合加权操作betaValue = (1.0 - alphaValue);addWeighted(srcImage2, alphaValue, srcImage3, betaValue, 0.0, dstImage);//[3]创建并显示原窗口namedWindow("<2>线性混合窗口原图>", 1);imshow("<2>线性混合窗口原图【窗口】", srcImage2);namedWindow("<2>线性混合窗口【效果图】");imshow("<2>线性混合窗口【效果图】", dstImage);return true;}bool ROI_LiearBlending()
{//[1]读取图像Mat srcImage4 = imread("../1.jpg", 1);Mat logsrcImage = imread("../2.jpg");if(!srcImage4.data) {cout <<"读取srcImage2 错误!" << endl;return false;}if(!logsrcImage.data) {cout << "读取srcImage3 错误!" << endl;return false;}//[2]定义一个Mat类的ROI,并设定区域Mat imageROI;//方法一imageROI = srcImage4(Rect(0, 0 , logsrcImage.cols, logsrcImage.rows));//方法二imageROI = srcImage4(Range(250, 250 + logsrcImage.rows), Range(200, 200 + logsrcImage.cols));//[3]将logo加到原图上addWeighted(imageROI, 0.5, logsrcImage, 0.3, 0.0, imageROI);//[4]显示结果namedWindow("<4>区域线性混合图实例");imshow("<4>区域线性混合实例图", srcImage4);return 0;}
CMakeLists.txt:
cmake_minimum_required(VERSION 3.6)
project(ROI)
find_package(OpenCV REQUIRED)
include_directories(${OpenCV_INCLUDE_DIRS})
add_executable(ROI ROI.cpp)
target_link_libraries(ROI ${OpenCV_LIBS})
实现效果是上述分装好的三个函数运行后的结果。
- 访问图像中的像素
5.1 图像在内存中的存储方式
图像矩阵的大小取决于所用的颜色模型,即所用的通道数。
如果是灰度图,则列数就是通道数,一一对应。
如果是彩色图,那就是多通道的,矩阵中的列会包含多个子列,子列个数与通道数相同。
opencv中列的通道数是反过来的,不是RGB而是BGR,很多情况下,由于内存空间足够大,可以实现连续存储,因此,图像个行能连续存储,形成一个长行。连续存储能提升图像扫描的速度,可以通过使用isContinuous()来判断矩阵是否连续存储的。
5.2 颜色空间缩减
矩阵的单通道像素值由无符号字符类表示,就是256个不同的值,多通道的更多,如此多的颜色处理起来,会影响算法的性能。
使用这些颜色中有代表性的就可以达到同样的效果。
颜色空间缩减:将现有的颜色值除以某个值,以获得较少的颜色数。如0-9可以取为0, 10-19可以取为10;
这样的缩减操作将三通道的颜色降低到26 x 26 x26种。
在C++中int类型除法会自动截余。
InewI_{new}Inew= Iold10∗10\frac {I_{old}}{10}*1010Iold∗10
在处理图像的过程中,对每个像素进行上述操作,也需要花时间。只有0~255中情况。进一步把256种计算好的结果存到table中,这样每种情况不用计算,直接取值。
int divideWith = 10;uchar table[256];for(int i = 0; i < 256; i++) {table[i] = divideWith * (i/divideWith);}
于是,table中存放的是值为i的像素减小颜色空间的结果。
颜色空间缩减有两部分组成:
(1)遍历图像矩阵的每个像素;
(2)对像素应用上述公式;
5.3 LUT函数:Look up table操作
通过operationsOnArrays::LUT()的函数来进行。它用于批量进行图像元素的查找,扫描于操作图像。使用方法如下:
首先建立一个Mat 型用于查表Mat loolUpTable(1, 256, CV_8U);uchar *p = LookUpTable.data;for(int i = 0; i < 256; i++){p[i] = table[i];}//然后调用函数(I是输入,J是输出)for(int i = 0; i<times; i++) {LUT(I, lookUpTable, J);
5.4 记时函数
getTickCount() , getTickFrequency()
- getTickCount()返回CPU自某个事件以来时钟周期数;
- getTickFrequency()函数返回CPU一秒走的时钟周期数。
使用方法:
double time0 = static_cast<double>(getTickCount)); //进行一些列图像操作time 1 = ((double)getTickCount() - time0)/getFrequency();cout << "操作运行的时间为:" << time0 << "秒“ << endl;
5.5 访问图像中像素的三种方法
OpenCV提供了三种访问每个像素的方法;
- 方法一:指针访问:c擦作符[];
- 方法二:迭代器iterator;
- 方法三:动态地址计算;
这三种访问的方式在bebug模式下访问速度略有差别,release下差不多。
程序的目的是减少图像中颜色的数量。
主程序:
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <iostream>using namespace std;
using namespace cv;void colorReduce(Mat& inputImage, Mat& outputImage, int div);int main()
{//[1]创建原始图像并显示Mat srcImage = imread("../1.jpg");imshow("原始图像", srcImage);//[2]按原始图像的参数规格来创建效果图Mat dstImage;dstImage.create(srcImage.rows, srcImage.cols, srcImage.type());//[3]记录时间double time0 = static_cast<double>(getTickCount());//[4]调用颜色空间缩减函数colorReduce(srcImage, dstImage, 32);//[5]计算运行时间并输出time0 = ((double)getTickCount() - time0)/getTickFrequency();cout << "此方法运行时间:" << time0 << "秒" << endl;//[6]显示效果图imshow("效果图", dstImage);waitKey(0);return 0;}
从上述代码可以看出,主程序中调用了colorReduce()函数来完成减少颜色的的工作,而我们访问像素有三种方法,所以有三个版本的colorReduce()
- 方法一:用指针访问像素
void colorReduce(Mat& inputImage, Mat& outputImage, int div)
{outputImage = inputImage.clone(); // 复制实参到临时变量int rowNumber = outputImage.rows; int colorNumber = outputImage.cols * outputImage.channels(); //列x的通道数等于每一行元素的个数for(int i = 0; i < rowNumber; i++) {uchar* data = outputImage.ptr<uchar>(i); //获取第i行的首地址for(int j = 0; j<colNumber; j++) {//开始处理每个像素data[j] = data[j]/div * div +div/2;//*data ++= *data/div * div + div/2; //使用指针运算从一列移动到下一列//处理结束}}}
- 方法二:迭代器操作像素
在迭代法中,我们需要做的就是获取图像矩阵的begin和end,然后增加迭代直至从begin到end。将*操作符加到指针的前面,可以访问当前指针所指的内容。
void colorReduce(Mat& inputImage, Mat& outputImage, int div)
{outputImage = inputImage.clone();//获取迭代器Mat_<Vec3b>::iterator it = outputImage.begin<Vec3b>(); //初始位置的迭代器Mat_<Vec3b>::iterator itend = outputImage.end<Vec3b>(); //终止//存取彩色图像 像素for(; it!= itend; ++it) {(*it)[0] = (*it)[0]/div * div + div/2;(*it)[1] = (*it)[1]/div * div + div/2;(*it)[2] = (*it)[2]/div * div + div/2;}
}
- 方法三:动态地址计算
用动态地址计算来操作像素,配合at方法。
void colorReduce(Mat& inputImage, Mat& outputImage, int div)
{outputImage = inputImage.clone();int rowNumber = outputImage.rows; int colNumber = outputImage.cols;//存取彩色图像像素for(int i = 0; i < rowNumber; i++) {for(int j = 0; j < colNumber; j++) {//开始处理每个像素outputImage.at<Vec3b>(i, j)[0] = outputImage.at<Vec3b>(i, j)[0] /div * div + div/2; // 蓝色通道outputImage.at<Vec3b>(i,j)[1] = outputImage.at<Vec3b>(i, j)[1] / div * div + div/2; //绿色outputImage.at<Vec3b>(i,j)[2] = outputImage.at<Vec3b>(i, j)[2] / div * div + div/2; //红色//处理结束}}
}
讲解上述代码:
成员函数at(int y, int x)可以用来存取图像元素,但必须在编译时知道图像的数据类型。我们一定要确保指定的数据类型和矩阵中的数据类型符合。因为at方法本身不会对任何数据类型进行转换。
对彩色图像,像素由三个部分组成:蓝通道,绿通道,红通道(BGR)。因此对于一个包含彩色图像的Mat,会返回一个由三个8位数组成的向量。 类型为Vec3b,即由三个unsigned char 组成的向量。
形式:image.at(j, i)[channel] = value; channel表明了颜色通道号;
opencv的core组件——像素,ROI,图像混合(3)相关推荐
- 【OpenCV】OpenCV函数精讲之 -- 初级图像混合
本文是将设置感兴趣区域ROI和使用addWeighted函数进行图像线性混合结合起来使用,下边是具体的代码示例: #include <opencv2/core/core.hpp> #inc ...
- C++OpenCV系统学习(3)——图像混合、调整亮度与对比度
1.图像混合 1.1线性混合 f0和f1分别表示两张图像,g(x)是混合后的图像. 1.2相关API addWeight(src1,alpha,src2,beta,gamma,dst,dtype), ...
- Opencv值core组件(二):感兴趣区域选取与计算数组加权和
文章目录 一.感兴趣区域提取 二.计算数组加权和 一.感兴趣区域提取 Opencv有两种定义ROI区域的方法, 第一种是使用表示矩形区域的Rect,它指定矩形的左上角坐标,和矩形的长宽,以定义一个矩形 ...
- 【OpenCV】OpenCV函数精讲之 -- 多通道图像混合
#include <opencv2/core/core.hpp> #include <opencv2/highgui/highgui.hpp> #include <ios ...
- OpenCV(C++)图像处理基础04:图像混合(线性混合操作)
文章目录 1.知识点:线性混合数学模型 2.代码实现+测试结果 参考文献 学习了这些图像处理技巧,可以为深度学习目标检测数据集采取一些数据增强的方法或其他优化方法. 1.知识点:线性混合数学模型 进一 ...
- OpenCV之core 模块. 核心功能(1)Mat - 基本图像容器 OpenCV如何扫描图像、利用查找表和计时 矩阵的掩码操作 使用OpenCV对两幅图像求和(求混合(blending))
Mat - 基本图像容器 目的 从真实世界中获取数字图像有很多方法,比如数码相机.扫描仪.CT或者磁共振成像.无论哪种方法,我们(人类)看到的是图像,而让数字设备来"看"的时候,则 ...
- 《OpenCV3编程入门》学习笔记5 Core组件进阶(二) ROI区域图像叠加图像混合
第5章 Core组件进阶 5.2 ROI区域图像叠加&图像混合 5.2.1 感兴趣区域ROI(region of interest) 1.定义ROI区域两种方法: (1)定义矩形区域Rect: ...
- 【opencv学习笔记】第五篇:访问图像中像素的三种方式、ROI区域图像叠加和图像混合
1. 访问图像中像素的三种方式 任何图像处理算法,都是从操作每个像素开始的.在OpenCV中,提供了三种访问每个像素的方法. 方法1:指针访问:C操作符[] 方法2:迭代器iterator 方法3:动 ...
- 《OpenCV3编程入门》学习笔记5 Core组件进阶(一)访问图像中的像素
第5章 Core组件进阶 5.1 访问图像中的像素 5.1.1 图像在内存中的存储方式 1.图像矩阵大小取决于通道数,矩阵中的子列个数与通道数相等. 2.如果内存足够大,可以实现连续存储,有助于提升图 ...
- 转载:【OpenCV入门教程之四】 ROI区域图像叠加初级图像混合 全剖析
[OpenCV入门教程之四] ROI区域图像叠加&初级图像混合 全剖析 浅墨_毛星云 2014-03-10 12:48:05 157425 收藏 19 最后发布:2014-03-10 12:4 ...
最新文章
- English in 999
- 关于文件格式和编码方式,乱码产生的原因?
- 全球及中国家用除湿机行业消费需求调研及十四五发展趋势研究报告2022-2027年
- OPENCV-5 学习笔记
- 京泉华:与小米生态链公司已在智能家居领域等展开相关业务合作
- 使用base标签后图片无法加载_Spring 源码学习(二)-默认标签解析
- Servlet中forward和redirect的区别
- 数学建模算法与应用学习blog
- 24小时改变你的人生 (1至12小时)很好的书,推荐大家有时间在网上看看。
- Win10 Pro自己解决系列~~~~菇凉手动折腾~~丰衣足食
- MySQL-14使用子查询-必知必会
- 局域网即时通讯软件应该怎么选择
- 效率工具 Snipaste、坚果云、印象笔记
- android mvvm
- 很遗憾,这就是现实!35岁之后软件测试工程师靠什么养家?
- python随机森林筛选变量_用随机森林分类器和GBDT进行特征筛选
- 电脑缩小,电脑网页缩小了怎么恢复?电脑网页缩小的三种恢复方法
- 用Java程序模拟实现新冠病毒传染
- 与计算机互动大学英语,【2017年整理】基于与网络和计算机的大学英语教学模式.ppt...
- 《屌丝:庶民的文化胜利》的精彩评论
热门文章
- 这些效果我很喜欢,但愿对你们也有帮助
- 手机短信小额支付接口.Net实现
- hive中英文分号问题
- 加拿大布兰登大学计算机专业,加拿大布兰登大学有哪些专业?
- Qt中mouseMoveEvent在MainWindow中使用
- 在Ubuntu下安装Visual Studio Code
- Gl计算机语言,计算机编程和编程语言 - osc_bkg5rgl1的个人空间 - OSCHINA - 中文开源技术交流社区...
- 1055 集体照 (25 分)
- CTF 每日一题 Day39 达芬奇密码
- 二元隐函数求二阶偏导_多元函数隐函数微分 二阶偏导的求法