OpenCV快速入门:图像读取、绘制与几何变换完整指南

引言

OpenCV(Open Source Computer Vision Library)是计算机视觉领域最重要和广泛使用的开源库之一。它提供了丰富的图像处理和计算机视觉算法,支持多种编程语言(C++、Python、Java等)。本文将详细介绍OpenCV的基本操作,包括图像读取、绘制基本图形、几何变换等核心功能,为后续深入学习计算机视觉打下坚实基础。

📂 所属阶段:第一阶段 — 图像处理基石(传统 CV 篇)
🔗 相关章节:CV 概览与数字图像基础 · 图像增强与滤波


1. OpenCV安装与基础配置

1.1 安装OpenCV

# 基础版OpenCV
pip install opencv-python

# 包含额外功能的贡献版(推荐)
pip install opencv-contrib-python

# 如果需要最小化安装
pip install opencv-python-headless  # 适用于服务器环境

1.2 验证安装

import cv2
print(f"OpenCV版本: {cv2.__version__}")

# 检查是否支持CUDA(GPU加速)
print(f"CUDA支持: {cv2.cuda.getCudaEnabledDeviceCount() > 0}")

1.3 基础导入和配置

import cv2
import numpy as np
import matplotlib.pyplot as plt

# 设置中文字体(可选)
plt.rcParams['font.sans-serif'] = ['SimHei']  # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False  # 用来正常显示负号

2. 图像读取与显示

2.1 图像读取

import cv2
import numpy as np

# 读取图像 - 彩色模式(默认)
img_color = cv2.imread("photo.jpg", cv2.IMREAD_COLOR)

# 读取图像 - 灰度模式
img_gray = cv2.imread("photo.jpg", cv2.IMREAD_GRAYSCALE)

# 读取图像 - 包含alpha通道
img_unchanged = cv2.imread("photo.png", cv2.IMREAD_UNCHANGED)

# 检查图像是否成功加载
if img_color is None:
    print("错误:无法读取图像文件")
else:
    print(f"图像形状: {img_color.shape}")  # (高度, 宽度, 通道数)
    print(f"数据类型: {img_color.dtype}")  # uint8
    print(f"图像尺寸: {img_color.shape[1]} x {img_color.shape[0]}")  # 宽度 x 高度

2.2 图像显示

# 使用OpenCV显示图像
cv2.imshow("彩色图像", img_color)
cv2.imshow("灰度图像", img_gray)

# 等待按键(0表示无限等待,其他数字表示毫秒)
cv2.waitKey(0)

# 销毁所有窗口
cv2.destroyAllWindows()

# 或者只销毁特定窗口
# cv2.destroyWindow("彩色图像")

2.3 图像保存

# 保存图像
success = cv2.imwrite("output.jpg", img_color)
if success:
    print("图像保存成功")
else:
    print("图像保存失败")

# 保存时设置压缩质量(JPEG)
cv2.imwrite("output_high_quality.jpg", img_color, [cv2.IMWRITE_JPEG_QUALITY, 95])

# 保存PNG图像时设置压缩级别
cv2.imwrite("output.png", img_color, [cv2.IMWRITE_PNG_COMPRESSION, 9])

2.4 Matplotlib显示(推荐用于Jupyter环境)

import matplotlib.pyplot as plt

def show_image(img, title="Image"):
    """
    使用matplotlib显示OpenCV图像(自动处理BGR到RGB转换)
    """
    if len(img.shape) == 3:  # 彩色图像
        # OpenCV使用BGR,matplotlib使用RGB
        img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
        plt.figure(figsize=(10, 6))
        plt.imshow(img_rgb)
    else:  # 灰度图像
        plt.figure(figsize=(10, 6))
        plt.imshow(img, cmap='gray')
    
    plt.title(title)
    plt.axis('off')
    plt.show()

# 使用示例
show_image(img_color, "原始图像")

3. 绘制基本图形

3.1 创建空白画布

