Урок 19
Введение: Данный урок, является первым из группы уроков на использование компьютерного зрения для работы с полезной нагрузкой полигона. В прошлых уроках по компьютерному зрению мы изучали теоретические аспекты обработки изображений и немного попрактиковались на "сферическом коне в вакууме" - желтом шарике. В этом уроке мы перейдем к более сложным задачам больше приближенным к реальности и первой из них будет определение положения шлагбаума на изображении.
В рамках данного урока мы также как и ранее получим изображение с камеры робота, переведем его в HSV и наложим маску. Собственно на этом вся предобработка изображения будет закончена. А далее для решения семантической задачи определения поднят ли шлагбаум на видео или опущен, мы будем сначала детектировать полоски шлагбаума на изображении в виде контуров, а затем аппроксимировать прямой линией координаты центров этих контуров по методу наименьших квадратов. И в результате по значению коэффициентов функции определяющих аппроксимирующую прямую мы и будем делать вывод как расположен шлагбаум - более параллельно или более перпендикулярно. Для визуализации результатов анализа изображения, мы наложим найденные контуры и рассчитанную по МНК линию на изначальное изображение и опубликуем это изображение в специальный топик.
Теория:
Подготовка: Данную задачу можно решать в комбинации полигона "Дорога". Непосредственно перед запуском программы попросите учащихся подвести робота к шлагбауму.
Задача: Определить координаты центра желтого шарика на изображении и вывести их в консоль.
Общий алгоритм решения:
- Получить изображение с камеры робота
- Преобразовать его в HSV (взять алгоритм из предыдущего урока)
- Определить цветовые границы
- Наложить цветовую маску - красного цвета (взять алгоритм из предыдущего урока)
- Выделить элементы полосок шлагбаума
- Найти формулу прямой аппроксимирующей центры найденных полосок по методу наименьших квадратов
- Оценить как расположена аппроксимирующая прямая, если параллельно оси X - то значит шлагбаум закрыт, если перпендикулярно - значит открыт.
- Опубликовать результаты оценки состояния шлагбаума в отдельный топик
Решение: Возьмем весь код программы прошлого урока, реализующий наложение маски на изображение и сохраним его в новом файле.
import rospy
import numpy as np
import cv2
from cv_bridge import CvBridge
from std_msgs.msg import String
from sensor_msgs.msg import CompressedImage
class BarrierDetector():
def __init__(self):
self.hue_red_l = 90
self.hue_red_h = 180
self.saturation_red_l = 128
self.saturation_red_h = 255
self.lightness_red_l = 90
self.lightness_red_h = 255
self.sub_image_original = rospy.Subscriber('/front_camera/compressed', CompressedImage, self.cbGetImage, queue_size = 1)
self.detect_pub = rospy.Publisher('/barrier_detect', String, queue_size = 1)
self.image_pub = rospy.Publisher('/barrier_image/compressed', CompressedImage, queue_size = 1)
self.counter = 0
self.cvBridge = CvBridge()
while not rospy.is_shutdown():
rospy.sleep(1)
self.BarrierDetect()
def cbGetImage(self, image_msg):
# Change the frame rate by yourself. Now, it is set to 1/3 (10fps).
# Unappropriate value of frame rate may cause huge delay on entire recognition process.
# This is up to your computer's operating power.
if self.counter % 3 != 0:
self.counter += 1
return
else:
self.counter = 1
np_arr = np.frombuffer(image_msg.data, np.uint8)
self.cv_image = cv2.imdecode(np_arr, cv2.IMREAD_COLOR)
def ApplyRedMaskOnIm(self):
image = np.copy(self.cv_image)
hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
# define range of red color in HSV
lower_red = np.array([self.hue_red_l, self.saturation_red_l, self.lightness_red_l])
upper_red = np.array([self.hue_red_h, self.saturation_red_h, self.lightness_red_h])
# Threshold the HSV image to get only red colors
mask = cv2.inRange(hsv, lower_red, upper_red)
return mask
def FindRectOnBarrier(self, mask):
params=cv2.SimpleBlobDetector_Params()
# Change thresholds
params.minThreshold = 200
params.maxThreshold = 255
params.filterByColor = True
params.blobColor = 255
# Filter by Area.
params.filterByArea = True
params.minArea = 300
params.maxArea = 5000
# Filter by Circularity
params.filterByCircularity = False
# Filter by Convexity
params.filterByConvexity = False
det=cv2.SimpleBlobDetector_create(params)
keypts=det.detect(mask)
if len(keypts) >= 2:
return keypts
else:
return []
def FindTheLineWithMSE(self, keypts):
#precalc
if keypts:
C1,C2,C3,C4 = 0,0,0,0
for keypt in keypts:
#sum(X)
C1 += keypt.pt[0]
#sum(Y)
C2 += keypt.pt[1]
#sum(X*X)
C3 += keypt.pt[0]*keypt.pt[0]
#sum(X*Y)
C4 += keypt.pt[0]*keypt.pt[1]
#n = number of points
n = len(keypts)
#Line сoefficients calculation according to MSE formulas
a = (C4*n - C1*C2) / (n*C3 - C1*C1)
b = (C2- C1*a) / n #for drawing purpose only. "a" is enough for detection.
return a,b
else:
return 0,0
def isBarrierClosed(self, a,b, rects):
if a < -2:
return "Open"
elif a == 0:
return "Unknown"
else:
return "Closed"
def ResultPub(self, result):
r = String()
r.data = result
self.detect_pub.publish(r)
def ImPub(self, rects, a, b):
x_1, y_1 = 0,0
x_2, y_2 = 1,1
if rects:
x_1, y_1 = int(rects[0].pt[0]), int(rects[0].pt[0]*a + b)
x_2, y_2 = int(rects[-1].pt[0]), int(rects[-1].pt[0]*a + b)
image_to_publish = cv2.drawKeypoints(self.cv_image,rects,np.array([]),(0,255,255),cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)
image_to_publish = cv2.line(image_to_publish, (x_1, y_1),(x_2, y_2), (255,0,0), 5)
#cv2.imwrite("/home/pi/projects/3.jpg", image_to_publish)
self.image_pub.publish(self.cvBridge.cv2_to_compressed_imgmsg(image_to_publish,"jpg"))
def BarrierDetect(self):
cv_image_mask = self.ApplyRedMaskOnIm()
cv_image_mask = cv2.GaussianBlur(cv_image_mask,(5,5),0)
rects= self.FindRectOnBarrier(cv_image_mask)
a,b = self.FindTheLineWithMSE(rects)
barrier_is_closed = self.isBarrierClosed(a,b, rects)
self.ResultPub(barrier_is_closed)
self.ImPub(rects, a ,b)
def main(self):
rospy.spin()
if __name__ == '__main__':
rospy.init_node('detect_barrier')
b = BarrierDetector()
b.main()
Проверка решения:
Попросите учащихся сохранить программу на роботе, подвести робота к шлагбауму и запустить программу. После запуска программы, при помощи веб-интерфейса, открывайте и закрывайте шлагбаум перед роботом. Попросите учащихся через Discord продемонстрировать вывод в консоль значения данных из топика, в который публикуются результаты определения состояния шлагбаума.
Итак, мы с вами применили встроенные в OpenCV функции по обработке изображения, а так же написали наш собственный семантический алгоритм проверки состояния шлагбаума основанный на методе наименьших квадратов.