cv mat保存图片_【OPENCV】cv::Mat像素遍历方法比较

本文对比了OpenCV中cv::Mat对象的七种像素遍历方法,包括at访问、指针访问和迭代器访问等,通过性能测试发现,直接使用指针计算地址偏移量的方法最快,且需确保Mat连续存储。如果不考虑连续存储,使用从cv::Mat::ptr()获取行首地址的方式也是高效的选择。
摘要由CSDN通过智能技术生成

像素级别遍历是我们在图像任务中经常遇到的问题,在实时的图像处理中,能够高效的访问像素数据是很重要的。OpenCV中的数据容器是cv::Mat,cv::Mat提供了三种数据访问的方式分别是下标寻址,指针访问,迭代器访问。下面我们对比下这几种不同方式的访问速度。

#include

#include

#include "opencv2/core/core.hpp"

using namespace std;

void method1(cv::Mat);

void method2(cv::Mat);

void method3(cv::Mat);

void method4(cv::Mat);

void method5(cv::Mat);

void method6(cv::Mat);

void method7(cv::Mat);

int main(int argc, char* argv[])

{

cv::Size imgSize(6400,4800);

cv::Mat image = cv::Mat(imgSize, CV_8UC3, cv::Scalar(1,1,1));

method1(image);

method2(image);

method3(image);

method4(image);

method5(image);

method6(image);

method7(image);

}

void method1(cv::Mat img){

// at access with Vec3b Vector

double t0 = (double) cv::getTickCount();

int height = img.rows;

int width = img.cols;

int sum = 0;

for(int row=0; row < height; row++){

for(int col=0; col < width; col++){

cv::Vec3b uc_pixel = img.at<:vec3b>(row, col);

int a = uc_pixel[0];

int b = uc_pixel[1];

int c = uc_pixel[2];

sum += a + b + c;

}

}

assert(sum==3*height*width);

double time = ((double) cv::getTickCount() - t0) / cv::getTickFrequency();

std::cout << "Time for method1: " << time << std::endl;

}

void method2(cv::Mat img){

// direct at access

double t0 = (double) cv::getTickCount();

int height = img.rows;

int width = img.cols;

int sum = 0;

for(int row=0; row < height; row++){

for(int col=0; col < width; col++){

int a = img.at<:vec3b>(row, col)[0];

int b = img.at<:vec3b>(row, col)[1];

int c = img.at<:vec3b>(row, col)[2];

sum += a + b + c;

}

}

assert(sum==3*height*width);

double time = ((double) cv::getTickCount() - t0) / cv::getTickFrequency();

std::cout << "Time for method2: " << time << std::endl;

}

void method3(cv::Mat img){

// pointer + Vec3b vector

double t0 = (double) cv::getTickCount();

int height = img.rows;

int width = img.cols;

int sum = 0;

for(int row=0; row < height; row++){

cv::Vec3b *ptr = img.ptr<:vec3b>(row);

for(int col=0; col < width; col++){

cv::Vec3b pixel = ptr[col];

int a = pixel[0];

int b = pixel[1];

int c = pixel[2];

sum += a + b + c;

}

}

assert(sum==3*height*width);

double time = ((double) cv::getTickCount() - t0) / cv::getTickFrequency();

std::cout << "Time for method3: " << time << std::endl;

}

void method4(cv::Mat img){

// pointer

double t0 = (double) cv::getTickCount();

int height = img.rows;

int width = img.cols;

int sum = 0;

for(int row=0; row < height; row++){

cv::Vec3b *ptr = img.ptr<:vec3b>(row);

for(int col=0; col < width; col++){

int a = ptr[col][0];

int b = ptr[col][1];

int c = ptr[col][2];

sum += a + b + c;

}

}

assert(sum==3*height*width);

double time = ((double) cv::getTickCount() - t0) / cv::getTickFrequency();

std::cout << "Time for method4: " << time << std::endl;

}

