OpenCV学习笔记:矩阵的掩码操作


矩阵的掩码操作很简单。其思想是:根据掩码矩阵(也称作核)重新计算图像中每个像素的值。掩码矩阵中的值表示近邻像素值(包括该像素自身的值)对新像素值有多大影响。从数学观点看,我们用自己设置的权值,对像素邻域内的值做了个加权平均。

 测试用例

思考一下图像对比度增强的问题。我们可以对图像的每个像素应用下面的公式:

I(i,j) = 5*I(i,j) - [ I(i-1,j) + I(i+1,j) + I(i,j-1) + I(i,j+1)]\iff I(i,j)*M, \text{where }M = \bordermatrix{ _i\backslash ^j  & -1 &  0 & +1 \cr                     -1 &  0 & -1 &  0 \cr                      0 & -1 &  5 & -1 \cr                     +1 &  0 & -1 &  0 \cr                 }

上面那种表达法是公式的形式,而下面那种是以掩码矩阵表示的紧凑形式。使用掩码矩阵的时候,我们先把矩阵中心的元素(上面的例子中是(0,0)位置的元素,也就是5)对齐到要计算的目标像素上,再把邻域像素值和相应的矩阵元素值的乘积加起来。虽然这两种形式是完全等价的,但在大矩阵情况下,下面的形式看起来会清楚得多。

现在,我们来看看实现掩码操作的两种方法。一种方法是用基本的像素访问方法,另一种方法是用 filter2D 函数。

 基本方法

下面是实现了上述功能的函数:

void Sharpen(const Mat& myImage,Mat& Result)
{CV_Assert(myImage.depth() == CV_8U);  // 仅接受uchar图像Result.create(myImage.size(),myImage.type());const int nChannels = myImage.channels();for(int j = 1 ; j < myImage.rows-1; ++j){const uchar* previous = myImage.ptr<uchar>(j - 1);const uchar* current  = myImage.ptr<uchar>(j    );const uchar* next     = myImage.ptr<uchar>(j + 1);uchar* output = Result.ptr<uchar>(j);for(int i= nChannels;i < nChannels*(myImage.cols-1); ++i){*output++ = saturate_cast<uchar>(5*current[i]-current[i-nChannels] - current[i+nChannels] - previous[i] - next[i]);}}Result.row(0).setTo(Scalar(0));Result.row(Result.rows-1).setTo(Scalar(0));Result.col(0).setTo(Scalar(0));Result.col(Result.cols-1).setTo(Scalar(0));
}

刚进入函数的时候,我们要确保输入图像是无符号字符类型的。为了做到这点,我们使用了 CV_Assert 函数。若该函数括号内的表达式为false,则会抛出一个错误。

CV_Assert(myImage.depth() == CV_8U);  // 仅接受uchar图像

然后,我们创建了一个与输入有着相同大小和类型的输出图像。在 图像矩阵是如何存储在内存之中的? 一节可以看到,根据图像的通道数,我们有一个或多个子列。我们用指针在每一个通道上迭代,因此通道数就决定了需计算的元素总数。

Result.create(myImage.size(),myImage.type());
const int nChannels = myImage.channels();

利用C语言的[]操作符,我们能简单明了地访问像素。因为要同时访问多行像素,所以我们获取了其中每一行像素的指针(分别是前一行、当前行和下一行)。此外,我们还需要一个指向计算结果存储位置的指针。有了这些指针后,我们使用[]操作符,就能轻松访问到目标元素。为了让输出指针向前移动,我们在每一次操作之后对输出指针进行了递增(移动一个字节):

for(int j = 1 ; j < myImage.rows-1; ++j)
{const uchar* previous = myImage.ptr<uchar>(j - 1);const uchar* current  = myImage.ptr<uchar>(j    );const uchar* next     = myImage.ptr<uchar>(j + 1);uchar* output = Result.ptr<uchar>(j);for(int i= nChannels;i < nChannels*(myImage.cols-1); ++i){*output++ = saturate_cast<uchar>(5*current[i]-current[i-nChannels] - current[i+nChannels] - previous[i] - next[i]);}
}

在图像的边界上,上面给出的公式会访问不存在的像素位置(比如(0,-1))。因此我们的公式对边界点来说是未定义的。一种简单的解决方法,是不对这些边界点使用掩码,而直接把它们设为0:

