矩阵掩膜(Mask,也被称为kernel):
用于选定图像、图形或物体,对处理图像(全部或局部)进行遮挡,来控制图像处理区域或处理过程。用于覆盖的特定图像或者物体称为掩膜或模板。管血图像处理中可以做胶片、滤光片等
数字图像处理中,掩膜为二维矩阵数组,有时也用多值图像。数字图像处理中,图像掩模主要用于:
- 提取感兴趣,用预先制作的感兴趣去掩膜与待处理图像相乘,得到感兴趣区图像,感兴趣区图内图像数值保持不变,而区外图像值都为0.
- 屏蔽作用,用掩膜对图像还是那个某些区域作屏障,使其不参与处理或不参加处理参数计算,或仅对屏蔽区域做处理或同级。
- 结构特征提取,用相似性变量或图像匹配方法检测或提取图像中与掩膜相似的结构特征。
- 特殊图像的制作。
- 提高对比度。
用途:掩膜是一种图像滤镜的模板,实用掩膜经常处理的是遥感图像。当提取道路或者河流或者房屋时,通过一个n*n的矩阵来对图像进行像素过滤,然后将我们需要的地物或者标志突出显示出来。这个矩阵就是一种掩膜。
在OpenCV中,掩模操作是相对简单的。大致的意思是,通过一个掩模矩阵,重新计算图像中的每一个像素值。掩模矩阵控制了旧图像当前位置以及周围位置像素对新图像当前位置像素值的影响力度。用数学术语讲,即我们自定义一个权重表。
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
int cols = (src.cols - 1) * src.channels(); int offsetx = src.channels(); int rows = src.rows; //图像的行数 dst = Mat::zeros(src.size(), src.type()); //输出容器 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); for (int col = offsetx; col < cols; col++) { output[col] = saturate_cast<uchar>( 5 * current[col] - (current[col - offsetx] + current[col + offsetx] + previous[col] + next[col])); } } |
获取图像的像素指针:
Mat数据类型指针(Mat::ptr):
格式:
1 2 3 4 5 6 7 |
C++: uchar* Mat::ptr(int i=0)
C++: const uchar* Mat::ptr(int i=0) const
C++: template<typename _Tp> _Tp* Mat::ptr(inti=0)
C++: template<typename _Tp> const _Tp*Mat::ptr(int i=0) const |
作用:表示返回指定矩阵行的指针,其中i表示基于0的索引。
常见用法:
例一:
1 2 3 4 5 6 7 |
cv::Mat image = cv::Mat(400, 600, CV_8UC3); //宽400,长600,3通道彩色图片 Vec3b* data00 = image.ptr<cv::Vec3b>(0); //data00是指向image第一行第一个元素指针 Vec3b* data10 = image.ptr<cv::Vec3b>(1); //data10是指向image第二行第一个元素指针 Vec3b* data01 = image.ptr<cv::Vec3b>(0, 1); //data01是指向image第一行第二个元素指针 data01->val[0] = 0; //修改data01即第一行第二个元素第一通道值为0 data01->val[1] = 0; //修改data01即第一行第二个元素第二通道值为0 data01->val[2] = 255; //修改data01即第一行第二个元素第三通道值为255 |
结果:
例二:
1 2 3 4 5 6 7 8 |
Mat.ptr<uchar>(i,j)//代表第i行,第j个点的值(j的大小包含通道数),这是一个地址 i = Mat.rols(); j = Mat.cols()*Mat.channels(); ucahr//代表这个容器存储的类型,和C++的Vector<int> test;一样的
Mat.ptr<uchar>(i) //获取像素矩阵的指针,索引i表示第几行,从0开始计行数。这是一个指针 const uchar* current= myImage.ptr<uchar>(row);//获得当前行指针 p(row,col) = current[col]//获取当前像素点P(row, col)的像素值 这是一个值 |
像素范围处理:saturate_cast<uchar>(data);
功能:确保RGB值的范围在0~255之间。
1 2 3 |
saturate_cast<uchar>(-100); //返回值为0 saturate_cast<uchar>(288); //返回值为255 saturate_cast<uchar>(100); //返回值为100 |
矩阵掩膜函数:filter2D()函数
格式:
1 2 3 4 5 6 7 |
void filter2D( InputArray src, OutputArray dst, int ddepth, InputArray kernel, Point anchor = Point(-1,-1), double delta = 0, int borderType = BORDER_DEFAULT ); |
参数:
参数 |
描述 |
||||||||||||||||
InputArray 类型的 src |
原图像 |
||||||||||||||||
OutputArray 类型的 dst |
目标图像与原图尺寸和通道数相同 |
||||||||||||||||
Int 类型的 ddepth |
目标图像的深度 |
||||||||||||||||
InputArray 类型的 kernel |
卷积核(或相关核),单通道浮点矩阵;如果将不同的内核应用于不同的通道,请使用拆分将图像拆分为单独的颜色平面,然后进行处理 |
||||||||||||||||
Point 类型的 ancho |
内核的锚点,指示内核中过滤点的相对位置;锚应位于内核中;默认值(-1,-1)表示锚位于内核中心 |
||||||||||||||||
double 类型的 delta |
在将它们存储在dst中之前,将可选值添加到已过滤的像素中。类似于偏置 |
||||||||||||||||
int 类型的 borderType |
表示边缘类型;像素外推法(一般不管它) 边缘处理类型:
|
代码
1 2 3 4 |
Mat kernel = (Mat_<char>(3, 3) << 0, -1, 0, -1, 5, -1, 0, -1, 0);//定义掩膜(锐化算子) filter2D(src, dst, src.depth(), kernel); |
获取当前CPU时间:getTickCount()函数
1 2 3 4 5 6 7 |
double t = getTickCount(); //获取当前CPU时间,返回毫秒数 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(); //时间差转换为s printf("time consume %.2f\n", timeconsume); |
完整代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
#include<opencv2/opencv.hpp> #include<iostream> #include<math.h>
using namespace cv; using namespace std;
int main(int argc, char** argv) { Mat src, dst; src = imread("./Picture/1.jpg"); if (!src.data) { printf("cloud not load image"); return -1; } namedWindow("input", WINDOW_AUTOSIZE); // WINDOW_AUTOSIZE 会根据图像大小显示窗口大小,无法修改窗口的小 imshow("input", src);
int cols = (src.cols - 1) * src.channels(); //图像的列数 int offsetx = src.channels(); //返回图像src的通道数目 int rows = src.rows; //图像的行数 dst = Mat::zeros(src.size(), src.type()); //创建与src大小、类型相同的零数组作为输出容器 for(int row = 1; row < (rows - 1); row++) { const uchar* previous = src.ptr<uchar>(row - 1);//previous指向src的前一行(row-1)第一个元素的指针 const uchar* current = src.ptr<uchar>(row); //当前行 const uchar* next = src.ptr<uchar>(row + 1);//后一行 uchar* output = dst.ptr<uchar>(row); for (int col = offsetx; col < cols; col++) { /* 其中 saturate_cast<uchar>(data) 的功能是确保data的值在0-255之间 1 saturate_cast<uchar>(-100); //返回值为0 2 saturate_cast<uchar>(288); //返回值为255 3 saturate_cast<uchar>(100); //返回值为100 */ output[col] = saturate_cast<uchar>( 5 * current[col] - (current[col - offsetx] + current[col + offsetx] + previous[col] + next[col])); } }
//double t = getTickCount(); //获取当前CPU时间 //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\n", timeconsume);
namedWindow("contrast image demo", WINDOW_AUTOSIZE); //WINDOW_NORMAL 可以利用鼠标改变图像大小显示窗口大小 imshow("contrast image demo", dst);
waitKey(0); return 0; } |
运行结果: