Python OpenCV快速入门:读取、显示与保存图像 – wiki基地


Python OpenCV快速入门:读取、显示与保存图像的艺术

引言:开启计算机视觉之门

在人工智能浪潮席卷全球的今天,计算机视觉(Computer Vision, CV)作为其核心分支之一,正以前所未有的深度和广度改变着我们的世界。从手机上的人脸识别解锁,到马路上飞驰的自动驾驶汽车,再到医疗领域的影像分析,计算机视觉技术已无处不在。它赋予了机器“看”懂世界的能力,而要踏入这个神奇的领域,有一个工具库我们无论如何也绕不开——它就是 OpenCV

OpenCV(Open Source Computer Vision Library)是一个开源的、跨平台的计算机视觉和机器学习软件库。它诞生于1999年,由英特尔公司发起,如今已发展成为全球最受欢迎的计算机视觉库。凭借其强大的功能、丰富的算法、高效的性能以及对 Python、C++、Java 等多种语言的良好支持,OpenCV 成为了学者、开发者和企业进行图像处理和计算机视觉项目开发的首选。

对于初学者而言,任何宏大的项目都是从最基础的操作开始的。在计算机视觉中,这个起点就是与图像的交互:如何让程序“看到”一张图片?如何将处理后的结果展示出来?又如何将我们的成果永久保存?这三个基本操作——读取(Read)、显示(Show)和保存(Write)——构成了所有复杂视觉任务的基石。

本文将作为您进入 Python OpenCV 世界的“第一课”,以尽可能详尽、通俗易懂的方式,带您深入探索这三个核心操作。我们将不仅仅满足于知道“如何做”,更要理解“为什么这么做”,并指出初学者最容易遇到的“坑”。读完本文,您将能够自信地使用 Python 和 OpenCV 对图像进行基本的输入输出操作,为后续学习更高级的图像处理、特征提取、目标检测等技术打下坚实的基础。

一、 环境准备与安装:万丈高楼平地起

在编写任何代码之前,我们需要确保开发环境已经准备就绪。这就像一位画家在挥毫泼墨前,需要备好画布、画笔和颜料。

1. Python环境:
首先,请确保您的计算机上已经安装了 Python。OpenCV 对 Python 3.x 版本有很好的支持,建议安装最新稳定版的 Python 3。您可以从 Python 官网 (python.org) 下载并安装。

2. 安装OpenCV库:
安装 OpenCV 的 Python 绑定非常简单,主要通过 Python 的包管理工具 pip 来完成。打开您的终端(在 Windows 上是命令提示符或 PowerShell,在 macOS 或 Linux 上是 Terminal),然后输入以下命令:

bash
pip install opencv-python

这个命令会下载并安装 OpenCV 的主模块包。对于大多数入门和常规应用,这个包已经足够。

小知识:opencv-python 的不同版本
你可能还会看到 opencv-contrib-pythonopencv-python-headless 等包。简单来说:
opencv-python:只包含 OpenCV 的核心功能和主要算法。
opencv-contrib-python:包含了核心功能以及社区贡献的扩展模块(contrib modules),功能更全,包含一些实验性或非自由的算法(如 SIFT)。初学者如果想探索更多功能,可以直接安装这个。
opencv-python-headless:无头版本,意味着它不包含任何用于显示图像的 GUI 功能(比如 cv2.imshow)。它适用于服务器、Docker 容器等没有图形化界面的环境。

对于本文的学习,请确保您安装的是 opencv-pythonopencv-contrib-python,而不是 headless 版本。

3. 验证安装:
安装完成后,我们来验证一下是否成功。在终端中启动 Python 解释器,或者在您的代码编辑器中新建一个 Python 文件,输入以下代码:

“`python
import cv2

打印 OpenCV 的版本号

print(f”OpenCV Version: {cv2.version}”)
“`

如果代码成功运行并打印出类似 OpenCV Version: 4.8.0 的版本信息,那么恭喜您,环境已经搭建完毕,我们可以正式开始图像探索之旅了!

二、 核心操作一:读取图像 (cv2.imread)

