1 . 定义
掩膜操作是指根据掩膜矩阵(也称作核kernel)重新计算图像中每个像素的值。掩膜矩阵中的值表示了邻近像素值(包括该像素自身的值)对新像素值有多大的影响。从数学的观点来看,我们用自己设置的权值,对像素领域内的值做了个加权平均。
比如,下面这个公式表示用5倍当前像素的值减去该像素上、下、左、右四个像素值和,得到的结果赋值给当前像素。使用该公式可以用于提升图像的对比度。调节I(i,j)的系数权重可以得到不同的对比度提升效果。
I(i,j)=5∗I(i,j)−[I(i−1,j)+I(i+1,j)+I(i,j−1)+I(i,j+1)]
上面的公式可以用掩膜矩阵表示成如下的形式。
2. 自定义的滤波器实现
#include <opencv2/imgcodecs.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/highgui/highgui_c.h>
#include <iostream>
using namespace std;
using namespace cv;
int main()
{
Mat img, dst;
img = imread("D:/VS项目/opencv3/1.jpg", IMREAD_UNCHANGED);
//如果没有读取到图像信息,直接退出
if (!img.data)
return -1;
dst = Mat::zeros(img.size(), img.type()); //自己定义的全零图像
int rows = img.rows;
int cols = (img.cols - 1) * img.channels(); //因为原图是个RGB图像,所以需要乘以通道数,因为每个列都是三个值
int offsetx = img.channels();
for (int row = 1; row < rows - 1; row++) //从第二行开始到倒数第二行结束
{
//uchar是指无符号字符型
const uchar* previous = img.ptr<uchar>(row - 1); //指向上一行
const uchar* current = img.ptr<uchar>(row); //指向当前行
const uchar* next = img.ptr<uchar>(row + 1); //指向下一行
uchar* output = dst.ptr<uchar>(row); // 指向定义的全零图像
for (int col = offsetx; col < cols; col++) // 因为是RGB三通道图像,所以要从第三个开始
{
//进行掩膜操作:I(i,j)=5∗I(i,j)−[I(i−1,j)+I(i+1,j)+I(i,j−1)+I(i,j+1)],增加对比度
//saturate_cast是为为了确保像素的大小在(0,255)之间
output[col] = saturate_cast<uchar>(5 * current[col] - (current[col - offsetx] + current[col + offsetx] + previous[col] + next[col]));//中间的像素值*5减去周围的像素值--前面是防止像素超过256
}
}
//调用opencv的API
//Mat kernel = (Mat_<char>(3, 3) << 0, -1, 0, -1, 5, -1, 0, -1, 0); //自定义掩膜(卷积核)
//filter2D(img, dst, img.depth(), kernel);//对图像进行掩膜操作
namedWindow("result view", CV_WINDOW_AUTOSIZE);
imshow("result view", dst);
imshow("origin view", img);
waitKey(0);
return 0;
}
3 . filter2D API实现
#include <opencv2/imgcodecs.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/highgui/highgui_c.h>
#include <iostream>
using namespace std;
using namespace cv;
int main()
{
Mat img, dst;
img = imread("D:/VS项目/opencv3/1.jpg", IMREAD_UNCHANGED);
//如果没有读取到图像信息,直接退出
if (!img.data)
return -1;
dst = Mat::zeros(img.size(), img.type()); //自己定义的全零图像
/*
int rows = img.rows;
int cols = (img.cols - 1) * img.channels(); //因为原图是个RGB图像,所以需要乘以通道数,因为每个列都是三个值
int offsetx = img.channels();
for (int row = 1; row < rows - 1; row++) //从第二行开始到倒数第二行结束
{
//uchar是指无符号字符型
const uchar* previous = img.ptr<uchar>(row - 1); //指向上一行
const uchar* current = img.ptr<uchar>(row); //指向当前行
const uchar* next = img.ptr<uchar>(row + 1); //指向下一行
uchar* output = dst.ptr<uchar>(row); // 指向定义的全零图像
for (int col = offsetx; col < cols; col++) // 因为是RGB三通道图像,所以要从第三个开始
{
//进行掩膜操作:I(i,j)=5∗I(i,j)−[I(i−1,j)+I(i+1,j)+I(i,j−1)+I(i,j+1)],增加对比度
//saturate_cast是为为了确保像素的大小在(0,255)之间
output[col] = saturate_cast<uchar>(5 * current[col] - (current[col - offsetx] + current[col + offsetx] + previous[col] + next[col]));//中间的像素值*5减去周围的像素值--前面是防止像素超过256
}
}
*/
//调用opencv的API
Mat kernel = (Mat_<char>(3, 3) << 0, -1, 0, -1, 5, -1, 0, -1, 0); //自定义掩膜(卷积核)
filter2D(img, dst, img.depth(), kernel);//对图像进行掩膜操作
namedWindow("result view", CV_WINDOW_AUTOSIZE);
imshow("result view", dst);
imshow("origin view", img);
waitKey(0);
return 0;
}
4 . 关于RGB三通道的说明
这段代码老是看的云里雾里的,于是就去查了点资料。
一个像素需要多个矩阵元素来保存,矩阵中的列会包含多个子列,且子列数和通道数相等,就如上图所示。