C++之OpenCV入门到提高003:矩阵的掩膜(Mask)处理

一、介绍

今天是这个系列《C++之 Opencv 入门到提高》得第三篇文章。今天这篇文章也不难,主要介绍如何使用 Opencv 对图像进行掩膜处理,提高图像的对比度。在这个过程中,我们可以学到如何获取图像指针、如何处理像素值越界等问题。我们一步一个脚印的走,收获就会越来越多。虽然操作很简单,但是要下功夫理解每个技术点,把基础打扎实,才能让以后得学习过程更顺利一点。OpenCV 具体的简介内容,我就不多说了,网上很多,大家可以自行脑补。

OpenCV 的官网地址:https://opencv.org/,组件下载地址:https://opencv.org/releases/

OpenCV 官网学习网站:https://docs.opencv.ac.cn/4.10.0/index.html

我需要进行说明,以防大家不清楚,具体情况我已经罗列出来。

操作系统:Windows Professional 10(64位)

开发组件:OpenCV -- 4.10.0

开发工具:Microsoft Visual Studio Community 2022 (64 位) - Current版本 17.8.3

开发语言:C++(VC16)

二、实例学习

这一节的内容不多,接口 API 其实也是挺简单的。但是我们学习接口,不能冷冰冰的只是学API、一些基础知识也是要掌握的。

图像中的掩膜(Mask)是什么?在图像处理中,掩膜(Mask)是一种用于控制图像处理区域或处理过程的特殊图像。它通常是一个与原始图像同样大小的二维矩阵,用于选择性地遮盖或显示图像的特定区域。掩膜可以用于多种图像处理任务,如图像分割、特征提取、增强等。

在数字图像处理中,掩膜通常是一个二进制图像,其中像素值为1的区域表示要保留的区域,像素值为0的区域表示要排除的区域。通过将掩膜与原始图像进行逻辑运算,可以创建新的图像,其中只有掩膜中标记为1的区域被保留,其他区域被排除。

掩膜在图像处理中有多种应用。例如,在图像分割中,掩膜可用于选择性地突出显示感兴趣的区域,以便进一步处理或分析。在特征提取中,掩膜可用于提取图像中的特定形状或结构。此外,掩膜还可以用于图像增强,例如通过模糊或锐化特定区域来改善图像质量。

数字图像处理中,掩模为二维矩阵数组,有时也用多值图像,图像掩模主要用于:

①提取感兴趣区,用预先制作的感兴趣区掩模与待处理图像相乘,得到感兴趣区图像,感兴趣区内图像值保持不变,而区外图像值都为0。

②屏蔽作用,用掩模对图像上某些区域作屏蔽,使其不参加处理或不参加处理参数的计算,或仅对屏蔽区作处理或统计。

③结构特征提取,用相似性变量或图像匹配方法检测和提取图像中与掩模相似的结构特征。

④特殊形状图像的制作。

什么是位图与掩膜的与运算?

其实就是原图中的每个像素和掩膜中的每个对应像素进行与运算。比如1 & 1 = 1;1 & 0 = 0;

比如一个3 * 3的图像与3 * 3的掩膜进行运算,得到的结果图像就是:

说白了,我们使用掩膜(mask)位图来选择哪个像素允许拷贝,哪个像素不允许拷贝。如果mask像素的值是非0的,我就拷贝它,也就是保留下,否则不拷贝,不保留。

在所有图像基本运算的操作函数中,凡是带有掩膜(mask)的处理函数,其掩膜都参与运算(输入图像运算完之后再与掩膜图像或矩阵运算)。

复制代码
 1 #include <opencv2/opencv.hpp>
 2 #include <iostream>
 3 #include <math.h>
 4 
 5 using namespace std;
 6 using namespace cv;
 7 
 8 
 9 int main()
