2.1 矩阵的掩膜操作
2.1.1 掩膜操作
掩膜操作是指根据掩膜矩阵(mask,也称为内核kernel)重新计算图像中每个像素的值。利用掩膜矩阵调整相邻像素对当前像素值的影响。从数学的角度来看,即是利用特定的权重值,对像素做一个加权平均。
假设有一个3*3的卷积核,矩阵参数如下图所示
用该核去遍历每个像素,及当前像素的值为5倍当前像素的值减去该像素上、下、左、右四个像素值和。如下图计算。该掩膜操作可用以调整图像的对比度。
在opencv中核可以如下定义:
Mat kernel = (Mat_<char>(3, 3) << 0, -1, 0, -1, 5, -1, 0, -1, 0);
为了进行掩膜操作,可以数组遍历或对图像数据进行像素指针的获取。
2.1.2 获取图像像素指针
▶确保输入图像是无符号字符类型(uchar)的:CV_Assert(Image.depth() == CV_8U);
▶获取像素矩阵的指针Mat.prt<uchar>(int i=0);当前row行为const uchar* currentPrt=Image.prt<uchar>(row);
▶获取(row,col)像素点的值p(row,col)=currentPrt[col];
2.1.3 获取Mat数组的像素值
可以使用图像坐标 at方法去进行像素的获取,但是效率慢,适用于随机访问。
动态地址遍历范例如下:
cv::Mat srcImg = cv::imread(sFileName, CV_8UC2); // 读灰度图时改为CV_8UC1
cv::Mat dstImg = cv::Mat::zeros(srcImg.size(), srcImg.type());
int nChannels = srcImg.channels();//获取通道数
for (int i = 0; i < srcImg.rows; i++)
{
for (int j = 0; j < srcImg.cols; j++)
{
for (int k = 0; k<nChannels; k++)
{
dstImg.at<uchar>(i, j*nChannels +k) = 255 - srcImg.at<uchar>(i, j*nChannels + k);
}
}
}
2.1.4 像素值范围处理--saturate_cast<uchar>
通过掩膜操作计算后,可通过该API保证像素灰度值的大小在0~255之间。
saturate_cast< <0 > | 返回0 |
saturate_cast<0·255 > | 返回输入值 |
saturate_cast< >255 > | 返回255 |
2.1.5 掩膜操作API(卷积函数)--filter2D
opencv提供了直接可对图像进行处理的API接口。
·void filter2D( InputArray src, OutputArray dst, int depth, InputArray kernel, Point anchor = Point(-1,-1), double delta = 0,int borderType = BORDER_DEFAULT)
参数 | 含义 | |
作用 | 对二维矩阵进行卷积运算 | |
输入 | src | 原图 |
dst | 结果图(变换后的图像) | |
depth | 输出图像的深度 [-1 将给出与输入图像相同的输出图像深度] | |
kernel | 卷积核:卷积矩阵中使用的简单二维矩阵或用于模糊、锐化和边检测图像的掩码 | |
Point | 锚点:被平滑的那个点,默认值Point(-1,-1)表示这个锚点在核的中心。。 | |
delta | 在将结果存储到输出图像之前添加到每个像素的可选值。默认值为 0。 | |
borderType | 边界填充类型,用于指定如何处理边界像素。默认值为 cv::BORDER_DEFAULT。 | |
返回值 | void | 无 |
2.2 代码范例
2.2.1 通过指针遍历进行掩膜操作
Mat srcImg = imread("D:\\testimg\\CSet12\\lena.png");
if (srcImg.empty())
{
printf("Could not load the image...\n");
return -1;
}
else;
//显示窗口
namedWindow("srcImg", WINDOW_AUTOSIZE);
imshow("srcImg", srcImg);
//使用指针遍历掩膜操作
int nCols = srcImg.cols;
int nRows = srcImg.rows;
//由于图像位深不同,会导致所占用的字节数不同
int nOffset = srcImg.channels(); //每个像素的占用字节数
int nColPrt = (nCols - 1)*nOffset;//确保每个通道的值都能遍历到
Mat dstImg= Mat::zeros(srcImg.size(), srcImg.type());
for (int row = 1; row < (nRows - 1); row++)
{
const uchar* previous = srcImg.ptr<uchar>(row - 1);
const uchar* current = srcImg.ptr<uchar>(row);
const uchar* next = srcImg.ptr<uchar>(row + 1);
uchar* output = dstImg.ptr<uchar>(row);
for (int col = nOffset; col < nColPrt; col++)
{
output[col] = saturate_cast<uchar>(5 * current[col] - (current[col - nOffset] + current[col + nOffset] + previous[col] + next[col]));
}
}
2.2.2 通过卷积操作API处理
// 使用 掩膜操作API
Mat dstImg2;
Mat kernel=(Mat_<char>(3,3)<<0,-1,0,-1,5,-1,0,-1,0);
filter2D(srcImg, dstImg2, srcImg.depth(), kernel);
处理结果如下图所示