日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

遍历opencv中的mat像素的几种方法和概念

發布時間:2023/12/18 编程问答 22 豆豆
生活随笔 收集整理的這篇文章主要介紹了 遍历opencv中的mat像素的几种方法和概念 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

今天在看矩形濾波的時候忽然腦子短路,把一些概念全弄混了,現總結一下,以便下次再混的時候可以參考確認下,自己的理解,有錯的地方還請指正。

?????????首先,在Opencv2中基本上都是用的Mat來表示圖像了,C++的函數調用中基本上也都是Mat圖,從根本上說,一張圖像是一個由數值組成的矩陣,矩陣的每一個元素代表一個像素。對于灰度圖像而言,像素有8位無符號數表示,其中0代表黑色,255代表白色。那么矩陣和圖像間到底是一個什么樣的關系呢。

?????????第一:Mat圖有行和列,即cv::Mat中有公有成員變量cols和rows,注意,這里的cols就是圖像的寬度width,rows就是圖像的高度height。這個width和height我們可以在其它Opencv的成員中得到,比如矩形Rect,而矩形Rect就是一個經常會用到的結構了,我自己接觸到的就包括鼠標選擇矩形區域、框住目標的矩形區域、濾波器矩形模版、目標的矩形特征、矩形內的運算等等。可以說Rect是一個非常常用的結構,也是Opencv里非常有用的一個結構,本質上矩形區域就是圖像的一個子部分,或者說圖像矩陣的一個子矩陣。

?????????這里我引用《OpenCV學習筆記(四十一)——再看基礎數據結構core》中關于Rect的介紹,Rect_類有些意思,成員變量x、y、width、height,分別為左上角點的坐標和矩形的寬和高。常用的成員函數有Size()返回值為一個Size,area()返回矩形的面積,contains(Point)用來判斷點是否在矩形內,inside(Rect)函數判斷矩形是否在該矩形內,tl()返回左上角點坐標,br()返回右下角點坐標。

?????????第二:Mat圖中的圖像像素位置表示和矩陣中元素的表示。這里引用《訪問Mat圖像中每個像素的值》中幾張圖來表示Mat矩陣中存數據的關系。單通道灰度圖數據存放格式:

???????????????????????????????????????????????????????????????????

多通道的圖像中,每列并列存放通道數量的子列,如RGB三通道彩色圖:

?????????????????????????????????????????????

這時,大家得注意了,二維矩陣的行和列用來表示一個元素,并且一般是從0標號開始,所以實際上是有m+1列,也就是說寬度width是m+1的,行類似。還有就是Mat.at(int y, int x)來訪問一個像素,這時候的y表示的行號,x表示的列號,相對應的就是x表示水平的寬,y表示的豎直的高,只不過x和y都是從0開始的標號。容易搞混的地方就在于一些矩陣的相減了,相減完后怎么表示像素位置,這個時候一般比較難把握,但是只要明白矩陣里x,y,width,height的關系,搞清楚就容易多了。

Mat dst;

?int height = dst.rows;
?? ?int width = dst.cols;

for (int i = 0; i < height; i++) {

?? ??? ??? ?for (int j = 0; j < width; j++) {

//假如以十字形遍歷索引,則十字中心(i * width + j),上下分別是((i -1)* width + j)((i +1)* width + j)

//左右分別是(i * width + j-1)(i * width + j+1)

//這里的i是代表行數,j代表列數,即所在的行的第幾列
?? ??? ??? ??? ?int index = i * width + j;
?? ??? ??? ??? ?//像素值?? ??? ?
?? ??? ??? ??? ?int data = (int)dst.data[index];
?? ??? ??? ??? ?
? ? ? ? ? ? ? ?}

?? ??? ??? ?}
?? ??? ??? ?

?

?

//=====================================指針法==================================================//

#include<opencv2/opencv.hpp>

#include<iostream>

using?namespace?std;

using?namespace?cv;

int?main()

