前言

從本文開始,我們將開始學習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文件將用到:

ROS機器視覺入門:從基礎到人臉識別與目標檢測_數據

(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>

ROS機器視覺入門:從基礎到人臉識別與目標檢測_ide_02

(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內容展示:

ROS機器視覺入門:從基礎到人臉識別與目標檢測_sed_03

(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內容展示:

ROS機器視覺入門:從基礎到人臉識別與目標檢測_數據_04

攝像頭標定

標定引入

(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

標定靶圖片:

ROS機器視覺入門:從基礎到人臉識別與目標檢測_ide_05

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

ROS機器視覺入門:從基礎到人臉識別與目標檢測_數據_06

不斷晃動,直到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機器視覺入門:從基礎到人臉識別與目標檢測_sed_07

根據這個圖,在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

ROS機器視覺入門:從基礎到人臉識別與目標檢測_數據_08

總結

本文主要系統介紹了機器視覺處理的外圍知識,引入了opencv和cv_bridge,後面幾篇文章,我們將用它們繼續豐富 robot_vision 軟件包。