10 {
11     Mat src, dst;
12     src = imread("D:\\360MoveData\\Users\\Administrator\\Desktop\\TestImage\\4.jpg", IMREAD_UNCHANGED);
13     if (!src.data)
14     {
15         cout << "图片加载错误!!!" << endl;
16         return -1;
17     }
18 
19     namedWindow("原始图像", WINDOW_AUTOSIZE);
20     imshow("原始图像", src);
21 
22     //1、获取图形像素指针
23     //CV_Assert(myImage.depth()==CV_8U);
24     //Mat.ptr<uchar>(int i=0)获取像素矩阵的指针,索引 i 表示第几行,从0开始计数。
25     //获取当前行指针 const uchar* current=myImage.ptr<uchar>(row);
26     //获取当前像素点 p(row,col) 的像素值 p(row,col)=current[col];
27 
28     //2、像素范围处理 saturate_cast<uchar>
29     //saturate_cast<uchar>(-100),返回 0.
30     //saturate_cast<uchar>(288),返回 255,
31     //saturate_cast<uchar>(100),返回 100.
32     // 这个函数的功能是确保 RGB 值的范围在 0-255 之间。
33 
34     double startDate = getTickCount();
35 
36     //第一种实现对比度
37     /*int cols = (src.cols - 1) * src.channels();
38     int offerts = src.channels();
39     int rows = src.rows;
40 
41     dst = Mat(src.size(), src.type());
42 
43     for (int row = 1; row < (rows - 1); row++)
44     {
45         const uchar* previous = src.ptr<uchar>(row - 1);
46         const uchar* current = src.ptr<uchar>(row);
47         const uchar* next = src.ptr<uchar>(row + 1);
48         uchar* output = dst.ptr<uchar>(row);
49         for (int col = offerts; col < cols; col++)
50         {
51             output[col] = saturate_cast<uchar>(5 * current[col] - (current[col - offerts] + current[col + offerts] + previous[col] + next[col]));
52         }
53     }*/
54 
55     //3、定义掩膜
56     // Mat kernel = (Mat_<char>(3, 3) << 0, -1, 0, -1, 5, -1, 0, -1, 0);
57     //filter2D(src, dst, src.depth(), kernel):src 是原图,dst 是目标图,src.depth() 表示位图深度,有 32,24,8 等。
58 
59     //第二种实现对比度
60     Mat kernel = (Mat_<char>(3, 3) << 0, -1, 0, -1, 5, -1, 0, -1, 0);
61     filter2D(src, dst, src.depth(), kernel);
62 
63     double totalTime = (getTickCount() - startDate) / getTickFrequency();
64 
65     cout << "消费时间:"<< totalTime << endl;
66 
67     namedWindow("对比度图像", WINDOW_AUTOSIZE);
68     imshow("对比度图像", dst);
69 
70 
71     waitKey(0);
72 
73     return 0;
74 }

生成的效果图如下:

当然了,在源码中,提供了两种实现,效果都是一样的。一种是自己实现的,一种是通过调用接口实现的。这也说明了一个问题,只要你掌握的够深入,和接口效果一样的问题,你也可以写得出。

如果我们吧注释的代码打开,把【第二种实现】注释掉,源码:

复制代码
 1 int cols = (src.cols - 1) * src.channels();
 2 int offerts = src.channels();
 3 int rows = src.rows;
 4 
 5 dst = Mat(src.size(), src.type());
 6 
 7 for (int row = 1; row < (rows - 1); row++)
 8 {
 9     const uchar* previous = src.ptr<uchar>(row - 1);
10     const uchar* current = src.ptr<uchar>(row);
11     const uchar* next = src.ptr<uchar>(row + 1);
12     uchar* output = dst.ptr<uchar>(row);
13     for (int col = offerts; col < cols; col++)
14     {
15         output[col] = saturate_cast<uchar>(5 * current[col] - (current[col - offerts] + current[col + offerts] + previous[col] + next[col]));
16     }
17 }

而且,自己写的性能更好。对比如图:

再看看我们自己写的运行时间:

我们自己写实现的效果:

三、总结

这是 C++ 使用 OpenCV 的第三篇文章,概念挺难懂的,其实操作起来也没那么难,当然了,这只是我们入门的开始,那就继续吧。皇天不负有心人,不忘初心,继续努力,做自己喜欢做的,开心就好。

相关推荐
捕鲸叉3 小时前
创建线程时传递参数给线程
开发语言·c++·算法
A charmer3 小时前
【C++】vector 类深度解析:探索动态数组的奥秘
开发语言·c++·算法
Peter_chq3 小时前
【操作系统】基于环形队列的生产消费模型
linux·c语言·开发语言·c++·后端
青花瓷5 小时前
C++__XCode工程中Debug版本库向Release版本库的切换
c++·xcode
幺零九零零6 小时前
【C++】socket套接字编程
linux·服务器·网络·c++
捕鲸叉6 小时前
MVC(Model-View-Controller)模式概述
开发语言·c++·设计模式
ctrey_7 小时前
2024-11-1 学习人工智能的Day20 openCV(2)
人工智能·opencv·学习
Dola_Pan7 小时前
C++算法和竞赛:哈希算法、动态规划DP算法、贪心算法、博弈算法
c++·算法·哈希算法
yanlou2337 小时前
KMP算法,next数组详解(c++)
开发语言·c++·kmp算法
小林熬夜学编程7 小时前
【Linux系统编程】第四十一弹---线程深度解析:从地址空间到多线程实践
linux·c语言·开发语言·c++·算法