0010-OpenCV对图像或矩阵元素遍历的四种方法

图像的点或矩阵中的元素,是我们进行运算时的基本元素,所以遍历图像的操作是经常要用到的,本文的代码用四种方式实现图像的遍历

我们通过元素遍历实现对图像降色彩处理,因为256*256*256种颜色实在太多了,在图像颜色聚类或绘制彩色直方图时,我们需要用一些代表性的颜色代替丰富的色彩空间,此时可以将每个通道的256种颜色用64种代替,即将原来256种颜色划分64个颜色段,每个颜色段取中间的颜色值作为代表色。

假设某点某个通道的值为value,则将原来256种颜色划分64个颜色段,每个颜色段取中间的颜色值作为代表色的数学运算为:
value/64*64+64/2,注意“/”是取整运算,不是除法运算哈!

接下来,我们介绍四种遍历方式的代码

图像处理开发资料、图像处理开发需求、图像处理接私活挣零花钱,可以搜索公众号"qxsf321",并关注!
代码中用到的图像下载链接:http://pan.baidu.com/s/1kVLx5v5 密码:3ikr1 下标遍历法 at<typename>(i,j)
代码如下:

//opencv版本:OpenCV3.0
//VS版本:VS2013
//Author:qxsf321.net

#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>    
#include <opencv2/imgproc/types_c.h>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/highgui/highgui_c.h>

int colorReduce(cv::Mat& image, cv::Mat& outImage, int div)
{
        outImage.create(image.size(), image.type());
        for (int i = 0; i<image.rows; i++)
        {
                for (int j = 0; j<image.cols; j++)
                {
                        outImage.at<cv::Vec3b>(i, j)[0] = image.at<cv::Vec3b>(i, j)[0] / div*div + div / 2;
                        outImage.at<cv::Vec3b>(i, j)[1] = image.at<cv::Vec3b>(i, j)[1] / div*div + div / 2;
                        outImage.at<cv::Vec3b>(i, j)[2] = image.at<cv::Vec3b>(i, j)[2] / div*div + div / 2;
                }
        }
        return 0;
}

int main()
{
        // 装载图像
        cv::Mat srcImage = cv::imread("02.jpg");
        cv::Mat dstImage;

        if (!srcImage.data)
                return -1;
        cv::imshow("srcImage", srcImage);

        colorReduce(srcImage, dstImage, 64);

        cv::imshow("dstImage", dstImage);

        cv::waitKey(0);
        return 0;
}


关键代码说明:
image.at<cv::Vec3b>(i,j):取出灰度图像中i行j列的点。
image.at<Vec3b>(i,j)[k]:取出彩色图像中i行j列第k通道的颜色点,Vec3b都是图像像素值的类型。2 效率较低的指针遍历法
指针遍历法比第1种的下标遍历法要高效!
代码如下:

//opencv版本:OpenCV3.0
//VS版本:VS2013
//Author:qxsf321.net

#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>    
#include <opencv2/imgproc/types_c.h>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/highgui/highgui_c.h>

void colorReduce(const cv::Mat& image, cv::Mat& outImage, int div)
{
        // 创建与原图像等尺寸的图像
        outImage.create(image.size(), image.type());
        int nr = image.rows;
        // 将3通道转换为1通道
        int nl = image.cols*image.channels();
        for (int k = 0; k<nr; k++)
        {
                // 每一行图像的指针
                const uchar* inData = image.ptr<uchar>(k);
                uchar* outData = outImage.ptr<uchar>(k);
                for (int i = 0; i<nl; i++)
                {
                        outData[i] = inData[i] / div*div + div / 2;
                }
        }
}

int main()
{
        // 装载图像
        cv::Mat srcImage = cv::imread("02.jpg");
        cv::Mat dstImage;

        if (!srcImage.data)
                return -1;
        cv::imshow("srcImage", srcImage);

        colorReduce(srcImage, dstImage, 64);

        cv::imshow("dstImage", dstImage);

        cv::waitKey(0);
        return 0;
}


