- memcpy 内存拷贝
- split多通道分离,merge多通道合并
用实现照片底片化的例子介绍一些基本函数
所谓照片底片化,就是将图像每个像素的RGB三个通道取反,如果是256色,就是R’=255-R, G’=255-G, B’=255-B,如果是0.0-1.0的颜色空间,就是R’=1.0-R, G’=1.0-G, B’=1.0-B。通过上述描述,可得出算法:遍历每个像素点RGB值-重新计算-生成图像 memcpy 内存拷贝
原始图像:
/#include#include using namespace cv; using namespace std; int main() { //设置图片路径 string path = "./yy.jpg"; //生成Mat型图像矩阵 Mat I = imread(path); //转化Mat内部数据类型,此处可以不加 1 / 255.0,后面取反的时候用255-像素值 I.convertTo(I, CV_32FC3, 1 / 255.0); //构建像素值数组 float* img_data = new float[I.cols * I.rows * 3]; //像素值拷贝 memcpy(img_data, I.data, I.cols * I.rows * 3 * sizeof(float)); //遍历像素并计算 for (int i = 0; i < I.cols * I.rows * 3; i++) { img_data[i] = 1 - img_data[i]; } //通过像素数组重新生成图像 Mat result1(I.rows, I.cols, CV_32FC3, img_data); //保存图片,由于像素值位0~1,打开后是黑色,显示时需要线性变换到0~255,才能显示图片 imwrite("./yy_result1.jpg", result1); //显示结果 imshow("result1", result1); waitKey(0); return 0; }
结果图:
函数void *memcpy(void *dest, const void *src, size_t n);
该函数在OpenGL部分文章中有提及,该函数并非OpenCV函数而是C++函数,作用是从源src所指的内存地址的起始位置开始拷贝n个字节到目标dest所指的内存地址的起始位置中,
参数void *dest:目标指针,文中为新创建空数组img_data
参数const void *src:源内存地址起始位置,Mat型图像起始指针表示为Mat.data,因此文中为I.data
参数size_t n:内存长度,文中为像素个数3通道(图像长宽*3),再乘以数据种类所占空间(sizeof(float)),最终形式为I.cols*I.rows * 3 * sizeof(float)
Mat构造函数 1. Mat(int rows, int cols, int type, const Scalar& s); 2. Mat(int rows, int cols, int type, void* data, size_t step=AUTO_STEP);
Mat类型的构造函数有很多种(20种左右吧),目的是为了满足不同作用矩阵的构造需求。对于图像处理和一般 *** 作来说,主要应用上述两种类型构造函数
参数int rows, int cols:矩阵长宽,文中构造与原图尺寸相同的矩阵以存储结果图像,因此设为I.rows, I.cols
参数int type:设置矩阵内数据类型,虽说该参数为int类型,但是为了直观,通常不用单一的数字表示。如文中需要创建数据为3通道float类型,所以参数设为CV_32FC3
1中参数const Scalar& s:设置矩阵每个数据的初始值。实际上,构造函数1通常用在空白图像的初始化,可将图像设为全白Scalar(1.0f,1.0f,1.0f)或全黑Scalar(0.0f,0.0f,0.0f),当然也可将图像设为其他颜色。三通道时该参数用Scalar(x,x,x)表示,四通道用Scalar(x,x,x,x)表示,单通道直接用数字表示即可
2中参数void* data:设置矩阵内数据,将data数组中的数据作为矩阵元素的值。由于矩阵的长宽通过构造函数设定,数组的长度也是认为设定的,因此只要矩阵尺寸和数组长度相对应即可成功构造矩阵。文中该参数为进行遍历运算之后的像素数组img_data
split多通道分离,merge多通道合并除了对每个像素进行遍历 *** 作外,OpenCV还提供了分通道对图像进行 *** 作的方法:
//划分BGR三通道 vectorchannels; split(I, channels); //分别处理三通道 vector newChannels; newChannels.push_back(1 - channels[0]); //B通道 newChannels.push_back(1 - channels[1]); //G通道 newChannels.push_back(1 - channels[2]); //R通道 //融合BGR三通道 Mat result2; merge(newChannels, result2); //显示结果 imshow("result2", result2);
-
函数void split(const Mat& m, vector
& mv )`
该函数作用是将多通道矩阵m中的每个通道单独提取,分别存入mv中
参数const Mat& m:多通道Mat型矩阵。文中要将彩色BGR三通道图像I分离通道,因此参数设为I
参数vector& mv: vector 结构,用来存储分离后的单通道矩阵。文中将彩色图像矩阵I拆分为三个单通道矩阵,这三个矩阵存于未初始化的vector channels中
注意:参数vector& mv不必初始化,参数const Mat& m有几个通道,mv就有几个,可用[n]来获取某通道的矩阵。如文中Mat I为三通道彩色图像,所以vector channels中共有3个Mat型,其中channels[0]代表I的B通道(蓝色),channels[1]代表I的G通道(绿色),channels[2]代表I的R通道(红色) -
函数void merge(const vector
& mv, OutputArray dst )
该函数作用和split相反,是将若干个单通道Mat型合成一个多通道矩阵
参数const vector& mv:vector 型若干个单通道Mat组成的vector。文中将分离的vector channels每个通道进行取反计算,因此该参数设为处理后的channels
参数OutputArray dst:输出矩阵,OutputArray类型可以用Mat类型表示。文中将分别处理之后的三通道合并回三通道BGR彩色图像,因此该参数设为未初始化的Mat result2以存储结果。
程序运行结果和之前的一样:
针对该问题,还有一种更为简单的处理方法,直接应用矩阵运算:
Mat result3 = Scalar(1,1,1) - I; imshow("result3", result3);
最终结果和前两种方法相同,并且花费时间最短。第一种方法时间花费在遍历全图像素点;第二种方法时间花费在split和merge两个函数上
完整代码:
#include#include using namespace cv; using namespace std; void main() { //设置图片路径 string path = "./yy.jpg"; //生成Mat型图像矩阵 Mat I = imread(path); //转化Mat内部数据类型 I.convertTo(I, CV_32FC3, 1 / 255.0); //构建像素值数组 float* img_data = new float[I.cols * I.rows * 3]; //像素值拷贝 memcpy(img_data, I.data, I.cols * I.rows * 3 * sizeof(float)); //遍历像素并计算 for (int i = 0; i < I.cols * I.rows * 3; i++) { img_data[i] = 1 - img_data[i]; } //通过像素数组重新生成图像 Mat result1(I.rows, I.cols, CV_32FC3, img_data); //显示结果 imshow("result1", result1); //划分BGR三通道 vector channels; split(I, channels); //分别处理三通道 channels[0] = 1 - channels[0]; channels[1] = 1 - channels[1]; channels[2] = 1 - channels[2]; //融合BGR三通道 Mat result2; merge(channels, result2); //显示结果 imshow("result2", result2); Mat result3 = Scalar(1, 1, 1) - I; imshow("result3", result3); //图像显示 imshow("ori", I); //程序挂起等待 waitKey(); }
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)