2、图像基础与OpenCV入门:图像读取、显示、保存、色彩空间转换(BGR、灰度、HSV)、图像属性

好,我们正式开始动手了。这一章,我会带你走一遍OpenCV最基础的操作——说白了就是怎么把一张图片请进内存,怎么看它一眼,怎么改改它的颜色,最后再把它存回去。这些操作在车牌识别里会反复出现,所以别嫌简单,基础打牢了后面才不慌。

2.1 图像读取:imread() 的玄机

在OpenCV里,读取图像用的是 cv2.imread()。这个函数有两个参数:第一个是图片路径,第二个是读取模式。模式有三种最常用的:

模式 标志 说明
彩色 cv2.IMREAD_COLOR 或 1 默认模式,读取为BGR三通道彩色图
灰度 cv2.IMREAD_GRAYSCALE 或 0 读取为单通道灰度图
不变 cv2.IMREAD_UNCHANGED 或 -1 包含Alpha通道(透明度)

我个人习惯用数字标志,因为写起来快。但团队协作时建议用常量名,可读性更好。

import cv2

# 读取彩色图像
img_color = cv2.imread('car_plate.jpg', 1)

# 读取灰度图像
img_gray = cv2.imread('car_plate.jpg', 0)

# 读取包含Alpha通道的图像
img_unchanged = cv2.imread('car_plate.jpg', -1)
注意:路径不能有中文!OpenCV底层用的是C++的fopen,对中文支持很差。我曾经因为这个坑,调试了半天才发现是图片路径里有个“测试”两个字。建议项目路径全用英文。

如果图片不存在,imread() 不会报错,而是返回 None。所以读取后最好检查一下:

if img is None:
    print("图片没找到,检查路径!")
    exit()

2.2 图像显示:imshow() 与窗口管理

显示图像用 cv2.imshow()。它创建一个窗口,把图像怼进去。配合 cv2.waitKey()cv2.destroyAllWindows() 一起用。

cv2.imshow('车牌原图', img_color)
cv2.waitKey(0)  # 等待按键,0表示无限等待
cv2.destroyAllWindows()

waitKey() 的参数是毫秒数。如果设为0,程序会一直停在那里,直到你按任意键。这个函数还会返回按键的ASCII码,后面做交互时会用到。

小技巧:在Jupyter Notebook里,imshow() 可能会卡住。我建议用 cv2.imshow() 配合 cv2.waitKey(1) 来刷新窗口,或者干脆用matplotlib来显示。

2.3 图像保存:imwrite() 的格式秘密

保存图像用 cv2.imwrite()。第一个参数是保存路径,第二个参数是图像数据。OpenCV会根据文件扩展名自动选择编码格式。

cv2.imwrite('output.jpg', img_color)
cv2.imwrite('output.png', img_color)

保存JPG时可以控制质量:

cv2.imwrite('output_high.jpg', img_color, [cv2.IMWRITE_JPEG_QUALITY, 95])

质量范围是0-100,默认95。数值越大,文件越大,画质越好。车牌识别时我一般用95以上,因为车牌上的字符边缘不能有太多压缩伪影。

2.4 色彩空间转换:BGR、灰度、HSV

OpenCV默认读取的是BGR格式,不是我们熟悉的RGB。这一点特别容易搞混。我第一次用OpenCV显示图片时,发现颜色偏蓝,查了半天才发现是通道顺序的问题。

转换色彩空间用 cv2.cvtColor()

# BGR 转灰度
gray = cv2.cvtColor(img_color, cv2.COLOR_BGR2GRAY)

# BGR 转 HSV
hsv = cv2.cvtColor(img_color, cv2.COLOR_BGR2HSV)

# HSV 转 BGR
bgr = cv2.cvtColor(hsv, cv2.COLOR_HSV2BGR)

为什么车牌识别要用HSV?因为HSV把颜色信息(Hue)和亮度(Value)分开了。蓝色车牌在HSV空间里,H值集中在100-130之间。这样我们就能通过阈值直接提取蓝色区域,不受光照影响。

核心思路:车牌识别中,我们经常先用HSV提取颜色区域,再用灰度图做边缘检测。两种色彩空间各有用途。

2.5 图像属性:shape、dtype、size

图像在OpenCV里就是一个NumPy数组。我们可以通过属性来了解它的结构:

print("图像形状:", img_color.shape)   # (高度, 宽度, 通道数)
print("数据类型:", img_color.dtype)   # uint8
print("像素总数:", img_color.size)    # 高 * 宽 * 通道数

举个例子,一张1080x1920的彩色图:

  • shape 返回 (1080, 1920, 3)
  • dtype 返回 uint8(0-255的整数)
  • size 返回 1080 * 1920 * 3 = 6,220,800

灰度图只有两个维度:(1080, 1920)。这一点在处理时要特别注意,因为很多函数要求输入是单通道或三通道。

避坑指南:我曾经在车牌定位时,把灰度图当三通道图传给了某个函数,结果程序直接崩了。检查图像维度是个好习惯,尤其是在调试阶段。

2.6 实战小练习:读取车牌并转换

我们来写一个完整的例子,把今天学的串起来:

import cv2

# 1. 读取车牌图像
plate = cv2.imread('blue_plate.jpg')
if plate is None:
    print("图片加载失败")
    exit()

# 2. 显示原图
cv2.imshow('原图', plate)

# 3. 转灰度
gray_plate = cv2.cvtColor(plate, cv2.COLOR_BGR2GRAY)
cv2.imshow('灰度图', gray_plate)

# 4. 转HSV
hsv_plate = cv2.cvtColor(plate, cv2.COLOR_BGR2HSV)
cv2.imshow('HSV图', hsv_plate)

# 5. 保存灰度图
cv2.imwrite('gray_plate.jpg', gray_plate)

# 6. 打印属性
print("原图属性:", plate.shape)
print("灰度图属性:", gray_plate.shape)

cv2.waitKey(0)
cv2.destroyAllWindows()

运行这段代码,你会看到三个窗口依次弹出。按任意键关闭。灰度图保存到了当前目录。

我的习惯:在车牌识别项目中,我会把原图、灰度图、HSV图都保存一份。调试时对照着看,能快速定位问题出在哪个环节。

嗯,这一章的内容就到这里。图像读取、显示、保存、色彩空间转换、图像属性——这些是OpenCV的“呼吸”和“心跳”。后面每一章都会用到它们。下一章我们聊聊图像预处理,那是车牌识别里最花功夫的部分。

公众号:蓝海资料掘金营,微信deep3321