{

????Mat?img=imread("f:/1.jpg");

????int?height=img.rows;

????int?width=img.cols;

????for(int?i=0;i<height;i++)

????{

????????unsigned?char?*data=img.data+i*width*img.channels();

????????for(int?j=0;j<width;j++)

????????{

????????????int?r=*(data+j*img.channels());

????????????int?g=*(data+j*img.channels()+1);

????????????int?b=*(data+j*img.channels()+2);

????????????cout<<r<<"?"<<g<<"?"<<b<<endl;

????????}

????}????????

????img.release();

????system("pause");

????return?1;

}

//============================================================================================//

推薦使用C++格式,比較方便使用

#include?"WangSetup.h"

?

#include?<iostream>

#include?<cv.h>

#include?<highgui.h>

?

using?namespace?std;

?

int?main()

{

????//C++?Format

????cv::Mat?img?=?cv::imread("lena.jpg");

????//取img中(30,?20)這個像素點的bgr信息

????cv::Vec3b?bgr?=?img.at<cv::Vec3b>(30,?20);

????cout?<<?"B:?"?<<?(unsigned?int)bgr.val[0]?<<?",?";

????cout?<<?"G:?"?<<?(unsigned?int)bgr.val[1]?<<?",?";

????cout?<<?"R:?"?<<?(unsigned?int)bgr.val[2]?<<?endl;

?

?

????//C?Format

????IplImage?*img2?=?cvLoadImage("lena.jpg");????????//8UC3,?(0,0)B,?(0,0)G,?(0,0)R,?(0,1)B,?...

????char?*ptr?=?img2->imageData???????//圖像首地址

????????+?img2->widthStep?*?30????????//每行大小?*?行數

????????+?3?*?20;????????????????????//BGR占3個大小空間?*?列數

????printf("B:?%d,?G:?%d,?R:?%d\n",?(uchar)ptr[0],?(uchar)ptr[1],?(uchar)ptr[2]);

????cvReleaseImage(&img2);

?????

????return?0;

}

?

?

?

1.用動態地址操作像素:

?? ?Mat srcImage(100, 100, CV_8UC3, Scalar(200,20,100));
?
?? ?imshow("顯示圖像", srcImage);
?
?
?? ?int rowNumber = srcImage.rows;
?? ?int colNumber = srcImage.cols;
?
?
?? ?for (int i = 0; i < rowNumber; i++)
?? ?{
?? ??? ?for (int j = 0; j < colNumber; j++)
?? ??? ?{
?? ??? ??? ?if (srcImage.at<Vec3b>(i, j)[0] > 180)?
?? ??? ??? ?{
?? ??? ??? ??? ?srcImage.at<Vec3b>(i, j)[0] = 0;?? ??? ??? ?
?? ??? ??? ?}
?? ??? ??? ?
?? ??? ??? ?if (srcImage.at<Vec3b>(i, j)[1] < 50)?
?? ??? ??? ?{
?? ??? ??? ??? ?srcImage.at<Vec3b>(i, j)[1] = 255;
?? ??? ??? ?}
?
?? ??? ??? ?if (srcImage.at<Vec3b>(i, j)[2] < 120)?
?? ??? ??? ?{
?? ??? ??? ??? ?srcImage.at<Vec3b>(i, j)[2] = 0;
?? ??? ??? ?}
?
?? ??? ?}
?? ?}
?
?
?? ?imshow("處理后的圖像", srcImage);
cv::mat的成員函數: .at(int y, int x)可以用來存取圖像中對應坐標為(x,y)的元素坐標。(Mat類中的cols和rows給出了圖像的寬和高。而成員函數at(int x, int y)可以用來存取圖像的元素。)由于at方法本身不會對任何數據類型進行轉化,故一定要確保指定的數據類型和矩陣中的數據類型相符合。
假設提前已知一幅圖像img的數據類型為 unsigned char型灰度圖(單通道),對像素的賦值操作為image.at<uchar>(i,j) = value。而對于彩色圖像,每個像素由三個部分構成:藍色通道、綠色通道和紅色通道(BGR),對于一個包含彩色圖像的Mat,會返回一個由三個8位數組組成的量。OpenCV將此類型定義為Vec3b,即由三個unsigned char組成的向量。這也解釋了為什么存取彩色圖像像素的代碼可以寫成:image.at<Vec3b>(i,j)[channel] = value;

