700字范文,内容丰富有趣,生活中的好帮手!
700字范文 > 【opencv学习笔记】第五篇:访问图像中像素的三种方式 ROI区域图像叠加和图像混合

【opencv学习笔记】第五篇:访问图像中像素的三种方式 ROI区域图像叠加和图像混合

时间:2023-04-12 07:59:02

相关推荐

【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*512Mat logoImage = imread("/home/liqiang/Data/vision/classic/WindowsLogo.jpg");//240*320if (!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)f1(x)+af2(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;}

输出效果:

还可以结合感兴趣区域和线性混合来实现部分混合,这里就不介绍了,实现起来应该不难。

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。