1.掩膜(mask)的定义

用选定的图像,图形或物体,对处理的图像(全部或局部)进行遮挡,来控制图像处理的区域或处理过程。用于覆盖的特定图像或物体称为掩模或模板。光学图像处理中,掩模可以足胶片,滤光片等。

掩模是由0和1组成的一个二进制图像。当在某一功能中应用掩模时,1值区域被处理,被屏蔽的0值区域不被包括在计算中。通过指定的数据值,数据范围,有限或无限值,感兴趣区和注释文件来定义图像掩模,也可以应用上述选项的任意组合作为输入来建立掩模。

掩膜是一种图像滤镜的模板,实用掩膜经常处理的是遥感图像。当提取道路或者河流,或者房屋时,通过一个N * N的矩阵来对图像进行像素过滤,然后将我们需要的地物或者标志突出显示出来。这个矩阵就是一种掩膜。

在OpenCV的中,掩模操作是相对简单的。大致的意思是,通过一个掩模矩阵,重新计算图像中的每一个像素值。掩模矩阵控制了旧图像当前位置以及周围位置像素对新图像当前位置像素值的影响力度。用数学术语讲,即我们自定义一个权重表。

2.掩膜的用法

2.1 提取感兴趣区:用预先制作的感兴趣区掩膜与待处理图像相乘,得到感兴趣区图像,感兴趣区内图像值保持不变,而区外图像值都为0; 
2.2 屏蔽作用:用掩膜对图像上某些区域作屏蔽,使其不参加处理或不参加处理参数的计算,或仅对屏蔽区作处理或统计; 
2.3 结构特征提取:用相似性变量或图像匹配方法检测和提取图像中与掩膜相似的结构特征; 
2.4 特殊形状图像的制作。

3.掩膜运算的一个小实例

以图和掩膜的与运算为例: 
原图中的每个像素和掩膜中的每个对应像素进行与运算。比如1 & 1 = 1;1 & 0 = 0; 
比如一个3 * 3的图像与3 * 3的掩膜进行运算,得到的结果图像就是:

掩膜操作实现图像对比度调整

矩阵的掩膜操作十分简单,根据掩膜来重新计算每个像素的像素值,掩膜(mask)也被称为内核。
通过掩膜操作实现图像对比度提高。
I(i,j) = 5*I(i,j) - [I(i-1,j) + I(i+1,j) + I(i,j-1) + I(i,j+1)]

Mat kern = (Mat_<char>(3,3) << 0, -1, 0,
                                                   -1, 5, -1,
                                                    0, -1, 0);

红色是中心像素,从上到下,从左到右对每个像素做同样的处理操作,得到最终结果就是对比度提高之后的输出图像垫对象。

实例代码:

