树莓派视觉小车 -- 小球追踪(颜色追踪)(OpenCV色彩空间HSV)

2023-11-20

目录

效果展示 

基础理论(HSV)

为什么用HSV空间而不是RGB空间?

HSV

1、Hue(色相)

2、Value(明度)

3、Saturation(饱和度)

一、初始化

滑动条初始化

1、创建回调函数

2、窗口设置(名称) 

3、滑动条设置

代码

二、运动函数

三、舵机控制

四、在HSV空间下获取二值图

1、获取滑动条的值

2、转HSV(三通道:H、S、V)

3、转二值图(单通道阈值处理)

4、结合HSV三个通道,得到最终二值图

代码

五、图像处理(总)

1、打开摄像头

2、获取HSV色彩空间得到的二值图

3、高斯滤波

4、开运算去噪

5、闭运算

6、霍夫圆检测

原理

API

 6-1、霍夫圆检测

6-2、获取圆心和半径坐标

6-3、画圆

代码

六、运动控制

总代码


效果展示 

 

 

基础理论(HSV)

为什么用HSV空间而不是RGB空间?

因为RGB通道并不能很好地反映出物体具体的颜色信息

HSV空间能够非常直观的表达色彩的明暗、色调、以及鲜艳程度,方便进行颜色之间的对比。

