【树莓派4B学习】十二、摄像头辅助opencv实现巡线

一、基础知识

二值化操作

首先看一个常用的图像处理操作“二值化” , opencv 提供了多种二值化形式:

  • cv2.THRESH_BINARY
  • cv2.THRESH_BINARY_INV
  • cv2.THRESH_TRUNC
  • cv2.THRESH_TOZERO
  • cv2.THRESH_TOZERO_INV

在灰度图像中, 0-255 代表着亮度等级, 黑色为 0, 白色为 255。 标准的二值化就是根据设定的亮度阈值, 将超过这个亮度的点赋值为白色的 255, 低于这个阈值的点赋值为黑色的0。

目前做巡黑线, 使用正常模式 cv2.THRESH_BINARY。 如果是巡白线, 就只需要修改成反色模式cv2.THRESH_BINARY_INV 就可以将白色线条转换成黑色点来用同一个程序了。

这几种操作的效果不同, 下图是一个灰度图在阈值为 127 下对应的输出图像

在这里插入图片描述

二、示例代码

#coding:utf-8
#Python中声明文件编码的注释,编码格式指定为utf-8
from socket import *
from time import ctime
import binascii
import RPi.GPIO as GPIO
import time
import threading
import cv2
import numpy as np
print '....WIFIROBOTS START!!!...'
global Path_Dect_px
Path_Dect_px = 320
GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)
########LED口定义#################
LED0 = 10
LED1 = 9
LED2 = 25
########电机驱动接口定义#################
ENA = 13	#//L298使能A
ENB = 20	#//L298使能B
IN1 = 19	#//电机接口1
IN2 = 16	#//电机接口2
IN3 = 21	#//电机接口3
IN4 = 26	#//电机接口4
#########led初始化为000##########
GPIO.setup(LED0,GPIO.OUT,initial=GPIO.HIGH)
GPIO.setup(LED1,GPIO.OUT,initial=GPIO.HIGH)
GPIO.setup(LED2,GPIO.OUT,initial=GPIO.HIGH)
#########电机初始化为LOW##########
GPIO.setup(ENA,GPIO.OUT,initial=GPIO.LOW)
ENA_pwm=GPIO.PWM(ENA,1000) 
ENA_pwm.start(0) 
ENA_pwm.ChangeDutyCycle(80)
GPIO.setup(IN1,GPIO.OUT,initial=GPIO.LOW)
GPIO.setup(IN2,GPIO.OUT,initial=GPIO.LOW)

GPIO.setup(ENB,GPIO.OUT,initial=GPIO.LOW)
ENB_pwm=GPIO.PWM(ENB,1000) 
ENB_pwm.start(0) 
ENB_pwm.ChangeDutyCycle(80)
GPIO.setup(IN3,GPIO.OUT,initial=GPIO.LOW)
GPIO.setup(IN4,GPIO.OUT,initial=GPIO.LOW)
##########机器人方向控制###########################
def Motor_Forward():
	print 'motor forward'
	GPIO.output(ENA,True)
	GPIO.output(ENB,True)
	GPIO.output(IN1,True)
	GPIO.output(IN2,False)
	GPIO.output(IN3,True)
	GPIO.output(IN4,False)
	GPIO.output(LED1,False)#LED1亮
	GPIO.output(LED2,False)#LED1亮	
def Motor_Backward():
	print 'motor_backward'
	GPIO.output(ENA,True)
	GPIO.output(ENB,True)
	GPIO.output(IN1,False)
	GPIO.output(IN2,True)
	GPIO.output(IN3,False)
	GPIO.output(IN4,True)
	GPIO.output(LED1,True)#LED1灭
	GPIO.output(LED2,False)#LED2亮	
def Motor_TurnLeft():
	print 'motor_turnleft'
	GPIO.output(ENA,True)
	GPIO.output(ENB,True)
	GPIO.output(IN1,True)
	GPIO.output(IN2,False)
	GPIO.output(IN3,False)
	GPIO.output(IN4,True)
	GPIO.output(LED1,False)#LED1亮
	GPIO.output(LED2,True) #LED2灭
def Motor_TurnRight():
	print 'motor_turnright'
	GPIO.output(ENA,True)
	GPIO.output(ENB,True)
	GPIO.output(IN1,False)
	GPIO.output(IN2,True)
	GPIO.output(IN3,True)
	GPIO.output(IN4,False)
	GPIO.output(LED1,False)#LED1亮
	GPIO.output(LED2,True) #LED2灭
