前言
從本文開始,我們將開始學習ROS機器視覺處理,剛開始先學習一部分外圍的知識,為後續的人臉識別、目標跟蹤和YOLOV5目標檢測做準備工作。我採用的筆記本是聯想拯救者遊戲本,系統採用Ubuntu20.04,ROS採用noetic。
顏色編碼格式,圖像格式和視頻壓縮格式
(1)RGB和BGR:這是兩種常見的顏色編碼格式,分別代表了紅、綠、藍三原色。不同之處在於,RGB按照紅、綠、藍的順序存儲顏色信息,而BGR按照藍、綠、紅的順序存儲。
rgb8圖像格式:常用於顯示系統,如電視和計算機屏幕。
RGB值以8 bits表示每種顏色,總共可以表示256×256×256=16777216種顏色。
例如: (255,0,0) 表示紅色,(0,255,0) 表示綠色,(0,0,255) 表示藍色。
bgr8圖像格式:由一些特定的硬件製造商採用,
軟件方面最著名的就是opencv,其默認使用BGR的顏色格式來處理圖像。
與RGB不同, (0,0,255) 在BGR中表示紅色,(0,255,0) 仍然表示綠色,(255,0,0) 表示藍色。
在自動駕駛裏,使用rgb8圖像格式的圖像,一般稱為原圖,是數據量最大的格式,沒有任何壓縮。(2)(2)YUV:這是另一種顏色編碼方法,與RGB模型不同的是,它將圖像信息分解為亮度(Y)和色度(U和V)兩部分。這種方式更接近於人類對顏色的感知方式。
Y:代表亮度信息,也就是灰階值。
U:從色度信號中減去Y得到的藍色信號的差異值。
V:從色度信號中減去Y得到的紅色信號的差異值。
YUV顏色編碼主要用在電視系統以及視頻編解碼標準中,在這些系統中,Y通道信息可以單獨使用,這樣黑白電視機也能接收和顯示信號。而彩色信息則通過U和V兩個通道傳輸,只有彩色電視機才能處理。這樣設計兼容了黑白電視和彩色電視。YUV色彩空間相比RGB色彩空間,更加符合人眼對亮度和色彩的敏感度,在視頻壓縮時,可以按照人眼的敏感度對YUV數據進行壓縮,以達到更高的壓縮比。由於歷史和技術的原因,YUV的標準存在多種,例如YUV 4:4:4、YUV 4:2:2和YUV 4:2:0等,這些主要是針對U和V通道的採樣方式不同定義的。採樣不同,對應的壓縮比也不同。
(3)圖像壓縮格式:
jpeg:Joint Photographic Experts Group,是一種常見的用於靜態圖像的損失性壓縮格式,
它特別適合於全綵色和灰度圖片,被廣泛使用。
通常情況下,JPEG可以提供10:1到20:1的有損壓縮比,根據圖像質量自由調整。
png: Portable Network Graphics,PNG是一種無損壓縮格式,主要使用了DEFLATE算法。
由於這是無損壓縮,所以解壓縮圖像可以完全恢復原始數據。
被廣泛應用於需要高質量圖像的場景,如網頁設計、藝術作品等。
bmp:Bitmap,BMP是Windows系統中常用的一種無壓縮的位圖圖像格式,通常會創造出較大的文件。
位圖(Bitmap)是一種常見的計算機圖形,最小單位是像素,每個像素都包含一定量的信息,如顏色和亮度等。位圖圖像的一個主要特點就是,在放大查看時,可以看到圖像的像素化現象,也就是我們常説的"馬賽克"。BMP、JPEG、GIF、PNG等都是常見的位圖格式。
(4)H264和H265:這是兩個視頻壓縮格式,也是兩種視頻編解碼標準。以1280*720的攝像頭為例,如果是rgb8格式的原圖,一幀圖像的大小是:
3*1280*720=27648000字節,即2.7648MB
如果是一小時的視頻,那將是非常大的數據量,對網絡傳輸,數據存儲,都是很大的壓力。而H264通過種種幀間操作,可以達到10:1到50:1的壓縮比,甚至更高。H265更進一步,壓縮比更高,用來解決4K或8K視頻的傳輸。
更具體的原理見:圖像編碼與 H264 基礎知識在自動駕駛領域,圖像數據也使用h264格式,主要用於數採和回放,控制數據量。
usb_cam
(1)linux針對攝像頭硬件有一套Video for Linux內核驅動框架,對應提供的有命令行工具 v4l2-ctl (Video for Linux 2),可以查看攝像頭硬件信息:
ls /dev/video0 //一般video0是筆記本自帶攝像頭設備文件
v4l2-ctl -d /dev/video0 --all
這裏截取了部分關鍵信息,下面的usb_cam的launch文件將用到:
(2)usb_cam是ros裏usb camera的軟件包,一般稱為ros攝像頭驅動,但這是一個應用程序,其調用v4l2並通過ros topic發出圖像數據。搞機器視覺,第一步就是要有圖。安裝並啓動usb_cam,查看圖像:
sudo apt-get install ros-noetic-usb-cam
roslaunch usb_cam usb_cam-test.launch
rqt_image_view
usb_cam-test.launch:
<launch>
<node name="usb_cam" pkg="usb_cam" type="usb_cam_node" output="screen" >
//指定設備文件名,默認是/dev/video0
<param name="video_device" value="/dev/video0" />
// 寬和高分辨率
<param name="image_width" value="640" />
<param name="image_height" value="480" />
// 像素編碼,可選值:mjpeg,yuyv,uyvy
<param name="pixel_format" value="yuyv" />
<param name="color_format" value="yuv422p" />
// camera座標系名
<param name="camera_frame_id" value="usb_cam" />
// IO通道,可選值:mmap,read,userptr,大數據量信息一般用mmap
<param name="io_method" value="mmap"/>
</node>
<node name="image_view" pkg="image_view" type="image_view" respawn="false" output="screen">
// 指定發出的topic名:/usb_cam/image_raw
<remap from="image" to="/usb_cam/image_raw"/>
<param name="autosize" value="true" />
</node>
</launch>
(3)/usb_cam/image_raw的數據結構體:
rostopic info /usb_cam/image_raw
rosmsg show sensor_msgs/Image
//消息頭,每個topic都有
std_msgs/Header header
uint32 seq
time stamp
// 座標系名
string frame_id
// 高和寬分辨率
uint32 height
uint32 width
// 無壓縮的圖像編碼格式,包括rgb8,YUV444
string encoding
// 圖像數據的大小端存儲模式
uint8 is_bigendian
// 一行圖像數據的字節數量,作為步長參數
uint32 step
// 存儲圖像數據的柔性數組,大小是step*height
uint8[] data
/usb_cam/image_raw內容展示:
(4)/usb_cam/image_raw/compressed的數據結構體:
rostopic info /usb_cam/image_raw/compressed
rosmsg show sensor_msgs/CompressedImage
std_msgs/Header header
uint32 seq
time stamp
string frame_id
// 壓縮的圖像編碼格式,jpeg,png
string format
uint8[] data
/usb_cam/image_raw/compressed內容展示:
攝像頭標定
標定引入
(1)Calibration:翻譯過來就是校準和標定。(2)攝像頭標定:Camera Calibration是計算機視覺中的一種關鍵技術,其目的是確定攝像頭的內部參數(Intrinsic Parameters)和外部參數(Extrinsic Parameters)。
內部參數:包括焦距、主點座標以及鏡頭畸變等因素。
這些參數與相機本身的硬件有關,如鏡頭和圖像傳感器等,一般由廠家提供。
外部參數:攝像頭相對於環境的位置和方向。
例如,它可能描述了一個固定攝像頭相對於周圍環境的姿態或者安裝位置。
以汽車為例,外參包括各個攝像頭之間的關係,攝像頭與radar,攝像頭與lidar的關係。
(3)汽車各種傳感器的之間的相對位置和朝向,用3自由度的旋轉矩陣和3自由度的平移向量表示,這些外參由整車廠自己標。一般整車下線之後,進入特定的房間,使用靜態標靶、定位器的等高精度設備,完成Camera、radar、Lidar等傳感器的標定,稱之為產線標定,也叫做下線標定。
筆記本攝像頭內參標定
這裏我們使用標定常用的標靶圖形,完成筆記本攝像頭的內參標定。usb_cam可以使用內參標定,避免圖像畸變。(1)安裝標定功能包(ubuntu20.04+noetic)
sudo apt-get install ros-noetic-camera-calibration
(2)創建 robot_vision 軟件包,並標定相關文件
cd ~/catkin_ws/src
catkin_create_pkg robot_vision cv_bridge image_transport sensor_msgs std_msgs geometry_msgs message_generation roscpp rospy
cd robot_vision
mkdir doc launch
touch launch/cameta_calibration.launch
標定靶圖片:
cameta_calibration.launch:
<launch>
// 使用usb_cam包,發出/usb_cam/image_raw圖像數據
<node name="usb_cam" pkg="usb_cam" type="usb_cam_node" output="screen" >
<param name="video_device" value="/dev/video0" />
<param name="image_width" value="640" />
<param name="image_height" value="480" />
<param name="pixel_format" value="yuyv" />
<param name="camera_frame_id" value="usb_cam" />
<param name="io_method" value="mmap"/>
</node>
// 使用標定功能包,完成標定。
// 參數中,8x6表示橫向8個內部角點,豎向有6個
// square 是每個棋盤格的邊長
// /usb_cam/image_raw是監聽的圖像topic
<node
pkg="camera_calibration"
type="cameracalibrator.py"
name="camera_calibration"
output="screen"
args="--size 8x6 --square 0.024 image:=/usb_cam/image_raw camera:=/usb_cam"
/>
</launch>
(3)編譯並運行
cd ~/catkin_ws/
catkin_make --source src/robot_vision
source devel/setup.bash
roslaunch robot_vision cameta_calibration.launch
不斷晃動,直到COMMIT按鍵亮起,然後點擊,即可生成標定文件,本人的路徑為:/home/mm/.ros/camera_info/head_camera.yaml。
opencv和cv_bridge引入
(1)opencv和cv_bridge
安裝opencv(ubuntu20.04+noetic):
sudo apt-get install ros-noetic-vision-opencv libopencv-dev python3-opencv
(2)opencv和cv_bridge的簡單架構圖如下:
根據這個圖,在ros裏,處理圖像的流程一般是:
# 第一步:使用cv_bridge將ROS的圖像數據轉換成OpenCV的圖像格式
cv_image = cv_bridge.imgmsg_to_cv2(msg, "bgr8")
# 第二步:使用opencv進行圖像處理
。。。
# 第三步,再將opencv格式額數據轉換成ros image格式的數據
ros_image = cv_bridge.cv2_to_imgmsg(cv_image, "bgr8")
(3)在 上節的robot_vision包裏,我們新增一個cv_bridge的小樣例,主要功能是在捕捉到的圖像上打個藍色的圓標。
本文不深入講解opencv,推薦一個資料:W3Cschool - OpenCV教程
cv_bridge_test.py:
#! /usr/bin/env python3
# -*- coding: utf-8 -*-
import rospy
import cv2
from functools import partial
from cv_bridge import CvBridge, CvBridgeError
from sensor_msgs.msg import Image
def image_cb(msg, cv_bridge, image_pub):
# 使用cv_bridge將ROS的圖像數據轉換成OpenCV的圖像格式
try:
cv_image = cv_bridge.imgmsg_to_cv2(msg, "bgr8")
except CvBridgeError as e:
print(e)
# 在opencv的顯示窗口中繪製一個圓,作為標記
# cv_image.shape返回一個元組,包含圖像的行數(高度),列數(寬度)和通道數(通常是3個通道:BGR)
(rows, cols, channels) = cv_image.shape
# 當圖像的寬度和高度都大於60時,才執行畫圓標動作
if cols > 60 and rows > 60:
# 在計算機圖像處理中,圖像的原點(0,0)通常定義為圖像的左上角。(60,60)是圓心的座標。
# 30是圓的半徑。
# (255,0,0)定義了圓的顏色。在OpenCV中,默認的顏色空間是BGR,所以這其實是繪製了一個藍色的圓。
# -1表示填充圓。如果這個值是正數,則代表繪製的圓的線寬;如果是負數,則代表填充該圓。
cv2.circle(cv_image, (60,60), 30, (255,0,0), -1)
# 使用Opencv的接口,顯示Opencv格式的圖像
cv2.imshow("ycao: opencv image window", cv_image)
cv2.waitKey(3)
# 再將opencv格式額數據轉換成ros image格式的數據發佈
try:
image_pub.publish(cv_bridge.cv2_to_imgmsg(cv_image, "bgr8"))
except CvBridgeError as e:
print(e)
def main():
rospy.init_node("cv_bridge_test")
rospy.loginfo("starting cv_bridge_test node")
bridge = CvBridge()
image_pub = rospy.Publisher("/cv_bridge_image", Image, queue_size=1)
bind_image_cb = partial(image_cb, cv_bridge=bridge, image_pub=image_pub)
// 訂閲/usb_cam/image_raw,然後再回調函數裏處理圖像,併發布出來
rospy.Subscriber("/usb_cam/image_raw", Image, bind_image_cb)
rospy.spin()
cv2.destroyAllWindows()
if __name__ == "__main__":
main()
cv_bridge_test.launch
<launch>
<node name="usb_cam" pkg="usb_cam" type="usb_cam_node" output="screen" >
<param name="video_device" value="/dev/video0" />
<param name="image_width" value="640" />
<param name="image_height" value="480" />
<param name="pixel_format" value="yuyv" />
<param name="camera_frame_id" value="usb_cam" />
<param name="io_method" value="mmap"/>
</node>
<node
pkg="robot_vision"
type="cv_bridge_test.py"
name="cv_bridge_test"
output="screen"
/>
<node
pkg="rqt_image_view"
type="rqt_image_view"
name="rqt_image_view"
output="screen"
/>
</launch>
(4)編譯並運行
cd ~/catkin_ws/
catkin_make --source src/robot_vision
source devel/setup.bash
roslaunch robot_vision cv_bridge_test.launch
總結
本文主要系統介紹了機器視覺處理的外圍知識,引入了opencv和cv_bridge,後面幾篇文章,我們將用它們繼續豐富 robot_vision 軟件包。