形态变换
<h1>目标</h1>
<p>在本文中,</p>
<ul>
<li>我们将学习不同的形态操作,如侵蚀,膨胀,打开,关闭等。</li>
<li>我们将看到不同的函数,如: cv.erode(), cv.dilate(), cv.morphologyEx()等。</li>
</ul>
<h1>原理</h1>
<p>形态学变换是一些基于图像形状的简单操作。它通常在二进制图像上执行。它需要两个输入,一个是原始图像,另一个是决定操作性质的结构化元素或内核。侵蚀和膨胀是两种基本的形态操作符。然后它的变体形式,如打开,关闭,梯度等也发挥作用。我们将看到他们一个一个的帮助下,如下图:</p>
<p><img src="https://www.showdoc.cc/server/api/common/visitfile/sign/1f387536c53ca41879c932971d6060ed?showdoc=.jpg" alt="" /></p>
<h3>1.侵蚀</h3>
<p>侵蚀的基本思想就像土壤侵蚀一样,它侵蚀前景对象的边界(总是尽量保持前景为白色)。那么它是做什么的呢?内核在图像中滑动(就像在2D卷积中一样)。只有当内核下的所有像素都是1时,原始图像中的像素(1或0)才会被认为是1,否则就会被侵蚀(变为0)。</p>
<p>结果是,所有边界附近的像素都会被丢弃,这取决于内核的大小。因此,前景对象的厚度或大小减小,或只是图像中的白色区域减小。它有助于去除小的白色噪声(正如我们在色彩空间一章中看到的),分离两个连接的物体等。</p>
<p>在这里,作为一个例子,我将使用一个包含所有1的5x5内核。让我们看看它是如何工作的:</p>
<pre><code class="language-python">import cv2 as cv
import numpy as np
img = cv.imread('j.png',0)
kernel = np.ones((5,5),np.uint8)
erosion = cv.erode(img,kernel,iterations = 1)</code></pre>
<p>结果:
<img src="https://www.showdoc.cc/server/api/common/visitfile/sign/93390ecadc342a361a9a473a879ea60a?showdoc=.jpg" alt="" /></p>
<h3>2. 膨胀</h3>
<p>它正好与侵蚀相反。这里,如果内核下至少有一个像素为1,则像素元素为"1"。所以它增加了图像中的白色区域或前景对象的大小增加。通常,在去除噪音等情况下,侵蚀往往伴随着膨胀。因为,侵蚀消除了白噪音,但也缩小了我们的物体。我们把它放大。因为噪声消失了,它们就不会回来了,但是我们的物体面积增加了。它在连接对象的破碎部分时也很有用。</p>
<pre><code class="language-python">dilation = cv.dilate(img,kernel,iterations = 1)</code></pre>
<h3>3. 打开</h3>
<p>打开只是侵蚀的另一个名称,其次是膨胀。如上所述,它在消除噪声方面很有用。这里我们使用这个函数,cv.morphologyEx()</p>
<pre><code class="language-python">opening = cv.morphologyEx(img, cv.MORPH_OPEN, kernel)</code></pre>
<p>结果:
<img src="https://www.showdoc.cc/server/api/common/visitfile/sign/e7fd1f5a11c89a8b40d65a37ef549f57?showdoc=.jpg" alt="" /></p>
<h3>4. 闭合</h3>
<p>闭合与打开相反,膨胀伴随着侵蚀。它在关闭前景对象内部的小洞或对象上的小黑点时非常有用。</p>
<pre><code class="language-python">closing = cv.morphologyEx(img, cv.MORPH_CLOSE, kernel)</code></pre>
<p>结果:
<img src="https://www.showdoc.cc/server/api/common/visitfile/sign/34c1cdaaa779e26fd79d0f5a9a8129ff?showdoc=.jpg" alt="" /></p>
<h3>5. 形态梯度</h3>
<p>它是图像膨胀和侵蚀的区别。</p>
<p>结果看起来像是对象的轮廓。</p>
<pre><code class="language-python">gradient = cv.morphologyEx(img, cv.MORPH_GRADIENT, kernel)</code></pre>
<p>结果:
<img src="https://www.showdoc.cc/server/api/common/visitfile/sign/abb03cd570ec8a07508f4a9bb3e3f87e?showdoc=.jpg" alt="" /></p>
<h3>6. 顶帽</h3>
<p>它是输入图像和打开图像之间的区别。下面的示例是针对9x9内核完成的。</p>
<pre><code class="language-python">tophat = cv.morphologyEx(img, cv.MORPH_TOPHAT, kernel)</code></pre>
<p>结果:
<img src="https://www.showdoc.cc/server/api/common/visitfile/sign/483b31eed255ce87ab4d87437ba1c5ec?showdoc=.jpg" alt="" /></p>
<h3>7. 黑帽</h3>
<p>它是输入图像与输入图像的闭合差。</p>
<pre><code class="language-python">blackhat = cv.morphologyEx(img, cv.MORPH_BLACKHAT, kernel)</code></pre>
<p>结果:
<img src="https://www.showdoc.cc/server/api/common/visitfile/sign/5e21012841d35839d26dcf0aef3bcf62?showdoc=.jpg" alt="" /></p>
<h1>结构化元素</h1>
<p>在前面的示例中,我们在Numpy的帮助下手动创建了一个结构化元素。它是长方形的。但在某些情况下,您可能需要椭圆/圆形内核。为此,OpenCV有一个函数cv.getstructuringelement()。只需传递内核的形状和大小,就可以得到所需的内核。</p>
<pre><code class="language-python"># Rectangular Kernel
>>> cv.getStructuringElement(cv.MORPH_RECT,(5,5))
array([[1, 1, 1, 1, 1],
[1, 1, 1, 1, 1],
[1, 1, 1, 1, 1],
[1, 1, 1, 1, 1],
[1, 1, 1, 1, 1]], dtype=uint8)
# Elliptical Kernel
>>> cv.getStructuringElement(cv.MORPH_ELLIPSE,(5,5))
array([[0, 0, 1, 0, 0],
[1, 1, 1, 1, 1],
[1, 1, 1, 1, 1],
[1, 1, 1, 1, 1],
[0, 0, 1, 0, 0]], dtype=uint8)
# Cross-shaped Kernel
>>> cv.getStructuringElement(cv.MORPH_CROSS,(5,5))
array([[0, 0, 1, 0, 0],
[0, 0, 1, 0, 0],
[1, 1, 1, 1, 1],
[0, 0, 1, 0, 0],
[0, 0, 1, 0, 0]], dtype=uint8)</code></pre>
<h1>其他资源</h1>
<p><a href="http://homepages.inf.ed.ac.uk/rbf/HIPR2/morops.htm" title="HIPR2的形态学操作">HIPR2的形态学操作</a></p>