(RGB受光线影响很大,所以采取HSV

这里用HSV的目的:得到合适的二值图

HSV

 

 

Hue(H):色调、色相(具体的颜色)

Saturation(S):饱和度、色彩纯净度

Value(V):明度

Hue范围是[0,179],饱和范围是[0,255],值范围是[0,255]

(写代码的时候,犯了蠢错误:把3个都设成了179,发现死活调不对)

1、Hue(色相)

Hue:色相(具体的颜色)


2、Value(明度)

明度:色彩的明亮程度,单通道亮度(并不等同于整体发光量)。

(明度越高越白,越低越黑,一般提高明度会同时提高R、G、B三通道的数值)

3、Saturation(饱和度)

Saturation:饱和度、色彩纯度(越低越灰,越高越纯)  。

(一般调高饱和度会降低RGB中相对较低的数值,凸显主要颜色的纯度。 )

B站视频讲解:

短动画慢语速1分钟讲清影视调色中色彩形成原理基础——RGB与HSV

一、初始化

滑动条初始化

这次的初始化,除了电机、舵机、窗口等等的初始化,还有滑动条的初始设置。 

在创建滑动条之前:

1、需要设置窗口名称,不然窗口都没有,自然也就无法设置滑动条了。

2、创建回调函数。

1、创建回调函数

def nothing(*arg):
    pass

2、窗口设置(名称) 

def Trackbar_Init():
    # 1 create windows
    cv2.namedWindow('h_binary')
    cv2.namedWindow('s_binary')
    cv2.namedWindow('v_binary')

3、滑动条设置

# 2 Create Trackbar
    cv2.createTrackbar('hmin', 'h_binary', 6, 179, nothing)  
    cv2.createTrackbar('hmax', 'h_binary', 26, 179, nothing)  
    cv2.createTrackbar('smin', 's_binary', 110, 255, nothing)
    cv2.createTrackbar('smax', 's_binary', 255, 255, nothing)
    cv2.createTrackbar('vmin', 'v_binary', 140, 255, nothing)
    cv2.createTrackbar('vmax', 'v_binary', 255, 255, nothing)
    #   创建滑动条     滑动条值名称 窗口名称   滑动条值 滑动条阈值 回调函数

代码

def Motor_Init():
    global L_Motor, R_Motor
    L_Motor= GPIO.PWM(l_motor,100)
    R_Motor = GPIO.PWM(r_motor,100)
    L_Motor.start(0)
    R_Motor.start(0)


def Direction_Init():
    GPIO.setup(left_back,GPIO.OUT)
    GPIO.setup(left_front,GPIO.OUT)
    GPIO.setup(l_motor,GPIO.OUT)
    
    GPIO.setup(right_front,GPIO.OUT)
    GPIO.setup(right_back,GPIO.OUT)
    GPIO.setup(r_motor,GPIO.OUT)


def Servo_Init():
    global pwm_servo
    pwm_servo=Adafruit_PCA9685.PCA9685()


def Trackbar_Init():
    # 1 create windows
    cv2.namedWindow('h_binary')
    cv2.namedWindow('s_binary')
    cv2.namedWindow('v_binary')
    # 2 Create Trackbar
    cv2.createTrackbar('hmin', 'h_binary', 6, 179, nothing)  
    cv2.createTrackbar('hmax', 'h_binary', 26, 179, nothing)  
    cv2.createTrackbar('smin', 's_binary', 110, 255, nothing)
    cv2.createTrackbar('smax', 's_binary', 255, 255, nothing)
    cv2.createTrackbar('vmin', 'v_binary', 140, 255, nothing)
    cv2.createTrackbar('vmax', 'v_binary', 255, 255, nothing)
    #   创建滑动条     滑动条值名称 窗口名称   滑动条值 滑动条阈值 回调函数


def Init():
    GPIO.setwarnings(False) 
    GPIO.setmode(GPIO.BCM)
    Direction_Init()
    Servo_Init()
    Motor_Init()
    Trackbar_Init()


# 回调函数
def nothing(*arg):
    pass

二、运动函数

常规操作:向前、向后、向左、向右、停止。 

def Front(speed):
    L_Motor.ChangeDutyCycle(speed)
    GPIO.output(left_front,1)   #left_front
    GPIO.output(left_back,0)    #left_back

    R_Motor.ChangeDutyCycle(speed)
    GPIO.output(right_front,1)  #right_front
    GPIO.output(right_back,0)   #right_back
     
    
def Back(speed):
    L_Motor.ChangeDutyCycle(speed)
    GPIO.output(left_front,0)   #left_front
    GPIO.output(left_back,1)    #left_back

    R_Motor.ChangeDutyCycle(speed)
    GPIO.output(right_front,0)  #right_front
    GPIO.output(right_back,1)   #right_back


def Left(speed):
    L_Motor.ChangeDutyCycle(speed)
    GPIO.output(left_front,0)   #left_front
    GPIO.output(left_back,1)    #left_back

    R_Motor.ChangeDutyCycle(speed)
    GPIO.output(right_front,1)  #right_front
    GPIO.output(right_back,0)   #right_back


def Right(speed):
    L_Motor.ChangeDutyCycle(speed)
    GPIO.output(left_front,1)   #left_front
    GPIO.output(left_back,0)    #left_back

    R_Motor.ChangeDutyCycle(speed)
    GPIO.output(right_front,0)  #right_front
    GPIO.output(right_back,1)   #right_back


def Stop():
    L_Motor.ChangeDutyCycle(0)
    GPIO.output(left_front,0)   #left_front
    GPIO.output(left_back,0)    #left_back

    R_Motor.ChangeDutyCycle(0)
    GPIO.output(right_front,0)  #right_front
    GPIO.output(right_back,0)   #right_back    

三、舵机控制

def set_servo_angle(channel,angle):
    angle=4096*((angle*11)+500)/20000
    pwm_servo.set_pwm_freq(50)                #frequency==50Hz (servo)
    pwm_servo.set_pwm(channel,0,int(angle))
set_servo_angle(4, 110)     #top servo     lengthwise
    #0:back    180:front    
    set_servo_angle(5, 90)     #bottom servo  crosswise
    #0:left    180:right  

四、在HSV空间下获取二值图

1、获取滑动条的值

# 1 get trackbar's value
    hmin = cv2.getTrackbarPos('hmin', 'h_binary')
    hmax = cv2.getTrackbarPos('hmax', 'h_binary')
    smin = cv2.getTrackbarPos('smin', 's_binary')
    smax = cv2.getTrackbarPos('smax', 's_binary')
    vmin = cv2.getTrackbarPos('vmin', 'v_binary')
    vmax = cv2.getTrackbarPos('vmax', 'v_binary')

2、转HSV(三通道:H、S、V)

# 2 to HSV
    hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
    cv2.imshow('hsv', hsv)
    h, s, v = cv2.split(hsv)

 

3、转二值图(单通道阈值处理)

分别对H、S、V三个道阈值处理:

# 3 set threshold (binary image)
    # if value in (min, max):white; otherwise:black
    h_binary = cv2.inRange(np.array(h), np.array(hmin), np.array(hmax))
    s_binary = cv2.inRange(np.array(s), np.array(smin), np.array(smax))
    v_binary = cv2.inRange(np.array(v), np.array(vmin), np.array(vmax))

# 5 Show
    cv2.imshow('h_binary', h_binary)
    cv2.imshow('s_binary', s_binary)
    cv2.imshow('v_binary', v_binary)

4、结合HSV三个通道,得到最终二值图

对H、S、V三个通道与操作。(H&&S&&V)

# 4 get binary
    binary = cv2.bitwise_and(h_binary, cv2.bitwise_and(s_binary, v_binary))

代码

# 在HSV色彩空间下得到二值图
def Get_HSV(image):
    # 1 get trackbar's value
    hmin = cv2.getTrackbarPos('hmin', 'h_binary')
    hmax = cv2.getTrackbarPos('hmax', 'h_binary')
    smin = cv2.getTrackbarPos('smin', 's_binary')
    smax = cv2.getTrackbarPos('smax', 's_binary')
    vmin = cv2.getTrackbarPos('vmin', 'v_binary')
    vmax = cv2.getTrackbarPos('vmax', 'v_binary')
    
    # 2 to HSV
    hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
    cv2.imshow('hsv', hsv)
    h, s, v = cv2.split(hsv)
    
    # 3 set threshold (binary image)
    # if value in (min, max):white; otherwise:black
    h_binary = cv2.inRange(np.array(h), np.array(hmin), np.array(hmax))
    s_binary = cv2.inRange(np.array(s), np.array(smin), np.array(smax))
    v_binary = cv2.inRange(np.array(v), np.array(vmin), np.array(vmax))
    
    # 4 get binary(对H、S、V三个通道分别与操作)
    binary = cv2.bitwise_and(h_binary, cv2.bitwise_and(s_binary, v_binary))
    
    # 5 Show
    cv2.imshow('h_binary', h_binary)
    cv2.imshow('s_binary', s_binary)
    cv2.imshow('v_binary', v_binary)
    cv2.imshow('binary', binary)
    
    return binary

五、图像处理(总)

1、打开摄像头

# 1 Capture the frames
    ret, frame = camera.read()
    image = frame
    cv2.imshow('frame', frame)

2、获取HSV色彩空间得到的二值图

(Get_HSV(frame)其实就是步骤四的HSV处理)

# 2 get HSV
    binary = Get_HSV(frame)

3、高斯滤波

# 3 Gausi blur
    blur = cv2.GaussianBlur(binary,(9,9),0)

4、开运算去噪

# 4 Open
    kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (9,9))
    Open = cv2.morphologyEx(blur, cv2.MORPH_OPEN, kernel)
    cv2.imshow('Open',Open)