import cv2
import numpy as np

# 创建空白图像(黑色背景)
height, width = 400, 600
blank_img = np.zeros((height, width, 3), dtype=np.uint8)

# 创建白色背景图像
white_img = 255 * np.ones((height, width, 3), dtype=np.uint8)

# 创建指定颜色背景
blue_bg = np.full((height, width, 3), (255, 0, 0), dtype=np.uint8)  # 蓝色背景

3.2 绘制直线

# 创建画布
img = np.zeros((400, 600, 3), dtype=np.uint8)

# 绘制直线:cv2.line(图像, 起点, 终点, 颜色(BGR), 线宽)
cv2.line(img, (50, 50), (550, 50), (0, 255, 0), 2)  # 绿色直线
cv2.line(img, (50, 100), (550, 300), (255, 0, 0), 3)  # 蓝色粗线

# 绘制虚线(自定义函数)
def draw_dashed_line(img, pt1, pt2, color, thickness=1, gap=10):
    """绘制虚线"""
    dist = ((pt1[0] - pt2[0])**2 + (pt1[1] - pt2[1])**2)**0.5
    dash_count = int(dist / gap)
    
    for i in range(dash_count):
        if i % 2 == 0:  # 只绘制偶数段
            start = (int(pt1[0] + (pt2[0] - pt1[0]) * i / dash_count),
                     int(pt1[1] + (pt2[1] - pt1[1]) * i / dash_count))
            end = (int(pt1[0] + (pt2[0] - pt1[0]) * (i + 1) / dash_count),
                   int(pt1[1] + (pt2[1] - pt1[1]) * (i + 1) / dash_count))
            cv2.line(img, start, end, color, thickness)

# 使用虚线函数
draw_dashed_line(img, (50, 200), (550, 200), (0, 255, 255), 2)

3.3 绘制矩形

# 绘制矩形:cv2.rectangle(图像, 左上角, 右下角, 颜色, 线宽(-1表示填充))
cv2.rectangle(img, (100, 100), (300, 200), (255, 0, 0), 2)  # 空心矩形
cv2.rectangle(img, (350, 100), (500, 180), (0, 255, 0), -1)  # 填充矩形

# 绘制圆角矩形(自定义函数)
def draw_rounded_rectangle(img, pt1, pt2, color, radius, thickness=1):
    """绘制圆角矩形"""
    x1, y1 = pt1
    x2, y2 = pt2
    
    # 绘制四个圆角
    cv2.circle(img, (x1 + radius, y1 + radius), radius, color, thickness)
    cv2.circle(img, (x2 - radius, y1 + radius), radius, color, thickness)
    cv2.circle(img, (x1 + radius, y2 - radius), radius, color, thickness)
    cv2.circle(img, (x2 - radius, y2 - radius), radius, color, thickness)
    
    # 绘制直线部分
    cv2.line(img, (x1 + radius, y1), (x2 - radius, y1), color, thickness)
    cv2.line(img, (x1 + radius, y2), (x2 - radius, y2), color, thickness)
    cv2.line(img, (x1, y1 + radius), (x1, y2 - radius), color, thickness)
    cv2.line(img, (x2, y1 + radius), (x2, y2 - radius), color, thickness)

# 使用圆角矩形函数
draw_rounded_rectangle(img, (100, 250), (250, 350), (255, 255, 0), 10, 2)

3.4 绘制圆形和椭圆

# 绘制圆形:cv2.circle(图像, 圆心, 半径, 颜色, 线宽)
cv2.circle(img, (450, 150), 50, (0, 0, 255), -1)  # 填充红色圆形

# 绘制椭圆:cv2.ellipse(图像, 中心, 轴长, 旋转角度, 起始角, 结束角, 颜色, 线宽)
cv2.ellipse(img, (300, 300), (80, 40), 0, 0, 360, (255, 0, 255), 2)  # 椭圆

