使用 OpenCV 特征工具进行实时椭球检测

问题描述

我正在尝试创建一个程序来从实时摄像头馈送中检测椭圆体形状。目标是识别餐具(例如碗或杯子)的边缘。在阅读了许多 OpenCV 教程和文档页面之后,我尝试了 FAST 角点检测、Canny 边缘(具有最小指定区域的拨号轮廓)、Sobel 边缘和 Hough 圆。即使调整参数,这些似乎都不起作用。

作为参考,这里是来自每种方法的示例场景的单帧图像集 https://imgur.com/a/A7d7UxI

countours 的多边形近似依赖于角的数量,但任何超过 4 个角的复杂形状似乎都被标记为“圆形或椭圆形”。同样,纯圆检测会创建重复发现,其中标记了椭圆体的每个切线拟合圆。拐角检测有点失败,因为曲线形状中显然没有拐角。

老实说,我很难过。我对 OpenCV 的经验有限,不知道是否有一种方法可以检测和标记椭球(连续的或破碎的),以便在屏幕上找到它们的边缘位置。任何方向或帮助将不胜感激(我可以自己做一些挖掘),我最大的优势是试图从 Sobel 检测中识别出最强的可见轮廓,但无法验证它们的曲率或识别这些线。

虽然没有明确的相关性,但这里是我用来显示来自相机提要的每一个的 WIP 代码

import numpy as np
import cv2

def empty():
    pass

def getContours(img,imgContour):
    contours,hierarchy = cv2.findContours(img,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_NONE)

    for cnt in contours:
        area = cv2.contourArea(cnt)
        if area > 1000:
            cv2.drawContours(imgContour,cnt,-1,(255,255),7)
            peri = cv2.arcLength(cnt,False)
            approx = cv2.approxpolyDP(cnt,0.02 * peri,False)
            if len(approx) > 5:
                x,y,w,h = cv2.boundingRect(approx)
                cv2.rectangle(imgContour,(x,y),(x + w,y + h),(0,255,0),5)

# Capturing from the video input
# Use VideoCapture(0) for default webcam
# I use a Feed from my phone via USB,which is webcam 2 that uses VideoCapture(1)
cap = cv2.VideoCapture(1)

"""Trackbar"""
cv2.namedWindow("Parameters")
cv2.createTrackbar("Threshold1_Canny","Parameters",150,empty)
cv2.createTrackbar("Threshold2_Canny",empty)

while True:
    # reading the frame
    ret,frame = cap.read()
    # flipping the frame
    frame = cv2.flip(frame,1)
    if ret:

        """Image Preprocessing"""
        img = cv2.GaussianBlur(frame,(21,21),cv2.BORDER_DEFAULT)
        imgContour = img.copy()
        img = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)

        """FAST Corner Detection"""
        # Initiate FAST object with default values
        fast = cv2.FastFeatureDetector_create()

        # find and draw the keypoints
        kp = fast.detect(img,None)
        img2 = cv2.drawKeypoints(img,kp,None,color=(255,0))

        # disable nonmaxSuppression
        fast.setNonmaxSuppression(0)
        kp = fast.detect(img,None)
        imgFAST = cv2.drawKeypoints(img,0))

        """Canny Edge Detection"""
        threshold1 = cv2.getTrackbarPos("Threshold1_Canny","Parameters")
        threshold2 = cv2.getTrackbarPos("Threshold2_Canny","Parameters")
        edges = cv2.Canny(img,threshold1,threshold2)
        kernal = np.ones((5,5))
        imgDil = cv2.dilate(edges,kernal,iterations=1)
        getContours(imgDil,imgContour)

        """Hough Circles"""
        #cimg = cv2.cvtColor(img,cv2.COLOR_GRAY2BGR)
        #circles = cv2.HoughCircles(img,cv2.HOUGH_GRADIENT,1,100,#                           param1=50,param2=30,minRadius=60,maxRadius=400)
        #circles = np.uint16(np.around(circles))


        """Sobel Edge Detection"""
        imgSobel = cv2.sobel(img,cv2.CV_16S,ksize=-1)
        imgSobel = np.absolute(imgSobel)
        imgSobel = np.uint8(imgSobel)

        """Show All Processing"""
        cv2.imshow('Raw',frame)
        cv2.imshow('FAST',imgFAST)
        cv2.imshow('Canny',imgDil)
        cv2.imshow('Sobel',imgSobel)
        #cv2.imshow('Hough',cimg)

        """Key Controls"""
        # getting the input from the keyboard
        k = cv2.waitKey(1) & 0xFF

        # press 'q' to quit
        if k == ord('q'):
            cap.release()
            break

cap.release()
cv2.destroyAllWindows()

解决方法

OpenCV 提供了三种不同的函数来将一组点拟合到椭圆上:

这些函数对一系列点(即轮廓)进行操作,因此您可以将这些函数中的任何一个用于轮廓。我建议的方法是:

  • 图像二值化
  • 检测二值图像中的轮廓
  • 只保留外部轮廓(即碗有很多椭圆,但实际上你只想要最外面的)
  • 对于每个剩余的轮廓:
    • 拟合椭圆
    • 将检测到的轮廓与椭圆的轮廓进行比较(例如使用 matchShapes())。
    • 阈值比较以说明轮廓是否为椭圆

这个过程只依赖于一个主要参数(比较阈值),可以根据标记数据来决定,所以作为一种算法还算不错。然而,这里的预处理步骤(Canny 或其他二值化)可能有很多变量,并且在给定光照等情况下可能会发生很大变化。

一般来说,像这样依赖图像处理的经典算法在受限环境(已知光照、固定相机位置等)中效果最好,因为您可能实际上可以找到效果很好的预处理参数(通常情况下不是这样)在这些工业环境中很难提供几个滑块来动态更改阈值)。但是,对于不受约束的环境,例如在任意灯光下挥动手机指向任何颜色的物体,ML 可能会更好地为您服务,使用您感兴趣的特定类训练物体或遮罩检测器。