以下是統計canndy后的0像素點與255像素點之間的數量的比值:

#define _CRT_SECURE_NO_WARNINGS
?
#include <iostream>
#include <opencv2/opencv.hpp>
?
using namespace std;
using namespace cv;
?
int main()?
{
?? ?Mat graySrc = imread("../../11.bmp", 0);
?
?? ?Mat canImage;
?? ?Canny(graySrc, canImage, 60, 120);
?
?? ?int PicZero = 0;
?? ?int PicFull = 0;
?
?? ?for (int i = 0; i < graySrc.rows; ++i)?
?? ?{
?? ??? ?for (int j = 0; j < graySrc.cols; ++j)?
?? ??? ?{
?? ??? ??? ?if (canImage.at<unsigned char>(i, j) == 0)?
?? ??? ??? ?{
?? ??? ??? ??? ?PicZero++;
?? ??? ??? ?}
?? ??? ??? ?else
?? ??? ??? ?{
?? ??? ??? ??? ?PicFull++;
?? ??? ??? ?}?? ?
?? ??? ?}
?? ?
?? ?}
?
?? ?cout << "0像素點比255像素點的比值為" << (double)PicZero / PicFull << endl;
?? ?system("pause");
}


2.用指針的方法:

有時候我們需要遍歷Mat中的每一個像素點,并且對像素點進行處理,這里以圖像所有像素點都減去div(div屬于int類型)

void colorReduce(Mat& inputImage, Mat& outputImage, int div)
{
?? ?// 參數準備
?? ?outputImage = inputImage.clone();
?
?? ?int rowNumber = outputImage.rows;
?? ?int colNumber = outputImage.cols*outputImage.channels();
?
?? ?for (int i = 0; i < rowNumber; i++)
?? ?{
?? ??? ?// 獲取第i行的首地址
?? ??? ?uchar* data = outputImage.ptr<uchar>(i);
?
?? ??? ?for (int j = 0; j < colNumber; j++) ?// 列循環
?? ??? ?{
?? ??? ??? ?// 開始處理每一個像素值,每一個像素值都減去div
?? ??? ??? ?data[j] = data[j] - div;
?? ??? ?}
?? ?}
}
也可以寫成如下形式:

Mat inverseColor1(Mat srcImage)?
{
?? ?Mat tempImage = srcImage.clone();
?? ?int row = tempImage.rows;
?? ?int col = tempImage.cols * tempImage.channels();
?
?? ?for (int i = 0; i < row; ++i)?
?? ?{
?? ??? ?const unsigned char* sourcedata = srcImage.ptr(i);
?? ??? ?unsigned char* data = tempImage.ptr(i);
?? ??? ?for (int j = 0; j < col; j++)
?? ??? ?{
?? ??? ??? ?data[j] = sourcedata[j] - div;
?? ??? ?}
?? ?}
?? ?return tempImage;
}
此時是定義了兩個指針類型: const unsigned char*和 unsigned char*,其中const unsigned char* 中的內容只能夠被讀取,不能被修改。

特別需要注意的是:Mat中每一行元素的個數=列數*通道數


如需要打印M,

?? ?Mat M(3, 2, CV_8UC3, Scalar(0, 0, 255));
?? ?cout << M << endl;
打印結果為:驗證了每一行元素的個數為: 列數*通道數

另外需要注意的是:Mat 除了擁有成員變量cols,rows,成員函數channels()之外,還提供了ptr函數可以返回得到圖像任意行的首地址。

3.用迭代器Matlterator_:

