1. 程式人生 > >0010-OpenCV對影象或矩陣元素遍歷的四種方法

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",並關注!