OpenCV之访问图像中像素的三类方法(C++实现)

69 篇文章 14 订阅
53 篇文章 15 订阅

目录

一、用指针访问像素

二、用迭代器操作像素

三、动态地址计算


图像处理中的操作都是从像素开始的,在OpenCV中,提供了三种访问每个像素的方法:

  • 指针访问:C操作符[];
  • 迭代器iterator;
  • 动态地址计算;

这三种方法在访问速度上略有差异。debug模式下,差异比较明显,release模式下,差异不大。下面通过具体的代码来说明这几种方法。

下面程序的主要目的是减少图像中颜色的数量,比如原来的图像是256中颜色,我们要将其变为64中颜色,只需要将原来的颜色除以4(整除)以后再乘以4就可以了。

主程序:

#include <iostream>
#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;

void colorReduce(Mat& inputImage, Mat& outputImage, int div);
int main() {
    std::cout << "Hello, World!" << std::endl;
    Mat image = imread("/Users/dwz/Desktop/cpp/1.jpg");
//    imshow("原图",image);
    cout << image.size() << endl;
    cout << image.rows << endl;
    cout << image.cols << endl;
    cout << image.channels() << endl;
    cout << image.type() << endl;

//    Mat dstImage(image.rows, image.cols, CV_8UC3);
    Mat dstImage;
    dstImage.create(image.rows, image.cols, image.type());

    double time0 = static_cast<double>(getTickCount());
    colorReduce(image, dstImage, 32);
    time0 = ((double)getTickCount() - time0) / getTickFrequency();
    cout << time0 << endl;
    imshow("效果图", dstImage);
    waitKey(0);
    return 0;
}

主程序中调用colorReduce函数来完成减少颜色的工作,我们根据访问像素的三类方法实现了三个colorReduce函数,下面分别进行讲解。

一、用指针访问像素

Mat类又若干成员函数可以获取图像的属性。公寓哦成员变量cols和rows给出了图像的宽和高,而成员函数channels()用于返回图像的通道数。灰度图像的通道数等于1,彩色图像的通道数等于3.

每行的像素值由一下语句得到:

void colorReduce(Mat& inputImage, Mat& outputImage, int div){
    outputImage = inputImage.clone();     // 复制实参到临时变量
    int rowNum = outputImage.rows;        // 图像的行数
    int colNum = outputImage.cols * outputImage.channels();   // 图像的列数 * 通道数 = 每一行元素的个数

    for (int i=0;i<rowNum;i++)
    {
//        获取第i行的首地址
        uchar* data = outputImage.ptr<uchar>(i);
        for (int j=0;j<colNum;j++)
        {
//            处理每个像素
            data[j] = data[j]/div *div + div/2;
        }
    }
}

为了简化指针运算,Mat类提供了ptr函数可以得到图像任意行的首地址。ptr是一个模板函数,它返回第i行的首地址:

uchar* data = outputImage.ptr<uchar>(i);

内部循环时处理像素,我们可以等效的使用指针运算从一列移动到下一列:

*data++=*data/div*div+div/2;

二、用迭代器操作像素

用迭代器操作像素,我们需要获取图像矩阵的begin和end,然后增加迭代从begin到end,将*操作符添加在迭代指针前,即可访问当前指向的内容。

相比用指针直接访问可能出现越界问题,迭代器是非常安全的方法。

void colorReduce(Mat& inputImage, Mat& outputImage, int div){
    outputImage = inputImage.clone();

    Mat_<Vec3b>::iterator it = outputImage.begin<Vec3b>();
    Mat_<Vec3b>::iterator itend = outputImage.end<Vec3b>();

    for (; it != itend; ++it) {
        (*it)[0] = (*it)[0] / div *div+div/2;
        (*it)[1] = (*it)[1] / div *div+div/2;
        (*it)[2] = (*it)[2] / div *div+div/2;
    }

}

三、动态地址计算

用动态地址计算来操作像素需要配合at方法,这种方法简单明了,符合我们对像素的直观认识。

void colorReduce(Mat& inputImage, Mat& outputImage, int div)
{
    outputImage = inputImage.clone();
    int rowNum = outputImage.rows;
    int colNum = outputImage.cols;

    for (int i=0; i<rowNum;i++)
    {
        for (int j=0;j<colNum;j++)
        {
            outputImage.at<Vec3b>(i, j)[0] = outputImage.at<Vec3b>(i, j)[0]/div*div + div/2;
            outputImage.at<Vec3b>(i, j)[1] = outputImage.at<Vec3b>(i, j)[1]/div*div +div/2;
            outputImage.at<Vec3b>(i, j)[2] = outputImage.at<Vec3b>(i, j)[2]/div*div + div/2;
        }
    }
}

Mat类中的cols和rows可以得到图像的行数和列数,成员函数at(int y, int x)用来存取图像像素,但是必须知道图像的数据类型,因为at不会对数据类型进行转换。

Vec3b表示由三个unsigned char组成的向量

 

  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值