5、闭运算

# 5 Close
    Close = cv2.morphologyEx(Open, cv2.MORPH_CLOSE, kernel)
    cv2.imshow('Close',Close)

 

6、霍夫圆检测

原理

霍夫变换圆检测是基于图像梯度实现:

圆心检测的原理︰圆心是圆周法线的交汇处设置一个阈值在某点的相交的直线的条数大于这个阈值就认为该交汇点为圆心
圆半径确定原理:圆心到圆周上的距离〔半径)是相同的设置一个阈值只要相同距离的数量大于该阈值就认为该距离是该圆心的半径

API

def HoughCircles(image: Any,
                 method: Any,
                 dp: Any,
                 minDist: Any,
                 circles: Any = None,
                 param1: Any = None,
                 param2: Any = None,
                 minRadius: Any = None,
                 maxRadius: Any = None) -> None

参数: 

  • 第二个参数,int类型的method,即使用的检测方法,目前OpenCV中就霍夫梯度法一种可以使用,它的标识符为CV_HOUGH_GRADIENT,在此参数处填这个标识符即可。
  • 第三个参数,double类型的dp,用来检测圆心的累加器图像的分辨率于输入图像之比的倒数,且此参数允许创建一个比输入图像分辨率低的累加器。上述文字不好理解的话,来看例子吧。例如,如果dp= 1时,累加器和输入图像具有相同的分辨率。如果dp=2,累加器便有输入图像一半那么大的宽度和高度。
  • 第四个参数,double类型的minDist,为霍夫变换检测到的圆的圆心之间的最小距离,即让我们的算法能明显区分的两个不同圆之间的最小距离。这个参数如果太小的话,多个相邻的圆可能被错误地检测成了一个重合的圆。反之,这个参数设置太大的话,某些圆就不能被检测出来了。
  • 第五个参数,InputArray类型的circles,经过调用HoughCircles函数后此参数存储了检测到的圆的输出矢量,每个矢量由包含了3个元素的浮点矢量(x, y, radius)表示。
  • 第六个参数,double类型的param1,有默认值100。它是第三个参数method设置的检测方法的对应的参数。对当前唯一的方法霍夫梯度法CV_HOUGH_GRADIENT,它表示传递给canny边缘检测算子的高阈值,而低阈值为高阈值的一半。
  • 第七个参数,double类型的param2,也有默认值100。它是第三个参数method设置的检测方法的对应的参数。对当前唯一的方法霍夫梯度法CV_HOUGH_GRADIENT,它表示在检测阶段圆心的累加器阈值。它越小的话,就可以检测到更多根本不存在的圆,而它越大的话,能通过检测的圆就更加接近完美的圆形了
  • 第八个参数,int类型的minRadius,有默认值0,表示圆半径的最小值
  • 第九个参数,int类型的maxRadius,也有默认值0,表示圆半径的最大值

 6-1、霍夫圆检测

# 6 Hough Circle detect
    circles = cv2.HoughCircles(Close,cv2.HOUGH_GRADIENT,2,120,param1=120,param2=20,minRadius=20,maxRadius=0)
    #                                                                     param2:决定圆能否被检测到(越少越容易检测到圆,但相应的也更容易出错)

6-2、获取圆心和半径坐标

# 1 获取圆的圆心和半径
        x, y, r = int(circles[0][0][0]),int(circles[0][0][1]),int(circles[0][0][2])
        print(x, y, r)

6-3、画圆

# 2 画圆
        cv2.circle(image, (x, y), r, (255,0,255),5)
        cv2.imshow('image', image)

