关注

深入学习OpenCV:第三章OpenCV 核心模块介绍(上)

以下是 OpenCV 核心模块教程 的介绍文章,涵盖图像基础操作、像素处理、算术运算、性能优化等核心功能。由于内容比较多,分为三篇文章进行描述,本文是第一部分。


目录

  1. 图像的基础操作
  2. 图像的算术运算
  3. 性能优化与测量
  4. 颜色空间转换
  5. 图像阈值处理
  6. 几何变换

1. 图像的基础操作

核心功能

  • 访问像素值:读取和修改像素。
  • 裁剪感兴趣区域(ROI):提取图像局部区域。
  • 通道拆分与合并:处理多通道图像。

代码示例

import cv2

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

# 访问像素值(BGR格式)
pixel = img[100, 100]  # 返回 [B, G, R] 值
print("Pixel at (100,100):", pixel)

# 修改像素值
img[100:150, 100:150] = [255, 0, 0]  # 将区域设为蓝色

# 裁剪ROI(例如提取左上角200x200区域)
roi = img[0:200, 0:200]

# 拆分通道
b, g, r = cv2.split(img)
# 合并通道
merged = cv2.merge([b, g, r])

cv2.imshow("Operations", img)
cv2.waitKey(0)
cv2.destroyAllWindows()

2. 图像的算术运算

核心函数

  • 加法cv2.add()(饱和运算)或 +(取模运算)。
  • 混合cv2.addWeighted() 实现加权混合。

代码示例

import cv2
import numpy as np

def validate_image(image, name="image"):
    """输入图像基础验证"""
    if image is None:
        raise ValueError(f"❌ {name} 加载失败,请检查文件路径")
    if image.dtype != np.uint8:
        raise TypeError(f"⚠️ {name} 数据类型应为np.uint8,当前类型为 {image.dtype}")


def add_images(img1, img2):
    """安全图像加法
    参数要求:
    - 两张图像必须同尺寸同通道
    """
    validate_image(img1, "img1")
    validate_image(img2, "img2")

    if img1.shape != img2.shape:
        raise ValueError(f"🚫 图像尺寸不匹配\n img1: {img1.shape}\n img2: {img2.shape}")

    return cv2.add(img1, img2)


def blend_images(img1, img2, alpha=0.7, beta=0.3, gamma=0):
    """带权重检查的图像混合"""
    validate_image(img1, "img1")
    validate_image(img2, "img2")

    if not (0 <= alpha <= 1 and 0 <= beta <= 1):
        raise ValueError(f"📊 权重系数应在[0,1]之间\n alpha={alpha}, beta={beta}")

    if img1.shape != img2.shape:
        raise ValueError(f"🚫 图像尺寸不匹配\n img1: {img1.shape}\n img2: {img2.shape}")

    return cv2.addWeighted(img1, alpha, img2, beta, gamma)


def bitwise_operation(img, mask_path):
    """安全的位运算操作
    要求:
    - mask必须是单通道二值图
    """
    mask = cv2.imread(mask_path, cv2.IMREAD_GRAYSCALE)
    validate_image(mask, "mask")

    if mask.ndim != 2:
        raise ValueError(f"🎭 mask必须为单通道灰度图,当前通道数: {mask.ndim}")

    _, binary_mask = cv2.threshold(mask, 1, 255, cv2.THRESH_BINARY)
    return cv2.bitwise_and(img, img, mask=binary_mask)


try:
    # 示例调用
    img1 = cv2.imread("img1.png")  # BGR三通道图像
    img2 = cv2.imread("img2.jpeg")

    # 图像加法
    added_img = add_images(img1, img2)

    # 图像混合
    blended_img = blend_images(img1, img2)

    # 位与操作(演示自动二值化)
    and_result = bitwise_operation(img1, "mask.png")

    # 显示结果(可选)
    cv2.imshow("Added", added_img)
    cv2.imshow("Blended", blended_img)
    cv2.imshow("Bitwise AND", and_result)
    cv2.waitKey(0)

