前言

在OpenCV中,图像的遍历有多种方法,其中常用的有:

1、最快速--ptr指针

2、最安全--迭代器

3、最便捷--at方法

下面引用大神的代码实验结果:很明显,指针的效率最高,迭代器的效率最低。

Time of scan_image_c        (averaged for 100 runs): 2.04884 ms.
Time of scan_image_iterator (averaged for 100 runs): 4.77701 ms.
Time of scan_image_random   (averaged for 100 runs): 3.64237 ms.

在此,我选择抄近路,着重学习效率最高的“指针遍历”。

代码示例

示例代码如下:

#include "widget.h"
#include "ui_widget.h"

#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>

using namespace cv;

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);


    //读取一张图片,将数据存储到mat3,然后进行处理
    cv::Mat mat1 = imread("C:/opencv/123.jpg");

    //复制图片(注意,如果是写mat2=mat1,那么结果是mat1和mat2指向同一片数据)
    Mat mat2 = mat1.clone();

    //遍历图像,将图像每一个像素点的色彩数值减半
    for (int i=0;i<mat2.rows;i++)
    {
        Vec3b *ptr = mat2.ptr<Vec3b>(i);//指针ptr指向图像的第i行

        for (int j =0;j<mat2.cols;j++)
        {
            ptr[j][0]=ptr[j][0]/2;//指针指向图像的第i行、第j列的第1通道
            ptr[j][1]=ptr[j][1]/2;//指针指向图像的第i行、第j列的第2通道
            ptr[j][2]=ptr[j][2]/2;//指针指向图像的第i行、第j列的第3通道
        }

    }

    namedWindow("显示器1", WINDOW_AUTOSIZE );
    imshow("显示器1", mat1 );

    namedWindow("显示器2", WINDOW_AUTOSIZE );
    imshow("显示器2", mat2 );

}

Widget::~Widget()
{
    delete ui;
}

代码运行结果如下,很明显,处理后的图片比原先暗淡了许多。

Android opencv指针仪表识别 opencv指针遍历图像_计算机视觉

知识点补充

1.关于Vec3b

在OpenCV中,使用imread读取的图像数据,都是uchar类型,存储到Mat矩阵中;矩阵中的每一个单位,对应图像的一个像素,包含一组数据,对应图像RGB三通道的数值。

将程序运行结果的图像放大,如下图:

Android opencv指针仪表识别 opencv指针遍历图像_数据_02

实际上,Vec3b可以理解为Vector的二次封装。

Vec3b可以看做是vector<uchar,3>,即一个uchar类型,长度为3的vectro容器。

利用Vec3b将图像中每一个像素的RGB三通道数据分离出来。

示例代码如下:

//读取图像存储到mat矩阵
Mat mat = imread("test.jpg");

//row为行,col为列
mat.at<Vec3b>(row, col)[0] = 255;  //修改 B 通道数据
mat.at<Vec3b>(row, col)[1] = 255;  //修改 G 通道数据
mat.at<Vec3b>(row, col)[2] = 255;  //修改 R 通道数据

2.关于cv::Mat.at 和 cv::Mat.ptr

at操作示例:

cv::Mat image(3,10,CV_32FC3);

     for(int i=0; i<image.rows;i++)
     {
        for(int j=0; j<image.cols; j++)
        {
          image.at<Vec3f>(i,j)[0]=image.at<Vec3f>(i.j)[0] / 2;
          image.at<Vec3f>(i,j)[1]=image.at<Vec3f>(i.j)[1] / 2;
          image.at<Vec3f>(i,j)[2]=image.at<Vec3f>(i.j)[2] / 2;
        }
     };

ptr操作示例:

cv::Mat image(3,10,CV_32FC3);

     for(int i=0; i<image.rows;i++)
     {
        Vect3b *ptr = image.ptr<Vec3b>(i);
        
        for(int j=0; j<image.cols; j++)
        {
          ptr[j][0]=ptr[j][0] / 2;
          ptr[j][1]=ptr[j][1] / 2;
          ptr[j][2]=ptr[j][2] / 2;
        }
     };

ptr操作效率比较高,程序也比较安全,有越界判断,推荐使用。

参考:

图像数据cv::Mat.at和cv::MaT.ptr对单个像素的访问和操作

OpenCV遍历图像

OpenCV关于Vec3b类型的含义与使用

OpenCV中Mat的type