def Motor_Stop():
	print 'motor_stop'
	GPIO.output(ENA,False)
	GPIO.output(ENB,False)
	GPIO.output(IN1,False)
	GPIO.output(IN2,False)
	GPIO.output(IN3,False)
	GPIO.output(IN4,False)
	GPIO.output(LED1,True)#LED1灭
	GPIO.output(LED2,True)#LED2亮
##########机器人速度控制###########################
def ENA_Speed(EA_num):
	speed=hex(eval('0x'+EA_num))
	speed=int(speed,16)
	print 'EA_A改变啦 %d '%speed
	ENA_pwm.ChangeDutyCycle(speed)
def ENB_Speed(EB_num):
	speed=hex(eval('0x'+EB_num))
	speed=int(speed,16)
	print 'EB_B改变啦 %d '%speed
	ENB_pwm.ChangeDutyCycle(speed)	
def PathDect(func):
	global Path_Dect_px	#巡线中心点坐标值
	while True:
		print 'Path_Dect_px %d '%Path_Dect_px	 #打印巡线中心点坐标值
		if (Path_Dect_px < 260)&(Path_Dect_px > 0):	#如果巡线中心点偏左,就需要左转来校正。
			print("turn left")
			Motor_TurnLeft()
		elif Path_Dect_px > 420:#如果巡线中心点偏右,就需要右转来校正。
			print("turn right")
			Motor_TurnRight()
		else :		#如果巡线中心点居中,就可以直行。
			print("go stright")
			Motor_Forward()
		time.sleep(0.007)
		Motor_Stop()
		time.sleep(0.007)
cap = cv2.VideoCapture(0)	#实例化摄像头
Path_Dect_px_sum  = 0	#坐标值求和
threads = []
t1 = threading.Thread(target=PathDect,args=(u'监听',))
threads.append(t1)
for t in threads:
		t.setDaemon(True)
		t.start()
while True:
	ret,frame = cap.read()	#capture frame_by_frame
	gray = cv2.cvtColor(frame,cv2.COLOR_BGR2GRAY)	#获取灰度图像
	ret,thresh1=cv2.threshold(gray,70,255,cv2.THRESH_BINARY)#对灰度图像进行二值化
	Path_Dect_fre_count = 1
	for j in range(0,640,5):		#采样像素点,5为步进,一共128个点
		if thresh1[240,j] == 0:		
			Path_Dect_px_sum = Path_Dect_px_sum + j		#黑色像素点坐标值求和
			Path_Dect_fre_count = Path_Dect_fre_count + 1	#黑色像素点个数求和
	Path_Dect_px = (Path_Dect_px_sum )/(Path_Dect_fre_count)	#黑色像素中心点为坐标和除以个数
	Path_Dect_px_sum = 0
	cv2.imshow('BINARY',thresh1)		#树莓派桌面显示二值化图像,比较占资源默认注释掉调试时可以打开
	if cv2.waitKey(1)&0XFF ==ord('q'):#检测到按键q退出
		Motor_Stop()
		break	
cap.release()
cv2.destroyAllWindows()

三、需要注意的地方

1、可以通过调整二值化中的阈值参数 70 为合适的值。

ret,thresh1=cv2.threshold(gray,70,255,cv2.THRESH_BINARY)#对灰度图像进行二值化

2、 可以通过打开如下位置的注释符号来开启在远程桌面显示二值化后的图像从而辅助上一步骤的阈值设定啦。

#cv2.imshow('BINARY',thresh1) #树莓派桌面显示二值化图像, 比较占资源默认注释掉调试时可以打开
ReCclay CSDN认证博客专家 视觉/OpenCV 图像处理 深度学习
大家好,我是CSDN博主ReCclay,目前处于研究生阶段,就读于电子科技大学,主攻方向为汽车辅助驾驶算法研究。入站以来,凭借坚持与热爱,以博文的方式分享所学,截止目前累计博文数量达800余篇,受益人次达135万余次,涉及领域包括但不限于物联网开发、单片机开发、Linux驱动开发、FPGA开发、前/后端软件开发等。在未来我将继续专注于嵌入式相关领域,学习更多的科技知识,输出更高质量的博文。希望在”2020博客之星年度总评选“中,可以大家的关注和投票,投票地址:https://bss.csdn.net/m/topic/blog_star2020/detail?username=recclay
已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 数字20 设计师:CSDN官方博客 返回首页