存取像素值
获取像素值:
灰度图片:
pixel = img.at<uchar>(100, 200);
彩色图片:
pixel = img.at<Vec3b>(100, 200);//对于彩色图像,Mat会返回一个有三个8位数组成的unsigned char类型向量
修改像素值:
灰度图片:
img.at<uchar>(100, 200) = 255;
彩色图片:
for(int i=0; i<3; i++){
img.at<Vec3b>(100, 200)[i] = 255
}
下述代码为一张图片随机加入噪声
void salt(Mat& image, int n) {
for (int k = 0; k < n; k++) {
int i = rand() % image.cols;
int j = rand() % image.rows;
if (image.channels() == 1)
image.at<uchar>(j, i) = 255;
else if (image.channels() == 3){
image.at<Vec3b>(j, i)[0] = 255;
image.at<Vec3b>(j, i)[1] = 255;
image.at<Vec3b>(j, i)[2] = 255;
}
}
}
使用指针遍历图像
遍历方式
void colorReduce(Mat& image, int div = 64) {
//注意opencv默认BGR通道数。缓冲区前三个字节是第一行第一列像素BGR值,之后是第一行第二列三个元素BGR值,以此类推。
int rows = image.rows;
int num = image.cols * image.channels();
for (int i = 0; i < rows; i++) {
uchar* data = image.ptr<uchar>(i);//ptr函数返回图像任意一行的首地址
for (int j = 0; j < num; j++) {
data[j] = data[j] / div * div + div / 2;
}
}
}
缩减图像颜色数量
void colorReduce(Mat& image, int div = 64) {
int rows = image.rows;
int num = image.cols * image.channels();
for (int i = 0; i < rows; i++) {
uchar* data = image.ptr<uchar>(i);//ptr函数返回图像任意一行的首地址
for (int j = 0; j < num; j++) {
data[j] = data[j] / div * div + div / 2;
}
}
}
上述颜色缩减函数在传入图片和缩减比例后,将直接在原图上对图片进行更改,如果我们想保留原图,可以拷贝一份图片进行更改。我们可以根据用户需要决定是否在原图上更改图片,代码如下:
void colorReduce(Mat& image_in, Mat& image_out, int div = 64) {
int rows = image_in.rows;
int num = image_in.cols * image_in.channels();
for (int i = 0; i < rows; i++) {
uchar* data_in = image_in.ptr<uchar>(i);
uchar* data_out = image_out.ptr<uchar>(i);
for (int j = 0; j < num; j++) {
data_out[j] = data_in[j] / div * div + data_in[j] / div;
}
}
}
int main() {
Mat img = imread("picture.png");
Mat result;
result.create(img.rows, img.cols, img.type());
colorReduce(img, img, 32);//在原图修改
colorReduce(img, result, 32);、、将修改后图片拷贝到result
imshow("hello", img);
waitKey(0);
imshow("hello", result);
waitKey(0);
destroyAllWindows();
return 0;
}
OpenCV中的图像填补
理论上来说,对于一张W×H的彩色图片,应该需要W×H×3个uchar构成的内存块来存储。但实际上由于一些媒体处理芯片(intel或者MMX)在行的长度为4或者8时可以更高效的处理图片,opencv往往会在行之后进行扩充,在行后加上几个元素,这些元素不会被处理和现实,填补的值会被忽略。图片是否被填充可以通过isContinuous()函数确定。
更加高效的遍历方式
在一些图像算法中如果图片没有被填充,利用图像存储的连续性,我们可以更高效的遍历图像。
void colorReduce(Mat& image, int div = 64) {
//注意opencv默认BGR通道数。缓冲区前三个字节是第一行第一列像素BGR值,之后是第一行第二列三个元素BGR值,以此类推。
int rows = image.rows;
int num = image.cols * image.channels();
if (image.isContinuous()) {
num = rows * num;
rows = 1;
}
for (int i = 0; i < rows; i++) {
uchar* data = image.ptr<uchar>(i);//ptr函数返回图像任意一行的首地址
for (int j = 0; j < num; j++) {
data[j] = data[j] / div * div + div / 2;
}
}
}
图片的指针运算
void pointerOperation(Mat& image) {
uchar *data = image.data;//获取图像首地址
data += image.step;//获取下一行地址
//获取第 i=10 行第 j=24 列的像素值
data = image.data + 100 * image.step + 225 * image.elemSize();
cout <<"像素值为"<< * data << endl;
}