OpenCV 的世界里图就是 NumPy 数组:读图、存图、灰度、缩放、裁剪都是【换个角度看数组】——认死两条规矩(BGR 顺序、高在前宽在后),后面的 AI 实验全是这套基本功的组合。
11-2 装好了库,本节练基本功。10-4 的 capture_array() 已经让你见过【画面=数组】(shape 是(高, 宽, 3));本节把这条路走全——picamera2 负责把画面递进来,OpenCV 负责在数组上做手术。6-6 的切片规则在这里原样复用(ROI 裁剪就是二维切片)。
一、两条刻进肌肉记忆的规矩
- BGR,不是 RGB:OpenCV 按蓝、绿、红顺序存颜色通道(历史原因,改不了了)。
(255, 0, 0)在 OpenCV 里是纯蓝。与别的库交换数据时用cv2.cvtColor(img, cv2.COLOR_BGR2RGB)转换——颜色诡异发蓝发红,八成是忘了这条 - shape 是(高, 宽, 通道):
img[100, 200]是【第 100 行、第 200 列】的像素——行号在前。切片同理:img[y1:y2, x1:x2],写反了裁出来的不是你想要的区域(还不报错,是最阴的一类 bug)
二、读图、存图、看图
import cv2
img = cv2.imread("photo.jpg") # 读图 → NumPy 数组;路径错返回 None(不报错!)
print(type(img), img.shape) # <class 'numpy.ndarray'> (480, 640, 3)
cv2.imwrite("copy.jpg", img) # 存图:按扩展名自动选格式
两个坑:
- imread 读不到只返回 None,后面才炸出难懂的报错——养成习惯:读完先
if img is None: ... cv2.imshow("win", img)要图形窗口——本课无头 SSH 环境开不了(4-6 的教训第 N 次重演);验收一律用 imwrite 存盘后拷回 Windows 看,回桌面跑时才用 imshow(配cv2.waitKey(0)与cv2.destroyAllWindows())
三、三把常用的刀:灰度、缩放、裁剪
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 灰度:3 通道变 1 通道
print(gray.shape) # (480, 640) —— 通道没了
small = cv2.resize(img, (320, 240)) # 缩放:注意参数是【宽, 高】!
print(small.shape) # (240, 320, 3) —— 又变回【高, 宽】
roi = img[100:200, 300:500] # 裁剪:行在前(y 范围),列在后(x 范围)
print(roi.shape) # (100, 200, 3) —— 高 100 宽 200
resize 是本节最容易写反的地方:参数顺序是 (宽, 高),而 shape 显示是 (高, 宽)——同一个尺寸的两种表达,OpenCV 里并存,写错不报错只是图变形。
灰度图为什么 AI 爱用:通道从 3 变 1,数据量直接砍三分之二(0.9 MB → 0.3 MB,按 11-2 的账);而【有没有脸】【有没有物体】这类判断不太需要颜色——11-4 的人脸检测第一步就是转灰度。
四、视频循环骨架:所有实时视觉程序的模板
import cv2
import numpy as np
from picamera2 import Picamera2
picam2 = Picamera2()
picam2.configure(picam2.create_video_configuration())
picam2.start()
while True:
frame = picam2.capture_array() # 取一帧(RGB888 格式)
frame = cv2.cvtColor(frame, cv2.COLOR_RGB2BGR) # picamera2 给 RGB,OpenCV 要 BGR!
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) # 在这行之后随意加工……
print(gray.shape)
break # 无头环境先跑通一轮;桌面版换 while True + waitKey
两个要点:
- picamera2 递来的是 RGB,OpenCV 吃的是 BGR——中间那次
cvtColor(RGB2BGR)不能省,省了颜色全反(规矩一的第一现场) - 桌面环境的完整骨架是【取帧 → 加工 →
cv2.imshow→cv2.waitKey(1)→ 按 q 退出】;无头 SSH 把 imshow 换成 print/imwrite/后续的 AI 判断
用 USB 摄像头的版本(10-6 的 OpenCV 取流):把前四行换成 cap = cv2.VideoCapture(0)、取帧换 ret, frame = cap.read()——框架不变,帧的来源随意,这就是【数组是通用语言】的威力。
五、动手 Demo:给摄像头画面做【三连加工】
把摄像头一帧画面加工成三个文件,验收基本功:
# cv_basic.py
import cv2
from picamera2 import Picamera2
picam2 = Picamera2()
picam2.configure(picam2.create_video_configuration())
picam2.start()
frame = picam2.capture_array() # RGB 数组
frame = cv2.cvtColor(frame, cv2.COLOR_RGB2BGR) # 转 BGR(OpenCV 世界)
h, w = frame.shape[:2]
small = cv2.resize(frame, (w // 2, h // 2)) # 缩一半
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) # 灰度
roi = frame[h//4 : h*3//4, w//4 : w*3//4] # 中央 1/2 区域
cv2.imwrite("f_small.jpg", small)
cv2.imwrite("f_gray.jpg", gray)
cv2.imwrite("f_roi.jpg", roi)
print("原尺寸:", frame.shape, "| 缩放:", small.shape,
"| 灰度:", gray.shape, "| 裁剪:", roi.shape)
picam2.stop()
看到什么算成功:当前目录多出三个 jpg;打印的四组 shape 分别是(原尺寸)、(一半)、(少一维)、(中央区域)。
读结果:对照数字读——缩放版【高宽各减半】、灰度版【shape 少了第三维】、裁剪版【行范围 h/4 到 3h/4=高一半,列同理】。每个数字都能用 shape 规矩推出来,推不出来回第二节重读。
六、30 秒实验:裁剪坐标的账
对一张 640 × 480 的图执行 img[100:200, 300:500]:
- 裁出区域的尺寸是多少?
- 想裁出【右下角 160 × 120】的区域,切片怎么写?
参考答案:1. shape = (100, 200, 3)——行范围 200−100=100(高),列范围 500−300=200(宽)。2. img[360:480, 480:640]——高 480−360=120、宽 640−480=160,含头不含尾(6-6 的切片规矩原样适用)。
七、常见误区
- 【OpenCV 里红色是 (255, 0, 0)】 —— 那是蓝!BGR 顺序,红色是
(0, 0, 255)。 - 【img[300:500, 100:200] 裁出宽 400 的区域】 —— 切片是行在前:这是【第 300~500 行、第 100~200 列】=高 200 宽 100;x、y 写反不报错。
- 【resize(frame, (480, 640))】 —— resize 参数是(宽, 高),与 shape 的显示顺序相反;写反只是图变形。
- 【imread 返回的图可以直接用】 —— 路径写错它只返回 None 不报错,炸点在后面;读后先判
img is None。 - 【picamera2 的帧直接喂 OpenCV 函数】 —— 中间要
cv2.cvtColor(frame, cv2.COLOR_RGB2BGR);省了这步,颜色通道全反。
自测三问(点开对答案)
1. `img[240, 320]` 和 `img[320, 240]` 是同一个像素吗? 2. 为什么 AI 实验常先转灰度? 3. 无头 SSH 环境里怎么验收视觉结果? **参考答案** 1. 不是。shape 是(高, 宽)=(行, 列):前者是第 240 行第 320 列,后者是第 320 行第 240 列——**写反了拿的是另一个位置的像素**。 2. 通道 3→1,数据量砍三分之二;而检测【有没有脸/物体】不太依赖颜色——更快且够用(11-4 第一步就是它)。 3. imshow 开不了窗口;用 `imwrite` 存盘后 `scp` 拷回 Windows 看(3-6),或干脆打印 shape/判断结果。- ☐ 下一节(11-4 AI 实验范例:OpenCV 人脸识别)
相关笔记
- 使用 Python 操作相机模块 —— capture_array 与 shape 规矩的出处
- 使用外接 USB 网络摄像头 —— OpenCV 取流的另一帧来源
- Python 列表与字符串 —— 切片【含头不含尾】规矩的出处
- AI 实验范例:OpenCV 人脸识别 —— 下一节:基本功合成第一个 AI 实验
- 🍓 树莓派 · 索引 —— 全课路线图