代码

# 图像处理
def Image_Processing():
    global h, s, v
    # 1 Capture the frames
    ret, frame = camera.read()
    image = frame
    cv2.imshow('frame', frame)
    
    # 2 get HSV
    binary = Get_HSV(frame)
    
    # 3 Gausi blur
    blur = cv2.GaussianBlur(binary,(9,9),0)
    
    # 4 Open
    kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (9,9))
    Open = cv2.morphologyEx(blur, cv2.MORPH_OPEN, kernel)
    cv2.imshow('Open',Open)
    # 5 Close
    Close = cv2.morphologyEx(Open, cv2.MORPH_CLOSE, kernel)
    cv2.imshow('Close',Close)

    # 6 Hough Circle detect
    circles = cv2.HoughCircles(Close,cv2.HOUGH_GRADIENT,2,120,param1=120,param2=20,minRadius=20,maxRadius=0)
    #                                                                     param2:决定圆能否被检测到(越少越容易检测到圆,但相应的也更容易出错)
    # judge if circles is exist
    if circles is not None:
        # 1 获取圆的圆心和半径
        x, y, r = int(circles[0][0][0]),int(circles[0][0][1]),int(circles[0][0][2])
        print(x, y, r)
        # 2 画圆
        cv2.circle(image, (x, y), r, (255,0,255),5)
        cv2.imshow('image', image)
    else:
        (x,y),r = (0,0), 0
        
    return (x,y), r

六、运动控制

根据检测到的圆,获取到的坐标、半径,进行运动控制。

这里可以做到跟踪小球,前进和后退相配合,“敌进我退,敌退我进”。

# 运动控制(这里可以做到跟踪小球,前景和后退相配合,“敌进我退,敌退我进”)
def Move((x,y), r):
    low_xlimit = width/4
    high_xlimit = 0.75 * width
    #low_ylimit = 3/4 * height
    ylimit = 0.75 * height
    print(high_xlimit, ylimit)
    # 没检测到,停止不动
    if x==0:
        Stop()
    # 检测到在图片0.75以上的区域(距离正常)
    elif x>low_xlimit and x<high_xlimit and y<ylimit:
        Front(60)
    # 检测到在图片0.75以下的区域(距离过近,后退)
    elif x>low_xlimit and x<high_xlimit and y>=ylimit:
        Back(60)
    # 在左0.25区域,向左跟踪
    elif x<low_xlimit:
        Left(60)
    # 在右0.25区域,向右跟踪
    elif x>high_xlimit:
        Right(60)

总代码

#Ball Tracking(HSV)
import  RPi.GPIO as GPIO
import time
import Adafruit_PCA9685
import numpy as np
import cv2

#set capture window
width, height = 320, 240
camera = cv2.VideoCapture(0)
camera.set(3,width) 
camera.set(4,height) 

l_motor = 18
left_front   =  22
left_back   =  27

r_motor = 23
right_front   = 25
right_back  =  24


def Motor_Init():
    global L_Motor, R_Motor
    L_Motor= GPIO.PWM(l_motor,100)
    R_Motor = GPIO.PWM(r_motor,100)
    L_Motor.start(0)
    R_Motor.start(0)


def Direction_Init():
    GPIO.setup(left_back,GPIO.OUT)
    GPIO.setup(left_front,GPIO.OUT)
    GPIO.setup(l_motor,GPIO.OUT)
    
    GPIO.setup(right_front,GPIO.OUT)
    GPIO.setup(right_back,GPIO.OUT)
    GPIO.setup(r_motor,GPIO.OUT)


def Servo_Init():
    global pwm_servo
    pwm_servo=Adafruit_PCA9685.PCA9685()


def Trackbar_Init():
    # 1 create windows
    cv2.namedWindow('h_binary')
    cv2.namedWindow('s_binary')
    cv2.namedWindow('v_binary')
    # 2 Create Trackbar
    cv2.createTrackbar('hmin', 'h_binary', 6, 179, nothing)  
    cv2.createTrackbar('hmax', 'h_binary', 26, 179, nothing)  
    cv2.createTrackbar('smin', 's_binary', 110, 255, nothing)
    cv2.createTrackbar('smax', 's_binary', 255, 255, nothing)
    cv2.createTrackbar('vmin', 'v_binary', 140, 255, nothing)
    cv2.createTrackbar('vmax', 'v_binary', 255, 255, nothing)
    #   创建滑动条     滑动条值名称 窗口名称   滑动条值 滑动条阈值 回调函数


def Init():
    GPIO.setwarnings(False) 
    GPIO.setmode(GPIO.BCM)
    Direction_Init()
    Servo_Init()
    Motor_Init()
    Trackbar_Init()


def Front(speed):
    L_Motor.ChangeDutyCycle(speed)
    GPIO.output(left_front,1)   #left_front
    GPIO.output(left_back,0)    #left_back

    R_Motor.ChangeDutyCycle(speed)
    GPIO.output(right_front,1)  #right_front
    GPIO.output(right_back,0)   #right_back
     
    