????????Matlterator_是Mat數據操作的迭代器,:begin()表示指向Mat數據的起始迭代器,:end()表示指向Mat數據的終止迭代器。迭代器方法是一種更安全的用來遍歷圖像的方式,首先獲取到數據圖像的矩陣起始,再通過遞增迭代實現移動數據指針。

Mat inverseColor4(Mat srcImage)?
{
?? ?Mat tempImage = srcImage.clone();
?
?? ?// 初始化原圖像迭代器
?? ?MatConstIterator_<Vec3b> srcIterStart = srcImage.begin<Vec3b>();
?? ?MatConstIterator_<Vec3b> srcIterEnd = srcImage.end<Vec3b>();
?
?? ?// 初始化輸出圖像迭代器
?? ?MatIterator_<Vec3b> resIterStart = tempImage.begin<Vec3b>();
?? ?MatIterator_<Vec3b> resIterEnd = tempImage.end<Vec3b>();
?
?? ?while (srcIterStart != srcIterEnd)?
?? ?{
?? ??? ?(*resIterStart)[0] = 255 - (*srcIterStart)[0];
?? ??? ?(*resIterStart)[1] = 255 - (*srcIterStart)[1];
?? ??? ?(*resIterStart)[2] = 255 - (*srcIterStart)[2];
?
?? ??? ?srcIterStart++;
?? ??? ?resIterStart++;
?? ?}
?
?? ?return tempImage;
?
}
?

?

?

Color Reduce
還是使用經典的Reduce Color的例子,即對圖像中的像素表達進行量化。如常見的RGB24圖像有256×256×256中顏色,通過Reduce Color將每個通道的像素減少8倍至256/8=32種,則圖像只有32×32×32種顏色。假設量化減少的倍數是N,則代碼實現時就是簡單的value/N*N,通常我們會再加上N/2以得到相鄰的N的倍數的中間值,最后圖像被量化為(256/N)×(256/N)×(256/N)種顏色。

方法零:.ptr和[]操作符
Mat最直接的訪問方法是通過.ptr<>函數得到一行的指針,并用[]操作符訪問某一列的像素值。

// using .ptr and []
void colorReduce0(cv::Mat &image, int div=64) {
?? ? ?int nr= image.rows; // number of rows
?? ? ?int nc= image.cols * image.channels(); // total number of elements per line
? ? ? for (int j=0; j<nr; j++) {
?? ??? ? ?uchar* data= image.ptr<uchar>(j);
? ? ? ? ? for (int i=0; i<nc; i++) {
? ? ? ? ? ? ? ? ? data[i]= data[i]/div*div + div/2;
? ? ? ? ? ? } ? ? ? ? ? ? ? ? ?
? ? ? }
}

方法一:.ptr和指針操作
除了[]操作符,我們可以移動指針*++的組合方法訪問某一行中所有像素的值。

// using .ptr and * ++?
void colorReduce1(cv::Mat &image, int div=64) {
?? ? ?int nr= image.rows; // number of rows
?? ? ?int nc= image.cols * image.channels(); // total number of elements per line
? ? ? for (int j=0; j<nr; j++) {
?? ??? ? ?uchar* data= image.ptr<uchar>(j);
? ? ? ? ? for (int i=0; i<nc; i++) {
?? ??? ??? ??? ? *data++= *data/div*div + div/2;
? ? ? ? ? ? } // end of row ? ? ? ? ? ? ? ??
? ? ? }
}

方法二:.ptr、指針操作和取模運算
方法二和方法一的訪問方式相同,不同的是color reduce用模運算代替整數除法

// using .ptr and * ++ and modulo
void colorReduce2(cv::Mat &image, int div=64) {
?? ? ?int nr= image.rows; // number of rows
?? ? ?int nc= image.cols * image.channels(); // total number of elements per line
? ? ? for (int j=0; j<nr; j++) {
?? ??? ? ?uchar* data= image.ptr<uchar>(j);
? ? ? ? ? for (int i=0; i<nc; i++) {
?? ??? ??? ? ? ? ?int v= *data;
? ? ? ? ? ? ? ? ? *data++= v - v%div + div/2;
? ? ? ? ? ? } // end of row ? ? ? ? ? ? ? ??
? ? ? }
}

