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