def Back(speed):
    L_Motor.ChangeDutyCycle(speed)
    GPIO.output(left_front,0)   #left_front
    GPIO.output(left_back,1)    #left_back

    R_Motor.ChangeDutyCycle(speed)
    GPIO.output(right_front,0)  #right_front
    GPIO.output(right_back,1)   #right_back


def Left(speed):
    L_Motor.ChangeDutyCycle(speed)
    GPIO.output(left_front,0)   #left_front
    GPIO.output(left_back,1)    #left_back

    R_Motor.ChangeDutyCycle(speed)
    GPIO.output(right_front,1)  #right_front
    GPIO.output(right_back,0)   #right_back


def Right(speed):
    L_Motor.ChangeDutyCycle(speed)
    GPIO.output(left_front,1)   #left_front
    GPIO.output(left_back,0)    #left_back

    R_Motor.ChangeDutyCycle(speed)
    GPIO.output(right_front,0)  #right_front
    GPIO.output(right_back,1)   #right_back


def Stop():
    L_Motor.ChangeDutyCycle(0)
    GPIO.output(left_front,0)   #left_front
    GPIO.output(left_back,0)    #left_back

    R_Motor.ChangeDutyCycle(0)
    GPIO.output(right_front,0)  #right_front
    GPIO.output(right_back,0)   #right_back


def set_servo_angle(channel,angle):
    angle=4096*((angle*11)+500)/20000
    pwm_servo.set_pwm_freq(50)                #frequency==50Hz (servo)
    pwm_servo.set_pwm(channel,0,int(angle))


# 回调函数
def nothing(*arg):
    pass


# 在HSV色彩空间下得到二值图
def Get_HSV(image):
    # 1 get trackbar's value
    hmin = cv2.getTrackbarPos('hmin', 'h_binary')
    hmax = cv2.getTrackbarPos('hmax', 'h_binary')
    smin = cv2.getTrackbarPos('smin', 's_binary')
    smax = cv2.getTrackbarPos('smax', 's_binary')
    vmin = cv2.getTrackbarPos('vmin', 'v_binary')
    vmax = cv2.getTrackbarPos('vmax', 'v_binary')
    
    # 2 to HSV
    hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
    cv2.imshow('hsv', hsv)
    h, s, v = cv2.split(hsv)
    
    # 3 set threshold (binary image)
    # if value in (min, max):white; otherwise:black
    h_binary = cv2.inRange(np.array(h), np.array(hmin), np.array(hmax))
    s_binary = cv2.inRange(np.array(s), np.array(smin), np.array(smax))
    v_binary = cv2.inRange(np.array(v), np.array(vmin), np.array(vmax))
    
    # 4 get binary(对H、S、V三个通道分别与操作)
    binary = cv2.bitwise_and(h_binary, cv2.bitwise_and(s_binary, v_binary))
    
    # 5 Show
    cv2.imshow('h_binary', h_binary)
    cv2.imshow('s_binary', s_binary)
    cv2.imshow('v_binary', v_binary)
    cv2.imshow('binary', binary)
    
    return binary


# 图像处理
def Image_Processing():
    global h, s, v
    # 1 Capture the frames
    ret, frame = camera.read()
    image = frame
    cv2.imshow('frame', frame)
    
    # 2 get HSV
    binary = Get_HSV(frame)
    
    # 3 Gausi blur
    blur = cv2.GaussianBlur(binary,(9,9),0)
    
    # 4 Open
    kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (9,9))
    Open = cv2.morphologyEx(blur, cv2.MORPH_OPEN, kernel)
    cv2.imshow('Open',Open)
    # 5 Close
    Close = cv2.morphologyEx(Open, cv2.MORPH_CLOSE, kernel)
    cv2.imshow('Close',Close)

    # 6 Hough Circle detect
    circles = cv2.HoughCircles(Close,cv2.HOUGH_GRADIENT,2,120,param1=120,param2=20,minRadius=20,maxRadius=0)
    #                                                                     param2:决定圆能否被检测到(越少越容易检测到圆,但相应的也更容易出错)
    # judge if circles is exist
    if circles is not None:
        # 1 获取圆的圆心和半径
        x, y, r = int(circles[0][0][0]),int(circles[0][0][1]),int(circles[0][0][2])
        print(x, y, r)
        # 2 画圆
        cv2.circle(image, (x, y), r, (255,0,255),5)
        cv2.imshow('image', image)
    else:
        (x,y),r = (0,0), 0
        
    return (x,y), r


