Python OpenCV学习第二天:读个图颜色居然是反的
昨天把环境和NumPy基础搞定了,今天信心满满地开始学图像读写,本来以为就是调几个函数的事,结果第一张图读出来直接给我整懵了——好好的蓝天变成了深蓝,红花变成了蓝花,折腾了半天才搞明白是OpenCV那个反人类的BGR颜色空间搞的鬼。今天踩的坑比昨天还多,不过好歹把核心操作都摸透了。
今日目标完成情况
- 图像的读取、显示、保存全流程搞定
- 搞懂了BGR和RGB的区别及转换方法
- 学会了通道分离与合并
- 成功调用电脑摄像头实时显示画面
- 完成了第一个小项目:简易图像查看器
一、图像基本操作:读、显、存(坑最多的部分)
这三个函数看起来简单,但新手能踩的坑一个都不少,我挨个说。
1. 读取图像:cv2.imread()
函数原型:cv2.imread(文件路径, 读取模式)
import cv2
import numpy as np
# 最常用的读取方式:彩色图模式(默认)
img = cv2.imread("test.jpg")
# 其他读取模式
img_gray = cv2.imread("test.jpg", cv2.IMREAD_GRAYSCALE) # 灰度图模式
img_unchanged = cv2.imread("test.jpg", cv2.IMREAD_UNCHANGED) # 包含透明通道路径绝对不能有中文! 这是90%新手第一个踩的坑。如果路径里有中文(虽然我用的是test.jpg),
imread()不会报错,但会返回None,后面所有操作都会报错。解决方法:把图片放到纯英文路径下,或者用下面的兼容写法:# 中文路径兼容写法 img = cv2.imdecode(np.fromfile("测试图片.jpg", dtype=np.uint8), cv2.IMREAD_COLOR)- 图片不存在/路径写错时,会出警告但不会直接崩溃!
第一步:它会先弹出一串黄色警告(Warning):
[ WARN:0@0.032] ... can't open/read file ...

(我把文件名字写错了)
意思是“找不到图片了,但我先不跟你玩命,提醒你一下”。
- 第二步:程序不会停止,继续往下运行,但会把
img变量赋值为None(空的)。 第三步:如果你没加判断,后面直接用
img(比如cv2.imshow),这时候才会弹出红色报错(Error),然后程序直接崩溃闪退。所以必须加这个判断! 把问题扼杀在摇篮里:
if img is None: print("图片读取失败!请检查路径/文件名/是否有中文") else: print("图片读取成功")