except Exception as e:
    print(f"❌ 处理失败: {str(e)}")
    exit(1)
finally:
    cv2.destroyAllWindows()


3. 性能优化与测量

核心方法

  • 测量执行时间:使用 cv2.getTickCount()cv2.getTickFrequency()
  • 优化技巧:避免使用 Python 循环,优先使用 OpenCV 内置函数。

代码示例

import cv2
import numpy as np


class PerfAnalyzer:
    """OpenCV 性能分析与优化工具箱"""

    def __init__(self, content:str):
        self.tick_freq = cv2.getTickFrequency()
        self.content = content
    def __enter__(self):
        """上下文管理器入口"""
        self.start = cv2.getTickCount()
        return self

    def __exit__(self, *args):
        """上下文管理器出口"""
        self.elapsed = (cv2.getTickCount() - self.start) / self.tick_freq
        print(f"⏱️ {self.content}耗时: {self.elapsed:.4f}s")


def validate_image(image):
    """图像有效性检查"""
    if image is None:
        raise ValueError("图像加载失败")
    if not isinstance(image, np.ndarray):
        raise TypeError("需传入NumPy数组")


def optimized_gaussian_blur(img, ksize=(5, 5)):
    """优化版高斯模糊(使用OpenCV内置加速)"""
    validate_image(img)
    return cv2.GaussianBlur(img, ksize, 0)


def unoptimized_box_filter(img, ksize=5):
    """未优化盒式滤波(Python循环实现)"""
    validate_image(img)
    h, w = img.shape[:2]
    dst = np.zeros_like(img)

    offset = ksize // 2
    for y in range(offset, h - offset):
        for x in range(offset, w - offset):
            roi = img[y - offset:y + offset + 1, x - offset:x + offset + 1]
            dst[y, x] = np.mean(roi, axis=(0, 1))

    return dst


def demo_optimizations(img_path):
    """全流程优化示范"""
    try:
        # 读取测试图像
        img = cv2.imread(img_path)
        validate_image(img)

        print("\n=== 性能对比实验 ===")

        # 实验1:OpenCV内置高斯模糊
        with PerfAnalyzer("OpenCV内置高斯模糊") as t:
            blur_cv = optimized_gaussian_blur(img)
        cv2.imshow("OpenCV Gaussian", blur_cv)

        # 实验2:Python实现的盒式模糊
        with PerfAnalyzer("Python实现的盒式模糊") as t:
            blur_py = unoptimized_box_filter(img)
        cv2.imshow("Python Box Filter", blur_py)

        # 实验3:内存预分配优化
        with PerfAnalyzer("内存预分配优化") as t:
            optimized = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
            optimized = cv2.Canny(optimized, 100, 200)

        # 实验4:多步骤合并(避免中间内存分配)
        with PerfAnalyzer("多步骤合并(避免中间内存分配)") as t:
            optimized = cv2.Canny(
                cv2.cvtColor(img, cv2.COLOR_BGR2GRAY),
                100, 200
            )

        cv2.waitKey(0)

    except Exception as e:
        print(f"错误: {str(e)}")
    finally:
        cv2.destroyAllWindows()


if __name__ == "__main__":
    demo_optimizations("img1.png")

输出的测试报告如下:

=== 性能对比实验 ===
⏱️ OpenCV内置高斯模糊耗时: 0.0035s
⏱️ Python实现的盒式模糊耗时: 2.4692s
⏱️ 内存预分配优化耗时: 0.0041s
⏱️ 多步骤合并(避免中间内存分配)耗时: 0.0040s

从输入的评测结果可以看出优化的效果


4. 颜色空间转换


📚 颜色空间基础