# 运动控制(这里可以做到跟踪小球,前景和后退相配合,“敌进我退,敌退我进”)
def Move((x,y), r):
    low_xlimit = width/4
    high_xlimit = 0.75 * width
    #low_ylimit = 3/4 * height
    ylimit = 0.75 * height
    print(high_xlimit, ylimit)
    # 没检测到,停止不动
    if x==0:
        Stop()
    # 检测到在图片0.75以上的区域(距离正常)
    elif x>low_xlimit and x<high_xlimit and y<ylimit:
        Front(60)
    # 检测到在图片0.75以下的区域(距离过近,后退)
    elif x>low_xlimit and x<high_xlimit and y>=ylimit:
        Back(60)
    # 在左0.25区域,向左跟踪
    elif x<low_xlimit:
        Left(60)
    # 在右0.25区域,向右跟踪
    elif x>high_xlimit:
        Right(60)

    
if __name__ == '__main__':
    Init()
    
    set_servo_angle(4, 110)     #top servo     lengthwise
    #0:back    180:front    
    set_servo_angle(5, 90)     #bottom servo  crosswise
    #0:left    180:right  
    
    while 1:
        # 1 Image Process
        (x,y), r = Image_Processing()
        
        # 2 Move
        Move((x,y), r)
        
        # must include this codes(otherwise you can't open camera successfully)
        if cv2.waitKey(1) & 0xFF == ord('q'):
            Stop()
            GPIO.cleanup()    
            break

这里的HSV是根据我自己当前的情况调节的,更改场景以后可能需要重新调节H、S、V三通道的阈值(max && min)

基础的视觉检测+运动,没有太多的运动控制算法(PID等等都没有涉及到)。有好的想法和建议欢迎交流讨论,Thanks♪(・ω・)ノ

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

