掩膜(mask)

掩膜(mask)

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 -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]);
        }

    }

    double timeconsume = ((double)getTickCount() - timeStart) / getTickFrequency();
    cout << "运行上面程序共耗时: " << timeconsume << endl;

    //输出 掩膜操作后的图像
    namedWindow("contrast image", CV_WINDOW_AUTOSIZE);
    imshow("contrast image", dst);

    waitKey(0);
    return 0;
}
 

 

 函数调用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(100, 100, 250, 300);
    Mat img1, img2, img3, img4;
    image = imread("E:\\VS2015Opencv\\vs2015\\project\\picture\\cat.jpg");
    mask = Mat::zeros(image.size(), CV_8UC1);
    mask(r1).setTo(255);
    img1 = image(r1);
    image.copyTo(img2, mask);

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


    imshow("Image sequence", image);
    imshow("img1", img1);
    imshow("img2", img2);
    imshow("img3", img3);
    imshow("mask", mask);

    waitKey();
    return 0;
}

注意程序中的这两句关于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,如下图

  • 44
    点赞
  • 234
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
GDAL 是一种强大的地理数据处理库,它支持多种数据格式和操作。而掩膜mask)是指在地理数据处理中,通过制定一个范围或区域,将不需要的部分屏蔽掉或剔除掉的一种操作。 在使用 GDAL 对 Shapefile 文件(shp)进行操作时,我们可以使用掩膜来选择性地提取或排除特定区域的数据。具体步骤如下: 1. 导入 GDAL 库并打开 Shapefile 文件:首先,我们需要引入 GDAL 库,并使用 GDAL 打开 Shapefile 文件,这可以通过调用 `gdal.Open()` 函数来实现。 2. 创建掩膜:接下来,我们可以使用 GDAL 的矢量处理功能,如 `gdal.VectorTranslate()` 函数来创建掩膜对象。我们可以通过指定一个范围或区域的边界来创建掩膜。 3. 应用掩膜:使用 `gdal.Rasterize()` 函数,我们可以将创建的掩膜应用到 Shapefile 文件上。这将在数据上生成一个新的栅格,其中只有在掩膜范围内的像元会被保留下来,而超出范围的像元则会被剔除。 4. 保存掩膜结果:最后,我们可以使用 GDAL 将生成的新栅格数据保存到磁盘上,并使用 `gdal.Warp()` 函数将其转换为其他格式,如 GeoTIFF 或 JPEG 等。 通过以上步骤,我们可以使用 GDAL 对 Shapefile 文件进行掩膜操作。这对于提取特定区域的地理数据或从数据中排除不需要的部分非常有用。同时,GDAL 还提供了许多其他数据处理功能,如投影变换、重采样、合并等,可以帮助我们更好地处理和分析地理数据。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值