Result.row(0).setTo(Scalar(0));             // 上边界
Result.row(Result.rows-1).setTo(Scalar(0)); // 下边界
Result.col(0).setTo(Scalar(0));             // 左边界
Result.col(Result.cols-1).setTo(Scalar(0)); // 右边界

 filter2D函数

滤波器在图像处理中的应用太广泛了,因此OpenCV也有个用到了滤波器掩码(某些场合也称作核)的函数。不过想使用这个函数,你必须先定义一个表示掩码的 Mat 对象:

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

然后调用 filter2D 函数,参数包括输入、输出图像以及用到的核:

filter2D(I, K, I.depth(), kern );

它还带有第五个可选参数——指定核的中心,和第六个可选参数——指定函数在未定义区域(边界)的行为。使用该函数有一些优点,如代码更加清晰简洁、通常比 自己实现的方法 速度更快(因为有一些专门针对它实现的优化技术)等等。例如,我测试的滤波器方法仅花了13毫秒,而前面那样自己实现迭代方法花了约31毫秒,二者有着不小差距。

示例:

A sample output of the program

你可以从 here 下载这个示例的源代码,也可浏览OpenCV源代码库的示例目录samples/cpp/tutorial_code/core/mat_mask_operations/mat_mask_operations.cpp 

转载于:https://www.cnblogs.com/james1207/p/3266586.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/424218.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

spring mvc学习(15)Referenced file contains errors