树莓派视觉小车 -- 小球追踪(颜色追踪)(OpenCV色彩空间HSV) 的相关文章

  • 屏幕截图中低分辨率文本的 OCR

    我正在编写一个 OCR 应用程序来从屏幕截图图像中读取字符 目前 我只关注数字 我的方法部分基于这篇博文 http blog damiles com 2008 11 basic ocr in opencv http blog damiles
  • 如何使用requirements.txt 在 Heroku python Web 应用程序中安装 Dlib?

    我构建了一个涉及机器学习的 Python Flask Web API 但在 Heroku 上部署它时遇到了很多挫折 问题是 我的应用程序依赖于 Dlib 一个库 我似乎找不到在我的 Heroku 服务器中安装的方法 我正在试图解决这个问题
  • 使用相位相关和对数极坐标变换获得旋转位移

    我一直在编写一个脚本 它使用 cv2 计算两个图像之间的旋转位移phaseCorrelate method 我有两张图像 第二张是第一张图像的 90 度旋转版本 加载图像后 我将它们转换为对数极坐标 然后将它们传递到phaseCorrela
  • 使用 openCV 和 python 检测物体

    我正在尝试使用 OpenCV 和 Python 检测下图中的白点 我尝试使用函数 cv2 HoughCircles 但没有成功 我需要使用不同的方法吗 这是我的代码 import cv2 cv import numpy as np impo
  • 计算两个描述符之间的距离

    我正在尝试计算已计算的两个描述符之间的距离 欧几里得或汉明 问题是我不想使用匹配器 我只想计算两个描述符之间的距离 我正在使用 OpenCV 2 4 9 并且我的描述符存储在 Mat 类型中 Mat descriptors1 Mat des
  • OpenCV:视频结束后如何重新启动?

    我正在播放视频文件 但播放完毕后如何再次播放 Javier 如果您想一遍又一遍地重新启动视频 也称为循环播放 可以通过在帧数达到时使用 if 语句来实现cap get cv2 cv CV CAP PROP FRAME COUNT 然后重置帧
  • 在 RGB 图像上绘制多类语义分割透明叠加

    我有语义分割掩码的结果 值在 0 1 之间 需要大津阈值来确定什么是积极的 我想直接在 RGB 图像上绘制 在 RGB 图像上每个预测类具有不同的随机颜色 我使用以下内容绘制了具有单一颜色的单个蒙版 是否有一个包或简单的策略可以为多类别做到
  • 如何在给定目标大小的情况下在 python 中调整图像大小,同时保留纵横比?

    首先 我觉得这是一个愚蠢的问题 对此感到抱歉 目前 我发现计算最佳缩放因子 目标像素数的最佳宽度和高度 同时保留纵横比 的最准确方法是迭代并选择最佳缩放因子 但是必须有更好的方法来做到这一点 一个例子 import cv2 numpy as
  • 来自 OpenCV 的外部参数

    我正在使用 OpenCV 来校准立体相机对 我拍摄了各种校准照片 并且使用 cv2 calibrateCamera 对内在参数进行了令人满意的拟合 然而 目前尚不清楚如何获取外部参数 该函数仅返回cameraMatrix 尽管它很有用 但实
  • Python:opencv warpPerspective 既不接受 2 个也不接受 3 个参数

    我发现单应矩阵如下特征匹配 单应性教程 https docs opencv org 3 4 1 d1 de0 tutorial py feature homography html using M mask cv2 findHomograp
  • 多视图几何

    我从相距一定距离的两台相同品牌的相机捕获了两张图像 捕获了相同的场景 我想计算两个相机之间的现实世界旋转和平移 为了实现这一点 我首先提取了两张图像的 SIFT 特征并进行匹配 我现在有基本矩阵也单应性矩阵 然而无法进一步进行 有很多混乱
  • opencv形态扩张滤波器作为最大滤波器

    就像中值滤波器的定义一样 我可以将 最大滤波器 定义为局部窗口 例如dst x y max 3x3 局部窗口像素 但我在opencv中找不到这样的过滤器 最接近的是 dilate 函数 然后我使用 dilate 函数的默认配置 但结果不正确
  • 如何将输出视频保存到 OpenCV 中的文件中

    我想将输出视频保存到文件中而不是显示它并尝试使用 cvcaptureimage 但仍然无法获得结果 include
  • 检查图像中是否有太薄的区域

    我正在尝试验证雕刻机的黑白图像 更多的是剪贴画图像 不是照片 我需要考虑的主要事情之一是区域的大小 或线条的宽度 因为机器无法处理太细的线条 所以我需要找到比给定阈值更细的区域 以此图为例 竖琴的琴弦可能太细而无法雕刻 我正在阅读有关 Ma
  • 为什么我无法在 Mac 12.0.1 (Monterey) 上使用 pip 安装 OpenCV? [复制]

    这个问题在这里已经有答案了 当我尝试使用 python pip 安装 OpenCV 时 它显示了以下内容 Remainder of file ignored Requirement already satisfied pip in Libr
  • 如何去除给定图像中的噪声,使 ocr 输出完美?

    我已经对这个孟加拉文本图像进行了大津阈值处理 并使用 tesseract 进行 OCR 但输出非常糟糕 我应该应用什么预处理来消除噪音 我也想校正图像 因为它有轻微的倾斜 我的代码如下 import tesserocr from PIL i
  • OpenCV IP 相机应用程序崩溃 [h264 @ 0xxxxx] 访问单元中缺少图片

    我在 cpp 中有一个 opencv 应用程序 它使用 opencv 的简单结构捕获视频流并将其保存到视频文件中 它与我的网络摄像头完美配合 但是 当我运行它从 IP 摄像机捕获流时 它可能会在大约十秒后崩溃 我的编译命令是 g O3 IP
  • 如何使用 Python 裁剪图像中的矩形

    谁能给我关于如何裁剪两个矩形框并保存它的建议 我已经尝试过这段代码 但效果不佳 import cv2 import numpy as np Run the code with the image name keep pressing spa
  • 如何使用 colorchecker 在 opencv 中进行颜色校准?

    我有数码相机获取的色彩检查器图像 我如何使用它来使用 opencv 校准图像 按照以下颜色检查器图像操作 您是想问如何进行颜色校准或如何使用 OpenCV 进行校准 为了进行颜色校准 您可以使用校准板的最后一行 灰色调 以下是您应该逐步进行
  • 如何将 Mat (opencv) 转换为 INDArray (DL4J)?

    我希望任何人都可以帮助我解决这个任务 我正在处理一些图像分类并尝试将 OpenCv 3 2 0 和 DL4J 结合起来 我知道DL4J也包含Opencv 但我认为它没什么用 谁能帮我 如何转换成 INDArray 我尝试阅读一些问题here