读取图像是与计算机视觉世界打交道的第一步。OpenCV 提供了一个非常直观的函数 cv2.imread() 来完成这个任务。

2.1 函数语法解析

cv2.imread() 的基本语法如下:

python
image = cv2.imread(filepath, flags)

它接受两个主要参数:

  • filepath (字符串类型):这是您要读取的图像文件的路径。它可以是相对路径(例如 'cat.jpg',表示图像与您的 Python 脚本在同一个目录下),也可以是绝对路径(例如 'C:/Users/YourUser/Pictures/cat.jpg')。
  • flags (整数类型,可选):这是一个标志,用于指定读取图像的方式。它决定了图像将被读成彩色、灰度还是包含透明通道。如果不提供此参数,默认值为 cv2.IMREAD_COLOR

2.2 flags 参数详解:图像的不同“面孔”

flags 参数是 imread 的灵魂,它让我们可以按需加载图像数据。最常用的三个标志是:

  1. cv2.IMREAD_COLOR (或整数 1)

    • 这是默认的加载方式。
    • 它会以彩色模式加载图像,忽略任何透明度信息(alpha 通道)。
    • 最终加载的图像将是一个包含 3 个颜色通道的图像。
  2. cv2.IMREAD_GRAYSCALE (或整数 0)

    • 它会以灰度模式加载图像。
    • 无论原始图像是彩色的还是黑白的,最终加载的图像都将是单通道的灰度图。每个像素只用一个值(通常是0到255)来表示其亮度。
  3. cv2.IMREAD_UNCHANGED (或整数 -1)

    • 它会加载图像的原始数据,包括其 alpha 通道(如果存在)。
    • 例如,对于一个具有透明背景的 PNG 图像,使用此标志加载后,将得到一个包含 4 个通道的图像(如 B, G, R, A)。

2.3 图像的数据结构:揭开 NumPy 的面纱

cv2.imread() 成功读取一张图像后,它返回的是什么呢?它返回的不是一个“图片”对象,而是一个 NumPy 数组(numpy.ndarray)!

这是 OpenCV 与 Python 生态无缝集成的关键所在。NumPy 是 Python 中用于科学计算的核心库,它提供了强大、高效的多维数组对象。将图像表示为 NumPy 数组,意味着我们可以利用 NumPy 强大的数组操作能力来对像素进行任何我们能想象到的数学运算。

  • 对于彩色图像 (BGR):返回的 NumPy 数组形状通常是 (height, width, 3)
  • height:图像的高度(像素行数)。
  • width:图像的宽度(像素列数)。
  • 3:代表三个颜色通道。
  • 对于灰度图像:返回的 NumPy 数组形状是 (height, width)。它是一个二维数组,因为每个像素只有一个值。

一个至关重要的知识点:BGR 色彩空间

几乎所有现代的图像格式(JPG, PNG)和显示设备都使用 RGB (Red, Green, Blue) 色彩空间。然而,由于历史原因,OpenCV 默认的色彩空间是 BGR (Blue, Green, Red)。这意味着当你用 cv2.imread() 读取一张彩色图片时,得到的 NumPy 数组中,第三个维度的顺序是 蓝、绿、红,而不是常规的红、绿、蓝。

这是一个新手最常犯的错误。如果您使用其他库(如 Matplotlib)来显示 OpenCV 读取的图像,或者进行需要精确颜色操作的任务,一定要记住这个差异,并在必要时进行通道转换(例如使用 cv2.cvtColor(image, cv2.COLOR_BGR2RGB))。

2.4 实践与常见陷阱

让我们来编写第一段读取图像的代码。首先,请准备一张名为 cat.jpg 的图片,并将其放在与您的 Python 脚本相同的文件夹下。

