opencv学习笔记访问图像中像素的三种方式ROI区域图像叠加和图像混合
Posted 非晚非晚
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了opencv学习笔记访问图像中像素的三种方式ROI区域图像叠加和图像混合相关的知识,希望对你有一定的参考价值。
1. 访问图像中像素的三种方式
任何图像处理算法,都是从操作每个像素开始的。在OpenCV中,提供了三种访问每个像素的方法。
- 方法1:指针访问:C操作符[]
- 方法2:迭代器iterator
- 方法3:动态地址计算
三种访问速度的比较:指针访问 > 迭代器访问 > 动态地址
。指针访问速度最快。需要注意的是,OpenCV中的彩色图像不是以RGB的顺序存放的,而是BGR
。
方法1:指针访问
通过调用函数 Mat::ptr(i) 来得到第i行的首地址地址,然后在行内访问像素;ptr是一个模板函数
,它返回第i行的首地址。注意uchar与Vec3b的区别,uchar需要乘以一个通道数
。
void test01()
{
//指针的访问
for (int i = 0; i < Row; i++)
{
for (int j = 0; j < Col; j++)
{
Scr.ptr<Vec3b>(i)[j][0] = 0;
//Scr.ptr<uchar>(i)(3*j) = 0; //注意uchar与Vec3b的区别,uchar需要乘以一个通道数
}
}
}
方法2:迭代器操作像素
这种方式与STL库的用法类似,创建一个Mat::Iterator对象it,通过it=Mat::begin()迭代器访问首地址,递增迭代器直到it==Mat::end()结束迭代;
void test02()
{
//迭代器访问,访问的是每一个像素点
Mat_<Vec3b>::iterator it = Scr.begin<Vec3b>();
while (it != Scr.end<Vec3b>())
{
//(*it)[0] = 0;//蓝色通道置零;BGR存储
(*it)[1] = 0; //绿色通道置零;
//(*it)[2] = 0;//红色通道置零;
it++;
}
}
方法3:动态地址计算
这种方法是最慢的一种方法,但是比较好理解,使用at函数来得到像素,Mat::at(i,j)为一个像素点的像素值数组,是一个大小为3的数组。从0到2存放了BGR三种颜色的灰度值。
void test03()
{
//动态访问,访问的是每一个像素点,最慢也是最直接易理解的访问方式
for (int i = 0; i < Row; i++)
{
for (int j = 0; j < Col; j++)
{
Scr.at<Vec3b>(i, j)[2] = 0;
}
}
}
整体代码示例
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace std;
using namespace cv;
Mat Scr;
int Row;
int Col;
void test01()
{
//指针的访问
for (int i = 0; i < Row; i++)
{
for (int j = 0; j < Col; j++)
{
Scr.ptr<Vec3b>(i)[j][0] = 0;
}
}
}
void test02()
{
//迭代器访问,访问的是每一个像素点
Mat_<Vec3b>::iterator it = Scr.begin<Vec3b>();
while (it != Scr.end<Vec3b>())
{
//(*it)[0] = 0;//蓝色通道置零;BGR存储
(*it)[1] = 0; //绿色通道置零;
//(*it)[2] = 0;//红色通道置零;
it++;
}
}
void test03()
{
//动态访问,访问的是每一个像素点,最慢也是最直接易理解的访问方式
for (int i = 0; i < Row; i++)
{
for (int j = 0; j < Col; j++)
{
Scr.at<Vec3b>(i, j)[2] = 0;
}
}
}
int main()
{
Scr = imread("/home/liqiang/Data/vision/classic/lena.jpg");
if (Scr.empty())
{
printf("%s\\n", "读入图片失败;");
return 1;
}
imshow("原始图像", Scr);
Row = Scr.rows; //获取行数目;
Col = Scr.cols; //获取列数目;
int Channel = Scr.channels(); //获取通道数目;
test01();//指针访问
test02();//迭代器访问
test03();//动态访问
imshow("改变后的图像", Scr);
waitKey(0);
return 1;
}
输出:
2. 感兴趣区域ROI和图像叠加
2.1 感兴趣区域ROI
在图像处理领域,常常需要设置感兴趣区域(ROI,region of interest)来专注或者简化工作过程。即从图像中圈定一个区域,以便进行进一步处理。使用ROI指定想读入的目标,可以减少处理时间,增加精度,给图像处理带来不小的便利。
定义ROI区域有两种方式:
- 方法1:使用矩形区域的Rect,指定矩形的左上角坐标(构造函数的前两个参数)和矩形的长宽(构造函数的后两个参数)就可以定义一个矩形区域。
Mat = image;//载入的图片
Mat imageROI = image(Rect(500, 250, logo.cols, logo.rows));//左上角,width,height
- 方法2:指定感兴趣区域
行和列的范围
(Range),Range表示从起始索引到终止(不包含)索引的一段连续序列。
imageROI = image(Range(250, 250 + logoImage.rows), Range(200, 200 + logoImage.cols));
注意两种形式,行和列的顺序不同
。
2.2 图像叠加
下面使用ROI实现图像的叠加操作,实际上就是copy操作。原书采用的掩模的形式,实际上不需要,可以直接拷贝。
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace std;
using namespace cv;
int main()
{
//【1】读入图像
Mat srcImage1 = imread("/home/liqiang/Data/vision/classic/lena.jpg");//512*512
Mat logoImage = imread("/home/liqiang/Data/vision/classic/WindowsLogo.jpg");//240*320
if (!srcImage1.data)
{
printf("读取srcImage1错误~! \\n");
return false;
}
if (!logoImage.data)
{
printf("读取logoImage错误~! \\n");
return false;
}
//【2】定义一个Mat类型并给其设定ROI区域
Mat imageROI = srcImage1(Rect(0, srcImage1.rows-logoImage.rows, logoImage.cols, logoImage.rows));//左下角
//【3】加载掩模(必须是灰度图)
Mat mask;
cvtColor(logoImage, mask, COLOR_RGB2GRAY);//用转换函数转换成灰度图
//【4】将掩膜拷贝到ROI
//logoImage.copyTo(imageROI, mask);
logoImage.copyTo(imageROI);//不用掩膜,直接拷贝
//【5】显示结果
namedWindow("<1>利用ROI实现图像叠加示例窗口");
imshow("<1>利用ROI实现图像叠加示例窗口", srcImage1);
waitKey(0);
return true;
}
输出效果:
3. 图像混合(线性混合)
线性混合操作是一种典型的二元(两个输入)的像素操作,它的理论公式如下:
g
(
x
)
=
(
1
−
a
)
f
1
(
x
)
+
a
f
2
(
x
)
g(x) = (1-a)f_1(x)+af_2(x)
g(x)=(1−a)f1(x)+af2(x)
可以通过alpha在0-1之间的变化,来对两幅图像或者两段视频产生时间上的画面叠化(cross-dissolve)
效果,类似于幻灯片和电影制作中的那样,也就是幻灯片翻页或者电影情节过渡
时的效果。
实现方面,主要运用了Opencv中的addWeighted函数来实现。它的函数原型如下:
addWeighted(InputArray src1, double alpha, InputArray src2, double beta, double gamma, OutputArray dst, int dtype = -1);
- src1和src2分别表示两幅图像
- alpha和beta分别表示两幅图像的权重
- dst表示输出图像
- gamma加载到dst的权重
- dtype表示图像深度,即src1.depth()
dst = src1 * alpha + src2 * beta + gamma
#include <opencv2/opencv.hpp>
using namespace cv;
int main()
{
Mat src1 = imread("/home/liqiang/Data/vision/classic/LinuxLogo.jpg");
Mat src2 = imread("/home/liqiang/Data/vision/classic/WindowsLogo.jpg");
Mat dst;
double alpha = 0.6;
addWeighted(src1, alpha, src2, 1 - alpha, 0.0, dst);
imshow("dst", dst);
waitKey(0);
return 0;
}
输出效果:
还可以结合感兴趣区域和线性混合来实现部分混合,这里就不介绍了,实现起来应该不难。
以上是关于opencv学习笔记访问图像中像素的三种方式ROI区域图像叠加和图像混合的主要内容,如果未能解决你的问题,请参考以下文章