随机推荐

  • JPEG编码原理与解码分析

    JPEG编码原理 JPEG Joint Photographic Experts Group 是JPEG标准的产物 该标准由国际标准化组织 ISO 制订 是面向连续色调静止图像的一种压缩标准 JPEG格式是最常用的图像文件格式 后缀名为 j
  • 数据挖掘—数据预处理

    文章目录 数据预处理 1 数据清洗 缺失值处理 异常值处理 2 数据集成 实体识别 冗余属性识别 数据变换 简单函数变换 规范化 连续属性离散化 属性构造 3 数据规约 属性归约 数值归约 Python主要数据预处理函数 数据预处理 数据预
  • 【uniapp】使用canvas组件编译到微信小程序兼容出错问题

    使用uniapp编译跨平台项目会遇到不少兼容问题 这里主要讲canvas组件的 编译到微信小程序会有兼容出错问题 这里给讲一下解决方案 希望有帮助 常见问题 draw无法绘制图形 如果使用CanvasContext绘制 以下代码 编译到微信
  • 值得收藏的UmiJS 教程

    点击上方关注 前端技术江湖 一起学习 天天进步 前言 网上的umi教程是真的少 很多人都只写了一点点 很多水文 所以打算自己写一篇 自己搭建umi 并封装了一下常用的功能 并用到公司实际项目中 umi介绍 Umi 是什么 Umi 中文可发音
  • maven学习笔记 maven的使用

    新建maven项目 使用mvn archetype generate命令新建一个maven项目 maven会自动下载必要的插件 还会下载一个所有项目模板的分类文件 这个文件有好几兆的大小 因此可能会持续比较长的时间 下载完毕之后 就会列出所
  • JAVA 8 新特性及使用

    1 前言 2019年9月19日java13已正式发布 感叹java社区强大 经久不衰 由于国内偏保守 新东西总要放一放 让其他人踩踩坑 等稳定了才会去用 并且企业目的还是赚钱 更不会因为一个新特性去重构代码 再开发一套程序出来 甚者国内大多
  • 不一样的联宇益通,不一样的SD-WAN+

    点击上方 中国云报 可关注 笔者有点挠头 究竟该用哪个词来描述联宇益通 Netpas 公司呢 低调 技术控 特立独行 还是自得其乐 似乎都有些影子 但又都不是最准确的表达 与联宇益通创始人兼CEO谢毅斌聊得越深入 感觉联宇益通身上矛盾的地方
  • 软件测试 app自动化02 Appium常用的元素定位工具 元素的属性 元素定位方法

    文章目录 1 Appium常用的元素定位工具 1 1 uiautomatorviewer 1 2 Appium Inspector 1 3 Weditor 2 元素的属性 3 元素定位方法 小结 1 Appium常用的元素定位工具 1 1
  • 数据库学习笔记(8)——mysql中的函数和存储过程

    1 MySQL中的函数 1 数据库主要做存储和查询操作 逻辑操作一般不在数据库中进行操作 2 MySQL中的函数主要是自定义函数 其中自定义函数格式如下 修改语句结束符 delimiter create function 函数名 参数名 数
  • 操作系统常见面试题

    1 什么是进程 Process 和线程 Thread 有何区别 进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动 进程是系统进行资源分配和调度的一个独立单位 线程是进程的一个实体 是CPU调度和分派的基本单位 它是比进程更小的能
  • java-线程锁

    实现锁 1 同步代码块 2 同步方法 在方法的头部加上synchronized 3 Lock 功能比synchronized更加的强大 但是加锁的时一定不要忘记解锁unlock 在使用lock锁时 想要实现睡眠唤醒功能 就要使用condit
  • 决策报表---动态参数实现多级下拉折叠菜单

    1 描述 在报表开发中 我们会遇到报表需要对行标题实现展开收起的折叠菜单的效果 这种效果一般在分析预览或者填报中应用下拉树控件来实现 但是在分页预览或者决策报表如何实现呢 这里我们使用动态参数的方式 预期效果 1 在分页预览和决策报表中能适
  • 操作系统中的线程&进程和同步&异步和并发&并行

    操作系统中的线程 进程和同步 异步和并发 并行 一 进程和线程 1 1 进程 1 2 线程 1 3 实现多任务的方法 1 3 1 使用多进程实现多任务 1 3 2 使用多线程 单个进程包含多个线程 实现多任务 1 3 3 使用多进程 多进程
  • vue+element中如何设置单个el-date-picker开始时间和结束时间关联

    功能 选了开始时间 则结束时间只能选择开始时间之后的 选了结束时间 则开始时间只能选择结束时间之前的 重点是picker options属性 图示 代码展示 body 内部
  • JavaScript设计模式-02-单例模式

    Javascript 设计模式 02 单例模式 简介 单例就是保证一个类只有一个实例 实现的方法一般是先判断实例是否存在 如果存在直接返回 如果不存在就创建了再返回 确保了一个类只有一个实例对象 在JavaScript里 单例作为一个命名空
  • AIGC(AI Generated Content,人工智能生成内容)

    AIGC AI Generated Content 人工智能生成内容 什么是AIGC AIGC Artificial Intelligence Generated Content AI Generated Content 中文译为人工智能生
  • 读《effective modern c++》笔记总结

    文章目录 一 类型推导与auto 模板类型推导 ParamType是一个指针或引用 但不是通用引用 ParamType是一个通用引用 ParamType即不是指针也不是引用 数组实参 函数实参 auto类型推导 二 decltype的理解
  • make menuconfig报错:Build dependency: Please install Git (git-core) >= 1.6.5

    版本号为chaos calmer 15 05 1 注意 在执行make menuconfig的时候 会报一个错误 如下 Build dependency Please install Git git core gt 1 6 5 这是open
  • 节流与防抖

    1 我们先了解为什么要节流和防抖 我们给一个inpu输入框绑定一个oninput事件 此时我们输入 前端开发 四个字 我们 观察以下后台打印
  • 树莓派视觉小车 -- 小球追踪(颜色追踪)(OpenCV色彩空间HSV)

    目录 效果展示 基础理论 HSV 为什么用HSV空间而不是RGB空间 HSV 1 Hue 色相 2 Value 明度 3 Saturation 饱和度 一 初始化 滑动条初始化 1 创建回调函数 2 窗口设置 名称 3 滑动条设置 代码 二