OpenCV 检测物体及其旋转

OM2*_*22O 3 python opencv image-processing

我正在开发一个机器人项目,我们需要实现某种形式的图像识别来找到正确的路径。有一个旋转的圆盘,其方向如下所示:

在此输入图像描述

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

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()
Run Code Online (Sandbox Code Playgroud)

这不太好用,但至少可以找到指南针的大致轮廓。但是我根本不知道如何找到圆的旋转!尺寸似乎也是一个问题(当距离太远或太近时,跟踪就会混乱)。这是我第一次做任何与图像识别有关的事情,一般来说这没有帮助,所以请尝试简化你的答案。谢谢。


我遇到了 cv2.findContours 问题。它似乎返回 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()
Run Code Online (Sandbox Code Playgroud)

这是示例输出的图片(我通过在手机上放置磁盘图片并将其在相机前旋转来获得此图片):

在此输入图像描述

小智 5

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

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)
Run Code Online (Sandbox Code Playgroud)

输出看起来像这样(我手动旋转了你的初始图片以获得一个角度)。 在此输入图像描述

然后我们可以检测图像中的第二大轮廓,它应该是我们的黑色半圆(最大轮廓将是整个图像边界旁边的轮廓)。这是通过 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
Run Code Online (Sandbox Code Playgroud)

裁剪到检测到的轮廓后,我们得到了这个图像: 在此输入图像描述

最后,您可以使用 HoughLines 找到图像中最长的线,该线应该是半圆的边缘。在这里,您将获得描述 rho 和 theta 的角度,这可能就是您想知道的。如果我们采用这些角度来获取 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
Run Code Online (Sandbox Code Playgroud)

然后我们可以确保检测到正确的行,在本例中看起来不错: 在此输入图像描述

希望这可以帮助您指明正确的方向,至少在几个位置手动旋转图像对我来说效果很好。lines[0] 中的角度应该是您在这里寻找的角度。