opencv3中圖形存儲基本為Mat格式,如果我們想獲取像素點的灰度值或者RGB值,可以通過image.at<uchar>(i,j)的方式輕松獲取。
Mat類中的at方法對於獲取圖像矩陣某點的RGB值或者改變某點的值很方便,對於單通道的圖像,則可以使用:
image.at<uchar>(i, j)
其中有一個要注意的地方是i對應的是點的y坐標,j對應的是點的x坐標,而不是我們習慣的(x,y)
來獲取或改變該點的值,而RGB通道的則可以使用:
image.at<Vec3b>(i, j)[0] image.at<Vec3b>(i, j)[1] image.at<Vec3b>(i, j)[2]
來分別獲取B、G、R三個通道的對應的值。下邊的代碼實現對圖像加椒鹽噪聲:
// created by ning zhang 2018/4/25
// The function of this program is to add noise to the image
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace std;
using namespace cv;
void salt_noise( Mat image, int time );
int main ( int argc, char** argv )
{
Mat image = imread("../lena.jpg",0); //input the gray image
if ( image.empty() )
{
cout << "Load image error" << endl;
return -1;
}
salt_noise(image, 3000);
namedWindow("image", 1);
imshow("image", image);
waitKey();
return 0;
}
void salt_noise ( Mat image, int time )
{
for (int k = 0; k < time; k++ ) //time is the number of noise you add
{
int i = rand() % image.rows;
int j = rand() % image.cols;
if (image.channels() == 1) //single channel
{
image.at<uchar>(i,j) = rand() % 255;
}
else if (image.channels() == 3) //RGB channel
{
image.at<Vec3b>(i, j)[0] = rand() % 255;
image.at<Vec3b>(i, j)[1] = rand() % 255;
image.at<Vec3b>(i, j)[2] = rand() % 255;
}
}
}
效果圖如下所示,可以為圖片增加噪點
代碼地址:https://github.com/feifanrensheng/salt_noise
還有比較省時的方法使用Mat的模板子類Mat_<T>,,對於單通道的具體使用:
Mat_<uchar> img = image; img(i, j) = rand() % 255;
對於RGB通道的使用:
Mat_<Vec3b> img = image; img(i, j)[0] = rand() % 255; img(i, j)[1] = rand() % 255; mg(i, j)[2] = rand() % 255;
還可以用指針的方法遍歷每一像素:(耗時較小)
void colorReduce(Mat image, int div = 64) { int nrow = image.rows; int ncol = image.cols*image.channels(); for (int i = 0; i < nrow; i++) { uchar* data = image.ptr<uchar>(i);//get the address of row i; for (int j = 0; j < ncol; j++) { data[i] = (data[i] / div)*div ; } } }
我們要尤其注意OpenCV坐標系與row&col的關系 (Mat::at(x,y)和Mat::at(Point(x, y))的區別)
直接給出對應關系吧
row == heigh == Point.y col == width == Point.x Mat::at(Point(x, y)) == Mat::at(y,x)
因為還有點的坐標,所以建議在訪問時都用Mat::at(Point(x, y))這種形式吧,免去了點坐標和行列的轉換
詳細說明:
-
坐標體系中的零點坐標為圖片的左上角,X軸為圖像矩形的上面那條水平線;Y軸為圖像矩形左邊的那條垂直線。該坐標體系在諸如結構體Mat,Rect,Point中都是適用的。(雖然網上有學着說OpenCV中有些數據結構的坐標原點是在圖片的左下角,但是我暫時還沒碰到過)。
-
在使用image.at(x1, x2)來訪問圖像中點的值的時候,x1並不是圖片中對應點的x軸坐標,而是圖片中對應點的y坐標。因此其訪問的結果其實是訪問image圖像中的Point(x2, x1)點,即與image.at(Point(x2, x1))效果相同。
-
如果所畫圖像是多通道的,比如說image圖像的通道數時n,則使用Mat::at(x, y)時,其x的范圍依舊是0到image的height,而y的取值范圍則是0到image的width乘以n,因為這個時候是有n個通道,所以每個像素需要占有n列。但是如果在同樣的情況下,使用Mat::at(point)來訪問的話,則這時候可以不用考慮通道的個數,因為你要賦值給獲取Mat::at(point)的值時,都不是一個數字,而是一個對應的n維向量。
參考:https://blog.csdn.net/xiao_lxl/article/details/69568963
https://blog.csdn.net/wangxuwen2/article/details/52443978
https://blog.csdn.net/u013203733/article/details/73742247
https://www.baidu.com/s?ie=utf-8&f=8&rsv_bp=0&rsv_idx=1&tn=baidu&wd=mat.at()&rsv_pq=ca3aed07000036c0&rsv_t=24317bhASyuKaxOgeSdlua%2FIqMWm7sRcP%2F1SQu5FhmelisfC2AWVqZaIMPk&rqlang=cn&rsv_enter=1&rsv_sug3=10&rsv_sug1=8&rsv_sug7=100&rsv_sug2=0&inputT=6442&rsv_sug4=7626