颜色空间组成要素典型用途范围说明
BGR蓝(B)、绿(G)、红®通道OpenCV默认图像存储格式每个通道0-255(uint8)
灰度单通道亮度值简化计算(如边缘检测)0(黑)-255(白)
HSV色相(H)、饱和度(S)、明度(V)色彩阈值分割H:0-180, S/V:0-255(OpenCV)
LAB亮度(L)、a轴(绿红)、b轴(蓝黄)色差分析、肤色检测L:0-100, a/b:-127-127

📌关键差异

  • HSV的H通道在OpenCV中范围是0-180(而非常规0-360),因使用uint8数据类型压缩存储
  • LAB颜色空间接近人类视觉感知特性,常用于复杂光照下的颜色分析

BGR ↔ 灰度转换原理

gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
转换算法公式适用场景
加权平均法Gray = 0.114*B + 0.587*G + 0.299*R符合人眼敏感度(默认)
简单平均法Gray = (B + G + R) / 3快速处理但精度较低
取单通道直接取B/G/R通道数据特定场景预处理

BGR ↔ HSV转换详解

hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)

HSV颜色模型参数范围

分量OpenCV范围物理意义
H0-179色相(色彩基色)
S0-255饱和度(色彩浓淡)
V0-255明度(亮度)

注意:常规HSV模型H范围为0-360°,但OpenCV用8位存储时压缩到0-179


🚀 演示代码,提取黄色

import cv2
import numpy as np


def extract_yellow_object(image_path):
    # 读取图像(支持jpg/png等格式)
    img = cv2.imread(image_path)
    if img is None:
        print("错误:无法读取图像文件,请检查路径是否正确")
        return

    # 转换为HSV颜色空间
    hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)

    # 定义黄色范围(H:20-30, S:100-255, V:100-255)
    lower_yellow = np.array([20, 100, 100])
    upper_yellow = np.array([30, 255, 255])

    # 创建二值化掩膜
    mask = cv2.inRange(hsv, lower_yellow, upper_yellow)

    # 形态学处理(消除噪声)
    kernel = np.ones((5, 5), np.uint8)
    mask = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel)  # 闭运算填充小孔
    mask = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel)  # 开运算消除小噪点

    # 提取黄色区域
    result = cv2.bitwise_and(img, img, mask=mask)

    # 可视化显示
    cv2.imshow('Original', img)
    cv2.imshow('Mask', mask)
    cv2.imshow('Result', result)

    # 按ESC退出
    while True:
        if cv2.waitKey(1) == 27:
            break
    cv2.destroyAllWindows()


if __name__ == "__main__":
    # 使用示例(替换为你的图片路径)
    extract_yellow_object("yello_test.png")

输出内容


🔧 参数调试建议

HSV范围获取技巧
下面的代码可以通过滑块演示HSV的取值范围

# 实时调节工具代码(可在另一个脚本运行)
def hsv_tuner():
    cv2.namedWindow("HSV Tuner")
    cv2.createTrackbar("H min", "HSV Tuner", 0, 180, lambda x: None)
    cv2.createTrackbar("H max", "HSV Tuner", 180, 180, lambda x: None)
    cv2.createTrackbar("S min", "HSV Tuner", 0, 255, lambda x: None)
    cv2.createTrackbar("V min", "HSV Tuner", 0, 255, lambda x: None)
  
    while True:
        img = cv2.imread("test_image.jpg")
        hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
      
        h_min = cv2.getTrackbarPos("H min", "HSV Tuner")
        h_max = cv2.getTrackbarPos("H max", "HSV Tuner")
        s_min = cv2.getTrackbarPos("S min", "HSV Tuner")
        v_min = cv2.getTrackbarPos("V min", "HSV Tuner")
      
        lower = np.array([h_min, s_min, v_min])
        upper = np.array([h_max, 255, 255])
        mask = cv2.inRange(hsv, lower, upper)
        res = cv2.bitwise_and(img, img, mask=mask)
      
        cv2.imshow("Mask", cv2.hconcat([img, res]))
        if cv2.waitKey(1) == 27:
            break

5. 图像阈值处理

