Matplotlib Python:通过轮廓偏移/收缩多边形分离圆形颗粒
在科学研究、地质勘探、图像识别等领域,我们经常需要将圆形颗粒从一幅图像中分离出来。除了通过标准偏差确定颗粒大小外,一种常见的方法是使用轮廓偏移/收缩多边形算法。
阅读更多:Matplotlib 教程
轮廓偏移算法
轮廓偏移算法是一种常见的图形变换技术,它可以通过改变多边形的内外轮廓几何形状来生成新形状。当应用于分离圆形颗粒时,该算法可以将圆形物体转换为多边形。具体方法如下:
- 从原始图像中提取圆形颗粒的二值图像。
- 使用cv2.findContours函数找到该二值图像中的边缘(轮廓)。
- 对于每个找到的轮廓,使用cv2.approxPolyDP函数得到一个近似的多边形轮廓。
- 对于每个生成的多边形,将其用一个相对较小的偏移量沿内部轮廓收缩,这样就可以将多边形从圆形颗粒中分离出来。
下面是一个示例代码:
import cv2
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches
# 生成原始图像
c1 = mpatches.Circle((50, 50), 30, fill=True, edgecolor='black', lw=2)
c2 = mpatches.Circle((80, 80), 20, fill=True, edgecolor='black', lw=2)
c3 = mpatches.Circle((120, 80), 15, fill=True, edgecolor='black', lw=2)
c4 = mpatches.Circle((160, 50), 25, fill=True, edgecolor='black', lw=2)
fig, ax = plt.subplots()
ax.add_patch(c1)
ax.add_patch(c2)
ax.add_patch(c3)
ax.add_patch(c4)
ax.set_xlim([0, 200])
ax.set_ylim([0, 100])
# 提取二值图像
binary = np.zeros((100, 200))
for i in range(100):
for j in range(200):
if c1.contains_point((j, i)) or c2.contains_point((j, i)) or c3.contains_point((j, i)) or c4.contains_point((j, i)):
binary[i, j] = 1
# 找到所有轮廓
contours, hierarchy = cv2.findContours(np.uint8(binary), cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
# 绘制轮廓
new_contours = []
for contour in contours:
if cv2.contourArea(contour) > 100: # 只绘制较大的轮廓
ax.plot(contour[:, :, 0], contour[:, :, 1], '-', linewidth=2)
new_contours.append(cv2.approxPolyDP(contour, 5, True))
# 收缩多边形并绘制结果
for i, new_contour in enumerate(new_contours):
ax.plot(new_contour[:, :, 0], new_contour[:, :, 1], '-', linewidth=6, color='yellow')
plt.show()
该代码生成一个原始图像,然后将二值图像转换为轮廓并绘制结果
多边形收缩算法
另一种常见的圆形颗粒分离算法是多边形收缩算法。其思路是通过向多边形内部偏移边缘,生成一个内部空心区域,这个空心区域可以将圆形颗粒从图像中分隔出来。具体方法如下:
- 从原始图像中提取圆形颗粒的二值图像。
- 使用cv2.findContours函数找到该二值图像中的轮廓。
- 对于每个找到的轮廓,使用cv2.approxPolyDP函数得到一个近似的多边形轮廓。
- 对于每个生成的多边形,使用cv2.SpatialTransformers.PolynomialWarp函数向内部偏移多边形轮廓,生成内部空心区域。
下面是一个示例代码:
import cv2
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches
# 生成原始图像
c1 = mpatches.Circle((50, 50), 30, fill=True, edgecolor='black', lw=2)
c2 = mpatches.Circle((80, 80), 20, fill=True, edgecolor='black', lw=2)
c3 = mpatches.Circle((120, 80), 15, fill=True, edgecolor='black', lw=2)
c4 = mpatches.Circle((160, 50), 25, fill=True, edgecolor='black', lw=2)
fig, ax = plt.subplots()
ax.add_patch(c1)
ax.add_patch(c2)
ax.add_patch(c3)
ax.add_patch(c4)
ax.set_xlim([0, 200])
ax.set_ylim([0, 100])
# 提取二值图像
binary = np.zeros((100, 200))
for i in range(100):
for j in range(200):
if c1.contains_point((j, i)) or c2.contains_point((j, i)) or c3.contains_point((j, i)) or c4.contains_point((j, i)):
binary[i, j] = 1
# 找到所有轮廓
contours, hierarchy = cv2.findContours(np.uint8(binary), cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
# 绘制轮廓
new_contours = []
for contour in contours:
if cv2.contourArea(contour) > 100: # 只绘制较大的轮廓
ax.plot(contour[:, :, 0], contour[:, :, 1], '-', linewidth=2)
new_contours.append(cv2.approxPolyDP(contour, 5, True))
# 沿着多边形内部收缩并绘制结果
for i, new_contour in enumerate(new_contours):
win_size = new_contour.shape[0] // 4
if win_size <= 1:
win_size = 2
order = 2
poly_coeffs = cv2.fitPoly(new_contour, order, cv2.DIST_L2, 0.0, win_size)
new_contour = cv2.SpatialTransformers.PolynomialWarp(poly_coeffs).apply(new_contour.astype(np.uint8)[None, ...])[0].astype(np.float32)
ax.plot(new_contour[:, 0], new_contour[:, 1], '-', linewidth=6, color='salmon')
plt.show()
该代码使用类似上面的方法,生成一个原始图像,并从中提取二值图像的轮廓,进而利用cv2.SpatialTransformers.PolynomialWarp函数实现多边形的向内收缩,并绘制结果
总结
在本文中,我们介绍了通过轮廓偏移/收缩多边形分离圆形颗粒的常用方法。这些方法需要将原始图像转换为二值图像,然后利用cv2.findContours函数找到轮廓,再利用cv2.approxPolyDP找到近似的多边形轮廓,最后分别应用多边形偏移/收缩算法,将圆形颗粒从图像中分离出来。这些算法在科学研究、地质勘探、图像识别等领域都得到了广泛应用,有效提高了图像分析与处理的效率。
需要注意的是,在实际应用中可能会涉及到一些边缘情况,比如颗粒重叠等。为了有效解决这些问题,需要对算法进行一定的修改与优化。例如,在圆形颗粒的尺寸相近但有重叠情况下,可以通过多种算法组合,或者使用改进的聚类算法来分离圆形颗粒。
总之,通过轮廓偏移/收缩多边形算法分离圆形颗粒在图像处理与分析中具有良好的应用前景,值得进一步深入研究和探讨。
极客笔记