当前位置:   article > 正文

基于HSV色域分割+Canny的特征圆检测算法实现(单张图片检测+realsense相机实时检测)_单张图片 目标检测

单张图片 目标检测


前言

本科毕设设计的一种传统视觉方案完成特征圆检测方法,仅基于OpenCV实现,可以完成一张图片的检测,也可以搭配相机进行实时的检测,已在Windows 10及Ubuntu 16.04下运行通过。

一、效果展示

单张图片的检测
单张图片的检测
使用realsense的实时检测,可以获取圆心的三维坐标。
在这里插入图片描述
在这里插入图片描述

二、算法流程

  1. 将RGB图片转换到HSV色域,去除背景干扰。
  2. 图像滤波处理。
  3. Canny算法边缘检测。
  4. 对检测到的轮廓边缘进行椭圆拟合,使用圆度值以及长短轴的值进行目标圆的筛选。

三、环境配置

基本只依赖numpy和cv2。

import numpy as np
import cv2
  • 1
  • 2

四、部分代码解释

4.1 前置滑块

设计滑块便于后面实时调试HSV、半径等参数。

def nothing(*arg):
    pass
para = (0, 127, 149, 255, 255, 255, 0, 50)
# lowHue lowSat lowVal highHue highSat highVal minRadius maxRadius
cv2.namedWindow('Trackbar')
cv2.resizeWindow('Trackbar', 400, 400)
cv2.createTrackbar('lowHue', 'Trackbar', para[0], 255, nothing)
cv2.createTrackbar('lowSat', 'Trackbar', para[1], 255, nothing)
cv2.createTrackbar('lowVal', 'Trackbar', para[2], 255, nothing)
cv2.createTrackbar('highHue', 'Trackbar', para[3], 255, nothing)
cv2.createTrackbar('highSat', 'Trackbar', para[4], 255, nothing)
cv2.createTrackbar('highVal', 'Trackbar', para[5], 255, nothing)
cv2.createTrackbar('minRadius', 'Trackbar', para[6], 500, nothing)
cv2.createTrackbar('maxRadius', 'Trackbar', para[7], 500, nothing)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

4.2 图形预处理

    frame = cv2.imread("./c1.jpg")
    lowHue = cv2.getTrackbarPos('lowHue', 'Trackbar')
    lowSat = cv2.getTrackbarPos('lowSat', 'Trackbar')
    lowVal = cv2.getTrackbarPos('lowVal', 'Trackbar')
    highHue = cv2.getTrackbarPos('highHue', 'Trackbar')
    highSat = cv2.getTrackbarPos('highSat', 'Trackbar')
    highVal = cv2.getTrackbarPos('highVal', 'Trackbar')
    minRadius = cv2.getTrackbarPos('minRadius', 'Trackbar')
    maxRadius = cv2.getTrackbarPos('maxRadius', 'Trackbar')
    print("para is ",[lowHue, lowSat, lowVal, highHue, highSat, highVal, minRadius, maxRadius])
    # Show the original image.
    cv2.namedWindow('frame', 0)
    cv2.imshow('frame', frame)
    # Blur methods available, comment or uncomment to try different blur methods.
    frame = cv2.medianBlur(frame, 5)
    # Convert the frame to HSV colour model.
    hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
    # HSV values to define a colour range.
    colorLow = np.array([lowHue, lowSat, lowVal])
    colorHigh = np.array([highHue, highSat, highVal])
    mask = cv2.inRange(hsv, colorLow, colorHigh)
    kernal = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5))
    mask = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernal)
    mask = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernal)
    result = cv2.bitwise_and(frame, frame, mask=mask)
    # Show final output image
    cv2.namedWindow('afterHSVmask', 0)
    cv2.imshow('afterHSVmask', result)
    gray = cv2.cvtColor(result, cv2.COLOR_BGR2GRAY)
    imgray = cv2.Canny(result, 600, 100, 3)  # Canny
    cv2.namedWindow('canny', 0)
    cv2.imshow('canny', imgray)
    ret, thresh = cv2.threshold(imgray, 127, 255, cv2.THRESH_BINARY)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33

4.3 椭圆检测

