Python OpenCV学习第二天:图像读写+BGR颜色空间+摄像头调用

Python OpenCV学习第二天:图像读写+BGR颜色空间+摄像头调用

寒烟似雪
昨天发布

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)  # 包含透明通道
  1. 路径绝对不能有中文! 这是90%新手第一个踩的坑。如果路径里有中文(虽然我用的是test.jpg),imread()不会报错,但会返回None,后面所有操作都会报错。解决方法:把图片放到纯英文路径下,或者用下面的兼容写法:

    # 中文路径兼容写法
    img = cv2.imdecode(np.fromfile("测试图片.jpg", dtype=np.uint8), cv2.IMREAD_COLOR)
  2. 图片不存在/路径写错时,会出警告但不会直接崩溃!
  3. 第一步:它会先弹出一串黄色警告(Warning):

    [ WARN:0@0.032] ... can't open/read file ...

mo6rtsk4.png

(我把文件名字写错了)

意思是“找不到图片了,但我先不跟你玩命,提醒你一下”。

  • 第二步:程序不会停止,继续往下运行,但会把 img 变量赋值为 None(空的)。
  • 第三步:如果你没加判断,后面直接用 img(比如 cv2.imshow),这时候才会弹出红色报错(Error),然后程序直接崩溃闪退。

    所以必须加这个判断! 把问题扼杀在摇篮里:

    if img is None:
        print("图片读取失败!请检查路径/文件名/是否有中文")
    else:
        print("图片读取成功")

mo6ryxne.png

文件路径错误调用后爆炸

  1. 读取模式参数:默认是cv2.IMREAD_COLOR(值为1),会自动忽略透明通道;如果需要保留透明通道(PNG图片),必须用cv2.IMREAD_UNCHANGED(值为-1)。

2. 显示图像:cv2.imshow()

函数原型cv2.imshow(窗口名, 图像数组)

# 显示图像
cv2.imshow("img", img)

# ↓必须加这两行!不然窗口会一闪而过
cv2.waitKey(0)  # 等待任意按键按下,0表示无限等待
cv2.destroyAllWindows()  # 关闭所有窗口

注意

  1. 不写waitKey()destroyAllWindows()会导致程序卡死,窗口关不掉,只能强制结束进程。
  2. waitKey(0)里的数字是等待时间(毫秒),如果写waitKey(1000)就是等待1秒后自动关闭窗口。
  3. 多个窗口的话,窗口名不能重复,否则后面的会覆盖前面的。

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键还能保存灰度图。

五、今日踩坑总结

  1. 中文路径大坑cv2.imread()cv2.imwrite()不支持中文路径,必须用imdecodeimencode的兼容写法。
  2. BGR颜色空间:OpenCV默认是BGR,和所有其他库(Matplotlib、PIL)都不一样,显示颜色不对先转RGB。
  3. 窗口关闭问题:必须写cv2.waitKey()cv2.destroyAllWindows(),不然窗口会卡死。
  4. 视频播放速度cv2.waitKey(1)里的数字控制视频播放速度,数字越小播放越快,一般用1-10毫秒。
  5. 摄像头权限:如果摄像头打不开,检查一下系统是否给了Python摄像头权限。

六、明天要学的

  1. 图像的几何变换:缩放、旋转、平移、翻转
  2. 图像阈值处理:简单阈值和自适应阈值
  3. 图像滤波:均值滤波、高斯滤波、中值滤波
  4. 写一个简单的图像滤镜小程序

今天学了大概两个小时,主要是颜色空间的坑浪费了不少时间,一开始还以为是我电脑出问题了,查了半天才知道是OpenCV的设计问题。不过搞懂之后就觉得其实也不难,都是固定的套路。果然学OpenCV就是不断踩坑然后填坑的过程,多敲代码多踩坑,自然就会了。

© 版权声明
THE END
喜欢就支持一下吧
点赞 0 分享 收藏
评论 抢沙发
OωO
取消