核心函数

  • cv2.threshold():全局阈值处理。
  • cv2.adaptiveThreshold():自适应阈值。

代码示例

import cv2
import numpy as np

def threshold_demo(image_path):
    # 读取图像并转换为灰度图
    img = cv2.imread(image_path)
    if img is None:
        print("错误:图像文件读取失败,请检查路径")
        return

    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

    # 全局阈值处理
    ret1, thresh_global = cv2.threshold(
        gray,
        127,  # 初始阈值
        255,  # 最大值
        cv2.THRESH_BINARY + cv2.THRESH_OTSU  # 使用大津算法自动确定阈值
    )

    # 自适应阈值处理
    thresh_adaptive = cv2.adaptiveThreshold(
        gray,
        255,  # 输出最大值
        cv2.ADAPTIVE_THRESH_GAUSSIAN_C,  # 自适应方法
        cv2.THRESH_BINARY,  # 阈值类型
        21,  # 邻域大小(必须奇数)
        10  # 常数C(从平均值减去的值)
    )

    # 可视化结果
    cv2.imshow('Original', img)
    cv2.imshow('Gray', gray)
    cv2.imshow('Global Threshold (OTSU)', thresh_global)
    cv2.imshow('Adaptive Threshold', thresh_adaptive)

    # 退出机制
    while True:
        key = cv2.waitKey(1)
        if key == 27:  # ESC退出
            break
    cv2.destroyAllWindows()


if __name__ == "__main__":
# 使用示例(替换为你的图片路径)
    threshold_demo("yello_test.png")

输出的效果


6. 几何变换

核心功能

  • 缩放cv2.resize()
  • 旋转cv2.getRotationMatrix2D() + cv2.warpAffine()
  • 仿射变换cv2.getAffineTransform()
  • 透视变换cv2.getPerspectiveTransform()

代码示例(旋转图像)

import cv2
import numpy as np

def validate_image(image):
    """图像有效性检查"""
    if image is None:
        raise ValueError("🔴 图像加载失败,请确认路径是否正确")
    if image.dtype != np.uint8:
        image = image.astype(np.uint8)
    if not isinstance(image, np.ndarray):
        raise TypeError("🟡 需传入NumPy数组类型的图像")


def visualize_transform(original, transformed, title="对比视图"):
    """改进版可视化工具,自动处理尺寸差异"""
    # 统一图像高度(通过底部填充黑色区域)
    h_orig, w_orig = original.shape[:2]
    h_trans, w_trans = transformed.shape[:2]

    # 计算最大高度
    max_height = max(h_orig, h_trans)

    # 对高度不足的图像进行底部填充
    if h_orig < max_height:
        original = cv2.copyMakeBorder(original,
                                      0, max_height - h_orig,
                                      0, 0,
                                      cv2.BORDER_CONSTANT,
                                      value=(0, 0, 0))
    if h_trans < max_height:
        transformed = cv2.copyMakeBorder(transformed,
                                         0, max_height - h_trans,
                                         0, 0,
                                         cv2.BORDER_CONSTANT,
                                         value=(0, 0, 0))

    # 执行拼接操作
    combined = cv2.hconcat([original, transformed])

    # 添加对比标注
    cv2.putText(combined, "Original -> Transformed", (10, 30),
                cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 255, 0), 2)
    cv2.imshow(title, combined)
    cv2.waitKey(500)