“`python
import cv2
import numpy as np

定义图像文件路径

image_path = ‘cat.jpg’

1. 以彩色模式读取图像(默认方式)

img_color = cv2.imread(image_path, cv2.IMREAD_COLOR)

2. 以灰度模式读取图像

img_gray = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE)

—————– 常见陷阱检查 —————–

陷阱1:文件路径错误或文件不存在

if img_color is None:
print(f”错误:无法在路径 ‘{image_path}’ 找到或打开图像。”)
print(“请检查:1. 文件名是否正确? 2. 文件是否在正确的目录下? 3. 文件是否损坏?”)
else:
# 验证返回类型和数据形状
print(f”成功读取彩色图像!”)
print(f”图像类型: {type(img_color)}”)
print(f”图像数据形状 (Height, Width, Channels): {img_color.shape}”)
print(f”图像数据类型: {img_color.dtype}”) # 通常是 uint8

print("-" * 30)

print(f"成功读取灰度图像!")
print(f"图像类型: {type(img_gray)}")
print(f"图像数据形状 (Height, Width): {img_gray.shape}")
print(f"图像数据类型: {img_gray.dtype}")

“`

重点关注“常见陷阱检查”部分! 如果 cv2.imread() 因为路径错误、文件不存在或文件格式不支持等原因读取失败,它不会抛出异常(Error),而是会静默地返回 None。如果不对其进行检查,后续的代码(比如 img_color.shape)就会因为对 None 进行操作而引发 AttributeError,这会让初学者感到困惑。因此,养成读取图像后检查其是否为 None 的好习惯至关重要

三、 核心操作二:显示图像 (cv2.imshow)

读取了图像数据,下一步自然是想亲眼看看它。cv2.imshow() 函数承担了这个职责,它能创建一个窗口来展示我们的图像。

3.1 函数语法与工作机制

cv2.imshow() 的语法非常简单:

python
cv2.imshow(winname, mat)

  • winname (字符串类型):将要创建或使用的窗口的名称。您可以把它看作是窗口的唯一标识符。如果使用一个已经存在的 winnameimshow 会更新那个窗口的内容;如果是一个新的名字,它会创建一个新窗口。
  • mat (NumPy 数组):需要显示的图像数据,也就是我们从 cv2.imread() 得到的那个 NumPy 数组。

然而,单独使用 cv2.imshow() 是不够的。在一个典型的 Python 脚本中,程序会瞬间执行完所有代码然后退出。如果你只写 cv2.imshow(),窗口可能会一闪而过,你甚至根本看不到它。为了让窗口能够持续显示并响应用户操作,我们需要请出它的两个“黄金搭档”:cv2.waitKey()cv2.destroyAllWindows()

3.2 黄金搭档:cv2.waitKey()cv2.destroyAllWindows()

  1. cv2.waitKey(delay):

    • 这是 OpenCV GUI 事件循环的核心。它会暂停程序的执行,等待键盘输入。
    • delay (整数):等待的毫秒数。
    • 如果 delay 是一个正整数(例如 1),它会等待 delay 毫秒。这在处理视频流时非常有用,可以控制帧率。
    • 如果 delay0,它会无限期地等待,直到用户按下任意一个键盘按键。这是显示单张静态图像时最常用的方式
    • 该函数还会返回被按下的键的 ASCII 码。这使得我们可以实现简单的交互,比如“按 ‘q’ 键退出”。
  2. cv2.destroyAllWindows():

    • 这个函数会关闭所有由 OpenCV 创建的窗口。
    • 在程序结束前调用它是一个良好的编程习惯,可以确保所有 GUI 资源被正确释放。你也可以使用 cv2.destroyWindow(winname) 来关闭指定的窗口。

3.3 组合实践:一个完整的显示流程

现在,我们将 imreadimshowwaitKey 组合起来,形成一个完整的读取并显示的流程。

“`python
import cv2

图像路径

image_path = ‘cat.jpg’

1. 读取图像

使用之前学到的知识,读取图像并进行检查

img = cv2.imread(image_path)
if img is None:
print(f”错误:无法读取图像 at ‘{image_path}'”)
else:
# 2. 显示图像
# 第一个参数是窗口名,第二个参数是图像数据
cv2.imshow(‘My Cat – Color Image’, img)

# 读取灰度图并显示在另一个窗口
img_gray = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE)
if img_gray is not None:
    cv2.imshow('My Cat - Grayscale Image', img_gray)

# 3. 等待键盘输入
# waitKey(0) 会无限等待,直到用户按下任意键
# 这是让窗口保持显示的关键!
print("图像已显示。请在图像窗口激活状态下按任意键继续...")
cv2.waitKey(0)

# 4. 销毁所有窗口
# 按键后,程序继续执行,销毁所有窗口并退出
cv2.destroyAllWindows()
print("窗口已关闭,程序结束。")

“`