#include <opencv2\opencv.hpp>
#include <iostream> #include <math.h> using namespace std;
using namespace cv; int main()
{
Mat src, dst;
src = imread("E:\\VS2015Opencv\\vs2015\\project\\picture\\cat.jpg");
if (!src.data)
{
cout << "could not load image..." << endl;
return -1;
}
namedWindow("source image", CV_WINDOW_AUTOSIZE);
imshow("source image", src); //1).empty() 判断文件读取是否正确
//2).rows 获取图像行数(高度)
//3).cols 获取图像列数(长度)
//4).channels() 获取图像通道数
//5).depth() 获取图像位深度
//【1】记录程序开始点timeStart
double timeStart = (double)getTickCount();//计算时间语句 //进行矩阵的掩膜操作
int cols = (src.cols - 1)*src.channels();//837 //获取图像的列数,一定不要忘记图像的通道数
int offsetx = src.channels();//图像的通道数 3
int rows = src.rows;//220 dst = Mat::zeros(src.size(), src.type());//返回指定的大小和类型的数组 创建一个跟src一样大小 类型的图像矩阵
for (int row = 1; row < (rows - 1); row++)
{
//Mat.ptr<uchar>(int i=0) 获取像素矩阵的指针,索引i表示第几行,从0开始计行数。
//获得当前行指针const uchar* current= myImage.ptr<uchar>(row );
//获取当前像素点P(row, col)的像素值 p(row, col) =current[col]
//Mat.ptr<uchar>(row):获取第row行的图像像素指针。图像的行数从0开始计数
//获取点P(row, col)的像素值:P(row.col) = Mat.ptr<uchar>(row)[col]
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++)
{
//current[col-offsetx]是当前的像素点的左边那个像素点的位置,因为一个像素点有三个通道
//current[col+offsetx]是当前的像素点的右边那个像素点的位置,因为一个像素点有三个通道
//previous[col]表示当前像素点对应的上一行的那个像素点
//next[col]表示当前像素点对应的下一行的那个像素点
output[col] = 5 * current[col] - (current[col - offsetx] + current[col + offsetx] + previous[col] + next[col]);
} } //OpenCV提高了函数filter2D来实现掩膜操作:
//Mat kernel = (Mat_<char>(3, 3) << 0, -1, 0, -1, 5, -1, 0, -1, 0);//定义掩膜
//调用filter2D
//filter2D(src, dst, src.depth(), kernel); double timeconsume = ((double)getTickCount() - timeStart) / getTickFrequency();
cout << "运行上面程序共耗时: " << timeconsume << endl; //输出 掩膜操作后的图像
namedWindow("contrast image", CV_WINDOW_AUTOSIZE);
imshow("contrast image", dst); waitKey(0);
return 0;
}

  

我们可以看见掩膜操作后的图像对比度明显提高了,但是美中不足的是出现了一些不好的小斑点。这是因为这项像素点的值的范围不在0~255之间了。
解决方法:
使用函数saturate_cast(像素值)
这个函数的作用就是确保RGB的值在0~255之间。

像素范围处理saturate_cast <typename _Tp>()

  • saturate_cast <UCHAR>( - 100),返回0
  • saturate_cast <UCHAR>(288),返回255
  • saturate_cast <UCHAR>(100),返回100

这个函数的功能是确保RGB值范围在0〜255之间。

添加上:

#include <opencv2\opencv.hpp>
#include <iostream> #include <math.h> using namespace std;
using namespace cv; int main()
{
Mat src, dst;
src = imread("E:\\VS2015Opencv\\vs2015\\project\\picture\\cat.jpg");
if (!src.data)
{
cout << "could not load image..." << endl;
return -;
}
namedWindow("source image", CV_WINDOW_AUTOSIZE);
imshow("source image", src); //1).empty() 判断文件读取是否正确
//2).rows 获取图像行数(高度)
//3).cols 获取图像列数(长度)
//4).channels() 获取图像通道数
//5).depth() 获取图像位深度
//【1】记录程序开始点timeStart
double timeStart = (double)getTickCount();//计算时间语句 //进行矩阵的掩膜操作
int cols = (src.cols - )*src.channels();//837 //获取图像的列数,一定不要忘记图像的通道数
int offsetx = src.channels();//图像的通道数 3
int rows = src.rows;// dst = Mat::zeros(src.size(), src.type());//返回指定的大小和类型的数组 创建一个跟src一样大小 类型的图像矩阵
for (int row = ; row < (rows - ); row++)
{
//Mat.ptr<uchar>(int i=0) 获取像素矩阵的指针,索引i表示第几行,从0开始计行数。
//获得当前行指针const uchar* current= myImage.ptr<uchar>(row );
//获取当前像素点P(row, col)的像素值 p(row, col) =current[col]
//Mat.ptr<uchar>(row):获取第row行的图像像素指针。图像的行数从0开始计数
//获取点P(row, col)的像素值:P(row.col) = Mat.ptr<uchar>(row)[col]
const uchar *previous = src.ptr<uchar>(row - );//获取上一行指针
const uchar *current = src.ptr<uchar>(row);//获取当前行的指针
const uchar *next = src.ptr<uchar>(row + );//获取下一行的指针
uchar *output = dst.ptr<uchar>(row);//目标对象像素
for (int col = offsetx; col < cols; col++)
{
//像素范围处理saturate_cast<uchar>
output[col] = saturate_cast<uchar>( * current[col] - (current[col - offsetx] + current[col + offsetx] + previous[col] + next[col]));
//current[col-offsetx]是当前的像素点的左边那个像素点的位置,因为一个像素点有三个通道
//current[col+offsetx]是当前的像素点的右边那个像素点的位置,因为一个像素点有三个通道
//previous[col]表示当前像素点对应的上一行的那个像素点
//next[col]表示当前像素点对应的下一行的那个像素点
//output[col] = 5 * current[col] - (current[col - offsetx] + current[col + offsetx] + previous[col] + next[col]);
} } double timeconsume = ((double)getTickCount() - timeStart) / getTickFrequency();
cout << "运行上面程序共耗时: " << timeconsume << endl; //输出 掩膜操作后的图像
namedWindow("contrast image", CV_WINDOW_AUTOSIZE);
imshow("contrast image", dst); waitKey();
return ;
}

