失眠网,内容丰富有趣,生活中的好帮手!
失眠网 > OpenCV Mat遍历的方法

OpenCV Mat遍历的方法

时间:2022-11-25 16:50:06

相关推荐

OpenCV Mat遍历的方法

OpenCV像素遍历常用的是三种方法:ptr指针,迭代器(iterator)以及动态地址at。

动态地址at不适合用于像素遍历,速度太慢了,比较适合随机访问的方式;使用Mat的ptr指针进行图像遍历更加高效,

特别的:一般图像行与行之间往往存储是不连续的,但是有些图像可以是连续的,Mat提供了一个检测图像是否连续的函数isContinuous()。当图像连通时,我们就可以把图像完全展开,看成是一行进行处理。

因此最高效的遍历方法如下

void image_copy(cv::Mat &src, cv::Mat &dst) {int h = src.rows;int w = src.cols;dst.create(src.size(), src.type());if (src.isContinuous() && dst.isContinuous()) {h = 1;w = w * src.rows * src.channels();}for (int i = 0; i < h; i++) {uchar *sptr = src.ptr<uchar>(i);uchar *dptr = dst.ptr<uchar>(i);for (int j = 0; j < w; j++) {*dptr++ = *sptr++;//dptr[j] = sptr[j];}}}

PS:一般经过裁剪的Mat图像,都不再连续了,如cv::Mat crop_img = src(rect);crop_img 是不连续的Mat图像,如果想转为连续的,最简单的方法,就是将不连续的crop_img 重新clone()一份给新的Mat就是连续的了。关于Mat连续存储的问题,可见:/guyuealian/article/details/78614662

其他遍历方式 ,可参考:

实现方式: /keith_bb/article/details/53071133

void image_copy1(cv::Mat &src, cv::Mat &dst) {//使用ptr指针int n = src.channels();int h = src.rows;//获取图像矩阵行数int w = src.cols;//获取图像矩阵列数dst.create(src.size(), src.type()); //初始化返回结果for (int i = 0; i < h; i++) {//获取矩阵每行首地址指针uchar *sptr = src.ptr<uchar>(i);uchar *dptr = dst.ptr<uchar>(i);for (int j = 0; j < w; j++) {uchar b = sptr[n * j];uchar g = sptr[n * j + 1];uchar r = sptr[n * j + 2];dptr[n * j] = b;dptr[n * j + 1] = g;dptr[n * j + 2] = r;}}}void image_copy2(cv::Mat &src, cv::Mat &dst) {//使用ptr指针int n = src.channels();int h = src.rows; //获取图像矩阵行数int w = src.cols * n;//三通道图像每行元素个数为列数x通道数dst.create(src.size(), src.type());for (int i = 0; i < h; i++) {//获取矩阵每行首地址指针uchar *sptr = src.ptr<uchar>(i);uchar *dptr = dst.ptr<uchar>(i);for (int j = 0; j < w; j++) {dptr[j] = sptr[j];}}}void image_copy3(cv::Mat &src, cv::Mat &dst) {//使用ptr指针int n = src.channels();int h = src.rows; //获取图像矩阵行数int w = src.cols * n;//三通道图像每行元素个数为列数x通道数dst.create(src.size(), src.type());for (int i = 0; i < h; i++) {//获取矩阵每行首地址指针uchar *sptr = src.ptr<uchar>(i);uchar *dptr = dst.ptr<uchar>(i);for (int j = 0; j < w; j += n) {//uchar b = sptr[j];//uchar g = sptr[j + 1];//uchar r = sptr[j + 2];uchar b = *sptr++;uchar g = *sptr++;uchar r = *sptr++;dptr[j] = b;dptr[j + 1] = g;dptr[j + 2] = r;}}}void image_copy(cv::Mat &src, cv::Mat &dst) {int h = src.rows;int w = src.cols;dst.create(src.size(), src.type());if (src.isContinuous() && dst.isContinuous()) {h = 1;w = w * src.rows * src.channels();}for (int i = 0; i < h; i++) {uchar *sptr = src.ptr<uchar>(i);uchar *dptr = dst.ptr<uchar>(i);for (int j = 0; j < w; j++) {*dptr++ = *sptr++;//dptr[j] = sptr[j];}}}

以下是图像融合的算法:

(1)这是完全使用OpenCV的接口实现的图像融合,循环5次耗时30.29811 ms

void image_fusion(cv::Mat &imgBGR, cv::Mat matte, cv::Mat &out, cv::Mat bg) {if (matte.channels() == 1) {matte.convertTo(matte, CV_32FC1, 1.0 / 255, 0);cv::cvtColor(matte, matte, cv::COLOR_GRAY2BGR);} else {matte.convertTo(matte, CV_32FC3, 1.0 / 255, 0);}//out = imgBGR.clone();vector<float> ratio{(float) imgBGR.cols / bg.cols, (float) imgBGR.rows / bg.rows};float max_ratio = *max_element(ratio.begin(), ratio.end());if (max_ratio > 1.0) {cv::resize(bg, bg, cv::Size(int(bg.cols * max_ratio), int(bg.rows * max_ratio)));}bg = image_center_crop(bg, imgBGR.cols, imgBGR.rows);bg.convertTo(bg, CV_32FC3, 1, 0);imgBGR.convertTo(out, CV_32FC3, 1, 0);// Fix a Bug: 1 - alpha实质上仅有B通道参与计算,多通道时(B,G,R),需改Scalar(1.0, 1.0, 1.0)-alpha// out = out.mul(alpha) + bgi.mul(1 - alpha);out = out.mul(matte) + bg.mul(cv::Scalar(1.0, 1.0, 1.0) - matte);out.convertTo(out, CV_8UC3, 1, 0);}

(2)这是通过遍历的方式实现的图像融合,循环5次耗时24.44169 ms

注意到matte需要除以255,把它放在循环体外进行乘法和除法运算,可以明显加快

void image_fusion_fast(cv::Mat &imgBGR, cv::Mat matte, cv::Mat &out, cv::Mat bg) {assert(matte.channels() == 1);out.create(imgBGR.size(), CV_8UC3);vector<float> ratio{(float) imgBGR.cols / bg.cols, (float) imgBGR.rows / bg.rows};float max_ratio = *max_element(ratio.begin(), ratio.end());if (max_ratio > 1.0) {cv::resize(bg, bg, cv::Size(int(bg.cols * max_ratio), int(bg.rows * max_ratio)));}bg = image_center_crop(bg, imgBGR.cols, imgBGR.rows);int h = imgBGR.rows;int w = imgBGR.cols;int n = imgBGR.channels();// 循环体外进行乘法和除法运算matte.convertTo(matte, CV_32FC1, 1.0 / 255, 0);for (int i = 0; i < h; ++i) {uchar *sptr = imgBGR.ptr<uchar>(i);uchar *dptr = out.ptr<uchar>(i);float *mptr = matte.ptr<float>(i);uchar *bptr = bg.ptr<uchar>(i);for (int j = 0; j < w; ++j) {//float alpha = mptr[j] / 255; //循环体尽量减少乘法和除法运算float alpha = mptr[j];float _alpha = 1.f - alpha;dptr[n * j] = uchar(sptr[n * j] * alpha + bptr[n * j] * _alpha);dptr[n * j + 1] = uchar(sptr[n * j + 1] * alpha + bptr[n * j + 1] * _alpha);dptr[n * j + 2] = uchar(sptr[n * j + 2] * alpha + bptr[n * j + 2] * _alpha);}}}

(3)如果输入的Mat都是连续存储的,则可以转换为向量的形式进行遍历,循环5次耗时23.10372ms

void image_fusion_fast(cv::Mat &imgBGR, cv::Mat matte, cv::Mat &out, cv::Mat bg) {assert(matte.channels() == 1);out.create(imgBGR.size(), CV_8UC3);vector<float> ratio{(float) imgBGR.cols / bg.cols, (float) imgBGR.rows / bg.rows};float max_ratio = *max_element(ratio.begin(), ratio.end());if (max_ratio > 1.0) {cv::resize(bg, bg, cv::Size(int(bg.cols * max_ratio), int(bg.rows * max_ratio)));}bg = image_center_crop(bg, imgBGR.cols, imgBGR.rows);int n = imgBGR.channels();int h = imgBGR.rows;int w = imgBGR.cols * n;// 循环体外进行乘法和除法运算matte.convertTo(matte, CV_32FC1, 1.0 / 255, 0);for (int i = 0; i < h; ++i) {uchar *sptr = imgBGR.ptr<uchar>(i);uchar *dptr = out.ptr<uchar>(i);float *mptr = matte.ptr<float>(i);uchar *bptr = bg.ptr<uchar>(i);for (int j = 0; j < w; j += n) {//float alpha = mptr[j] / 255; //循环体尽量减少乘法和除法运算float alpha = mptr[j / 3];float _alpha = 1.f - alpha;dptr[j] = uchar(sptr[j] * alpha + bptr[j] * _alpha);dptr[j + 1] = uchar(sptr[j + 1] * alpha + bptr[j + 1] * _alpha);dptr[j + 2] = uchar(sptr[j + 2] * alpha + bptr[j + 2] * _alpha);}}}

(3)这是通过遍历的方式,去除了循环体内大部分乘法,实现的图像融合,循环5次耗时23.10372ms

void image_fusion_fast(cv::Mat &imgBGR, cv::Mat matte, cv::Mat &out, cv::Mat bg) {assert(matte.channels() == 1);out.create(imgBGR.size(), CV_8UC3);vector<float> ratio{(float) imgBGR.cols / bg.cols, (float) imgBGR.rows / bg.rows};float max_ratio = *max_element(ratio.begin(), ratio.end());if (max_ratio > 1.0) {cv::resize(bg, bg, cv::Size(int(bg.cols * max_ratio), int(bg.rows * max_ratio)));}bg = image_center_crop(bg, imgBGR.cols, imgBGR.rows);int n = imgBGR.channels();int w = imgBGR.cols * imgBGR.rows * n;// 循环体外进行乘法和除法运算matte.convertTo(matte, CV_32FC3, 1.0 / 255, 0);uchar *sptr = imgBGR.ptr<uchar>(0);uchar *dptr = out.ptr<uchar>(0);float *mptr = matte.ptr<float>(0);uchar *bptr = bg.ptr<uchar>(0);for (int j = 0; j < w; j += n) {//float alpha = mptr[j] / 255; //循环体尽量减少乘法和除法运算float alpha = mptr[j / 3];float _alpha = 1.f - alpha;dptr[j] = uchar(sptr[j] * alpha + bptr[j] * _alpha);dptr[j + 1] = uchar(sptr[j + 1] * alpha + bptr[j + 1] * _alpha);dptr[j + 2] = uchar(sptr[j + 2] * alpha + bptr[j + 2] * _alpha);}}

如果觉得《OpenCV Mat遍历的方法》对你有帮助,请点赞、收藏,并留下你的观点哦!

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