SSM项目中出现Referenced file contains errors (http://www.springframework.org/schema/mvc/spring-mvc-3.0.xs 2019-01-20 22:37:06 薛定谔小猫Historia 阅读数 468更多 分类专栏&#xff1a; java及其框架学习 版权声明&#xff1a;本文为博主原创文章&#xff0c;遵循…

spring mvc学习(16)Could not publish server configuration for Tomcat v8.0 Server at localhost.

这个问题本质是我们有多个重名项目&#xff0c;为什么我们会有多个重名项目&#xff0c;其实一般都是我们删除以前的项目&#xff0c;然后再把它重新导进eclipse时以前的项目删除不彻底造成的&#xff0c;以前的项目在"Servers"里面的"server.xml"文件下的…

产品管理:启示录 - 特约客户、产品验证、原型测试

• 如何挑选有潜力的产品&#xff1f; • 如何证明产品设计符合用户需求&#xff1f; • 如何确认产品设计满足三个基本条件&#xff1f; • 如何运用敏捷方法管理产品&#xff1f; • …… 这是《启示录》上说的一些话&#xff0c;去年看了一下&#xff0c;和团队分享了其中几…

37. Sudoku Solver

文章目录1 题目理解2 回溯1 题目理解 Write a program to solve a Sudoku puzzle by filling the empty cells. A sudoku solution must satisfy all of the following rules: Each of the digits 1-9 must occur exactly once in each row. Each of the digits 1-9 must oc…

spring mvc学习(17)Intellij IDEA创建maven项目无java文件问题

1.解决之前项目结构如下&#xff1a; 2.选择File->Project Structure... 3.选择Modules选项卡下面的Sources项&#xff0c;在main文件夹上右键&#xff0c;选择New Folder...并点击OK   4.输入要创建的文件夹名称java&#xff0c;并点击OK继续 5.在创建好的java文件夹上右…

51. N-Queens

文章目录1 题目理解2 回溯2.1 直观解法2.2 按行遍历1 题目理解 The n-queens puzzle is the problem of placing n queens on an n x n chessboard such that no two queens attack each other. Given an integer n, return all distinct solutions to the n-queens puzzle. …

第一百零一期:如何处理ASP .NET Core中的cookie?

本文介绍了ASP.NET Core Web应用程序中利用cookie存储和检索针对特定用户的信息。 作者&#xff1a;布加迪编译 【51CTO.com快译】cookie是通常用于存储有关用户信息的一条数据&#xff0c;存储在用户的计算机上。在大多数浏览器中&#xff0c;每个cookie都存储为一个小文件&a…

第一百零二期:5G套餐到底该不该换?看完你就明白啦!

日前&#xff0c;三大运营商正式宣布5G商用&#xff0c;并公布了各自的5G资费套餐。毫无意外&#xff0c;正式公布出来的套餐内容&#xff0c;和之前运营商内部泄露的内容完全一致。 作者&#xff1a;小枣君 日前&#xff0c;三大运营商正式宣布5G商用&#xff0c;并公布了各自…

79. Word Search

文章目录1题目理解2 回溯3 212 word search II1题目理解 Given an m x n board and a word, find if the word exists in the grid. The word can be constructed from letters of sequentially adjacent cells, where “adjacent” cells are horizontally or vertically ne…

127. Word Ladder

文章目录1 题目理解2 BFS3 双向BFS1 题目理解 给定两个单词&#xff08;beginWord 和 endWord&#xff09;和一个字典&#xff0c;找到从 beginWord 到 endWord 的最短转换序列的长度。转换需遵循如下规则&#xff1a; 每次转换只能改变一个字母。 转换过程中的中间单词必须是…

第一百零三期:解读回归测试:类型、选择、挑战和实践

本文介绍了回归测试的基本概念、工作方式、面临的挑战、以及业界的优秀实践。 作者&#xff1a;陈峻编译 【51CTO.com快译】有研究表明&#xff1a;在安装了新的应用程序之后&#xff0c;只有四分之一的用户会在次日回到该应用。而大多数用户在首次使用之后就直接将其卸载掉了…

如何构建积木式Web应用

上下文基本上我们在儿童时代都玩过积木玩具。通过一块块的积木&#xff0c;再加上我们的想象力&#xff0c;就可以构造出非常多不同的风格的建筑。那么&#xff0c; 我们可不可以把这种搭积木的方式应用到我们的web应用上呢。问题web应用通过提供给用户一整套组件&#xff08;相…

第一百零四期:搞清这些陷阱,NULL和三值逻辑再也不作妖

NULL 用于表示缺失的值或遗漏的未知数据&#xff0c;不是某种具体类型的值。数据表中的 NULL 值表示该值所处的字段为空&#xff0c;值为 NULL 的字段没有值&#xff0c;尤其要明白的是&#xff1a;NULL 值与 0 或者空字符串是不同的。 作者&#xff1a;youzhibing2904 NULL …

spring mvc学习(19):cookievalue注解(显示cookie的值,默认必须有值)

目录结构 web.xml <?xml version"1.0" encoding"UTF-8"?> <web-app xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance" xmlns"http://java.sun.com/xml/ns/javaee" xsi:schemaLocation"http://java.sun.com/xml…

spring mvc学习(20):RequestHeader(获取请求头中某一部分值)

目录结构 web.xml <?xml version"1.0" encoding"UTF-8"?> <web-app xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance" xmlns"http://java.sun.com/xml/ns/javaee" xsi:schemaLocation"http://java.sun.com/xml…

spring mvc学习(21):testparam请求参数和请求头表达式

目录结构 web.xml <?xml version"1.0" encoding"UTF-8"?> <web-app xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance" xmlns"http://java.sun.com/xml/ns/javaee" xsi:schemaLocation"http://java.sun.com/xml…

spring mvc学习(22):/textpath/*/helen

目录结构 web.xml <?xml version"1.0" encoding"UTF-8"?> <web-app xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance" xmlns"http://java.sun.com/xml/ns/javaee" xsi:schemaLocation"http://java.sun.com/xml…

第一百零五期:5年前,跳槽涨薪,你笑了,5年后,跳槽降薪,你慌了!

去年&#xff0c;我在年度绩效面谈中与某中年技术男就 “从测试转向产品经理” 的这个话题上进行了一些探讨与分析。 作者&#xff1a;王晔倞 图片来自 Pexels 或许是因为分析的角度比较客观、真实&#xff0c;再加上俩人都比较会演戏&#xff0c;我激情&#xff0c;他投入&a…

第一百零六期:长相不讨AI喜欢面试就会挂?全球百万求职者经历AI“看脸”面试

AI不仅会筛选你的简历&#xff0c;还会通过看脸决定你能否通过面试。这不是将来时。全球已有超过一百万求职者&#xff0c;经历过AI面试官的冷酷“凝视”。 作者&#xff1a;鱼羊 本文经AI新媒体量子位&#xff08;公众号ID:QbitAI&#xff09;授权转载&#xff0c;转载请联系…

服务器控件HtmlTable下控件赋值问题

在程序开发过程中&#xff0c;碰到这样的问题&#xff1a; 1<table>2<tr>3<td><asp:DropDownList iddropdownlist1 runatserver></td>4</tr>5</table>在基类页中有如下代码&#xff1a;1foreach(Control pagectl inPage.Controls[1]…