OpenCV计算机视觉编程之三种图像像素的遍历方法

本文详细介绍使用OpenCV遍历图像像素的三种方法:通过cv::Mat类的at方法、指针和迭代器,每种方法都有其适用场景和优缺点,通过实例代码展示如何修改图像像素。

为了构建计算机视觉应用程序,需要学会访问图像内容,有时也要修改或创建图像,如何操作图像的像素,就需要遍历一幅图像并处理每一个像素。现在我们就来介绍OpenCV三种图像像素的遍历方法:

一、 用cv::Mat类的at方法扫描图像
利用cv::Mat的at(int x,int y)方法可以访问元素,其中x是行号,y是列号。在编译时必须明确方法返回值的类型,因为cv::Mat可以接受任何类型的元素,所以程序员需要指定返回值的预期类型。正因为如此,at方法被实现成一个模板方法。在调用at方法时,你必须指定图像元素的类型,例如:

001

002

003

004

// 单通道图像

image.at<uchar>(i,j)= 255;

// 三通道图像

image.at<cv::Vec3b>(i, j) = cv::Vec3b(255, 255, 255);

用cv::Mat类的at方法扫描图像代码如下:

001

002

003

004

005

006

007

008

009

010

011

012

013

014

015

016

017

018

019

020

021

void visit_mat_by_at(cv::Mat &img)

{

    for (int i = 0; i < img.rows; i++)

    {

        for (int j = 0; j < img.cols; j++)

        {

            // 单通道图像

            if (img.channels() == 1)

            {

                img.at<uchar>(i, j) += 50;

            }

            // 三通道图像

            else

            {

                img.at<cv::Vec3b>(i, j)[0] += 50;

                img.at<cv::Vec3b>(i, j)[1] += 50;

                img.at<cv::Vec3b>(i, j)[2] += 50;

            }

        }

    }

}

二、 用指针扫描图像
一般来说,用指针扫描图像比较高效。在大多数图像处理任务中,执行计算时你都需要对图像的所有像素进行扫描。需要访问的像素数量非常庞大,因此你必须采用高效的方式来执行这个任务。

用指针扫描图像代码如下:

001

002

003

004

005

006

007

008

009

010

011

void visit_mat_by_pointer(cv::Mat &img)

{

    for (int i = 0; i < img.rows; i++)

    {

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

        for (int j = 0; j < img.cols * img.channels(); j++)

        {

            data[j] += 50;

        }

    }

}

三、 用迭代器扫描图像
在面向对象编程时,我们通常用迭代器对数据集合进行循环遍历。迭代器是一种类,专门用于遍历集合的每个元素,并能隐藏遍历过程的具体细节。标准模板库(Standard Template Library,STL)对每个集合类都定义了对应的迭代器类,OpenCV也提供了cv::Mat的迭代器类,并且与C++ STL中的标准迭代器兼容。

用迭代器扫描图像代码如下:

001

002

003

004

005

006

007

008

009

010

011

012

013

014

015

016

017

018

019

020

021

022

023

024

025

026

027

void visit_mat_by_iterator(cv::Mat &img)

{

    // 单通道图像

    if (img.channels() == 1)

    {

        cv::Mat_<uchar>::iterator begin = img.begin<uchar>();

        cv::Mat_<uchar>::iterator end = img.end<uchar>();

 

        for (auto it = begin; it != end; it++)

        {

            *it += 50;

        }

    }

    // 三通道图像

    else

    {

        cv::Mat_<cv::Vec3b>::iterator begin = img.begin<cv::Vec3b>();

        cv::Mat_<cv::Vec3b>::iterator end = img.end<cv::Vec3b>();

 

        for (auto it = begin; it != end; it++)

        {

            (*it)[0] += 50;

            (*it)[1] += 50;

            (*it)[2] += 50;

        }

    }

}

测试代码:

001

002

003

004

005

006

007

008

009

010

011

012

013

014

015

016

017

018

019

020

021

022

023

024

025

026

027

028

029

030

031

#include <iostream>

#include <opencv2/opencv.hpp>

 

int main()

{

    // 单通道图像

    cv::Mat img1(3, 4, CV_8UC1, 100);

    std::cout << "单通道图像像素修改前:" << std::endl;

    std::cout << img1 << std::endl;

 

    visit_mat_by_at(img1);

    //visit_mat_by_pointer(img1);

    //visit_mat_by_iterator(img1);

    std::cout << "单通道图像像素修改后:" << std::endl;

    std::cout << img1 << std::endl;

 

    // 三通道图像

    cv::Mat img2(3, 4, CV_8UC3, cv::Scalar(100, 150, 200));

    std::cout << "三通道图像像素修改前:" << std::endl;

    std::cout << img2 << std::endl;

 

    visit_mat_by_at(img2);

    //visit_mat_by_pointer(img2);

    //visit_mat_by_iterator(img2);

    std::cout << "三通道图像像素修改后:" << std::endl;

    std::cout << img2 << std::endl;

 

    cv::waitKey();

 

    return 0;

}

运行结果:

001

002

003

004

005

006

007

008

009

010

011

012

013

014

015

016

单通道图像像素修改前:

[100, 100, 100, 100;

 100, 100, 100, 100;

 100, 100, 100, 100]

单通道图像像素修改后:

[150, 150, 150, 150;

 150, 150, 150, 150;

 150, 150, 150, 150]

三通道图像像素修改前:

[100, 150, 200, 100, 150, 200, 100, 150, 200, 100, 150, 200;

 100, 150, 200, 100, 150, 200, 100, 150, 200, 100, 150, 200;

 100, 150, 200, 100, 150, 200, 100, 150, 200, 100, 150, 200]

三通道图像像素修改后:

[150, 200, 250, 150, 200, 250, 150, 200, 250, 150, 200, 250;

 150, 200, 250, 150, 200, 250, 150, 200, 250, 150, 200, 250;

 150, 200, 250, 150, 200, 250, 150, 200, 250, 150, 200, 250]

说明我们完成遍历图像,并成功修改图像像素,至此大功告成~

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值