简体   繁体   中英

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. In Matplotlib, they are of type LineCollection and Path .

In OpenCV, I cannot pass a float array to cv2.contourArea or similar functions. On the other hand, converting to uint8 coordinates loses important data like nesting structure. 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?

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.

在此处输入图像描述

I assume, you have full control over the Matplotlib part. So, let's try to get an image from there, which can you easily use for further image processing with OpenCV.

We start with some common contour plot as shown in your question:

共同等高线图

You can set the levels parameter to get a single contour level. That's helpful to work on several levels individually. In the following, I will focus on levels=[1.75] (the most inner green ellipse). 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 . So, we have known dimensions for the actual canvas. We get rid of the axes using axis('off') , and the margins around the canvas using tight_layout(pad=0) . 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):

自定义等高线图

Now, we save the canvas to some NumPy array, cf. this Q&A . From there, we can perform any OpenCV operation. 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 . 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. 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").

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:

Area: 0.861408

Now, you're open to do any image processing with OpenCV you like. 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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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