# 绘制部分椭圆弧
cv2.ellipse(img, (400, 300), (60, 30), 45, 0, 180, (0, 255, 255), 3)  # 椭圆弧

3.5 绘制多边形

# 绘制多边形
points = np.array([[100, 350], [150, 300], [200, 320], [180, 370], [120, 380]], np.int32)
points = points.reshape((-1, 1, 2))

# 绘制多边形:cv2.polylines(图像, 点集, 是否闭合, 颜色, 线宽)
cv2.polylines(img, [points], True, (255, 255, 255), 2)  # 闭合多边形

# 填充多边形
cv2.fillPoly(img, [points], (128, 128, 128))  # 填充灰色

3.6 添加文字

# 添加文字:cv2.putText(图像, 文本, 位置, 字体, 大小, 颜色, 粗细)
cv2.putText(img, "Hello OpenCV!", (50, 350), 
            cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 255, 255), 2)

# 不同的字体样式
cv2.putText(img, "Normal Text", (50, 50), 
            cv2.FONT_HERSHEY_PLAIN, 1, (255, 255, 255), 1)

cv2.putText(img, "Complex Text", (50, 75), 
            cv2.FONT_HERSHEY_COMPLEX, 0.8, (255, 255, 255), 1)

# 带背景的文字(自定义函数)
def put_text_with_background(img, text, position, font, font_scale, color, thickness, bg_color=(0,0,0)):
    """添加带背景的文字"""
    # 获取文本边界框
    text_size = cv2.getTextSize(text, font, font_scale, thickness)[0]
    
    # 计算背景矩形坐标
    text_x, text_y = position
    bg_top_left = (text_x, text_y - text_size[1] - 10)
    bg_bottom_right = (text_x + text_size[0], text_y + 5)
    
    # 绘制背景矩形
    cv2.rectangle(img, bg_top_left, bg_bottom_right, bg_color, -1)
    
    # 添加文字
    cv2.putText(img, text, position, font, font_scale, color, thickness)

# 使用带背景文字函数
put_text_with_background(img, "带背景文字", (200, 50), 
                        cv2.FONT_HERSHEY_SIMPLEX, 0.8, (255, 255, 255), 2)

4. 几何变换详解

4.1 图像缩放

import cv2
import numpy as np

img = cv2.imread("photo.jpg")

# 方法一:指定输出大小
resized_fixed = cv2.resize(img, (300, 200))  # (宽度, 高度)

# 方法二:指定缩放比例
resized_scaled = cv2.resize(img, None, fx=0.5, fy=0.5)  # 宽高各缩小一半

# 方法三:保持宽高比的缩放
def resize_keep_aspect_ratio(image, target_width=None, target_height=None):
    """保持宽高比的缩放"""
    h, w = image.shape[:2]
    
    if target_width and target_height:
        # 计算缩放比例,保持宽高比
        scale = min(target_width/w, target_height/h)
        new_w, new_h = int(w * scale), int(h * scale)
    elif target_width:
        scale = target_width / w
        new_w, new_h = target_width, int(h * scale)
    elif target_height:
        scale = target_height / h
        new_w, new_h = int(w * scale), target_height
    else:
        return image
    
    return cv2.resize(image, (new_w, new_h))

# 使用示例
resized_aspect = resize_keep_aspect_ratio(img, target_width=400)

# 不同的插值方法
resized_cubic = cv2.resize(img, (800, 600), interpolation=cv2.INTER_CUBIC)  # 放大用
resized_area = cv2.resize(img, (200, 150), interpolation=cv2.INTER_AREA)    # 缩小用

4.2 图像旋转