执行这段代码,你会看到两个窗口弹出,一个显示彩色的猫,另一个显示灰度的猫。你的程序会停在 cv2.waitKey(0) 这一行,直到你激活其中一个窗口并按下键盘上的任意一个键,之后两个窗口会同时关闭,程序结束。

四、 核心操作三:保存图像 (cv2.imwrite)

经过一系列处理后(未来你会学习裁剪、缩放、滤镜、添加文字等),我们希望将最终的成果保存为一张新的图片文件。cv2.imwrite() 函数就是为此而生。

4.1 函数语法与智能格式推断

cv2.imwrite() 的语法也相当直接:

python
cv2.imwrite(filename, img, params)

  • filename (字符串类型):要保存的文件的完整路径和文件名,包含文件扩展名(如 .jpg, .png 等)。OpenCV 会根据你提供的扩展名来自动选择合适的图像编码格式。
  • img (NumPy 数组):要保存的图像数据。
  • params (可选列表):一个用于指定特定格式编码参数的列表。

4.2 params 参数:精细化控制保存质量

params 参数让我们可以对保存过程进行更精细的控制,这在对文件大小或图像质量有特殊要求时非常有用。它以一个包含 [flag, value] 对的列表形式提供。

  • 对于 JPEG 格式 (.jpg, .jpeg):
  • cv2.IMWRITE_JPEG_QUALITY: 设置 JPEG 的压缩质量,取值范围是 0 到 100。值越高,图像质量越好,文件也越大。默认值是 95。

  • 对于 PNG 格式 (.png):

  • cv2.IMWRITE_PNG_COMPRESSION: 设置 PNG 的压缩级别,取值范围是 0 到 9。值越高,压缩率越高,文件越小,但压缩过程会更耗时。这是一种无损压缩,不影响图像质量。默认值是 3。

4.3 保存实践

让我们将之前读取的灰度图像保存为一个新的文件,并尝试控制 JPEG 的保存质量。

“`python
import cv2

image_path = ‘cat.jpg’
img_gray = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE)

if img_gray is None:
print(f”错误:无法读取图像 at ‘{image_path}'”)
else:
# 1. 以默认方式保存灰度图为 PNG
# OpenCV会根据扩展名 .png 自动使用PNG编码器
save_path_png = ‘cat_grayscale.png’
success_png = cv2.imwrite(save_path_png, img_gray)
if success_png:
print(f”灰度图像已成功保存到: {save_path_png}”)
else:
print(f”保存失败: {save_path_png}”)

# 2. 保存为高质量的 JPEG
save_path_jpeg_high = 'cat_grayscale_high_quality.jpg'
jpeg_quality_high = [cv2.IMWRITE_JPEG_QUALITY, 98]
success_jpeg_high = cv2.imwrite(save_path_jpeg_high, img_gray, jpeg_quality_high)
if success_jpeg_high:
    print(f"高质量JPEG图像已成功保存到: {save_path_jpeg_high}")
else:
    print(f"保存失败: {save_path_jpeg_high}")

# 3. 保存为低质量的 JPEG
save_path_jpeg_low = 'cat_grayscale_low_quality.jpg'
jpeg_quality_low = [cv2.IMWRITE_JPEG_QUALITY, 10]
success_jpeg_low = cv2.imwrite(save_path_jpeg_low, img_gray, jpeg_quality_low)
if success_jpeg_low:
    print(f"低质量JPEG图像已成功保存到: {save_path_jpeg_low}")
else:
    print(f"保存失败: {save_path_jpeg_low}")

“`

执行完这段代码后,去你的脚本所在的文件夹看看。你会发现多了三个新文件。比较 cat_grayscale_high_quality.jpgcat_grayscale_low_quality.jpg 的文件大小和视觉质量,你会对 params 参数的作用有非常直观的理解。

