【问题标题】:Convert ellipse in image to circle (warp ellipse to circle like a polygon warping to rectangle)将图像中的椭圆转换为圆形(将椭圆变形为圆形,就像多边形变形为矩形)
【发布时间】:2019-06-19 08:39:27
【问题描述】:

我有一个椭圆的图像,如果图像有椭圆,我会使用 findcontours() 找到它,然后我想将此椭圆转换为圆形。

查看示例

我想让他们每个人都喜欢这个

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

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

然后我尝试通过旋转角度旋转图像,然后缩放图像 w.r.t 短轴和长轴的高度和宽度(即使长轴和短轴长度相同)但我也没有得到正确的圆形对象图像如上。左边会有一些旋转/它仍然会像一个接近圆形的椭圆。

     _, 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() 后,我得到了帖子中第一张图像的这 2 个轮廓

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

已编辑 - 如果有任何更好的方法来解决这个问题,那将会很有帮助。

【问题讨论】:

  • 无需查看您的代码,这似乎是使用 SVD 的最佳案例(请参阅 wikipedia 中的动画 gif)
  • @Veer 关于您对我的回答的评论(我将在您可能edited your question 之后删除):我建议您选择另一个输入示例,就像您在评论中描述的那样,让我们说一些带有红色矩形的黑色椭圆,为了清楚起见,您也想转换椭圆内的任何东西(如果我理解正确的话)。从最初的问题陈述来看,我不会这样想(显然,安德是这样想的,但我不是)。
  • 为什么不直接从一端到另一端获取长轴直径,除以 2 并使用它作为半径在椭圆上绘制一个新的黑色圆圈?
  • @fmw42 如果椭圆颜色不同,我需要椭圆内的信息将不起作用。
  • @AnderBiguri 我已经阅读了 SVD wiki 页面,但是我如何使用椭圆的图像获取矩阵,并且 wiki 中的方法解释是从圆形到椭圆的转换,但这个问题恰恰相反

标签: python opencv image-processing computer-vision


【解决方案1】:

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

  1. 您正在使用边缘检测算法,并获取结果的轮廓。这在原则上是可以的,但它会导致图像具有两个轮廓:一个用于边缘检测结果的内边缘,一个用于外边缘。只对图像进行阈值处理并获得单个边缘更容易。虽然如果图像变得更复杂,边缘检测可能是相关的。实际上,您获得的两个轮廓中的任何一个都应该是有用的。

  2. if abs(x-image.shape[0]/2) &lt;= 2 and abs(y-image.shape[1]/2) &lt;= 2 and (area - cv2.contourArea(c)) &lt; 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()

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-11-19
    • 1970-01-01
    • 1970-01-01
    • 2017-10-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多