方法三:.ptr、指針運算和位運算
由于進行量化的單元div通常是2的整次方,因此所有的乘法和除法都可以用位運算表示。

// using .ptr and * ++ and bitwise
void colorReduce3(cv::Mat &image, int div=64) {
?? ? ?int nr= image.rows; // number of rows
?? ? ?int nc= image.cols * image.channels(); // total number of elements per line
?? ? ?int n= static_cast<int>(log(static_cast<double>(div))/log(2.0));
?? ? ?// mask used to round the pixel value
?? ? ?uchar mask= 0xFF<<n; // e.g. for div=16, mask= 0xF0
? ? ? for (int j=0; j<nr; j++) {
?? ??? ? ?uchar* data= image.ptr<uchar>(j);
? ? ? ? ? for (int i=0; i<nc; i++) {
? ? ? ? ? ? *data++= *data&mask + div/2;
? ? ? ? ? ? } // end of row ? ? ? ? ? ? ? ??
? ? ? }
}

方法四:指針運算
方法四和方法三量化處理的方法相同,不同的是用指針運算代替*++操作。

// direct pointer arithmetic
void colorReduce4(cv::Mat &image, int div=64) {
?? ? ?int nr= image.rows; // number of rows
?? ? ?int nc= image.cols * image.channels(); // total number of elements per line
?? ? ?int n= static_cast<int>(log(static_cast<double>(div))/log(2.0));
?? ? ?int step= image.step; // effective width
?? ? ?// mask used to round the pixel value
?? ? ?uchar mask= 0xFF<<n; // e.g. for div=16, mask= 0xF0
? ? ? // get the pointer to the image buffer
?? ? ?uchar *data= image.data;
? ? ? for (int j=0; j<nr; j++) {
? ? ? ? ? for (int i=0; i<nc; i++) {
? ? ? ? ? ? *(data+i)= *data&mask + div/2;
? ? ? ? ? ? } // end of row ? ? ? ? ? ? ? ??
? ? ? ? ? ? data+= step; ?// next line
? ? ? }
}

方法五:.ptr、*++、位運算以及image.cols * image.channels()
這種方法就是沒有計算nc,基本是個充數的方法。

// using .ptr and * ++ and bitwise with image.cols * image.channels()
void colorReduce5(cv::Mat &image, int div=64) {
?? ? ?int nr= image.rows; // number of rows
?? ? ?int n= static_cast<int>(log(static_cast<double>(div))/log(2.0));
?? ? ?// mask used to round the pixel value
?? ? ?uchar mask= 0xFF<<n; // e.g. for div=16, mask= 0xF0
? ? ? for (int j=0; j<nr; j++) {
?? ??? ? ?uchar* data= image.ptr<uchar>(j);
? ? ? ? ? for (int i=0; i<image.cols * image.channels(); i++) {
? ? ? ? ? ? *data++= *data&mask + div/2;
? ? ? ? ? ? } // end of row ? ? ? ? ? ? ? ??
? ? ? }
}
?

方法六:連續圖像
Mat提供了isContinuous()函數用來查看Mat在內存中是不是連續存儲,如果是則圖片被存儲在一行中。

// using .ptr and * ++ and bitwise (continuous)
void colorReduce6(cv::Mat &image, int div=64) {
?? ? ?int nr= image.rows; // number of rows
?? ? ?int nc= image.cols * image.channels(); // total number of elements per line
?? ? ?if (image.isContinuous()) ?{
?? ??? ? ?// then no padded pixels
?? ??? ? ?nc= nc*nr;?
?? ??? ? ?nr= 1; ?// it is now a 1D array
?? ? ? }
?? ? ?int n= static_cast<int>(log(static_cast<double>(div))/log(2.0));
?? ? ?// mask used to round the pixel value
?? ? ?uchar mask= 0xFF<<n; // e.g. for div=16, mask= 0xF0
? ? ? for (int j=0; j<nr; j++) {
?? ??? ? ?uchar* data= image.ptr<uchar>(j);
? ? ? ? ? for (int i=0; i<nc; i++) {
? ? ? ? ? ? *data++= *data&mask + div/2;
? ? ? ? ? ? } // end of row ? ? ? ? ? ? ? ??
? ? ? }
}