原理是使用findContours函数寻找所有的轮廓,在所有的轮廓中拟合椭圆,根据圆度和长短轴的限制进行筛选。

contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)  # contours为轮廓集,可以计算轮廓的长度、面积等
    for cnt in contours:
        if len(cnt) > 50:
            ell = cv2.fitEllipse(cnt)  # 拟合椭圆 ell = [ center(x, y) , long short (a, b), angle ]
            a = ell[1][0]  # long
            b = ell[1][1]  # short
            x = int(ell[0][0])
            y = int(ell[0][1])
            if (b / a) < 1.2 and a > minRadius and b > minRadius and a < maxRadius and b < maxRadius:
                frame = cv2.ellipse(frame, ell, (0, 0, 200), 2)
                cv2.circle(frame, (x, y), 2, (255, 255, 255), 3)
                cv2.putText(frame, str((x, y, (a + b) // 2)), (x + 20, y + 10), 0, 1,
                            [225, 255, 255], thickness=1, lineType=cv2.LINE_AA)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

五、完整代码

完整代码已上传GitHub,附带一些图片。
https://github.com/Thinkin99/HSV_Canny_Circle_Detection

5.1 对于一张图片的检测

import numpy as np
import cv2

def nothing(*arg):
    pass

para = (0, 127, 149, 255, 255, 255, 0, 50)
# lowHue lowSat lowVal highHue highSat highVal minRadius maxRadius
cv2.namedWindow('Trackbar')
cv2.resizeWindow('Trackbar', 400, 400)
cv2.createTrackbar('lowHue', 'Trackbar', para[0], 255, nothing)
cv2.createTrackbar('lowSat', 'Trackbar', para[1], 255, nothing)
cv2.createTrackbar('lowVal', 'Trackbar', para[2], 255, nothing)
cv2.createTrackbar('highHue', 'Trackbar', para[3], 255, nothing)
cv2.createTrackbar('highSat', 'Trackbar', para[4], 255, nothing)
cv2.createTrackbar('highVal', 'Trackbar', para[5], 255, nothing)
cv2.createTrackbar('minRadius', 'Trackbar', para[6], 500, nothing)
cv2.createTrackbar('maxRadius', 'Trackbar', para[7], 500, nothing)

while True:
    frame = cv2.imread("./c1.jpg")
    lowHue = cv2.getTrackbarPos('lowHue', 'Trackbar')
    lowSat = cv2.getTrackbarPos('lowSat', 'Trackbar')
    lowVal = cv2.getTrackbarPos('lowVal', 'Trackbar')
    highHue = cv2.getTrackbarPos('highHue', 'Trackbar')
    highSat = cv2.getTrackbarPos('highSat', 'Trackbar')
    highVal = cv2.getTrackbarPos('highVal', 'Trackbar')
    minRadius = cv2.getTrackbarPos('minRadius', 'Trackbar')
    maxRadius = cv2.getTrackbarPos('maxRadius', 'Trackbar')
    print("para is ",[lowHue, lowSat, lowVal, highHue, highSat, highVal, minRadius, maxRadius])
    # Show the original image.
    cv2.namedWindow('frame', 0)
    cv2.imshow('frame', frame)
    # Blur methods available, comment or uncomment to try different blur methods.
    frame = cv2.medianBlur(frame, 5)
    # Convert the frame to HSV colour model.
    hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
    # HSV values to define a colour range.
    colorLow = np.array([lowHue, lowSat, lowVal])
    colorHigh = np.array([highHue, highSat, highVal])
    mask = cv2.inRange(hsv, colorLow, colorHigh)
    kernal = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5))
    mask = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernal)
    mask = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernal)
    result = cv2.bitwise_and(frame, frame, mask=mask)
    # Show final output image
    cv2.namedWindow('afterHSVmask', 0)
    cv2.imshow('afterHSVmask', result)
    gray = cv2.cvtColor(result, cv2.COLOR_BGR2GRAY)
    imgray = cv2.Canny(result, 600, 100, 3)  # Canny
    cv2.namedWindow('canny', 0)
    cv2.imshow('canny', imgray)
    ret, thresh = cv2.threshold(imgray, 127, 255, cv2.THRESH_BINARY)
    contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)  # contours为轮廓集,可以计算轮廓的长度、面积等
    for cnt in contours:
        if len(cnt) > 50:
            ell = cv2.fitEllipse(cnt)  # 拟合椭圆 ell = [ center(x, y) , long short (a, b), angle ]
            a = ell[1][0]  # long
            b = ell[1][1]  # short
            x = int(ell[0][0])
            y = int(ell[0][1])
            if (b / a) < 1.2 and a > minRadius and b > minRadius and a < maxRadius and b < maxRadius:
                frame = cv2.ellipse(frame, ell, (0, 0, 200), 2)
                cv2.circle(frame, (x, y), 2, (255, 255, 255), 3)
                cv2.putText(frame, str((x, y, (a + b) // 2)), (x + 20, y + 10), 0, 1,
                            [225, 255, 255], thickness=1, lineType=cv2.LINE_AA)
    cv2.namedWindow("circle_detect", 0)
    cv2.imshow("circle_detect", frame)
    k = cv2.waitKey(5) & 0xFF
    if k == 27:
        break
cv2.destroyAllWindows()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72

5.2 使用Realsense相机进行实时检测

因为已经毕业了, 手上没有Realsense,云改了一下代码,没有实际跑过,可能会有bug,相较于单张图片的检测多了深度值的检测。

from __future__ import division
import numpy as np
import pyrealsense2 as rs
# sys.path.remove('/opt/ros/kinetic/lib/python2.7/dist-packages')
import cv2

def nothing(*arg):
        pass
 

para = (0, 127, 149, 255, 255, 255, 0, 50)
# lowHue lowSat lowVal highHue highSat highVal minRadius maxRadius
cv2.namedWindow('Trackbar')
cv2.resizeWindow('Trackbar', 500, 400)
cv2.createTrackbar('lowHue', 'Trackbar', para[0], 255, nothing)
cv2.createTrackbar('lowSat', 'Trackbar', para[1], 255, nothing)
cv2.createTrackbar('lowVal', 'Trackbar', para[2], 255, nothing)
cv2.createTrackbar('highHue', 'Trackbar', para[3], 255, nothing)
cv2.createTrackbar('highSat', 'Trackbar', para[4], 255, nothing)
cv2.createTrackbar('highVal', 'Trackbar', para[5], 255, nothing)
cv2.createTrackbar('minRadius', 'Trackbar', para[6], 500, nothing)
cv2.createTrackbar('maxRadius', 'Trackbar', para[7], 500, nothing)

def get_aligned_images():
    frames = pipeline.wait_for_frames()  # 等待获取图像帧
    aligned_frames = align.process(frames)  # 获取对齐帧
    aligned_depth_frame = aligned_frames.get_depth_frame()  # 获取对齐帧中的depth帧
    color_frame = aligned_frames.get_color_frame()  # 获取对齐帧中的color帧

    ############### 相机参数的获取 #######################
    intr = color_frame.profile.as_video_stream_profile().intrinsics  # 获取相机内参
    depth_intrin = aligned_depth_frame.profile.as_video_stream_profile(
    ).intrinsics  # 获取深度参数(像素坐标系转相机坐标系会用到)

    depth_image = np.asanyarray(aligned_depth_frame.get_data())  # 深度图(默认16位)
    depth_image_8bit = cv2.convertScaleAbs(depth_image, alpha=0.03)  # 深度图(8位)
    depth_image_3d = np.dstack(
        (depth_image_8bit, depth_image_8bit, depth_image_8bit))  # 3通道深度图
    color_image = np.asanyarray(color_frame.get_data())  # RGB图

    # 返回相机内参、深度参数、彩色图、深度图、齐帧中的depth帧
    return intr, depth_intrin, color_image, depth_image, aligned_depth_frame
pipeline = rs.pipeline()  # 定义流程pipeline
config = rs.config()  # 定义配置config
# config.enable_stream(rs.stream.depth, 1280, 720, rs.format.z16, 15)
# config.enable_stream(rs.stream.color, 1280, 720, rs.format.bgr8, 15)
config.enable_stream(rs.stream.depth, 640, 480, rs.format.z16, 30)
config.enable_stream(rs.stream.color, 640, 480, rs.format.bgr8, 30)
profile = pipeline.start(config)  # 流程开始
align_to = rs.stream.color  # 与color流对齐
align = rs.align(align_to)


while True:
    # Get HSV values from the GUI sliders.
    intr, depth_intrin, color_image, depth_image, aligned_depth_frame = get_aligned_images()  # 获取对齐的图像与相机内参
    if not depth_image.any() or not color_image.any():
        continue
    lowHue = cv2.getTrackbarPos('lowHue', 'Trackbar')
    lowSat = cv2.getTrackbarPos('lowSat', 'Trackbar')
    lowVal = cv2.getTrackbarPos('lowVal', 'Trackbar')
    highHue = cv2.getTrackbarPos('highHue', 'Trackbar')
    highSat = cv2.getTrackbarPos('highSat', 'Trackbar')
    highVal = cv2.getTrackbarPos('highVal', 'Trackbar')
    minRadius = cv2.getTrackbarPos('minRadius', 'Trackbar')
    maxRadius = cv2.getTrackbarPos('maxRadius', 'Trackbar')
    print("para is ", [lowHue, lowSat, lowVal, highHue, highSat, highVal, minRadius, maxRadius])
    frame=color_image
    # Show the original image.
    cv2.namedWindow('frame',0)
    cv2.imshow('frame', frame)
    # Blur methods available, comment or uncomment to try different blur methods.
    frame = cv2.medianBlur(frame, 9)
    # Convert the frame to HSV colour model.
    hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
    # HSV values to define a colour range.
    colorLow = np.array([lowHue,lowSat,lowVal])
    colorHigh = np.array([highHue,highSat,highVal])
    mask = cv2.inRange(hsv, colorLow, colorHigh)
    kernal = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (7, 7))
    mask = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernal)
    mask = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernal)
    result = cv2.bitwise_and(frame, frame, mask = mask)
    # Show final output image
    cv2.namedWindow('afterHSVmask',0)
    cv2.imshow('afterHSVmask', result)
    gray = cv2.cvtColor(result, cv2.COLOR_BGR2GRAY)
    imgray = cv2.Canny(result, 600, 100, 3)  # Canny
    cv2.namedWindow('canny',0)
    cv2.imshow('canny',imgray)
    ret, thresh = cv2.threshold(imgray, 127, 255, cv2.THRESH_BINARY)
    contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)  # contours为轮廓集,可以计算轮廓的长度、面积等
    list_xy=[]
    for cnt in contours:
        if len(cnt) > 50:
            ell = cv2.fitEllipse(cnt)  # 拟合椭圆 ellipse = [ center(x, y) , long short (a, b), angle ]
            a = ell[1][0]
            b = ell[1][1]
            x = int(ell[0][0])
            y = int(ell[0][1])
            if (b / a) < 1.2 and a > minRadius and b > minRadius and a < maxRadius and b < maxRadius:
                frame = cv2.ellipse(frame, ell, (0, 0, 200), 2)
                cv2.circle(frame, (x, y), 2, (255, 255, 255), 3)
                cv2.putText(frame, str((x, y,(a+b)//2)), (x + 20, y + 10), 0, 1,
                            [225, 255, 255], thickness=1, lineType=cv2.LINE_AA)
                dis = aligned_depth_frame.get_distance(x, y)
                if dis == 0:
                    dis = aligned_depth_frame.get_distance(100, 100)#如果检测不到距离,就取(100100)像素点的距离.
                camera_xyz = rs.rs2_deproject_pixel_to_point(
                    depth_intrin, (x, y), dis)  # 计算相机坐标系的xyz
                camera_xyz = np.round(np.array(camera_xyz), 3)  # 转成3位小数
                camera_xyz = camera_xyz.tolist()
                cv2.putText(frame, str(camera_xyz), (x - 50, y + 50), 0, 1,
                            [225, 255, 255], thickness=1, lineType=cv2.LINE_AA)  # 标出坐标
    cv2.namedWindow("circle_detect",0)
    cv2.imshow("circle_detect", frame)
    k = cv2.waitKey(5) & 0xFF
    if k == 27:
        break

cv2.destroyAllWindows()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小蓝xlanll/article/detail/86712
推荐阅读
相关标签
  

闽ICP备14008679号