在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; } } }