方法七:continuous+channels
與方法六基本相同,也是充數的。

// using .ptr and * ++ and bitwise (continuous+channels)
void colorReduce7(cv::Mat &image, int div=64) {
?? ? ?int nr= image.rows; // number of rows
?? ? ?int nc= image.cols ; // number of columns
?? ? ?if (image.isContinuous()) ?{
?? ??? ? ?// then no padded pixels
?? ??? ? ?nc= nc*nr;?
?? ??? ? ?nr= 1; ?// it is now a 1D array
?? ? ? }
?? ? ?int n= static_cast<int>(log(static_cast<double>(div))/log(2.0));
?? ? ?// mask used to round the pixel value
?? ? ?uchar mask= 0xFF<<n; // e.g. for div=16, mask= 0xF0
? ? ? for (int j=0; j<nr; j++) {
?? ??? ? ?uchar* data= image.ptr<uchar>(j);
? ? ? ? ? for (int i=0; i<nc; i++) {
? ? ? ? ? ? *data++= *data&mask + div/2;
? ? ? ? ? ? *data++= *data&mask + div/2;
? ? ? ? ? ? *data++= *data&mask + div/2;
? ? ? ? ? ? } // end of row ? ? ? ? ? ? ? ??
? ? ? }
}

方法八:Mat _iterator
真正有區別的方法來啦,用Mat提供的迭代器代替前面的[]操作符或指針,血統純正的官方方法~

// using Mat_ iterator?
void colorReduce8(cv::Mat &image, int div=64) {
?? ? ?// get iterators
?? ? ?cv::Mat_<cv::Vec3b>::iterator it= image.begin<cv::Vec3b>();
?? ? ?cv::Mat_<cv::Vec3b>::iterator itend= image.end<cv::Vec3b>();
?? ? ?for ( ; it!= itend; ++it) {
? ? ? ? (*it)[0]= (*it)[0]/div*div + div/2;
? ? ? ? (*it)[1]= (*it)[1]/div*div + div/2;
? ? ? ? (*it)[2]= (*it)[2]/div*div + div/2;
?? ? ?}
}
?

方法九:Mat_ iterator 和位運算
把方法八中的乘除法換成位運算。

// using Mat_ iterator and bitwise
void colorReduce9(cv::Mat &image, int div=64) {
?? ? ?// div must be a power of 2
?? ? ?int n= static_cast<int>(log(static_cast<double>(div))/log(2.0));
?? ? ?// mask used to round the pixel value
?? ? ?uchar mask= 0xFF<<n; // e.g. for div=16, mask= 0xF0
?? ? ?// get iterators
?? ? ?cv::Mat_<cv::Vec3b>::iterator it= image.begin<cv::Vec3b>();
?? ? ?cv::Mat_<cv::Vec3b>::iterator itend= image.end<cv::Vec3b>();
?? ? ?for ( ; it!= itend; ++it) {
? ? ? ? (*it)[0]= (*it)[0]&mask + div/2;
? ? ? ? (*it)[1]= (*it)[1]&mask + div/2;
? ? ? ? (*it)[2]= (*it)[2]&mask + div/2;
?? ? ?}
}

方法十:MatIterator_
和方法八基本相同。

// using MatIterator_?
void colorReduce10(cv::Mat &image, int div=64) {
?? ? ?cv::Mat_<cv::Vec3b> cimage= image;
?? ? ?cv::Mat_<cv::Vec3b>::iterator it=cimage.begin();
?? ? ?cv::Mat_<cv::Vec3b>::iterator itend=cimage.end();
?? ? ?for ( ; it!= itend; it++) {?
? ? ? ? (*it)[0]= (*it)[0]/div*div + div/2;
? ? ? ? (*it)[1]= (*it)[1]/div*div + div/2;
? ? ? ? (*it)[2]= (*it)[2]/div*div + div/2;
?? ? ?}
}
?

