上一个教程: 使用OpenCV扫描图像、查找表和时间测量
下一个教程: 图像操作
| 原始作者 | Bernát Gábor |
| 兼容性 | OpenCV >= 3.0 |
矩阵上的掩模操作非常简单。其思想是根据掩模矩阵(也称为核)重新计算图像中每个像素的值。该掩模包含一些值,这些值将调整相邻像素(以及当前像素)对新像素值的影响程度。从数学角度来看,我们使用指定的值进行加权平均。
让我们考虑一个图像对比度增强方法的问题。基本上,我们想对图像中的每个像素应用以下公式:
\[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{其中 } M = \bordermatrix{ _i\backslash ^j & -1 & 0 & +1 \cr -1 & 0 & -1 & 0 \cr 0 & -1 & 5 & -1 \cr +1 & 0 & -1 & 0 \cr }\]
第一种表示方法是使用公式,而第二种是通过使用掩模对第一种进行压缩。使用掩模时,您将掩模矩阵的中心(在上述情况下由零零索引表示)放在您要计算的像素上,并将像素值与重叠的矩阵值相乘后求和。它们是相同的,但在大型矩阵的情况下,后一种表示方法更容易查看。
您可以从此处下载此源代码,或者在OpenCV源代码库的示例目录中查看samples/cpp/tutorial_code/core/mat_mask_operations/mat_mask_operations.cpp。
您可以从此处下载此源代码,或者在OpenCV源代码库的示例目录中查看samples/java/tutorial_code/core/mat_mask_operations/MatMaskOperations.java。
您可以从此处下载此源代码,或者在OpenCV源代码库的示例目录中查看samples/python/tutorial_code/core/mat_mask_operations/mat_mask_operations.py。
现在让我们看看如何通过使用基本的像素访问方法或使用filter2D()函数来实现这一点。
这是一个可以实现此功能的函数
首先,我们确保输入图像数据是无符号字符(unsigned char)格式。为此,我们使用CV_Assert函数(宏),当其中的表达式为假时,它会抛出错误。
首先,我们确保输入图像数据是无符号8位格式。
首先,我们确保输入图像数据是无符号8位格式。
我们创建一个与输入图像大小和类型相同的输出图像。如您在存储一节中所见,根据通道数,我们可能有一个或多个子列。
我们将通过指针遍历它们,因此元素的总数取决于此(通道)数量。
我们将使用普通的C语言 `[]` 运算符来访问像素。由于我们需要同时访问多行,因此我们将获取每行的指针(前一行、当前行和下一行)。我们还需要另一个指针来保存计算结果。然后,只需使用 `[]` 运算符访问正确的项。为了将输出指针向前移动,我们只需在每次操作后将其增加(一个字节)。
在图像的边界处,上述表示法会导致不存在的像素位置(例如负一 - 负一)。在这些点,我们的公式是未定义的。一个简单的解决方案是,在这些点不应用核,例如,将边界上的像素设置为零。
我们需要访问多行和多列,这可以通过向当前中心(i,j)添加或减去1来实现。然后我们应用求和并将新值放入Result矩阵中。
在图像的边界处,上述表示法会导致不存在的像素位置(例如(-1,-1))。在这些点,我们的公式是未定义的。一个简单的解决方案是,在这些点不应用核,例如,将边界上的像素设置为零。
我们需要访问多行和多列,这可以通过向当前中心(i,j)添加或减去1来实现。然后我们应用求和并将新值放入Result矩阵中。
这类滤波器在图像处理中非常常见,以至于OpenCV中有一个函数可以处理掩模(在某些地方也称为核)的应用。为此,您首先需要定义一个持有掩模的对象。
然后调用filter2D()函数,指定输入图像、输出图像和要使用的核。
该函数甚至还有一个可选的第五个参数用于指定核的中心,第六个参数用于在将过滤后的像素存储到K中之前向其添加一个可选值,以及第七个参数用于确定在操作未定义的区域(边界)中如何处理。
这个函数更短,更简洁,而且由于存在一些优化,它通常比手动编写的方法更快。例如,在我的测试中,第二个方法只用了13毫秒,而第一个方法用了大约31毫秒。差异相当大。
例如
在我们的YouTube频道上查看运行该程序的一个实例。
