Learning Objectives
- 說明樹莓派攝影機模組怎麼連接,以及 rpicam-apps 指令與 Picamera2 函式庫的用途
- 用 Python 把一張影像當成二維的像素陣列來讀取與計算
- 把彩色像素換成灰階,算出不同解析度下未壓縮影像的資料量
- 比較前後兩張畫面的差異,判斷有沒有東西在動,並控制 LED
- 說明在教室使用攝影機時要注意的隱私原則
讓樹莓派「看見」
前面幾課的感測器都只回傳一個數字:溫度幾度、距離幾公分、有沒有人經過。 攝影機一次回傳的是幾百萬個數字,每一個數字代表畫面上一個小點的顏色。 樹莓派是單板電腦(第 01 課),有足夠的記憶體與運算能力處理這麼多資料,這是它和多數微控制器很不一樣的地方。
這一課要回答:
- 攝影機模組怎麼接到樹莓派,用什麼指令或函式庫拍照?
- 一張照片在程式裡長什麼樣子?
- 怎麼用很簡單的計算,從影像裡得到有用的資訊(例如「有沒有東西在動」)?
這個網頁上的模擬器沒有攝影機。拍照的步驟用終端機示範;影像處理的觀念用很小的像素陣列在瀏覽器裡實際執行, 道理和處理真正的照片一樣,只是數字少很多。
攝影機模組與連接方式
樹莓派官方的攝影機模組透過排線接在板子上的 CSI(Camera Serial Interface,攝影機序列介面)接頭,不是 USB。 官方文件列出的接頭有兩種尺寸:
| 接頭 | 出現在哪些板子 |
|---|---|
| 15 針 CSI | Raspberry Pi 5 之前的標準型號(例如 Raspberry Pi 4) |
| 22 針 CSI | Raspberry Pi Zero 系列、Compute Module 的 IO 板、Raspberry Pi 5 之後的標準型號 |
排線兩端的接頭尺寸要和板子對上;買攝影機時先確認板子是哪一種接頭。
常見的官方模組(撰寫時的產品線):
| 模組 | 重點 |
|---|---|
| Camera Module 3 | 1200 萬像素(12MP)的 Sony IMX708 感光元件、自動對焦;有標準與廣角、一般與無紅外線濾鏡(NoIR)等版本 |
| High Quality Camera | 約 1230 萬像素,可以換鏡頭(C/CS 接環或 M12 接環),焦距與視角由鏡頭決定 |
安全與操作:插拔排線前先關機、拔電源;排線的金屬接點朝向要照說明,扣環輕輕拉起、插好再壓下; 不要用手指碰感光元件與鏡頭。
軟體:指令列工具與 Python 函式庫
| 工具 | 用途 |
|---|---|
rpicam-hello、rpicam-still、rpicam-vid |
終端機指令:預覽、拍照、錄影(Raspberry Pi OS Bookworm 起改名,舊版叫 libcamera-hello 等) |
| Picamera2 | Python 函式庫,建立在 libcamera 之上;官方建議用 apt 安裝(python3-picamera2),較新的系統通常已預先安裝 |
舊教材裡的 picamera(沒有 2)與 raspistill 屬於舊的攝影機架構,在新系統上不再適用,看到時要換成上面的工具。
在終端機測試攝影機與拍照
以下是在接好 Camera Module 3 的樹莓派上操作的步驟。輸出經過節錄與簡化,實際的訊息會多很多行。
-
1
確認系統找得到攝影機
--list-cameras列出接上的攝影機與它支援的感光元件模式。如果出現「No cameras available」,先關機檢查排線方向與扣環。 -
2
拍一張照片
rpicam-still會先開預覽一段時間讓自動曝光與對焦穩定,再拍照存檔;-o指定檔名。用ls -lh看檔案大小。 -
3
錄一段影片
-t是錄影時間,單位毫秒;10000就是 10 秒。輸出是未封裝的 H.264 影像串流。 -
4
用 Python 拍照
Picamera2 官方手冊裡最短的拍照程式只有三行:
from picamera2 import Picamera2 picam2 = Picamera2() picam2.start_and_capture_file("test.jpg")要在程式裡處理影像,改用
capture_array()取得一個 NumPy 陣列,形狀是(高, 寬, 顏色通道數)。下一段開始,我們就用小小的陣列練習怎麼處理它。
影像就是一個二維陣列
數位影像由一格一格的像素(pixel)組成。灰階影像的每個像素用一個 0~255 的整數表示亮度(8 位元), 所以一張灰階影像在 Python 裡可以寫成「串列的串列」:外層每一個元素是一列,內層每一個數字是一個像素。
img[列][行] ← 先列(y,往下)再行(x,往右)
img[0][0] 左上角
img[1][2] 第 2 列、第 3 個像素(索引從 0 開始)
Picamera2 的 capture_array() 回傳的 NumPy 陣列也是這個順序:高度在前、寬度在後,彩色影像再多一個顏色通道的維度。
下面的範例是一張 5×5 的小影像,看程式怎麼算平均亮度,再用一個門檻把它變成只有黑白兩種的圖。
逐步看:讀像素、算平均、門檻化
右邊變數區的 img 是一個 5×5 的串列。注意第 15 行每執行一次,total 就加上一整列的亮度;第 21 行把每個像素換成 #(亮)或 .(暗)。
彩色、灰階與資料量
彩色影像最常見的表示法是 RGB:每個像素有紅、綠、藍三個 0~255 的數字,各佔 1 個位元組,合起來 3 個位元組(24 位元)。
很多影像處理(偵測移動、找邊緣、辨識形狀)只需要亮度,先把彩色換成灰階可以讓資料量變成三分之一。 換算時三個顏色的權重不一樣,因為人眼對綠色最敏感、對藍色最不敏感。下面的範例用一組常見的權重(ITU-R BT.601):
灰階 = 0.299 × 紅 + 0.587 × 綠 + 0.114 × 藍
未壓縮的資料量 = 寬 × 高 × 每個像素的位元組數。拍成 JPEG 檔時會壓縮得小很多,但程式拿到陣列處理時,記憶體裡是未壓縮的大小。
逐步看:RGB 轉灰階與資料量
第 9 行的 for r, g, b in row 一次把一個像素的三個數字拆開。看純紅、純綠、純藍三個像素換成灰階後亮度差多少。
小測驗:攝影機與像素
一張 1920 × 1080、每個像素 3 個位元組(RGB)的影像,未壓縮時大約多大?
下面的程式會印出什麼?
img = [[10, 200, 30], [220, 40, 250]]
count = 0
for row in img:
for v in row:
if v > 128:
count += 1
print(len(img), len(img[0]), count)
在新版 Raspberry Pi OS 上,要用終端機列出接上的攝影機,應該用哪一個指令?
用畫面差異偵測移動
要知道畫面裡「有沒有東西在動」,最簡單的方法不需要人工智慧:拿這一張和上一張比。
- 逐一比較同一個位置的兩個像素,亮度差的絕對值超過門檻,就算這個像素「變了」。
- 數一數有幾個像素變了;超過一定數量,就判斷有東西在動。
兩個門檻都很重要:攝影機的畫面本來就有細微的雜訊(同一個場景,連續兩張的亮度也會差一兩個單位), 第一個門檻擋掉雜訊;第二個門檻擋掉零星幾個像素的變化(例如光線閃一下)。
下一個範例把三張小畫面寫在程式裡(模擬器沒有攝影機),比較的結果用來控制 GPIO17 上的 LED。
逐步看:畫面差異與警示燈
上方的電路畫面只有一顆 LED。第一次比較時畫面幾乎沒變(只有雜訊),第二次右上角變亮了,看 LED 什麼時候亮起來。
在教室使用攝影機:先想隱私
攝影機拍到的是人。在教室、宿舍、實驗室裝攝影機之前,先回答這幾個問題:
- 真的需要影像嗎? 只想知道「有沒有人」,第 12 課的人體感測器(PIR)就夠了,不會留下任何人的長相。
- 讓被拍到的人知道。 事先說明拍什麼、為什麼、存多久,取得同意;在現場貼出告示。
- 能在樹莓派上算完就不要傳出去。 像上一個範例,只保留「有幾個像素變了」這個數字,畫面處理完就丟掉。
- 不要存不需要的東西。 需要存影像時設定保存期限、限制誰能看,網路傳輸要加密。
樹莓派讓「在裝置上就把影像處理完」變得容易,這是保護隱私很實用的設計方式。
小測驗:影像處理與隱私
畫面差異偵測裡,「亮度差超過 30 才算變化」這個門檻主要是為了什麼?
下面的程式會印出什麼?
a = [[50, 52], [49, 51]]
b = [[51, 200], [48, 210]]
changed = 0
for y in range(2):
for x in range(2):
if abs(b[y][x] - a[y][x]) > 30:
changed += 1
print(changed)
想在自習室統計「現在有沒有人在使用」,下面哪一種做法最符合隱私原則?
重點整理
- 官方攝影機模組接在 CSI 接頭(15 針或 22 針,依板子而定);終端機用
rpicam-hello、rpicam-still、rpicam-vid,Python 用 Picamera2。 - 影像在程式裡是二維陣列:
img[列][行],高度在前、寬度在後;灰階每個像素 1 個位元組,RGB 3 個位元組。 - 未壓縮的資料量=寬 × 高 × 每個像素的位元組數;彩色轉灰階用加權平均,資料量變成三分之一。
- 門檻化把灰階變黑白;比較前後兩張畫面、數變化的像素,就能偵測移動,兩個門檻分別擋掉雜訊與零星變化。
- 攝影機拍的是人:能不用影像就不用,要用就事先告知、在裝置上處理、只保留需要的結果。
下一課把感測器的數值定時寫進檔案與資料庫,讓資料可以留下來分析。