函数调用filter2D功能

定义掩膜:Mat kernel = (Mat_(3,3) << 0, -1, 0, -1, 5, -1, 0, -1, 0);
                    filter2D( src, dst, src.depth(), kernel ); 其中src与dst是Mat类型变量、src.depth表示位图深度,有32、24、8等。

#include <opencv2\opencv.hpp>
#include <iostream> #include <math.h> using namespace std;
using namespace cv; int main()
{
Mat src, dst;
src = imread("E:\\VS2015Opencv\\vs2015\\project\\picture\\cat.jpg");
if (!src.data)
{
cout << "could not load image..." << endl;
return -1;
}
namedWindow("source image", CV_WINDOW_AUTOSIZE);
imshow("source image", src); //1).empty() 判断文件读取是否正确
//2).rows 获取图像行数(高度)
//3).cols 获取图像列数(长度)
//4).channels() 获取图像通道数
//5).depth() 获取图像位深度
//【1】记录程序开始点timeStart
double timeStart = (double)getTickCount();//计算时间语句 //进行矩阵的掩膜操作
int cols = (src.cols - 1)*src.channels();//837 //获取图像的列数,一定不要忘记图像的通道数
int offsetx = src.channels();//图像的通道数 3
int rows = src.rows;//220 dst = Mat::zeros(src.size(), src.type());//返回指定的大小和类型的数组 创建一个跟src一样大小 类型的图像矩阵
for (int row = 1; row < (rows - 1); row++)
{
//Mat.ptr<uchar>(int i=0) 获取像素矩阵的指针,索引i表示第几行,从0开始计行数。
//获得当前行指针const uchar* current= myImage.ptr<uchar>(row );
//获取当前像素点P(row, col)的像素值 p(row, col) =current[col]
//Mat.ptr<uchar>(row):获取第row行的图像像素指针。图像的行数从0开始计数
//获取点P(row, col)的像素值:P(row.col) = Mat.ptr<uchar>(row)[col]
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++)
{
//像素范围处理saturate_cast<uchar>
output[col] = saturate_cast<uchar>(5 * current[col] - (current[col - offsetx] + current[col + offsetx] + previous[col] + next[col]));
//current[col-offsetx]是当前的像素点的左边那个像素点的位置,因为一个像素点有三个通道
//current[col+offsetx]是当前的像素点的右边那个像素点的位置,因为一个像素点有三个通道
//previous[col]表示当前像素点对应的上一行的那个像素点
//next[col]表示当前像素点对应的下一行的那个像素点
//output[col] = 5 * current[col] - (current[col - offsetx] + current[col + offsetx] + previous[col] + next[col]);
} } //OpenCV提高了函数filter2D来实现掩膜操作:
Mat kernel = (Mat_<char>(3, 3) << 0, -1, 0, -1, 5, -1, 0, -1, 0);//定义掩膜
//调用filter2D
filter2D(src, dst, src.depth(), kernel); double timeconsume = ((double)getTickCount() - timeStart) / getTickFrequency();
cout << "运行上面程序共耗时: " << timeconsume << endl; //输出 掩膜操作后的图像
namedWindow("contrast image", CV_WINDOW_AUTOSIZE);
imshow("contrast image", dst); waitKey(0);
return 0;
}

  


