为什么cv2.fitEllipse将轮廓视为椭圆的一边?

时间:2018-03-07 23:00:23

标签: python opencv computer-vision ellipse

我遇到了cv2.fitEllipse的一个奇怪的问题。当我试图得到这种形式的椭圆时, form

可以定义(抱歉长定义列表):

import numpy as np
test=np.zeros((400,1000))
test[241, 113]=1
test[241, 114]=1
test[242, 112]=1
test[242, 113]=1
test[242, 114]=1
test[242, 115]=1
test[242, 116]=1
test[242, 118]=1
test[242, 119]=1
test[242, 120]=1
test[242, 121]=1
test[243, 111]=1
test[243, 112]=1
test[243, 113]=1
test[243, 114]=1
test[243, 115]=1
test[243, 116]=1
test[243, 117]=1
test[243, 118]=1
test[243, 119]=1
test[243, 120]=1
test[243, 121]=1
test[244, 110]=1
test[244, 111]=1
test[244, 112]=1
test[244, 113]=1
test[244, 114]=1
test[244, 115]=1
test[244, 116]=1
test[244, 117]=1
test[244, 118]=1
test[244, 119]=1
test[244, 120]=1
test[245, 109]=1
test[245, 110]=1
test[245, 111]=1
test[245, 112]=1
test[245, 113]=1
test[245, 114]=1
test[245, 115]=1
test[245, 116]=1
test[245, 117]=1
test[245, 118]=1
test[245, 119]=1
test[245, 120]=1
test[246, 109]=1
test[246, 110]=1
test[246, 111]=1
test[246, 112]=1
test[246, 113]=1
test[246, 114]=1
test[246, 115]=1
test[246, 116]=1
test[246, 117]=1
test[246, 118]=1
test[246, 119]=1
test[247, 109]=1
test[247, 110]=1
test[247, 111]=1
test[247, 112]=1
test[247, 113]=1
test[247, 114]=1
test[247, 115]=1
test[247, 116]=1
test[247, 117]=1
test[247, 118]=1
test[247, 119]=1
test[248, 108]=1
test[248, 109]=1
test[248, 110]=1
test[248, 111]=1
test[248, 112]=1
test[248, 113]=1
test[248, 114]=1
test[248, 115]=1
test[248, 116]=1
test[248, 117]=1
test[248, 118]=1
test[248, 119]=1
test[249, 109]=1
test[249, 110]=1
test[249, 111]=1
test[249, 112]=1
test[249, 113]=1
test[249, 114]=1
test[249, 115]=1
test[249, 116]=1
test[249, 117]=1
test[249, 118]=1
test[249, 119]=1
test[250, 109]=1
test[250, 110]=1
test[250, 111]=1
test[250, 112]=1
test[250, 113]=1
test[250, 114]=1
test[250, 115]=1
test[250, 116]=1
test[250, 117]=1
test[250, 118]=1
test[250, 119]=1
test[250, 120]=1
test[251, 109]=1
test[251, 110]=1
test[251, 111]=1
test[251, 112]=1
test[251, 113]=1
test[251, 114]=1
test[251, 115]=1
test[251, 116]=1
test[251, 117]=1
test[251, 118]=1
test[251, 119]=1
test[251, 120]=1
test[252, 109]=1
test[252, 110]=1
test[252, 111]=1
test[252, 112]=1
test[252, 113]=1
test[252, 114]=1
test[252, 115]=1
test[252, 116]=1
test[252, 117]=1
test[252, 118]=1
test[252, 119]=1
test[252, 120]=1
test[252, 121]=1
test[253, 109]=1
test[253, 112]=1
test[253, 113]=1
test[253, 114]=1
test[253, 115]=1
test[253, 116]=1
test[253, 117]=1
test[253, 118]=1
test[253, 119]=1
test[253, 120]=1
test[253, 121]=1
test[254, 112]=1
test[254, 113]=1
test[254, 114]=1
test[254, 115]=1
test[254, 116]=1
test[254, 117]=1
test[254, 118]=1
test[254, 119]=1
test[254, 120]=1
test[254, 121]=1
test[255, 110]=1
test[255, 112]=1
test[255, 113]=1
test[255, 114]=1
test[255, 115]=1
test[255, 116]=1
test[255, 117]=1
test[255, 118]=1
test[255, 119]=1
test[255, 120]=1
test[255, 121]=1
test[255, 122]=1
test[256, 108]=1
test[256, 110]=1
test[256, 111]=1
test[256, 112]=1
test[256, 113]=1
test[256, 114]=1
test[256, 115]=1
test[256, 116]=1
test[256, 117]=1
test[256, 118]=1
test[256, 119]=1
test[256, 120]=1
test[256, 121]=1
test[256, 122]=1
test[256, 123]=1
test[257, 106]=1
test[257, 107]=1
test[257, 108]=1
test[257, 109]=1
test[257, 110]=1
test[257, 111]=1
test[257, 112]=1
test[257, 113]=1
test[257, 114]=1
test[257, 115]=1
test[257, 116]=1
test[257, 117]=1
test[257, 118]=1
test[257, 119]=1
test[257, 120]=1
test[257, 121]=1
test[257, 122]=1
test[257, 123]=1
test[257, 124]=1
test[258, 103]=1
test[258, 104]=1
test[258, 105]=1
test[258, 106]=1
test[258, 107]=1
test[258, 108]=1
test[258, 109]=1
test[258, 110]=1
test[258, 111]=1
test[258, 112]=1
test[258, 113]=1
test[258, 114]=1
test[258, 115]=1
test[258, 116]=1
test[258, 117]=1
test[258, 118]=1
test[258, 119]=1
test[258, 120]=1
test[258, 121]=1
test[258, 122]=1
test[258, 123]=1
test[258, 124]=1
test[259, 103]=1
test[259, 104]=1
test[259, 105]=1
test[259, 106]=1
test[259, 107]=1
test[259, 108]=1
test[259, 109]=1
test[259, 110]=1
test[259, 111]=1
test[259, 112]=1
test[259, 113]=1
test[259, 114]=1
test[259, 115]=1
test[259, 116]=1
test[259, 117]=1
test[259, 118]=1
test[259, 119]=1
test[259, 120]=1
test[259, 121]=1
test[259, 122]=1
test[259, 123]=1
test[259, 124]=1
test[260, 106]=1
test[260, 107]=1
test[260, 108]=1
test[260, 109]=1
test[260, 110]=1
test[260, 111]=1
test[260, 112]=1
test[260, 113]=1
test[260, 114]=1
test[260, 115]=1
test[260, 116]=1
test[260, 117]=1
test[260, 118]=1
test[260, 119]=1
test[260, 120]=1
test[260, 121]=1
test[260, 122]=1
test[260, 123]=1
test[260, 124]=1
test[261, 107]=1
test[261, 110]=1
test[261, 111]=1
test[261, 112]=1
test[261, 113]=1
test[261, 114]=1
test[261, 115]=1
test[261, 116]=1
test[261, 117]=1
test[261, 118]=1
test[261, 119]=1
test[261, 120]=1
test[261, 121]=1
test[261, 122]=1
test[261, 123]=1
test[261, 124]=1
test[261, 125]=1
test[262, 115]=1
test[262, 116]=1
test[262, 117]=1
test[262, 118]=1
test[262, 119]=1
test[262, 120]=1
test[262, 121]=1
test[262, 122]=1
test[262, 123]=1
test[262, 124]=1
test[262, 125]=1
test[262, 126]=1
test[262, 127]=1
test[262, 128]=1
test[263, 116]=1
test[263, 117]=1
test[263, 118]=1
test[263, 119]=1
test[263, 120]=1
test[263, 121]=1
test[263, 122]=1
test[263, 123]=1
test[263, 127]=1
test[263, 128]=1
test[263, 129]=1
test[263, 130]=1
test[263, 131]=1
test[263, 132]=1
test[263, 133]=1
test[263, 134]=1
test[263, 135]=1
test[264, 117]=1
test[264, 118]=1
test[264, 119]=1
test[264, 120]=1
test[264, 121]=1
test[264, 122]=1
test[264, 123]=1
test[264, 127]=1
test[264, 128]=1
test[264, 129]=1
test[264, 130]=1
test[264, 131]=1
test[264, 132]=1
test[264, 133]=1
test[264, 134]=1
test[264, 135]=1
test[264, 136]=1
test[264, 138]=1
test[264, 139]=1
test[265, 118]=1
test[265, 119]=1
test[265, 120]=1
test[265, 121]=1
test[265, 122]=1
test[265, 123]=1
test[265, 131]=1
test[265, 134]=1
test[265, 135]=1
test[265, 136]=1
test[265, 137]=1
test[265, 138]=1
test[265, 139]=1
test[265, 140]=1
test[265, 141]=1
test[265, 142]=1
test[265, 143]=1
test[266, 120]=1
test[266, 121]=1
test[266, 122]=1
test[266, 135]=1
test[266, 136]=1
test[266, 137]=1
test[266, 138]=1
test[266, 139]=1
test[266, 140]=1
test[266, 141]=1
test[266, 142]=1
test[266, 143]=1
test[266, 144]=1
test[266, 145]=1
test[267, 121]=1
test[267, 122]=1
test[267, 142]=1
test[267, 143]=1
test[267, 144]=1
test[267, 145]=1
test[267, 146]=1
test[267, 147]=1
test[267, 148]=1
test[267, 149]=1
test[267, 150]=1
test[267, 151]=1
test[268, 147]=1
test[268, 148]=1
test[268, 149]=1
test[268, 150]=1
test[268, 151]=1
test[268, 152]=1
test[268, 153]=1
test[268, 154]=1
test[268, 155]=1
test[268, 156]=1
test[268, 157]=1
test[269, 149]=1
test[269, 151]=1
test[269, 152]=1
test[269, 153]=1
test[269, 154]=1
test[269, 155]=1
test[269, 156]=1
test[269, 157]=1
test[270, 156]=1

