简体   繁体   English

有没有办法在 OpenCV/skimage 中获取浮点坐标的轮廓属性?

[英]Is there a way to get contour properties in OpenCV/skimage for floating point coordinates?

I have contour plots created in Matplotlib, that I need to analyze further to see if they are closed curves, and then look at area, convexity, solidity, etc. for cellular structures.我在 Matplotlib 中创建了等高线图,我需要进一步分析它们是否是闭合曲线,然后查看细胞结构的面积、凸度、坚固度等。 In Matplotlib, they are of type LineCollection and Path .在 Matplotlib 中,它们的类型为LineCollectionPath

In OpenCV, I cannot pass a float array to cv2.contourArea or similar functions.在 OpenCV 中,我无法将float组传递给cv2.contourArea或类似函数。 On the other hand, converting to uint8 coordinates loses important data like nesting structure.另一方面,转换为uint8坐标会丢失嵌套结构等重要数据。 In this case, I need to get to the inner nested convex contours.在这种情况下,我需要到达内部嵌套的凸轮廓。

Are there any options to find information like area, convex hull, bounding rectangle in Python?是否有任何选项可以在 Python 中查找区域、凸包、边界矩形等信息?

I could enlarge the image, but I'm worried it might skew the picture unpredictably.我可以放大图像,但我担心它可能会意外地扭曲图像。

For example: Attached image with floating point and integer coordinates.例如:带有浮点和 integer 坐标的附加图像。

在此处输入图像描述

I assume, you have full control over the Matplotlib part.我假设,您可以完全控制 Matplotlib 部分。 So, let's try to get an image from there, which can you easily use for further image processing with OpenCV.因此,让我们尝试从那里获取图像,您可以轻松地将其用于 OpenCV 的进一步图像处理。

We start with some common contour plot as shown in your question:我们从一些常见的contour plot 开始,如您的问题所示:

共同等高线图

You can set the levels parameter to get a single contour level.您可以设置levels参数以获得单个轮廓级别。 That's helpful to work on several levels individually.这对于单独在多个级别上工作很有帮助。 In the following, I will focus on levels=[1.75] (the most inner green ellipse).下面,我将重点关注levels=[1.75] (最内层的绿色椭圆)。 Later, you can simply loop through all desired levels, and perform your analyses.稍后,您可以简单地遍历所有所需的级别,并执行您的分析。

For our custom contour plot, we will set a fixed x, y domain, for example [-3, 3] x [-2, 2] , using xlim and ylim .对于我们的自定义轮廓 plot,我们将使用xlimylim设置固定的x, y域,例如[-3, 3] x [-2, 2] So, we have known dimensions for the actual canvas.因此,我们已经知道实际 canvas 的尺寸。 We get rid of the axes using axis('off') , and the margins around the canvas using tight_layout(pad=0) .我们使用axis('off')摆脱了轴,使用tight_layout(pad=0)了 canvas 周围的边距。 What's left is the plain canvas in full size (figure size was adjusted to (10, 5) , and colors are automatically adjusted to the number of levels):剩下的是全尺寸的普通 canvas (图形尺寸调整为(10, 5) , colors 自动调整为级别数):

自定义等高线图

Now, we save the canvas to some NumPy array, cf.现在,我们将 canvas 保存到一些 NumPy 阵列,参见。 this Q&A .这个问答 From there, we can perform any OpenCV operation.从那里,我们可以执行任何 OpenCV 操作。 For finding the combined area of this level contours, we might threshold the grayscaled image, find all contours, and calculate their areas using cv2.contourArea .为了找到这个级别轮廓的组合区域,我们可以对灰度图像进行阈值化,找到所有轮廓,并使用cv2.contourArea计算它们的区域。 We sum those areas, and get the whole area in pixels.我们对这些区域求和,并以像素为单位获得整个区域。 From the known canvas dimensions, we know the whole canvas area in "units", and from the image dimensions, we know the whole canvas area in pixels.从已知的 canvas 尺寸,我们知道整个 canvas 区域以“单位”为单位,从图像尺寸中,我们知道整个 canvas 区域以像素为单位。 So, we just need to divide the whole contour area (in pixels) by the whole canvas area (in pixels), and multiply with the whole canvas area (in "units").因此,我们只需要将整个轮廓区域(以像素为单位)除以整个 canvas 区域(以像素为单位),然后乘以整个 canvas 区域(以“单位”为单位)。

That'd be the whole code:这就是整个代码:

import cv2
import matplotlib.pyplot as plt
import numpy as np

# Generate some data for some contour plot
delta = 0.025
x = np.arange(-3.0, 3.0, delta)
y = np.arange(-2.0, 2.0, delta)
X, Y = np.meshgrid(x, y)
Z1 = np.exp(-(X + 1.5)**2 - Y**2)
Z2 = np.exp(-(X - 1.5)**2 - Y**2)
Z = (Z1 + Z2) * 2

# Custom contour plot
x_min, x_max = -3, 3
y_min, y_max = -2, 2
fig = plt.figure(2, figsize=(10, 5))    # Set large figure size
plt.contour(X, Y, Z, levels=[1.75])     # Set single levels if needed
plt.xlim([x_min, x_max])                # Explicitly set x limits
plt.ylim([y_min, y_max])                # Explicitly set y limits
plt.axis('off')                         # No axes shown at all
plt.tight_layout(pad=0)                 # No margins at all

# Get figure's canvas as NumPy array, cf. https://stackoverflow.com/a/7821917/11089932
fig.canvas.draw()
img = np.frombuffer(fig.canvas.tostring_rgb(), dtype=np.uint8)
img = img.reshape(fig.canvas.get_width_height()[::-1] + (3,))

# Grayscale, and threshold image
mask = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
mask = cv2.threshold(mask, 0, 255, cv2.THRESH_OTSU + cv2.THRESH_BINARY_INV)[1]

# Find contours, calculate areas (pixels), sum to get whole area (pixels) for certain level
cnts = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
area = np.sum(np.array([cv2.contourArea(cnt) for cnt in cnts]))

# Whole area (coordinates) from canvas area (pixels), and x_min, x_max, etc.
area = area / np.prod(mask.shape[:2]) * (x_max - x_min) * (y_max - y_min)
print('Area:', area)

The output area seems reasonable: output 区域似乎是合理的:

Area: 0.861408

Now, you're open to do any image processing with OpenCV you like.现在,您可以使用您喜欢的 OpenCV 进行任何图像处理。 Always remember to convert any results in pixels to some result in "units".永远记得将任何以像素为单位的结果转换为以“单位”为单位的某些结果。

----------------------------------------
System information
----------------------------------------
Platform:      Windows-10-10.0.16299-SP0
Python:        3.9.1
PyCharm:       2021.1.1
Matplotlib:    3.4.1
NumPy:         1.20.2
OpenCV:        4.5.1

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM