• opencv-01--图像的遍历


    遍历图像的4种方式

    一、at<typename>(i,j)

    Mat类提供了一个at的方法用于取得图像上的点,它是一个模板函数,可以取到任何类型的图像上的点。下面我们通过一个图像处理中的实际来说明它的用法。

    在实际应用中,我们很多时候需要对图像降色彩,因为256*256*256实在太多了,在图像颜色聚类或彩色直方图时,我们需要用一些代表性的颜色代替丰富的色彩空间,我们的思路是将每个通道的256种颜色用64种代替,即将原来256种颜色划分64个颜色段,每个颜色段取中间的颜色值作为代表色。

    void colorReduce(Mat& image,int div)
    {
      for(int i=0;i<image.rows;i++)
      {
        for(int j=0;j<image.cols;j++)
        {
          image.at<Vec3b>(i,j)[0]=image.at<Vec3b>(i,j)[0]/div*div+div/2;
          image.at<Vec3b>(i,j)[1]=image.at<Vec3b>(i,j)[1]/div*div+div/2;
          image.at<Vec3b>(i,j)[2]=image.at<Vec3b>(i,j)[2]/div*div+div/2;
        }
      }
    }
    
    
    

    通过上面的例子我们可以看出,at方法取图像中的点的用法:

    image.at<uchar>(i,j):取出灰度图像中i行j列的点。

    image.at<Vec3b>(i,j)[k]:取出彩色图像中i行j列第k通道的颜色点。其中uchar,Vec3b都是图像像素值的类型,不要对Vec3b这种类型感觉害怕,其实在core里它是通过typedef Vec<T,N>来定义的,N代表元素的个数,T代表类型。

    更简单一些的方法:OpenCV定义了一个Mat的模板子类为Mat_,它重载了operator()让我们可以更方便的取图像上的点。

    Mat_<uchar> im=image;

    im(i,j)=im(i,j)/div*div+div/2;

    二、高效一点:用指针来遍历图像

    上面的例程中可以看到,我们实际喜欢把原图传进函数内,但是在函数内我们对原图像进行了修改,而将原图作为一个结果输出,很多时候我们需要保留原图,这样我们需要一个原图的副本。

    void colorReduce(const Mat& image,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;
            }
        }
    }

    从上面的例子中可以看出,取出图像中第i行数据的指针:image.ptr<uchar>(i)。

    值得说明的是:程序中将三通道的数据转换为1通道,在建立在每一行数据元素之间在内存里是连续存储的,每个像素三通道像素按顺序存储。也就是一幅图像数据最开始的三个值,是最左上角的那像素的三个通道的值。

    但是这种用法不能用在行与行之间,因为图像在OpenCV里的存储机制问题,行与行之间可能有空白单元。这些空白单元对图像来说是没有意思的,只是为了在某些架构上能够更有效率,比如intel MMX可以更有效的处理那种个数是4或8倍数的行。但是我们可以申明一个连续的空间来存储图像,这个话题引入下面最为高效的遍历图像的机制。

    三、更高效的方法

    上面已经提到过了,一般来说图像行与行之间往往存储是不连续的,但是有些图像可以是连续的,Mat提供了一个检测图像是否连续的函数isContinuous()。当图像连通时,我们就可以把图像完全展开,看成是一行。

    void colorReduce(const Mat& image,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;
            }
        }
    }

    用指针除了用上面的方法外,还可以用指针来索引固定位置的像素:

    image.step返回图像一行像素元素的个数(包括空白元素),image.elemSize()返回一个图像像素的大小。

    image.at<uchar>(i,j)=image.data+i*image.step+j*image.elemSize();

    四、还有吗?用迭代器来遍历。

    下面的方法可以让我们来为图像中的像素声明一个迭代器:

    MatIterator_<Vec3b> it;

    Mat_<Vec3b>::iterator it;

    如果迭代器指向一个const图像,则可以用下面的声明:

    MatConstIterator<Vec3b> it; 或者

    Mat_<Vec3b>::const_iterator it;

    下面我们用迭代器来简化上面的colorReduce程序:

    void colorReduce(const Mat& image,Mat& outImage,int div)
    {
        outImage.create(image.size(),image.type());
        MatConstIterator_<Vec3b> it_in=image.begin<Vec3b>();
        MatConstIterator_<Vec3b> itend_in=image.end<Vec3b>();
        MatIterator_<Vec3b> it_out=outImage.begin<Vec3b>();
        MatIterator_<Vec3b> itend_out=outImage.end<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++;
        }
    }

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

    上面4种方法中,第3种方法的效率最高!

  • 相关阅读:
    Hive之序列化与反序列化(SerDe)
    Hive从入门到精通
    HIVE从路人到入门
    Intel IDEA 2018破解(亲测成功)
    在小红家里面,有n组开关,触摸每个开关,可以使得一组灯泡点亮。
    一个n*n 的方格,要从左上角走到右下角,一次只能往右或往下走一步,求算法得出所有走动的方法数。
    Ionic3多个自定义过滤器--管道(pipe)
    ionic3 添加多个自定义组件
    ionic3 slides轮播图手动滑动后无法自动播放问题
    Vue2.2版本学习小结
  • 原文地址:https://www.cnblogs.com/wjgaas/p/3594017.html
Copyright © 2020-2023  润新知