我有以下椭圆: bad ellipse

要获得此结果,您只需使用python

运行以下代码即可
import cv2
import numpy as np
import itertools
import matplotlib.pyplot as mpl
def getContours_test(clusters):
        copy_clusters=clusters.copy()
        int_cnts=[]
        for label in np.unique(clusters):
                mask = np.zeros(clusters.shape, dtype="uint8")
                mask[clusters == label] = 255
                cnts, hierarchy = \
                        cv2.findContours(mask.copy(),cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_NONE)
                int_cnts.append(cnts)
        list_contours=list(itertools.chain.from_iterable(int_cnts))
        return copy_clusters, list_contours


def getEllipse_test(clusters, list_contours):
        for index,cnt in enumerate(list_contours):
                map_ellipse=np.ones(clusters.shape).astype(np.uint8)
                cimg = np.zeros_like(clusters)
                cv2.drawContours(cimg, list_contours, index, color=255, thickness=-1)
                ellipse=cv2.fitEllipse(cnt)
                cv2.ellipse(map_ellipse,ellipse,(255,0,0))
        return map_ellipse

test_result, test_contours = getContours_test(test)
test_ellipse = getEllipse_test(test_result,test_contours)
mpl.contourf(test_result)
mpl.contour(test_ellipse)
mpl.show()

