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

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

enter image description here enter image description here

从这些轮廓中任何轮廓都可以,对吧?我按照代码使用 countour 中的第一个轮廓, fitellipse() 给了我这个椭圆

enter image description here

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

最佳答案

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

  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()

关于python - 将图像中的椭圆转换为圆形(将椭圆扭曲为圆形,就像多边形扭曲为矩形一样),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56663459/

相关文章:

python - 从对象子类化是否与将类型定义为元类相同?

python - 当 URL 存在时,与 Django 进行 NoReverseMatch

c++ - 在 C++ 中实现变形/液化工具

php - 动态生成 map

algorithm - 为什么我们在 SIFT 算法中将图像大小调整为一半?

image-processing - aforge和c#连接字符时的图像分割

python - 递归后序树导航

Docker中的Python脚本在子目录中找不到模块

python - 如何使用来自 python 的请求使用 opencv 从 url 打开图像

c++ - opencv 多进程的小加速