1. 矩阵掩模原理:
矩阵掩模算法feic非常简单,例如将一个3X3的矩阵,一张图像,对图像的每个像素点进行如下操作:
1.分别从左到右,从上到下,每个通道,拿3X3矩阵和原图对应位置做内积,最后得到的值在赋值给zhon中心像素点
简单例子:
通过如下3X3矩阵来做掩模,可以提高图像的对比度
如上图所示,红色是中心像素,从上到下,从左到右对每个像素做同样的处理操作,得到最终结果就是对比度提高之后的输出图像Mat对象。
开始之前,首先需要知道opencv中图像的存储方式:
还是先看Mat的存储形式。Mat和Matlab里的数组格式有点像,但一般是二维向量,如果是灰度图,一般存放<uchar>类型;如果是RGB彩色图,存放<Vec3b>类型。
单通道灰度图数据存放格式:
图1
matlab是从(1,1)到(rows,cols),而opencv是从(0,0)到(rows-1,cols-1);
多通道的图像中,每列并列存放通道数量的子列,如RGB三通道彩色图:
图2
注意通道的顺序反转了:BGR。所以说,在col上,图像的存储是每个通道连续的,每一列有三个通道的数据。所以在处理RGB图像时需要把图像的通道考虑进去。
#include <opencv2/opencv.hpp>
#include <iostream>
#include <math.h>
using namespace cv;
int main(int arc, char** argv)
{
Mat src, dst;
src = imread("H:/opencv/milu.png");
if (!src.data)
{
printf("cound not load image...\n");
return -1;
}
namedWindow("input image", CV_WINDOW_AUTOSIZE);
imshow("input image", src);
//每一列必须乘以通道数,因为有可能为彩色图像,列数为灰度的三倍
//由于最外围的一圈像素点没办法进行图像掩模(因为我们算的是模板中心点的值,模板放在最边缘也算不出最外圈的掩模值),所以减1
int cols = (src.cols - 1)*src.channels();
int offsetx = src.channels();
//行为图像的行
int rows = src.rows;
dst = Mat::zeros(src.size(), src.type());
//row从1开始一直取到rows-2结束,表示不要对最外围的像素点掩模
for (int row = 1; row < rows - 1; row++)
{
//通过像素指针,拿到行指针
const uchar* previous = src.ptr<uchar>(row - 1);
const uchar* current = src.ptr<uchar>(row);
const uchar* next = src.ptr<uchar>(row + 1);
uchar* output = dst.ptr<uchar>(row);
//col从3开始(注意这里的3是图2中大的第四列了),因为彩色图像会有三个通道,最外围的像素点不掩模
//每一个像素有BGR三个通道
//分别对每个像素的BGR都要分别进行掩模操作
for (int col = offsetx; col < cols; col++)
//这里可以依据图2理解
output[col] = saturate_cast<uchar>(5 * current[col] - (current[col - offsetx] + current[col + offsetx]
+ previous[col] + next[col]));
}
}
namedWindow("contrast image demo", CV_WINDOW_AUTOSIZE);
imshow("contrast image demo", dst);
waitKey(0);
return 0;
}
使用filter2D来实现只需要几行代码就可以搞定上面所有操作
double t = getTickCount();
Mat kernel = (Mat_<char>(3, 3) << 0, -1, 0, -1, 5, -1, 0, -1, 0);
filter2D(src, dst, src.depth(), kernel);
double timeconsume = (getTickCount() - t) / getTickFrequency();
printf("time consume %.2f", timeconsume);