我试着看看是否可以用类似但更简单的形式重现相同的行为(在原始轮廓的情况下填充新表格): 2 forms 但这一次,我用相同的代码得到了正确的结果 good ellipse

test2=np.zeros((400,1000))
test2[240:260,110:120]=1
test2[260:265,117:134]=1
test2[265:270,134:155]=1
test_result2, test_contours2 = getContours_test(test2)
test_ellipse2 = getEllipse_test(test_result2,test_contours2)
mpl.contourf(test_result2)
mpl.contour(test_ellipse2)
mpl.show()

知道为什么会这样吗? cv2.fitEllipse应该有足够的轮廓点来估计原始形式的正确椭圆。

编辑: 在@ user2518618的评论之后,我尝试在方法中包含cv2.convexHull():

def remove_convex(clusters,list_contours):
        for index,cnt in enumerate(list_contours):
                map_ellipse=np.ones(clusters.shape).astype(np.uint8)
                cimg = np.zeros_like(clusters)
                hull = cv2.convexHull(cnt)
                cv2.drawContours(cimg, hull, -1, color=255, thickness=-1)
        return cimg

得到适合于估计椭圆的点的选择:

test_hull = remove_convex(test_result,test_contours)

mpl.contour(test_hull)
mpl.show()

points remaining after cv2.convexHull()

但如果我在remove_convex中ellipse=cv2.fitEllipse(hull)之后添加hull = cv2.convexHull(cnt),则会收到以下错误: 错误:( - 201)函数cvFitEllipse2中的点数应>> =

虽然我理解错误,因为opencv文档说凸包返回矩形的四个角,我真的不明白为什么我不能使用上面绘制的点来拟合我的新椭圆

1 个答案:

答案 0 :(得分:0)

如果您的形状看起来像椭圆,

fitEllipse()会更好。

你的形状非常嘈杂,很难找到适合它的椭圆。实际上,算法找到的两个椭圆都在你的形状点附近通过,两者都可能是很好的近似值。

您应该预处理形状,使其看起来更像椭圆形。我会尝试在fitEllipse()

之前使用cv2.convexHull()