代码之家  ›  专栏  ›  技术社区  ›  OM222O

OpenCV检测对象及其旋转

  •  0
  • OM222O  · 技术社区  · 6 年前

    我正在从事一个机器人技术项目,我们需要实现某种形式的图像识别来找到正确的路径。

    enter image description here

    我编写了以下代码,该代码使用网络摄像头成功捕获视频流,并尝试从提供的模板中查找磁盘图像:

    import cv2
    
    IMGn = cv2.imread("North.png",0)
    webcam = cv2.VideoCapture(0)
    grayScale = True
    key = 0
    
    def transformation(frame,template):
        w, h = template.shape[::-1]
        res = cv2.matchTemplate(frame,template,cv2.TM_SQDIFF_NORMED)
        min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res)
        top_left = min_loc
        bottom_right = (top_left[0] + w, top_left[1] + h)
        cv2.rectangle(frame,top_left, bottom_right, 255, 2)
        return frame
    
    while (key!=ord('q')):
        check, frame = webcam.read()
        if(grayScale):
            frame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
    
        frame = transformation(frame,IMGn)
    
        cv2.imshow("Capturing", frame)
        key = cv2.waitKey(1)
    
    webcam.release()
    cv2.destroyAllWindows()
    

    这并不是非常有效,但至少可以找到指南针的大致轮廓。然而,我根本不知道如何找到圆的旋转!此外,尺寸似乎也是一个问题(如果拖得太远或太近,跟踪就会混乱)。这是我第一次用图像识别做任何事情,但通常没有帮助,所以请尽量简化你的答案。谢谢

    0 回复  |  直到 6 年前
        1
  •  3
  •   Max Kaha    6 年前

    首先,您可能希望在图片上设置一个阈值,以便将所有灰色元素转换为白色或黑色,以便于检测。

    img = cv2.imread(r"C:\Users\Max\Desktop\North_rotated_2.png")
    img = cv2.resize(img, None, fx=3, fy=3)
    imgray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    blur = cv2.GaussianBlur(imgray, (5, 5), 0)
    ret, thresh = cv2.threshold(blur, 0, 255, cv2.THRESH_BINARY+cv2.THRESH_OTSU)
    

    输出如下所示(我手动旋转了初始图片以获得角度)。 enter image description here

    然后我们可以检测图像中的第二大轮廓,它应该是我们的黑色半圆(最大轮廓将是整个图像边界附近的轮廓)。这是通过findContours()函数完成的:

    contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
    areas = []
    
    for cnt in contours:
        area = cv2.contourArea(cnt)
        areas.append((area, cnt))
    
    areas.sort(key=lambda x: x[0], reverse=True)
    areas.pop(0) # remove biggest contour
    x, y, w, h = cv2.boundingRect(areas[0][1]) # get bounding rectangle around biggest contour to crop to
    img = cv2.rectangle(img, (x, y), (x+w, y+h), (255,0,0), 2)
    crop = thresh[y:y+h, x:x+w] # crop to size
    

    在裁剪到检测到的轮廓后,我们得到了以下图像: enter image description here

    最后,您可以使用HoughLines查找图像中最长的线,该线应该是半圆的边缘。这里你可以得到描述它的角度,ρ和θ,这很可能是你想知道的。如果我们取这些角度得到x,y点,然后像这样画在图像上:

    edges = cv2.Canny(crop, 50, 150, apertureSize=3)
    lines = cv2.HoughLines(edges, 1, np.pi/180, 200) # Find lines in image
    
    img = cv2.cvtColor(crop, cv2.COLOR_GRAY2BGR) # Convert cropped black and white image to color to draw the red line
    for rho, theta in lines[0]:
        a = np.cos(theta)
        b = np.sin(theta)
        x0 = a*rho
        y0 = b*rho
        x1 = int(x0 + 1000*(-b))
        y1 = int(y0 + 1000*(a))
        x2 = int(x0 - 1000*(-b))
        y2 = int(y0 - 1000*(a))
    
        cv2.line(img, (x1, y1), (x2, y2), (0, 0, 255), 2) # draw line
    

    然后,我们可以确保检测到正确的线路,在这种情况下,这似乎很好: enter image description here

    希望这有助于为您指出正确的方向,至少手动将图像旋转到几个位置对我来说效果很好。第[0]行中的角度应该是您在此处查找的角度。

        2
  •  0
  •   OM222O    6 年前

    我有一个cv2的问题。发现的恐龙。它似乎返回3个值,而不是2个。除此之外,代码成功地检测并裁剪了图像,但在最后一步中没有找到行。还有一个问题,如果图片旋转超过180度,它将给出错误的结果,因为线条旋转超过180度。在黑色方块中使用白色小方块应该可以解决这个问题,并为图像添加180度的偏移,这取决于此,但我也不确定如何做到这一点。

    import cv2
    webcam = cv2.VideoCapture(0)
    
    def find_disk(frame,template):
        w, h = template.shape[::-1]
        res = cv2.matchTemplate(frame,template,cv2.TM_SQDIFF_NORMED)
        min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res)
        top_left = min_loc
        bottom_right = (top_left[0] + w, top_left[1] + h)
        frame = frame[top_left[1]:bottom_right[1],top_left[0]:bottom_right[0]]
        return frame
    
    def thresh_img(frame):
        frame = cv2.GaussianBlur(frame, (5, 5), 0)
        ret, thresh = cv2.threshold(frame, 0, 255, cv2.THRESH_BINARY+cv2.THRESH_OTSU)
        return thresh
    
    def crop_disk(frame):
        _, contours, hierarchy = cv2.findContours(frame, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
        areas = []
        for cnt in contours:
            area = cv2.contourArea(cnt)
            areas.append((area, cnt))
    
        areas.sort(key=lambda x: x[0], reverse=True)
        areas.pop(0) # remove biggest contour
        if (len(areas)>0):
            x, y, w, h = cv2.boundingRect(areas[0][1]) # get bounding rectangle around biggest contour to crop to
            crop = frame[y:y+h, x:x+w]
        else:
            crop = frame
        return crop
    
    def find_lines(frame):
        edges = cv2.Canny(frame, 50, 150, apertureSize=3)
        lines = cv2.HoughLines(edges, 1, np.pi/180, 200)
        if (lines!=None):
            print(lines)
            img = cv2.cvtColor(frame, cv2.COLOR_GRAY2BGR) # Convert cropped black and white image to color to draw the red line
            for rho, theta in lines[0]:
                a = np.cos(theta)
                b = np.sin(theta)
                x0 = a*rho
                y0 = b*rho
                x1 = int(x0 + 1000*(-b))
                y1 = int(y0 + 1000*(a))
                x2 = int(x0 - 1000*(-b))
                y2 = int(y0 - 1000*(a))
    
                return cv2.line(img, (x1, y1), (x2, y2), (0, 0, 255), 2)
        else:
            return frame
    
    key = 0
    
    while (key!=ord('q')):
        check, frame = webcam.read()
        if(grayScale):
            frame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
    
        frame = find_lines(crop_disk(thresh_img(find_disk(frame,IMGn))))
    
        cv2.imshow("Capturing", frame)
        key = cv2.waitKey(1)
        #key = ord('q')
    
    webcam.release()
    cv2.destroyAllWindows()
    

    这是一张示例输出的图片(我在手机上放了一张磁盘的图片,然后在相机前旋转它得到了这张图片):

    enter image description here