与前面没有区别;

小结

1.图像中,各种位运算,比如与、或、非运算与普通的位运算类似。 
2.如果用一句话总结,掩膜就是两幅图像之间进行的各种位运算操作。

可以看看下面代码:

#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include <iostream>
#include <stdio.h> using namespace std;
using namespace cv; int main()
{
Mat image, mask;
Rect r1(, , , );
Mat img1, img2, img3, img4;
image = imread("E:\\VS2015Opencv\\vs2015\\project\\picture\\cat.jpg");
mask = Mat::zeros(image.size(), CV_8UC1);
mask(r1).setTo();
img1 = image(r1);
image.copyTo(img2, mask); image.copyTo(img3);
img3.setTo(, mask); imshow("Image sequence", image);
imshow("img1", img1);
imshow("img2", img2);
imshow("img3", img3);
imshow("mask", mask); waitKey();
return ;
}

注意程序中的这两句关于Mask的操作。

    mask = Mat::zeros(image.size(), CV_8UC1);
mask(r1).setTo(255); //r1是已经设置好的兴趣区域

解释一下上面两句的操作。

  • 第一步建立与原图一样大小的mask图像,并将所有像素初始化为0,因此全图成了一张全黑色图。
  • 第二步将mask图中的r1区域的所有像素值设置为255,也就是整个r1区域变成了白色。

这样就能得到Mask图像了。

image.copyTo(img2, mask);

 这句是原始图image拷贝到目的图img2上。

其实拷贝的动作完整版本是这样的:

原图(image)与掩膜(mask)进行与运算后得到了结果图(img2)。

说白了,mask就是位图啊,来选择哪个像素允许拷贝,哪个像素不允许拷贝。如果mask像素的值是非0的,我就拷贝它,否则不拷贝。

因为我们上面得到的mask中,感兴趣的区域是白色的,表明感兴趣区域的像素都是非0,而非感兴趣区域都是黑色,表明那些区域的像素都是0。一旦原图与mask图进行与运算后,得到的结果图只留下原始图感兴趣区域的图像了。也正如下图所示。

如果想要直接抠出目标区域,直接这样写就OK了:

img1 = image(r1);

image.copyTo(img3);
img3.setTo(0, mask);

上面两句代码所做的事情是首先将原始图image拷贝一份给img3,然后img3将mask白色区域设置为0(黑色),好比如果mask中像素非0的,我就把我图像对应的那个点的像素值设置为0,否则啥也不做。伪代码是   if mask(i,j)>0 then img3(i,j)=0。

也就是说mask为1的位置设置为0,如下图

												