void method5(cv::Mat img){

// raw pointer

double t0 = (double) cv::getTickCount();

int height = img.rows;

int width = img.cols;

int sum=0;

for(int row=0; row < height; row++){

const uchar *ptr = img.ptr(row);

for(int col=0; col < width; col++){

const uchar *uc_pixel = ptr;

int a = uc_pixel[0];

int b = uc_pixel[1];

int c = uc_pixel[2];

// int a = ptr[0];

// int b = ptr[1];

// int c = ptr[2];

sum += a + b + c;

ptr += 3;

}

}

assert(sum==3*height*width);

double time = ((double) cv::getTickCount() - t0) / cv::getTickFrequency();

std::cout << "Time for method5: " << time << std::endl;

}

void method6(cv::Mat img){

// raw pointer + raw step

double t0 = (double) cv::getTickCount();

int height = img.rows;

int width = img.cols;

int sum = 0;

const uchar *uc_pixel = img.data;

for(int row=0; row < height; row++){

uc_pixel = img.data + row*img.step;

for(int col=0; col < width; col++){

int a = uc_pixel[0];

int b = uc_pixel[1];

int c = uc_pixel[2];

sum += a + b + c;

uc_pixel += 3;

}

}

assert(sum==3*height*width);

double time = ((double) cv::getTickCount() - t0) / cv::getTickFrequency();

std::cout << "Time for method6: " << time << std::endl;

}

void method7(cv::Mat image){

double t0 = (double) cv::getTickCount();

int height = image.rows;

int width = image.cols;

cv::MatConstIterator_<:vec3b> it = image.begin<:vec3b>(), it_end = image.end<:vec3b>();

int sum = 0;

for(; it != it_end; ++it){

int a = (*it)[0];

int b = (*it)[1];

int c = (*it)[2];

sum += a + b + c;

}

assert(sum==3*height*width);

double time = ((double) cv::getTickCount() - t0) / cv::getTickFrequency();

std::cout << "Time for method7: " << time << std::endl;

}

Time for method1: 0.586296

Time for method2: 0.414636

Time for method3: 0.504703

Time for method4: 0.207575

Time for method5: 0.111554

Time for method6: 0.0940078

Time for method7: 0.522204

对比这几种方式我们可以发现,最为高效的还是直接使用指针计算地址偏移量, 然而这种方式必须保证Mat在内存的存储是连续的,可以通过cv::Mat::isContinous()函数检测,如果是连续的则可以处理为单行向量,使用最为高效的方式访问。如果不想这么麻烦,其实method5是一种较为可取的方式,通过从cv::Mat::ptr()得到每一行的首地址,这样就不需要保证连续存储,速度和纯粹使用指针也差不了多少。

实际上对于method5,不使用中间指针进行改写的话:

void method5(cv::Mat img){

// raw pointer

double t0 = (double) cv::getTickCount();

int height = img.rows;

int width = img.cols;

int sum=0;

for(int row=0; row < height; row++){

const uchar *ptr = img.ptr(row);

for(int col=0; col < width; col++){

// const uchar *uc_pixel = ptr;

// int a = uc_pixel[0];

// int b = uc_pixel[1];

// int c = uc_pixel[2];

// 不使用中间指针

int a = ptr[0];

int b = ptr[1];

int c = ptr[2];

sum += a + b + c;

ptr += 3;

}

}

assert(sum==3*height*width);

double time = ((double) cv::getTickCount() - t0) / cv::getTickFrequency();

std::cout << "Time for method5: " << time << std::endl;

}

重新测试下:

Time for method1: 0.58601

Time for method2: 0.416404

Time for method3: 0.507943

Time for method4: 0.208068

Time for method5: 0.0918915

Time for method6: 0.0917811

Time for method7: 0.523099

时间上已经十分接近method6,实际操作的时候直接使用method5,不使用中间指针即可。

Reference

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值