方法十一:圖像坐標
// using (j,i)
void colorReduce11(cv::Mat &image, int div=64) {
?? ? ?int nr= image.rows; // number of rows
?? ? ?int nc= image.cols; // number of columns
? ? ? for (int j=0; j<nr; j++) {
? ? ? ? ? for (int i=0; i<nc; i++) {
? ? ? ? ? ? ? ? ? image.at<cv::Vec3b>(j,i)[0]=?? ? image.at<cv::Vec3b>(j,i)[0]/div*div + div/2;
? ? ? ? ? ? ? ? ? image.at<cv::Vec3b>(j,i)[1]=?? ? image.at<cv::Vec3b>(j,i)[1]/div*div + div/2;
? ? ? ? ? ? ? ? ? image.at<cv::Vec3b>(j,i)[2]=?? ? image.at<cv::Vec3b>(j,i)[2]/div*div + div/2;
? ? ? ? ? ? } // end of row ? ? ? ? ? ? ? ??
? ? ? }
}

方法十二:創建輸出圖像
之前的方法都是直接修改原圖,方法十二新建了輸出圖像,主要用于后面的時間對比。

// with input/ouput images
void colorReduce12(const cv::Mat &image, // input image?
? ? ? ? ? ? ? ? ?cv::Mat &result, ? ? ?// output image
? ? ? ? ? ? ? ? ?int div=64) {
?? ? ?int nr= image.rows; // number of rows
?? ? ?int nc= image.cols ; // number of columns
?? ? ?// allocate output image if necessary
?? ? ?result.create(image.rows,image.cols,image.type());
?? ? ?// created images have no padded pixels
?? ? ?nc= nc*nr;?
?? ? ?nr= 1; ?// it is now a 1D array
?? ? ?int n= static_cast<int>(log(static_cast<double>(div))/log(2.0));
?? ? ?// mask used to round the pixel value
?? ? ?uchar mask= 0xFF<<n; // e.g. for div=16, mask= 0xF0
? ? ? for (int j=0; j<nr; j++) {
?? ??? ? ?uchar* data= result.ptr<uchar>(j);
?? ??? ? ?const uchar* idata= image.ptr<uchar>(j);
? ? ? ? ? for (int i=0; i<nc; i++) {
? ? ? ? ? ? *data++= (*idata++)&mask + div/2;
? ? ? ? ? ? *data++= (*idata++)&mask + div/2;
? ? ? ? ? ? *data++= (*idata++)&mask + div/2;
? ? ? ? ? } // end of row ? ? ? ? ? ? ? ??
? ? ? }
}

方法十三:重載操作符
Mat重載了+&等操作符,可以直接將兩個Scalar(B,G,R)數據進行位運算和數學運算。

// using overloaded operators
void colorReduce13(cv::Mat &image, int div=64) {
?? ? ?int n= static_cast<int>(log(static_cast<double>(div))/log(2.0));
?? ? ?// mask used to round the pixel value
?? ? ?uchar mask= 0xFF<<n; // e.g. for div=16, mask= 0xF0
?? ? ?// perform color reduction
?? ? ?image=(image&cv::Scalar(mask,mask,mask))+cv::Scalar(div/2,div/2,div/2);
}

時間對比
通過迭代二十次取平均時間,得到每種方法是運算時間如下。

可以看到,指針*++訪問和位運算是最快的方法;而不斷的計算image.cols*image.channles()花費了大量重復的時間;另外迭代器訪問雖然安全,但性能遠低于指針運算;通過圖像坐標(j,i)訪問時最慢的,使用重載操作符直接運算效率最高。
?

總結

以上是生活随笔為你收集整理的遍历opencv中的mat像素的几种方法和概念的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。