掩膜(mask)的更多相关文章

  1. OpenCV探索之路(十三):详解掩膜mask

    在OpenCV中我们经常会遇到一个名字:Mask(掩膜).很多函数都使用到它,那么这个Mask到底什么呢? 一开始我接触到Mask这个东西时,我还真是一头雾水啊,也对无法理解Mask到底有什么用.经过 ...

  2. 详解掩膜mask

    什么是掩膜(mask) 数字图像处理中的掩膜的概念是借鉴于PCB制版的过程,在半导体制造中,许多芯片工艺步骤采用光刻技术,用于这些步骤的图形“底片”称为掩膜(也称作“掩模”),其作用是:在硅片上选定的 ...

  3. 图像中的掩膜(Mask)是什么

    在图像处理中,经常会碰到掩膜(Mask)这个词.那么这个词到底是什么意思呢?下面来简单解释一下. 1.什么是掩膜 首先我们从物理的角度来看看mask到底是什么过程. 在半导体制造中,许多芯片工艺步骤采 ...

  4. [计算机视觉]掩膜(mask)

    转自->这里 刚开始涉及到图像处理的时候,在OpenCV等库中总会看到mask这么一个参数,非常的不理解,在查询一系列资料之后,写下它们,以供翻阅. 什么是掩膜(mask) 数字图像处理中的掩膜 ...

  5. OpenCV计算机视觉学习(2)——图像算术运算 & 掩膜mask操作(数值计算,图像融合,边界填充)

    在OpenCV中我们经常会遇到一个名字:Mask(掩膜).很多函数都使用到它,那么这个Mask到底是什么呢,下面我们从图像基本运算开始,一步一步学习掩膜. 1,图像算术运算 图像的算术运算有很多种,比 ...

  6. OTP语音芯片和掩模语音芯片(mask)的区别

    OTP(One Time Programable)是MCU的一种存储器类型,意思是一次性可编程:程序烧入IC后,将不可再次更改和:因此OTP语音芯片就是指一次性烧录的语音IC. 从OTP定义上来看,只 ...

  7. opencv 掩膜操作 滤波 卷积核定义 增强对比度 掩膜运算

    /* 矩阵的掩膜操作 0 掩膜mask 在这里进行增强对比度: [ [ 0,-1, 0 ], [-1, 5, -1], [ 0,-1, 0 ] ] 使用mask滑动图片每一个位置,进行卷积运算 这里这 ...

  8. PIE SDK创建掩膜

      1.算法功能简介 图像掩膜(Mask)用选定的图像.图形或物体,对处理的图像(全部或局部)进行遮挡,来控制图像处理的区域或处理过程.掩膜是一种图像滤镜的模板,实用掩膜经常处理的是遥感图像.当提取道 ...

  9. GDAL生成Erdas Imagine

    GDAL原生支持超过100种栅格数据类型,涵盖所有主流GIS与RS数据格式,包括•  ArcInfo grids, ArcSDE raster, Imagine, Idrisi, ENVI, GRAS ...

随机推荐

  1. Mybatis和Hibernate面试问题及答案

    1.@Qualifier 注解 答:当有多个相同类型的bean却只有一个需要自动装配时,将@Qualifier 注解和@Autowire 注解结合使用以消除这种混淆,指定需要装配的确切的bean.   ...

  2. 用友UAP NC 单据节点_打开参照字段的问题_从打不开参照放大镜_到成功打开了但是取不到值_到修复成功

    项目的这个功能是17年开发的,但是当时没有测试通过,今年拿出来测试(通过后会上线). 有两个表数据一开始只打算用来计算时查询,没打算放到目标单据中做表体参照字段.后来改细节问题后放到目标单据中做参照字 ...

  3. vim和emacs

    vim和emacs 在编程界一直有两大神器的传说.这两大神器一个是emacs,一个是vim.一个是神的编辑器,一个是编辑器之神. 程序员的圈子里面也一直流传着一个段子,说是世界上的程序员分为三种.使用 ...

  4. openlayers添加弹出框

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  5. [Err] 1248 - Every derived table must have its own alias

    问题描述 [Err] 1248 - Every derived table must have its own alias 问题原因 这句话的意思是说每个派生出来的表都必须有一个自己的别名 我的Mys ...

  6. C语言实现 循环队列

    #include <stdio.h>#include <stdlib.h>#include <stdbool.h> typedef struct queue{ in ...

  7. Ex-KMP(模板)

    首先,明白Ex-KMP是干什么的: 给定两个字符串母串S和子串T(长度分别为n和m),下标从0开始,定义extend[i]等于S[i]-S[n-1]与T的最长公共前缀的长度,求出所有的extend[i ...

  8. k8s获取apiversion下面的对应可用资源

    1- 获取api命令 [注:以下命令的url地址http://127.0.0.1/为k8s master的地址] kubectl api-versions 输出内容如下: apps/v1beta1 a ...

  9. ST表求区间最值

    #include<bits/stdc++.h> #define ll long long #define lowbit(x) x&-x using namespace std; ; ...

  10. [Python]python对csv去除重复行 python 2020.2.11

    用pandas库的.drop_duplicates函数 代码如下: import shutil import pandas as pd frame=pd.read_csv('E:/bdbk.csv', ...