将图像中的椭圆转换为圆形(将椭圆变形为圆形,就像将多边形变形为矩形一样)

时间:2019-06-19 08:39:27

标签: python opencv image-processing computer-vision

我有一个椭圆的图像,如果图像有椭圆,我正在使用findcontours()找到它,然后我想将这个椭圆转换成一个圆。

查看示例

enter image description here

enter image description here

我想将它们每个变换成这样

enter image description here

首先,我应用了canny边缘检测。然后在此图像上应用了findcontour()。

我已经使用findcontours()找到了椭圆,以获取所有轮廓并获取所需的椭圆轮廓,然后我使用fitellipse()来获取椭圆的中心,旋转角度以及长短轴。

然后我尝试将图像旋转一个旋转的角度,然后缩放图像的高度和宽度,使短轴和长轴(即,使长轴和短轴长度相同),但是我没有得到合适的圆形物体图像如上。将会有一些旋转/仍然会像一个椭圆,接近圆形。

     _, contours, _ = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)

    for c in contours:
        if len(c) >= 5:
            a = cv2.fitEllipse(c)
            (x, y), (MA, ma), angle = a
            area = (math.pi * MA * ma)/4
            if abs(x-image.shape[0]/2) <= 2 and abs(y-image.shape[1]/2) <= 2 and (area - cv2.contourArea(c)) < 50:
                screenCount = c
                width, height = MA, ma
                centerX, centerY = x, y
                ellipseAngle = angle
                print(width, height, centerX, centerY, ellipseAngle)
                # cv2.drawContours(img, c, -1, (0, 255, 0), 4)
                cv2.ellipse(img, a, (0, 0, 255), 2, 8)
                cv2.imshow("ellipse", img)
                break

    img = image.copy()
    if ellipseAngle < 90:
        rotatedImg = imutils.rotate(img, ellipseAngle)
    else:
        rotatedImg = imutils.rotate(img, -(ellipseAngle - 90))

然后我按长轴和短轴进行缩放

应用findcontour()后,我在帖子中得到了第一张图片的这两个轮廓

enter image description here enter image description here

从这些

任何轮廓都可以吧?我根据代码使用了countour的第一个轮廓,而fitellipse()给了我这个椭圆

enter image description here

已编辑-如果有更好的方法来解决此问题,将很有帮助。

1 个答案:

答案 0 :(得分:0)

我可以在代码中看到几个问题:

  1. 您正在使用边缘检测算法,并获得结果的轮廓。原则上可以,但是会导致图像具有两个轮廓:一个用于边缘检测结果的内边缘,另一个用于边缘检测结果的外边缘。仅对图像进行阈值处理并获得单个边缘会更容易。尽管如果图像变得更加复杂,则可能需要进行边缘检测。确实,您获得的两个轮廓中的任何一个都应该是有用的。

  2. if abs(x-image.shape[0]/2) <= 2 and abs(y-image.shape[1]/2) <= 2 and (area - cv2.contourArea(c)) < 50行是非常严格的,它并没有为我触发第二张图像。

  3. 如果角度为负数,以-(ellipseAngle - 90)旋转很奇怪。您应该以相同的方式旋转所有椭圆。

下面的代码为问题中所示的两个椭圆图像生成一个圆形输出。我认为以这种方式确定的椭圆参数不是很精确,但是看起来对于此应用程序来说已经足够了。

import cv2
import numpy as np

img = cv2.imread('im1.png',0)
_, thresh = cv2.threshold(img, 128, 255, type=cv2.THRESH_BINARY_INV)
_, contours, _ = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
params = cv2.fitEllipse(contours[0])
angle = params[2]
scale = params[1]
scale = scale[0]/scale[1]

M = cv2.getRotationMatrix2D((img.shape[0]/2, img.shape[1]/2), angle, 1)
# Let's add the scaling too:
M[:,0:2] = np.array([[1,0],[0,scale]]) @ M[:,0:2]
M[1,2] = M[1,2] * scale # This moves the ellipse so it doesn't end up outside the image (it's not correct to keep the ellipse in the middle of the image)

out = cv2.warpAffine(img, M, img.shape, borderValue=255)
cv2.imshow('out',out)
cv2.waitKey()

使用PyDIP(我是作者),您可以通过不设置阈值并使用椭圆边缘周围的灰度值来获得更精确的度量,从而在OP的理想情况下获得更精确的度量精确契合。我们计算图像的二阶中心矩,并从中得出椭圆参数。重要的是,背景恰好为0,前景(椭圆像素)的强度要均匀,除了边缘处(中间的灰度值会添加有关边缘的子像素位置的信息)。

import PyDIP as dip
import numpy as np

img = -dip.ImageRead('im1.png').TensorElement(0) # We use the inverted first channel
params = dip.Moments(img).secondOrder
M = np.array([[params[0],params[2]],[params[2],params[1]]])
d, V = np.linalg.eig(M)
d = np.sqrt(d)
scale = d[0]/d[1]
angle = np.arctan2(V[1,0],V[0,0])

img = dip.Rotation2D(img, -angle)
img = dip.Resampling(img, [scale, 1])
img.Show()