关键代码说明:
image.ptr<uchar>(i):取出图像中第i行数据的指针
程序中将三通道的数据转换为1通道,在建立在每一行数据元素之间在内存里是连续存储的,每个像素三通道像素按顺序存储。也就是一幅图像数据最开始的三个值,是最左上角的那像素的三个通道的值。但是这种用法不能直接用在行与行之间,因为图像在OpenCV里的存储机制问题,行与行之间可能有空白单元。当行存储也是连续的时候,有下面这种更高效的指针遍历方法。3 高效的指针遍历法
当一个矩阵或一幅图像是行连续时,可以用这种方法。具体的操作是先用函数isContinuous()。当图像连通时,我们就可以把图像完全展开,看成是一行。
代码如下:
 

//opencv版本:OpenCV3.0
//VS版本:VS2013
//Author:qxsf321.net


#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>    
#include <opencv2/imgproc/types_c.h>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/highgui/highgui_c.h>

void colorReduce(const cv::Mat& image, cv::Mat& outImage, int div)
{
        int nr = image.rows;
        int nc = image.cols;
        outImage.create(image.size(), image.type());
        if (image.isContinuous() && outImage.isContinuous())
        {
                nr = 1;
                nc = nc*image.rows*image.channels();
        }
        for (int i = 0; i<nr; i++)
        {
                const uchar* inData = image.ptr<uchar>(i);
                uchar* outData = outImage.ptr<uchar>(i);
                for (int j = 0; j<nc; j++)
                {
                        *outData++ = *inData++ / div*div + div / 2;
                }
        }
}

int main()
{
        // 装载图像
        cv::Mat srcImage = cv::imread("02.jpg");
        cv::Mat dstImage;

        if (!srcImage.data)
                return -1;
        cv::imshow("srcImage", srcImage);

        colorReduce(srcImage, dstImage, 64);

        cv::imshow("dstImage", dstImage);

        cv::waitKey(0);
        return 0;
}


4 用迭代器来遍历
声明一个迭代器的方法如下:
MatIterator_<Vec3b> it;
Mat_<Vec3b>::iterator it;
如果迭代器指向一个const图像,则可以用下面的声明:
MatConstIterator<Vec3b> it; 或者
Mat_<Vec3b>::const_iterator it;示例代码如下:

//opencv版本:OpenCV3.0
//VS版本:VS2013
//Author:qxsf321.net


#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>    
#include <opencv2/imgproc/types_c.h>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/highgui/highgui_c.h>

void colorReduce(const  cv::Mat& image, cv::Mat& outImage, int div)
{
        outImage.create(image.size(), image.type());
        cv::MatConstIterator_<cv::Vec3b> it_in = image.begin<cv::Vec3b>();
        cv::MatConstIterator_<cv::Vec3b> itend_in = image.end<cv::Vec3b>();
        cv::MatIterator_<cv::Vec3b> it_out = outImage.begin<cv::Vec3b>();
        cv::MatIterator_<cv::Vec3b> itend_out = outImage.end<cv::Vec3b>();
        while (it_in != itend_in)
        {
                (*it_out)[0] = (*it_in)[0] / div*div + div / 2;
                (*it_out)[1] = (*it_in)[1] / div*div + div / 2;
                (*it_out)[2] = (*it_in)[2] / div*div + div / 2;
                it_in++;
                it_out++;
        }
}


int main()
{
        // 装载图像
        cv::Mat srcImage = cv::imread("02.jpg");
        cv::Mat dstImage;

        if (!srcImage.data)
                return -1;
        cv::imshow("srcImage", srcImage);

        colorReduce(srcImage, dstImage, 64);

        cv::imshow("dstImage", dstImage);

        cv::waitKey(0);
        return 0;
}


代码说明:如果你想从第二行开始,则可以从image.begin<Vec3b>()+image.rows开始。

四种方法的运行结果都是一样的,运行结果截图如下

在上面四种方法中,第三种方法即“高效的指针遍历法”程序运行效率是最高的!

图像处理开发资料、图像处理开发需求、图像处理接私活挣零花钱,可以搜索公众号"qxsf321",并关注!

猜你喜欢

转载自blog.csdn.net/lehuoziyuan/article/details/84031429