文件路径错误调用后爆炸
- 读取模式参数:默认是
cv2.IMREAD_COLOR(值为1),会自动忽略透明通道;如果需要保留透明通道(PNG图片),必须用cv2.IMREAD_UNCHANGED(值为-1)。
2. 显示图像:cv2.imshow()
函数原型:cv2.imshow(窗口名, 图像数组)
# 显示图像
cv2.imshow("img", img)
# ↓必须加这两行!不然窗口会一闪而过
cv2.waitKey(0) # 等待任意按键按下,0表示无限等待
cv2.destroyAllWindows() # 关闭所有窗口注意:
- 不写
waitKey()和destroyAllWindows()会导致程序卡死,窗口关不掉,只能强制结束进程。 waitKey(0)里的数字是等待时间(毫秒),如果写waitKey(1000)就是等待1秒后自动关闭窗口。- 多个窗口的话,窗口名不能重复,否则后面的会覆盖前面的。
3. 保存图像:cv2.imwrite()
函数原型:cv2.imwrite(保存路径, 图像数组)
# 保存图像
cv2.imwrite("output.jpg", img)
# 中文路径兼容写法
cv2.imencode(".jpg", img)[1].tofile("输出图片.jpg")注意:保存格式由后缀名决定,支持jpg、png、bmp等常见格式。保存jpg时可以指定压缩质量(0-100,默认95):
cv2.imwrite("output.jpg", img, [cv2.IMWRITE_JPEG_QUALITY, 100]) # 最高质量二、BGR颜色空间:OpenCV最反人类的设计
终于说到今天最大的坑了!为什么我读出来的图颜色不对?因为OpenCV默认的彩色图像格式是BGR,而不是我们平时用的RGB!
也就是说,普通图片的像素顺序是(红、绿、蓝),但OpenCV读进来之后会变成(蓝、绿、红),所以红色会变成蓝色,蓝色会变成红色,绿色不变。
1. BGR转RGB
解决方法很简单,用cv2.cvtColor()转换一下就行:
# BGR转RGB
img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
# 其他转换
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # BGR转灰度图
img_hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV) # BGR转HSV(后面颜色识别会用到)2. 通道分离与合并
有时候我们需要单独处理某一个颜色通道,比如只提取红色通道,这就需要用到通道分离和合并:
# 分离通道(B、G、R三个通道)
b, g, r = cv2.split(img)
# 合并通道(注意顺序!)
img_merged = cv2.merge([b, g, r]) # 合并成BGR图像
img_rgb_merged = cv2.merge([r, g, b]) # 合并成RGB图像偷鸡技巧:如果想单独显示某个通道,直接cv2.imshow("Red", r)会显示成灰度图,因为单通道数组会被当成灰度图处理。如果想显示成红色,需要把其他两个通道设为0:
# 只显示红色通道(彩色,黑白没有红色)
red_channel = np.zeros_like(img)
red_channel[:, :, 2] = r # 红色通道是第三个通道(索引2)
cv2.imshow("Red Channel", red_channel)三、视频与摄像头调用:比想象中简单(康养写过)
OpenCV处理视频的逻辑很简单:把视频当成一帧一帧的图像,逐帧处理就行。
1. 读取本地视频
# 创建视频捕获对象,参数是视频文件路径
cap = cv2.VideoCapture("video.mp4")
# 循环读取每一帧
while cap.isOpened():
# 读取一帧,ret表示是否读取成功,frame是当前帧的图像
ret, frame = cap.read()
if not ret:
print("视频读取完毕")
break
# 显示当前帧
cv2.imshow("Video", frame)
# 按q键退出(等待1毫秒,这样视频才能正常播放)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
# 释放资源
cap.release()
cv2.destroyAllWindows()2. 调用电脑摄像头
把上面的视频路径换成0就行,0表示默认摄像头:
cap = cv2.VideoCapture(0) # 0是默认摄像头,1是第二个摄像头
while cap.isOpened():
ret, frame = cap.read()
if not ret:
break
# 这里可以对frame进行处理,比如转成灰度图(不建议,因为会变成遗照🤔)
#gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
cv2.imshow("摄像头", gray)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
cap.release()
cv2.destroyAllWindows()踩坑:如果摄像头被其他程序占用,cap.isOpened()会返回False,关闭其他占用摄像头的程序就行。
四、今日小项目:简易图像查看器
把今天学的内容整合起来,写一个能打开、显示、保存图像的小程序:
import cv2
import numpy as np
def main():
# 读取图像(中文路径兼容)
img_path = "test.jpg"
img = cv2.imdecode(np.fromfile(img_path, dtype=np.uint8), cv2.IMREAD_COLOR)
if img is None:
print("图片读取失败!")
return
# 显示原图
cv2.imshow("原图", img)
# 转成灰度图并显示
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
cv2.imshow("灰度图", gray)
# 转成RGB并显示
rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
cv2.imshow("RGB图", rgb)
# 等待按键
key = cv2.waitKey(0)
# 按s键保存灰度图
if key == ord('s'):
cv2.imencode(".jpg", gray)[1].tofile("灰度图.jpg")
print("灰度图已保存")
# 关闭所有窗口
cv2.destroyAllWindows()
if __name__ == "__main__":
main()运行这个程序,就能同时看到原图、灰度图和RGB图的区别了,按s键还能保存灰度图。
五、今日踩坑总结
- 中文路径大坑:
cv2.imread()和cv2.imwrite()不支持中文路径,必须用imdecode和imencode的兼容写法。 - BGR颜色空间:OpenCV默认是BGR,和所有其他库(Matplotlib、PIL)都不一样,显示颜色不对先转RGB。
- 窗口关闭问题:必须写
cv2.waitKey()和cv2.destroyAllWindows(),不然窗口会卡死。 - 视频播放速度:
cv2.waitKey(1)里的数字控制视频播放速度,数字越小播放越快,一般用1-10毫秒。 - 摄像头权限:如果摄像头打不开,检查一下系统是否给了Python摄像头权限。
六、明天要学的
- 图像的几何变换:缩放、旋转、平移、翻转
- 图像阈值处理:简单阈值和自适应阈值
- 图像滤波:均值滤波、高斯滤波、中值滤波
- 写一个简单的图像滤镜小程序
今天学了大概两个小时,主要是颜色空间的坑浪费了不少时间,一开始还以为是我电脑出问题了,查了半天才知道是OpenCV的设计问题。不过搞懂之后就觉得其实也不难,都是固定的套路。果然学OpenCV就是不断踩坑然后填坑的过程,多敲代码多踩坑,自然就会了。