def rotate_image(image, angle, center=None, scale=1.0):
    """
    旋转图像
    """
    h, w = image.shape[:2]
    
    # 如果未指定中心点,则使用图像中心
    if center is None:
        center = (w // 2, h // 2)
    
    # 获取旋转矩阵
    rotation_matrix = cv2.getRotationMatrix2D(center, angle, scale)
    
    # 应用旋转
    rotated = cv2.warpAffine(image, rotation_matrix, (w, h))
    
    return rotated

# 基本旋转
img_rotated = rotate_image(img, 45)  # 旋转45度

# 旋转并缩放
img_rotated_scaled = rotate_image(img, 30, scale=0.8)

# 完整旋转(保持完整图像)
def rotate_image_complete(image, angle):
    """完整旋转(防止裁剪)"""
    h, w = image.shape[:2]
    center = (w // 2, h // 2)
    
    # 获取旋转矩阵
    rotation_matrix = cv2.getRotationMatrix2D(center, angle, 1.0)
    
    # 计算旋转后的新边界
    cos = abs(rotation_matrix[0, 0])
    sin = abs(rotation_matrix[0, 1])
    
    new_w = int((h * sin) + (w * cos))
    new_h = int((h * cos) + (w * sin))
    
    # 调整旋转矩阵的平移部分
    rotation_matrix[0, 2] += (new_w / 2) - center[0]
    rotation_matrix[1, 2] += (new_h / 2) - center[1]
    
    # 应用旋转
    rotated = cv2.warpAffine(image, rotation_matrix, (new_w, new_h))
    
    return rotated

# 完整旋转示例
img_rotated_full = rotate_image_complete(img, 45)

4.3 仿射变换

def apply_affine_transform(image, src_points, dst_points):
    """
    应用仿射变换
    """
    # 获取仿射变换矩阵
    matrix = cv2.getAffineTransform(src_points.astype(np.float32), 
                                   dst_points.astype(np.float32))
    
    # 应用变换
    h, w = image.shape[:2]
    transformed = cv2.warpAffine(image, matrix, (w, h))
    
    return transformed, matrix

# 仿射变换示例
h, w = img.shape[:2]

# 定义原图的三个点和目标位置
src_triangle = np.float32([[50, 50], [200, 50], [50, 200]])
dst_triangle = np.float32([[10, 100], [200, 50], [100, 250]])

img_affine, transform_matrix = apply_affine_transform(img, src_triangle, dst_triangle)

# 平行四边形变换
def parallelogram_transform(image):
    """平行四边形变换示例"""
    h, w = image.shape[:2]
    
    # 原图的四个角点
    src_points = np.float32([
        [0, 0],           # 左上
        [w-1, 0],         # 右上
        [0, h-1]          # 左下
    ])
    
    # 目标位置(创建倾斜效果)
    dst_points = np.float32([
        [50, 0],          # 左上向右偏移
        [w-1, 0],         # 右上不变
        [0, h-1]          # 左下不变
    ])
    
    matrix = cv2.getAffineTransform(src_points, dst_points)
    transformed = cv2.warpAffine(image, matrix, (w, h))
    
    return transformed

img_parallelogram = parallelogram_transform(img)

4.4 透视变换

def perspective_transform(image, src_points, dst_points):
    """
    透视变换
    """
    # 获取透视变换矩阵
    matrix = cv2.getPerspectiveTransform(src_points.astype(np.float32), 
                                        dst_points.astype(np.float32))
    
    # 应用变换
    h, w = image.shape[:2]
    transformed = cv2.warpPerspective(image, matrix, (w, h))
    
    return transformed, matrix

# 透视变换示例:矫正倾斜的文档
def document_correction(image):
    """文档矫正示例"""
    h, w = image.shape[:2]
    
    # 假设原图是倾斜的矩形,四个角点
    src_points = np.float32([
        [100, 100],       # 左上
        [400, 50],        # 右上
        [80, 300],        # 左下
        [380, 280]        # 右下
    ])
    
    # 目标位置:标准矩形
    dst_points = np.float32([
        [0, 0],           # 左上
        [300, 0],         # 右上
        [0, 300],         # 左下
        [300, 300]        # 右下
    ])
    
    corrected, matrix = perspective_transform(image, src_points, dst_points)
    
    return corrected

# 如果图像存在,应用文档矫正
# img_corrected = document_correction(img)

4.5 平移变换

def translate_image(image, dx, dy):
    """
    图像平移
    dx: 水平平移距离(正右负左)
    dy: 垂直平移距离(正下负上)
    """
    # 创建平移矩阵
    translation_matrix = np.float32([
        [1, 0, dx],
        [0, 1, dy]
    ])
    
    # 应用平移
    h, w = image.shape[:2]
    translated = cv2.warpAffine(image, translation_matrix, (w, h))
    
    return translated

# 平移示例
img_translated = translate_image(img, 100, 50)  # 向右平移100像素,向下平移50像素

5. 实战项目:图像几何变换工具

class ImageTransformationTool:
    """
    图像几何变换工具类
    """
    
    def __init__(self, image_path):
        self.original_image = cv2.imread(image_path)
        if self.original_image is None:
            raise ValueError(f"无法读取图像: {image_path}")
        self.current_image = self.original_image.copy()
    
    def resize(self, width=None, height=None, fx=None, fy=None):
        """缩放图像"""
        if width and height:
            self.current_image = cv2.resize(self.current_image, (width, height))
        elif fx and fy:
            self.current_image = cv2.resize(self.current_image, None, fx=fx, fy=fy)
        return self
    
    def rotate(self, angle, scale=1.0):
        """旋转图像"""
        h, w = self.current_image.shape[:2]
        center = (w // 2, h // 2)
        matrix = cv2.getRotationMatrix2D(center, angle, scale)
        self.current_image = cv2.warpAffine(self.current_image, matrix, (w, h))
        return self
    
    def translate(self, dx, dy):
        """平移图像"""
        translation_matrix = np.float32([[1, 0, dx], [0, 1, dy]])
        h, w = self.current_image.shape[:2]
        self.current_image = cv2.warpAffine(self.current_image, translation_matrix, (w, h))
        return self
    
    def flip(self, flip_code):
        """翻转图像: 0垂直, 1水平, -1水平+垂直"""
        self.current_image = cv2.flip(self.current_image, flip_code)
        return self
    
    def reset(self):
        """重置为原始图像"""
        self.current_image = self.original_image.copy()
        return self
    
    def show(self, window_name="Transformed Image"):
        """显示当前图像"""
        cv2.imshow(window_name, self.current_image)
        cv2.waitKey(0)
        cv2.destroyAllWindows()
        return self
    
    def save(self, output_path):
        """保存图像"""
        success = cv2.imwrite(output_path, self.current_image)
        return success

# 使用示例
# tool = ImageTransformationTool("input.jpg")
# tool.rotate(45).resize(fx=0.8, fy=0.8).translate(50, 30).show()

相关教程

OpenCV是计算机视觉的基础工具,熟练掌握图像读取、绘制和几何变换是进行后续学习的关键。建议多练习不同参数的效果,理解每个函数的作用。

6. 小结

OpenCV是计算机视觉领域不可或缺的工具,掌握其基本操作是学习计算机视觉的重要基础:

核心知识点总结:

  1. 图像读取/显示/保存:imread、imshow、imwrite
  2. 基本图形绘制:line、rectangle、circle、polylines、putText
  3. 几何变换:resize(缩放)、rotate(旋转)、warpAffine(仿射变换)、warpPerspective(透视变换)
  4. 色彩空间:注意OpenCV使用BGR而非RGB顺序

最佳实践:

  • 在Jupyter环境中使用matplotlib显示图像,自动处理BGR到RGB转换
  • 根据需求选择合适的插值方法(放大用INTER_CUBIC,缩小用INTER_AREA)
  • 保持宽高比的缩放以避免图像变形
  • 使用完整的旋转函数防止图像裁剪

💡 重要提醒:OpenCV使用BGR颜色顺序,不是标准的RGB!如果颜色显示异常,请检查通道顺序。

🔗 扩展阅读