在Mat中訪問獨立元素,只須要輸入行號和列號便可,下面經過一個例子來講明最基本的像素操做。安全
我針對一個圖像,先加入鹽噪聲,而後使用均值濾波手動緩解噪聲的影響:函數
加鹽噪音的方法:spa
void salt(cv::Mat image, int n)//(圖像矩陣,噪音點的個數) { int i, j; for (int k = 0; k < n; k++) //n個噪音點,n個循環 { i = rand() % image.rows; //隨機找一行 j = rand() % image.cols; //隨機找一列 if (image.type() == CV_8UC1)//對於灰度圖像 image.at<uchar>(i, j) =255; //將這個點變爲白點 else if (image.type() == CV_8UC3)//對於三通道彩色圖像 { image.at<cv::Vec3b>(i, j)[0] = 255;//將三個 image.at<cv::Vec3b>(i, j)[1] = 255;//通道都 image.at<cv::Vec3b>(i, j)[2] = 255;//調成最大值 } } }
重點函數:訪問像素:image.at<uchar>(i, j)//image.at<cv::Vec3b>(i, j)[0]指針
手動編寫的均值濾波,將一個點的值變爲周圍9個點的平均值:code
void averagefilter(cv::Mat image) //3X3均值濾波 { for (int i = 1; i < image.rows - 1; i++) //從2到max-1行 { for (int j = 1; j < image.cols - 1; j++)//從2到max-1列 { if (image.type() == CV_8UC1) image.at<uchar>(i, j) = image.at<uchar>(i, j) * 1 / 9 + image.at<uchar>(i - 1, j) * 1 / 9 +image.at<uchar>(i+1, j) * 1 / 9+\ image.at<uchar>(i, j - 1) * 1 / 9 + image.at<uchar>(i - 1, j - 1) * 1 / 9 + image.at<uchar>(i+1, j-1) * 1 / 9+\ image.at<uchar>(i, j+1) * 1 / 9+ image.at<uchar>(i-1, j+1) * 1 / 9 +image.at<uchar>(i+1, j+1) * 1 / 9; else if (image.type() == CV_8UC3) { image.at<cv::Vec3b>(i, j)[0] = image.at<cv::Vec3b>(i, j)[0] * 1 / 9 + image.at<cv::Vec3b>(i - 1, j)[0] * 1 / 9 + image.at<cv::Vec3b>(i + 1, j)[0] * 1 / 9 + \ image.at<cv::Vec3b>(i, j - 1)[0] * 1 / 9 + image.at<cv::Vec3b>(i - 1, j - 1)[0] * 1 / 9 + image.at<cv::Vec3b>(i + 1, j - 1)[0] * 1 / 9 + \ image.at<cv::Vec3b>(i, j + 1)[0] * 1 / 9 + image.at<cv::Vec3b>(i - 1, j + 1)[0] * 1 / 9 + image.at<cv::Vec3b>(i + 1, j + 1)[0] * 1 / 9; image.at<cv::Vec3b>(i, j)[1] = image.at<cv::Vec3b>(i, j)[1] * 1 / 9 + image.at<cv::Vec3b>(i - 1, j)[1] * 1 / 9 + image.at<cv::Vec3b>(i + 1, j)[1] * 1 / 9 + \ image.at<cv::Vec3b>(i, j - 1)[1] * 1 / 9 + image.at<cv::Vec3b>(i - 1, j - 1)[1] * 1 / 9 + image.at<cv::Vec3b>(i + 1, j - 1)[1] * 1 / 9 + \ image.at<cv::Vec3b>(i, j + 1)[1] * 1 / 9 + image.at<cv::Vec3b>(i - 1, j + 1)[1] * 1 / 9 + image.at<cv::Vec3b>(i + 1, j + 1)[1] * 1 / 9; image.at<cv::Vec3b>(i, j)[2] = image.at<cv::Vec3b>(i, j)[2] * 1 / 9 + image.at<cv::Vec3b>(i - 1, j)[2] * 1 / 9 + image.at<cv::Vec3b>(i + 1, j)[2] * 1 / 9 + \ image.at<cv::Vec3b>(i, j - 1)[2] * 1 / 9 + image.at<cv::Vec3b>(i - 1, j - 1)[2] * 1 / 9 + image.at<cv::Vec3b>(i + 1, j - 1)[2] * 1 / 9 + \ image.at<cv::Vec3b>(i, j + 1)[2] * 1 / 9 + image.at<cv::Vec3b>(i - 1, j + 1)[2] * 1 / 9 + image.at<cv::Vec3b>(i + 1, j + 1)[2] * 1 / 9; } } } }
主函數:blog
cv::Mat image = cv::imread("D://ALL_ESCAPE.jpg"); salt(image, 3000); cv::namedWindow("Salted"); cv::imshow("Salted", image); cv::waitKey(1); averagefilter(image); cv::namedWindow("Filtered"); cv::imshow("Filtered", image); cv::waitKey(0); return 0;
這回咱們找一個暗一些的圖來作實驗,來個1000個點的:it
用指針能夠更高效的掃描圖像,下面看一個減小圖像中位深度的例子:io
首先爲了方便運算,OpenCV中 cv::Mat提供了一個方法能夠直接訪問圖像中某一行的地址:class
uchar *pointer = image.ptr<uchar>(j) // image: image matrix j:number of column循環
而對於列來講, pointer[0]表示第一列的第一個通道元素,pointer[1]表示第一列第二通道,pointer[5]表示第二列第三通道。
也就是說這個圖像若爲100*100的RGB圖像, 則其指針爲100*300;最後一列能夠表示爲pointer[299];
下面是程序,一個就地轉換的實例:
void Quantization(cv::Mat image, int div) { int row_number = image.rows; int col_number = image.cols*image.channels(); for (int i = 0; i < row_number; i++) { uchar *row_pointer = image.ptr<uchar>(i); for (int j = 0; j < col_number; j++) { row_pointer[j] = row_pointer[j] / div*div + div / 2; } } } int _tmain(int argc, _TCHAR* argv[]) { cv::Mat image; image = cv::imread("D://Scene.jpg"); cv::namedWindow("Original"); cv::imshow("Original", image); cv::waitKey(1); Quantization(image, 64); cv::namedWindow("Reduced"); cv::imshow("Reduced", image); cv::waitKey(0); return 0; }
重點函數:uchar *row_pointer = image.ptr<uchar>(i);//i:行數 ; row_pointer[列數/通道數]
運行結果以下:
在上面的例子中,我使用了循環來遍歷全部的像素,OpenCV中提供了迭代器來封裝這些循環,可使操做更加安全:
void ColorReduce(cv::Mat &image, int div = 64) { 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; } }
從循環中咱們能夠看到it其實是一個地址
若是執行cout<<*it;一次,咱們能夠看到輸出是:
[205, 205, 205],是一個像素點上的3個通道的向量,這個向量就由函數cv::Mat_<cv::Vec3b>::iterator定義,因爲<cv::Vec3b>的存在,因此三個通道都被看作是一個總體。
爲方便你們理解uchar與<cv::Vec3b>的區別,我將最開始的減色程序改寫成<cv::Vec3b>的形式:
void Quantization(cv::Mat image, int div) { int row_number = image.rows; int col_number = image.cols; for (int i = 0; i < row_number; i++) { cv::Vec3b *row_pointer = image.ptr<cv::Vec3b>(i); for (int j = 0; j < col_number; j++) { row_pointer[j][0] = row_pointer[j][0] / div*div + div / 2; row_pointer[j][1] = row_pointer[j][1] / div*div + div / 2; row_pointer[j][2] = row_pointer[j][2] / div*div + div / 2; } } }