五、 综合实战案例:一个简单的交互式图像应用

现在,让我们把今天学到的所有知识点串联起来,构建一个稍微有趣一点的综合应用。这个应用将实现以下功能:

  1. 加载一张彩色图片。
  2. 将其转换为灰度图。
  3. 同时显示原图和灰度图。
  4. 等待用户按键:
    • 如果用户按下 ‘s’ 键,就保存灰度图并退出。
    • 如果用户按下 ‘q’ 键或任意其他键,直接退出。

“`python
import cv2

— 配置 —

input_image_path = ‘cat.jpg’ # 输入图像
output_image_path = ‘cat_saved_grayscale.jpg’ # 按’s’键保存的路径

— 主程序 —

1. 读取原始图像

original_image = cv2.imread(input_image_path)

检查图像是否加载成功

if original_image is None:
print(f”致命错误:无法在路径 ‘{input_image_path}’ 加载图像。请检查文件是否存在。”)
exit() # 退出程序

2. 图像处理:将彩色图像转换为灰度图像

我们使用一个新的函数 cv2.cvtColor 来进行色彩空间转换

gray_image = cv2.cvtColor(original_image, cv2.COLOR_BGR2GRAY)

3. 显示图像

创建两个窗口分别显示

cv2.imshow(‘Original Image’, original_image)
cv2.imshow(‘Grayscale Image’, gray_image)

print(“窗口已显示。按 ‘s’ 保存灰度图,按 ‘q’ 或其他任意键退出。”)

4. 等待并响应用户输入

waitKey(0) 会返回按键的ASCII码

key_pressed = cv2.waitKey(0)

检查按下的键

ord(‘s’) 获取字符’s’的ASCII码

if key_pressed == ord(‘s’):
# 用户按下了 ‘s’,执行保存操作
cv2.imwrite(output_image_path, gray_image)
print(f”检测到 ‘s’ 键按下。灰度图像已保存至 ‘{output_image_path}'”)
elif key_pressed == ord(‘q’):
# 用户按下了 ‘q’
print(“检测到 ‘q’ 键按下。程序即将退出。”)
else:
# 用户按下了其他键
print(f”检测到其他按键(ASCII: {key_pressed})。程序即将退出。”)

5. 善后工作:销毁所有窗口

cv2.destroyAllWindows()
print(“所有窗口已关闭,程序执行完毕。”)

“`

这个综合案例不仅复习了 imread, imshow, imwritewaitKey,还通过 cv2.cvtColor 悄悄地为您打开了通往图像处理世界的大门,并展示了如何通过 waitKey 的返回值创建简单的交互逻辑。

总结:奠定坚实的第一步

恭喜您!通过本文的学习,您已经完全掌握了 Python OpenCV 中最基础也是最重要的三个操作:

  • cv2.imread():将磁盘上的图像文件加载为内存中的 NumPy 数组,并理解了不同加载标志(彩色、灰度)以及 BGR 色彩空间的特殊性。
  • cv2.imshow()cv2.waitKey():学会了如何将 NumPy 数组形式的图像数据显示在窗口中,并利用 waitKey 来控制程序的暂停与交互。
  • cv2.imwrite():能够将处理后的 NumPy 数组保存为新的图像文件,并能根据需要控制输出格式和质量。

您还了解了几个至关重要的“最佳实践”:始终检查 imread 的返回值是否为 None,以及在程序结束时使用 destroyAllWindows() 清理资源。

这“三板斧”看似简单,但它们是您未来构建任何复杂计算机视觉应用的起点。无论是人脸识别、目标跟踪还是风格迁移,第一步永远是读取图像,最后一步往往是显示或保存结果。牢固掌握这些基础,您就拥有了在计算机视觉的广阔天地中自由探索的“通行证”。现在,您已经准备好去学习更多激动人心的内容了,比如图像的几何变换、滤波、形态学操作、特征检测等等。祝您在计算机视觉的旅程中一帆风顺,充满发现的乐趣!

发表评论

您的邮箱地址不会被公开。 必填项已用 * 标注

滚动至顶部