OpenCV Mat遍历的方法
Posted pan_jinquan
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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连续存储的问题,可见:http://blog.csdn.net/guyuealian/article/details/78614662
其他遍历方式 ,可参考:
实现方式: https://blog.csdn.net/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遍历的方法的主要内容,如果未能解决你的问题,请参考以下文章