def geometric_transform_demo(img_path):
    """几何变换综合演示"""
    try:
        img = cv2.imread(img_path)
        validate_image(img)
        h, w = img.shape[:2]

        print("=== 几何变换测试启动 ===")

        # ------------------------
        # 1. 图像缩放(保持长宽比)
        # ------------------------
        scale_factor = 0.6
        # 确保输出尺寸为整数
        new_width = int(w * scale_factor)
        new_height = int(h * scale_factor)
        scaled = cv2.resize(img, (new_width, new_height),
                            interpolation=cv2.INTER_AREA)
        print(f"缩放比例: {scale_factor} | 缩放后尺寸: {scaled.shape[1]}x{scaled.shape[0]}")
        visualize_transform(img, scaled, "缩放对比")

        # ------------------------
        # 2. 旋转变换(可调参数)
        # ------------------------
        angle = 45
        # 参数验证:防止输入异常角度
        if not -360 <= angle <= 360:
            raise ValueError(f"⚙️ 非法旋转角度值 {angle}°,应在[-360, 360]范围内")

        # 计算旋转矩阵(旋转中心/角度/缩放因子)
        rotation_matrix = cv2.getRotationMatrix2D((w // 2, h // 2), angle, 1.0)
        # 边缘处理:保留完整旋转内容
        rotated = cv2.warpAffine(img, rotation_matrix, (w, h),
                                 flags=cv2.INTER_LINEAR,
                                 borderMode=cv2.BORDER_CONSTANT,
                                 borderValue=(255, 255, 255))
        print(f"旋转矩阵:\n{rotation_matrix}")
        visualize_transform(img, rotated, f"旋转 {angle}°")

        # ------------------------
        # 3. 仿射变换(三点定位)
        # ------------------------
        # 源点(原始图像三角形)
        src_pts = np.float32([[50, 50], [200, 50], [50, 200]])
        # 目标点(变换后位置)
        dst_pts = np.float32([[10, 100], [200, 50], [100, 250]])

        affine_matrix = cv2.getAffineTransform(src_pts, dst_pts)
        print(f"仿射矩阵:\n{affine_matrix}")

        # 绘制标记点用于验证变换准确性
        marked_img = img.copy()
        for pt in src_pts:
            cv2.circle(marked_img, tuple(pt.astype(int)), 5, (0, 0, 255), -1)

        affine_output = cv2.warpAffine(marked_img, affine_matrix, (w, h))
        visualize_transform(marked_img, affine_output, "仿射变换")

        # ------------------------
        # 4. 透视变换(四点定位)
        # ------------------------
        # 源点(书本的四角坐标示例)
        src_quad = np.float32([[0, 0], [w, 0], [w, h], [0, h]])
        # 目标点(投影后的四边形)
        dst_quad = np.float32([[50, 50], [w - 50, 100], [w - 100, h - 50], [50, h - 50]])

        perspective_matrix = cv2.getPerspectiveTransform(src_quad, dst_quad)
        print(f"透视矩阵:\n{perspective_matrix}")
        warped = cv2.warpPerspective(img, perspective_matrix, (w, h),
                                     flags=cv2.INTER_LANCZOS4)
        visualize_transform(img, warped, "透视变换")

        cv2.waitKey(0)

    except Exception as e:
        print(f"❌ 处理失败: {str(e)}")
    finally:
        cv2.destroyAllWindows()


if __name__ == "__main__":
    # 参数配置区(可修改数值实时验证效果)
    TEST_IMAGE_PATH = "yello_test.png"  # 替换为你的测试图片路径
    geometric_transform_demo(TEST_IMAGE_PATH)


总结

OpenCV 核心模块提供了图像处理的底层能力,涵盖:

  • 基础操作:像素访问、ROI、通道处理。
  • 数学运算:加减乘除、位运算、混合。
  • 图像增强:滤波、阈值、形态学操作。
  • 高级分析:边缘检测、轮廓提取、直方图均衡化。

通过灵活组合这些功能,可实现图像增强、目标检测、特征提取等复杂任务。

转载自CSDN-专业IT技术社区

版权声明:本文为博主原创文章,遵循 CC 4.0 BY 版权协议,转载请附上原文出处链接和本声明。

原文链接:https://blog.csdn.net/desert_fish1976/article/details/147193389

评论

赞0

评论列表

微信小程序
QQ小程序

关于作者

点赞数:0
关注数:0
粉丝:0
文章:0
关注标签:0
加入于:--