【Python图像处理】2 数字图像基础与Python图像表示

摘要 :本文深入讲解数字图像的基础概念,包括像素、分辨率、色彩空间等核心概念,以及如何在Python中使用NumPy数组表示和操作图像数据。文章详细介绍了RGB、HSV、LAB等常用色彩空间的原理与转换方法,并通过大量代码示例演示图像数据的各种操作技巧。由于国内无法访问OpenAI官网,因此使用国内镜像站可以注册使用GPT-5.4最新模型。注册入口:AIGCBAR镜像站。如果涉及到调用API,则有API独立站。请广大读者遵守法律法规,切勿翻墙访问境外网站,使用国内合法镜像站即可满足学习需求。

2.1 数字图像的基本概念

2.1.1 像素与分辨率

数字图像是由有限个离散点组成的二维矩阵,每个离散点称为像素(Pixel),它是数字图像的最小单位。每个像素包含了该位置的颜色或亮度信息,通过将这些像素按照空间位置排列,就形成了我们看到的数字图像。像素的概念是理解数字图像处理的基础,所有的图像处理操作本质上都是对像素值的数学运算。

分辨率是描述数字图像精细程度的重要指标,它表示图像在单位长度内包含的像素数量。常见的分辨率表示方法有两种:一种是用图像的总像素数表示,如1920×1080表示图像宽1920像素、高1080像素;另一种是用每英寸像素数(PPI,Pixels Per Inch)表示,如300PPI表示每英寸包含300个像素。分辨率越高,图像越清晰,但同时文件大小也越大。

像素深度是指每个像素用于存储颜色信息的位数,它决定了图像能够表示的颜色数量。常见的像素深度包括8位、16位、24位和32位。8位图像可以表示256种颜色或灰度级别,24位真彩色图像可以表示约1677万种颜色(每个颜色通道8位,共3个通道),32位图像通常是在24位基础上增加8位透明度通道(Alpha通道)。

2.1.2 图像的数据结构

在计算机中,数字图像以矩阵形式存储,每个矩阵元素对应一个像素的值。对于灰度图像,使用二维矩阵表示,矩阵的行数对应图像高度,列数对应图像宽度,每个元素的值表示该像素的灰度级别。对于彩色图像,通常使用三维数组表示,第三维表示颜色通道,如RGB图像有三个通道,分别表示红、绿、蓝三个颜色分量。

在Python中,NumPy数组是表示图像数据的标准数据结构。NumPy提供了高效的多维数组对象,支持各种数学运算和数组操作,非常适合图像处理任务。使用NumPy数组表示图像有以下优势:首先,NumPy数组支持向量化操作,可以高效地对整个图像进行数学运算;其次,NumPy提供了丰富的数组索引和切片功能,可以方便地访问和修改图像的特定区域;第三,NumPy与其他科学计算库(如SciPy、scikit-image)无缝集成,便于进行复杂的图像处理操作。

以下表格总结了不同类型图像在NumPy中的数据表示方式。

图像类型 数组维度 数据类型 数值范围 通道数
二值图像 2D bool/uint8 0或1/0或255 1
灰度图像 2D uint8 0-255 1
灰度图像(16位) 2D uint16 0-65535 1
RGB彩色图像 3D uint8 0-255 3
RGBA图像 3D uint8 0-255 4
浮点图像 2D/3D float32/float64 0.0-1.0 1/3

2.2 NumPy数组与图像表示

2.2.1 创建图像数组

理解如何在NumPy中创建和操作图像数组是进行图像处理的基础。NumPy提供了多种创建数组的方法,可以用于生成各种类型的测试图像或初始化图像数据。以下代码展示了如何使用NumPy创建不同类型的图像数组。

python 复制代码
"""
NumPy图像数组创建与操作
演示如何使用NumPy创建和操作各种类型的图像数据
兼容Python 3.13
"""

import numpy as np
import cv2
import matplotlib.pyplot as plt
from typing import Tuple, Optional
from numpy.typing import NDArray


class ImageArrayFactory:
    """
    图像数组工厂类
    提供创建各种类型图像数组的静态方法
    """
    
    @staticmethod
    def create_solid_color(
        height: int,
        width: int,
        color: Tuple[int, int, int] = (255, 255, 255),
        dtype: np.dtype = np.uint8
    ) -> NDArray:
        """
        创建纯色图像
        
        参数:
            height: 图像高度
            width: 图像宽度
            color: RGB颜色值,范围0-255
            dtype: 数据类型
            
        返回:
            纯色图像数组
        """
        image = np.zeros((height, width, 3), dtype=dtype)
        image[:, :] = color
        return image
    
    @staticmethod
    def create_gradient(
        height: int,
        width: int,
        direction: str = 'horizontal',
        start_value: int = 0,
        end_value: int = 255
    ) -> NDArray:
        """
        创建渐变图像
        
        参数:
            height: 图像高度
            width: 图像宽度
            direction: 渐变方向,'horizontal'或'vertical'
            start_value: 起始灰度值
            end_value: 结束灰度值
            
        返回:
            渐变图像数组(灰度图)
        """
        if direction == 'horizontal':
            gradient = np.linspace(start_value, end_value, width)
            image = np.tile(gradient, (height, 1))
        else:
            gradient = np.linspace(start_value, end_value, height)
            image = np.tile(gradient.reshape(-1, 1), (1, width))
        
        return image.astype(np.uint8)
    
    @staticmethod
    def create_checkerboard(
        height: int,
        width: int,
        block_size: int = 50
    ) -> NDArray:
        """
        创建棋盘格图像
        
        参数:
            height: 图像高度
            width: 图像宽度
            block_size: 每个格子的大小
            
        返回:
            棋盘格图像数组
        """
        # 创建基础棋盘格单元
        unit = np.zeros((block_size * 2, block_size * 2), dtype=np.uint8)
        unit[:block_size, :block_size] = 255
        unit[block_size:, block_size:] = 255
        
        # 计算需要重复的次数
        repeat_y = (height + block_size * 2 - 1) // (block_size * 2)
        repeat_x = (width + block_size * 2 - 1) // (block_size * 2)
        
        # 重复单元并裁剪到目标尺寸
        checkerboard = np.tile(unit, (repeat_y, repeat_x))
        return checkerboard[:height, :width]
    
    @staticmethod
    def create_circles(
        height: int,
        width: int,
        center: Tuple[int, int] = None,
        radius: int = 100,
        color: Tuple[int, int, int] = (255, 0, 0)
    ) -> NDArray:
        """
        创建带圆形的图像
        
        参数:
            height: 图像高度
            width: 图像宽度
            center: 圆心坐标
            radius: 半径
            color: 圆形颜色
            
        返回:
            带圆形的图像数组
        """
        if center is None:
            center = (width // 2, height // 2)
        
        # 创建坐标网格
        y, x = np.ogrid[:height, :width]
        
        # 计算到圆心的距离
        distance = np.sqrt((x - center[0])**2 + (y - center[1])**2)
        
        # 创建掩码
        mask = distance <= radius
        
        # 创建彩色图像
        image = np.zeros((height, width, 3), dtype=np.uint8)
        image[mask] = color
        
        return image
    
    @staticmethod
    def create_noise(
        height: int,
        width: int,
        channels: int = 3,
        noise_type: str = 'gaussian',
        mean: float = 128,
        std: float = 50
    ) -> NDArray:
        """
        创建噪声图像
        
        参数:
            height: 图像高度
            width: 图像宽度
            channels: 通道数
            noise_type: 噪声类型,'gaussian'或'uniform'
            mean: 高斯噪声均值
            std: 高斯噪声标准差
            
        返回:
            噪声图像数组
        """
        if noise_type == 'gaussian':
            noise = np.random.normal(mean, std, (height, width, channels))
        else:
            noise = np.random.uniform(0, 256, (height, width, channels))
        
        # 裁剪到有效范围
        noise = np.clip(noise, 0, 255).astype(np.uint8)
        return noise


def demonstrate_array_creation():
    """
    演示各种图像数组的创建方法
    """
    factory = ImageArrayFactory()
    
    # 创建各种测试图像
    images = {
        '纯色图像': factory.create_solid_color(200, 300, (100, 150, 200)),
        '水平渐变': factory.create_gradient(200, 300, 'horizontal'),
        '垂直渐变': factory.create_gradient(200, 300, 'vertical'),
        '棋盘格': factory.create_checkerboard(200, 300, 25),
        '圆形': factory.create_circles(200, 300, radius=80),
        '高斯噪声': factory.create_noise(200, 300, noise_type='gaussian')
    }
    
    # 打印图像信息
    for name, img in images.items():
        print(f"{name}: 形状={img.shape}, 类型={img.dtype}, "
              f"范围=[{img.min()}, {img.max()}]")
    
    return images


def analyze_image_array(image: NDArray, name: str = "Image") -> dict:
    """
    分析图像数组的各种属性
    
    参数:
        image: 输入图像数组
        name: 图像名称
        
    返回:
        包含图像属性的字典
    """
    properties = {
        'name': name,
        'shape': image.shape,
        'dtype': image.dtype,
        'ndim': image.ndim,
        'size': image.size,
        'nbytes': image.nbytes,
        'min': float(image.min()),
        'max': float(image.max()),
        'mean': float(image.mean()),
        'std': float(image.std())
    }
    
    return properties


def print_image_properties(properties: dict) -> None:
    """
    打印图像属性信息
    
    参数:
        properties: 图像属性字典
    """
    print(f"\n{'='*50}")
    print(f"图像属性分析: {properties['name']}")
    print(f"{'='*50}")
    print(f"形状: {properties['shape']}")
    print(f"数据类型: {properties['dtype']}")
    print(f"维度数: {properties['ndim']}")
    print(f"总元素数: {properties['size']:,}")
    print(f"内存占用: {properties['nbytes'] / 1024:.2f} KB")
    print(f"数值范围: [{properties['min']:.2f}, {properties['max']:.2f}]")
    print(f"均值: {properties['mean']:.2f}")
    print(f"标准差: {properties['std']:.2f}")
    print(f"{'='*50}")


if __name__ == "__main__":
    # 演示图像创建
    images = demonstrate_array_creation()
    
    # 分析图像属性
    for name, img in images.items():
        props = analyze_image_array(img, name)
        print_image_properties(props)

2.2.2 数组索引与切片

NumPy的数组索引和切片功能是图像处理中最常用的操作之一。通过索引和切片,可以方便地访问图像的特定像素、区域或通道。以下代码详细演示了各种索引和切片操作在图像处理中的应用。

python 复制代码
"""
NumPy数组索引与切片在图像处理中的应用
演示如何使用索引和切片操作图像数据
兼容Python 3.13
"""

import numpy as np
import cv2
from typing import Tuple, List, Optional
from numpy.typing import NDArray


class ImageRegionOperations:
    """
    图像区域操作类
    提供基于NumPy索引和切片的图像区域操作方法
    """
    
    def __init__(self, image: NDArray):
        """
        初始化图像区域操作器
        
        参数:
            image: 输入图像数组
        """
        self.image = image.copy()
        self.height, self.width = image.shape[:2]
    
    def get_pixel(self, y: int, x: int) -> np.ndarray:
        """
        获取指定位置的像素值
        
        参数:
            y: 行坐标(垂直位置)
            x: 列坐标(水平位置)
            
        返回:
            像素值数组
        """
        return self.image[y, x]
    
    def set_pixel(self, y: int, x: int, value: np.ndarray) -> None:
        """
        设置指定位置的像素值
        
        参数:
            y: 行坐标
            x: 列坐标
            value: 新的像素值
        """
        self.image[y, x] = value
    
    def get_region(self, y_start: int, y_end: int, 
                   x_start: int, x_end: int) -> NDArray:
        """
        获取图像的矩形区域(ROI)
        
        参数:
            y_start: 起始行
            y_end: 结束行(不包含)
            x_start: 起始列
            x_end: 结束列(不包含)
            
        返回:
            区域图像数组
        """
        return self.image[y_start:y_end, x_start:x_end].copy()
    
    def set_region(self, y_start: int, y_end: int,
                   x_start: int, x_end: int, 
                   value: NDArray) -> None:
        """
        设置图像的矩形区域
        
        参数:
            y_start: 起始行
            y_end: 结束行
            x_start: 起始列
            x_end: 结束列
            value: 新的区域值
        """
        self.image[y_start:y_end, x_start:x_end] = value
    
    def get_row(self, y: int) -> NDArray:
        """获取指定行"""
        return self.image[y, :].copy()
    
    def get_column(self, x: int) -> NDArray:
        """获取指定列"""
        return self.image[:, x].copy()
    
    def get_channel(self, channel: int) -> NDArray:
        """
        获取指定颜色通道
        
        参数:
            channel: 通道索引(0, 1, 2对应B, G, R)
            
        返回:
            单通道图像
        """
        if len(self.image.shape) == 2:
            return self.image.copy()
        return self.image[:, :, channel].copy()
    
    def set_channel(self, channel: int, value: NDArray) -> None:
        """
        设置指定颜色通道
        
        参数:
            channel: 通道索引
            value: 新的通道值
        """
        if len(self.image.shape) == 3:
            self.image[:, :, channel] = value
    
    def get_corners(self, size: int = 50) -> Tuple[NDArray, NDArray, NDArray, NDArray]:
        """
        获取图像四个角的区域
        
        参数:
            size: 角落区域大小
            
        返回:
            四个角落区域的元组(左上、右上、左下、右下)
        """
        top_left = self.image[:size, :size].copy()
        top_right = self.image[:size, -size:].copy()
        bottom_left = self.image[-size:, :size].copy()
        bottom_right = self.image[-size:, -size:].copy()
        
        return top_left, top_right, bottom_left, bottom_right
    
    def get_center(self, size: Tuple[int, int]) -> NDArray:
        """
        获取图像中心区域
        
        参数:
            size: 区域大小 (height, width)
            
        返回:
            中心区域图像
        """
        h, w = size
        y_start = (self.height - h) // 2
        x_start = (self.width - w) // 2
        
        return self.image[y_start:y_start+h, x_start:x_start+w].copy()
    
    def get_border(self, thickness: int = 10) -> NDArray:
        """
        获取图像边框区域
        
        参数:
            thickness: 边框厚度
            
        返回:
            边框区域的掩码
        """
        mask = np.zeros((self.height, self.width), dtype=bool)
        mask[:thickness, :] = True  # 上边框
        mask[-thickness:, :] = True  # 下边框
        mask[:, :thickness] = True  # 左边框
        mask[:, -thickness:] = True  # 右边框
        
        return mask
    
    def apply_mask(self, mask: NDArray, value: int = 0) -> NDArray:
        """
        应用掩码到图像
        
        参数:
            mask: 布尔掩码数组
            value: 掩码区域的填充值
            
        返回:
            应用掩码后的图像
        """
        result = self.image.copy()
        result[mask] = value
        return result


def demonstrate_advanced_indexing():
    """
    演示高级索引技术在图像处理中的应用
    """
    # 创建测试图像
    image = np.zeros((400, 600, 3), dtype=np.uint8)
    
    # 填充背景
    image[:, :] = [240, 240, 240]  # 浅灰色背景
    
    # 使用花式索引创建图案
    y_coords, x_coords = np.ogrid[:400, :600]
    
    # 创建对角线条纹
    diagonal_mask = (x_coords + y_coords) % 40 < 20
    image[diagonal_mask] = [200, 200, 200]
    
    # 创建圆形区域
    center_y, center_x = 200, 300
    distance = np.sqrt((x_coords - center_x)**2 + (y_coords - center_y)**2)
    circle_mask = distance < 100
    image[circle_mask] = [0, 100, 255]  # 橙色圆形
    
    # 创建环形区域
    ring_mask = (distance >= 120) & (distance < 150)
    image[ring_mask] = [255, 0, 0]  # 蓝色环形
    
    # 创建网格线
    image[::50, :] = [100, 100, 100]  # 水平线
    image[:, ::50] = [100, 100, 100]  # 垂直线
    
    return image


def create_mosaic(images: List[NDArray], 
                  grid_size: Tuple[int, int]) -> NDArray:
    """
    创建图像马赛克
    
    参数:
        images: 图像列表
        grid_size: 网格大小 (rows, cols)
        
    返回:
        拼接后的马赛克图像
    """
    rows, cols = grid_size
    
    # 获取单个图像尺寸
    h, w = images[0].shape[:2]
    
    # 确定输出图像的通道数
    if len(images[0].shape) == 3:
        channels = images[0].shape[2]
        mosaic = np.zeros((h * rows, w * cols, channels), dtype=images[0].dtype)
    else:
        mosaic = np.zeros((h * rows, w * cols), dtype=images[0].dtype)
    
    # 填充马赛克
    for idx, img in enumerate(images[:rows * cols]):
        row = idx // cols
        col = idx % cols
        y_start = row * h
        x_start = col * w
        mosaic[y_start:y_start+h, x_start:x_start+w] = img
    
    return mosaic


def extract_color_statistics(image: NDArray) -> dict:
    """
    提取图像的颜色统计信息
    
    参数:
        image: 输入图像
        
    返回:
        颜色统计信息字典
    """
    if len(image.shape) == 2:
        return {
            'grayscale': {
                'mean': float(image.mean()),
                'std': float(image.std()),
                'min': int(image.min()),
                'max': int(image.max()),
                'median': float(np.median(image))
            }
        }
    
    # 彩色图像,分别计算各通道统计
    stats = {}
    channel_names = ['blue', 'green', 'red']
    
    for i, name in enumerate(channel_names):
        channel = image[:, :, i]
        stats[name] = {
            'mean': float(channel.mean()),
            'std': float(channel.std()),
            'min': int(channel.min()),
            'max': int(channel.max()),
            'median': float(np.median(channel))
        }
    
    return stats


if __name__ == "__main__":
    # 创建测试图像
    test_image = np.random.randint(0, 256, (400, 600, 3), dtype=np.uint8)
    
    # 初始化操作器
    ops = ImageRegionOperations(test_image)
    
    # 演示各种操作
    print("图像区域操作演示")
    print(f"图像形状: {test_image.shape}")
    
    # 获取像素
    pixel = ops.get_pixel(100, 200)
    print(f"像素(100, 200): {pixel}")
    
    # 获取区域
    region = ops.get_region(50, 150, 100, 200)
    print(f"区域形状: {region.shape}")
    
    # 获取通道
    red_channel = ops.get_channel(2)
    print(f"红色通道形状: {red_channel.shape}")
    
    # 创建高级索引图案
    pattern = demonstrate_advanced_indexing()
    print(f"图案图像形状: {pattern.shape}")
    
    # 提取颜色统计
    stats = extract_color_statistics(test_image)
    print(f"颜色统计: {stats}")

2.3 色彩空间详解

2.3.1 RGB色彩空间

RGB色彩空间是最常用的颜色表示方法,它基于三原色光模型,通过红(Red)、绿(Green)、蓝(Blue)三种颜色光的叠加来产生各种颜色。在数字图像处理中,RGB色彩空间是图像采集和显示的标准格式,大多数图像文件(如JPEG、PNG)都使用RGB格式存储颜色信息。

RGB色彩空间的原理可以追溯到人眼的生理结构。人眼视网膜上有三种感光细胞,分别对红、绿、蓝三种波长的光敏感。通过这三种感光细胞的不同响应组合,人眼可以感知数百万种不同的颜色。RGB色彩空间正是模拟了这种人眼感知颜色的机制。

在OpenCV中,彩色图像默认使用BGR格式存储,即通道顺序为蓝、绿、红,这与通常的RGB顺序不同。这是因为OpenCV在早期开发时采用了这种顺序,为了保持向后兼容性一直沿用至今。在使用OpenCV读取图像时需要注意这一点,特别是在与其他库(如Matplotlib)交互时,需要进行颜色通道的转换。

以下代码展示了RGB色彩空间的各种操作。

python 复制代码
"""
RGB色彩空间操作详解
演示RGB通道的各种操作技巧
兼容Python 3.13
"""

import numpy as np
import cv2
from typing import Tuple, List, Optional
from numpy.typing import NDArray


class RGBColorSpace:
    """
    RGB色彩空间操作类
    提供RGB通道的各种操作方法
    """
    
    def __init__(self, image: NDArray):
        """
        初始化RGB操作器
        
        参数:
            image: BGR格式的图像(OpenCV默认格式)
        """
        self.bgr_image = image.copy()
        self.height, self.width = image.shape[:2]
    
    def split_channels(self) -> Tuple[NDArray, NDArray, NDArray]:
        """
        分离BGR通道
        
        返回:
            (Blue, Green, Red)三个通道的元组
        """
        b, g, r = cv2.split(self.bgr_image)
        return b, g, r
    
    def merge_channels(self, b: NDArray, g: NDArray, r: NDArray) -> NDArray:
        """
        合并BGR通道
        
        参数:
            b: 蓝色通道
            g: 绿色通道
            r: 红色通道
            
        返回:
            合并后的BGR图像
        """
        return cv2.merge([b, g, r])
    
    def get_rgb_image(self) -> NDArray:
        """
        获取RGB格式的图像(用于Matplotlib显示)
        
        返回:
            RGB格式图像
        """
        return cv2.cvtColor(self.bgr_image, cv2.COLOR_BGR2RGB)
    
    def get_channel_histogram(self, channel: int, 
                              bins: int = 256) -> Tuple[NDArray, NDArray]:
        """
        计算指定通道的直方图
        
        参数:
            channel: 通道索引(0=蓝, 1=绿, 2=红)
            bins: 直方图bin数量
            
        返回:
            (直方图值, bin边界)
        """
        channel_img = self.bgr_image[:, :, channel]
        hist, bins_edges = np.histogram(channel_img.flatten(), bins=bins, 
                                        range=[0, 256])
        return hist, bins_edges
    
    def adjust_channel(self, channel: int, 
                       factor: float, 
                       offset: int = 0) -> NDArray:
        """
        调整指定通道的亮度和对比度
        
        参数:
            channel: 通道索引
            factor: 对比度因子(乘法)
            offset: 亮度偏移(加法)
            
        返回:
            调整后的图像
        """
        result = self.bgr_image.copy()
        adjusted = result[:, :, channel].astype(np.float32)
        adjusted = adjusted * factor + offset
        adjusted = np.clip(adjusted, 0, 255).astype(np.uint8)
        result[:, :, channel] = adjusted
        return result
    
    def swap_channels(self, order: Tuple[int, int, int] = (2, 1, 0)) -> NDArray:
        """
        交换通道顺序
        
        参数:
            order: 新的通道顺序,默认(2,1,0)将BGR转为RGB
            
        返回:
            通道重排后的图像
        """
        b, g, r = self.split_channels()
        channels = [b, g, r]
        return cv2.merge([channels[i] for i in order])
    
    def isolate_channel(self, channel: int) -> NDArray:
        """
        隔离指定通道(其他通道置零)
        
        参数:
            channel: 要保留的通道索引
            
        返回:
            单通道显示的图像
        """
        result = np.zeros_like(self.bgr_image)
        result[:, :, channel] = self.bgr_image[:, :, channel]
        return result
    
    def create_color_cast(self, color: Tuple[int, int, int]) -> NDArray:
        """
        添加颜色偏色效果
        
        参数:
            color: (B, G, R)偏色值
            
        返回:
            添加偏色后的图像
        """
        result = self.bgr_image.astype(np.float32)
        for i, c in enumerate(color):
            result[:, :, i] = result[:, :, i] * (1 + c / 255)
        return np.clip(result, 0, 255).astype(np.uint8)
    
    def analyze_color_distribution(self) -> dict:
        """
        分析图像的颜色分布
        
        返回:
            颜色分布统计信息
        """
        b, g, r = self.split_channels()
        
        return {
            'blue': {
                'mean': float(b.mean()),
                'std': float(b.std()),
                'median': float(np.median(b))
            },
            'green': {
                'mean': float(g.mean()),
                'std': float(g.std()),
                'median': float(np.median(g))
            },
            'red': {
                'mean': float(r.mean()),
                'std': float(r.std()),
                'median': float(np.median(r))
            },
            'dominant_channel': ['blue', 'green', 'red'][
                np.argmax([b.mean(), g.mean(), r.mean()])
            ]
        }


def create_rgb_color_wheel(size: int = 400) -> NDArray:
    """
    创建RGB色轮图
    
    参数:
        size: 图像尺寸
        
    返回:
        RGB色轮图像
    """
    image = np.zeros((size, size, 3), dtype=np.uint8)
    center = size // 2
    radius = size // 2 - 10
    
    y, x = np.ogrid[:size, :size]
    distance = np.sqrt((x - center)**2 + (y - center)**2)
    angle = np.arctan2(y - center, x - center)
    
    # 只在圆形区域内填充颜色
    mask = distance <= radius
    
    # 根据角度计算RGB值
    angle_normalized = (angle + np.pi) / (2 * np.pi)  # 0到1
    
    # HSV到RGB转换(简化版)
    h = angle_normalized * 360
    s = np.ones_like(h) * 255
    v = np.ones_like(h) * 255
    
    # 使用OpenCV进行HSV到RGB转换
    hsv = np.stack([h/2, s, v], axis=-1).astype(np.uint8)
    rgb = cv2.cvtColor(hsv, cv2.COLOR_HSV2RGB)
    
    # 应用距离衰减
    intensity = np.clip(distance / radius, 0, 1)
    for i in range(3):
        rgb[:, :, i] = (rgb[:, :, i] * (1 - intensity * 0.3)).astype(np.uint8)
    
    image[mask] = rgb[mask]
    
    return image


def demonstrate_rgb_operations():
    """
    演示RGB色彩空间的各种操作
    """
    # 创建测试图像
    test_image = np.random.randint(0, 256, (300, 400, 3), dtype=np.uint8)
    
    rgb_ops = RGBColorSpace(test_image)
    
    # 分离通道
    b, g, r = rgb_ops.split_channels()
    print(f"通道分离 - B: {b.shape}, G: {g.shape}, R: {r.shape}")
    
    # 分析颜色分布
    distribution = rgb_ops.analyze_color_distribution()
    print(f"颜色分布: {distribution}")
    
    # 调整通道
    adjusted = rgb_ops.adjust_channel(2, 1.5, 20)  # 增强红色通道
    print(f"调整后图像形状: {adjusted.shape}")
    
    # 创建色轮
    color_wheel = create_rgb_color_wheel(400)
    print(f"色轮图像形状: {color_wheel.shape}")
    
    return {
        'original': test_image,
        'blue_channel': b,
        'green_channel': g,
        'red_channel': r,
        'adjusted': adjusted,
        'color_wheel': color_wheel
    }


if __name__ == "__main__":
    results = demonstrate_rgb_operations()
    print("\nRGB色彩空间操作演示完成")

2.3.2 HSV色彩空间

HSV色彩空间是另一种常用的颜色表示方法,它将颜色表示为色调(Hue)、饱和度(Saturation)和明度(Value)三个分量。与RGB色彩空间相比,HSV色彩空间更符合人类对颜色的感知方式,因此在图像分割、颜色检测等任务中更为实用。

色调(Hue)表示颜色的类型,如红色、绿色、蓝色等,取值范围通常是0到360度。在OpenCV中,为了适应8位存储,色调值被缩放到0到179的范围。饱和度(Saturation)表示颜色的纯度,取值范围是0到255,饱和度越高颜色越鲜艳,饱和度为0时为灰色。明度(Value)表示颜色的亮度,取值范围也是0到255,明度为0时为黑色。

HSV色彩空间在颜色分割任务中特别有用。例如,要检测图像中的红色物体,在RGB空间中需要同时考虑R、G、B三个通道的关系,比较复杂;而在HSV空间中,只需要设定色调的范围即可,更加直观和方便。以下代码展示了HSV色彩空间的各种操作技巧。

python 复制代码
"""
HSV色彩空间操作详解
演示HSV在颜色检测和分割中的应用
兼容Python 3.13
"""

import numpy as np
import cv2
from typing import Tuple, Optional, List
from numpy.typing import NDArray


class HSVColorSpace:
    """
    HSV色彩空间操作类
    提供基于HSV的颜色检测和分割功能
    """
    
    # 常见颜色的HSV范围
    COLOR_RANGES = {
        'red': [
            (np.array([0, 100, 100]), np.array([10, 255, 255])),
            (np.array([170, 100, 100]), np.array([180, 255, 255]))
        ],
        'orange': [(np.array([11, 100, 100]), np.array([25, 255, 255]))],
        'yellow': [(np.array([26, 100, 100]), np.array([35, 255, 255]))],
        'green': [(np.array([36, 100, 100]), np.array([85, 255, 255]))],
        'cyan': [(np.array([86, 100, 100]), np.array([100, 255, 255]))],
        'blue': [(np.array([101, 100, 100]), np.array([130, 255, 255]))],
        'purple': [(np.array([131, 100, 100]), np.array([155, 255, 255]))],
        'pink': [(np.array([156, 100, 100]), np.array([169, 255, 255]))]
    }
    
    def __init__(self, bgr_image: NDArray):
        """
        初始化HSV操作器
        
        参数:
            bgr_image: BGR格式的输入图像
        """
        self.bgr_image = bgr_image.copy()
        self.hsv_image = cv2.cvtColor(bgr_image, cv2.COLOR_BGR2HSV)
        self.height, self.width = bgr_image.shape[:2]
    
    def get_hsv_channels(self) -> Tuple[NDArray, NDArray, NDArray]:
        """
        分离HSV通道
        
        返回:
            (H, S, V)三个通道
        """
        h, s, v = cv2.split(self.hsv_image)
        return h, s, v
    
    def detect_color(self, color_name: str) -> NDArray:
        """
        检测指定颜色
        
        参数:
            color_name: 颜色名称,如'red', 'blue', 'green'等
            
        返回:
            颜色区域的二值掩码
        """
        if color_name.lower() not in self.COLOR_RANGES:
            raise ValueError(f"未知颜色: {color_name}")
        
        ranges = self.COLOR_RANGES[color_name.lower()]
        mask = np.zeros((self.height, self.width), dtype=np.uint8)
        
        for lower, upper in ranges:
            color_mask = cv2.inRange(self.hsv_image, lower, upper)
            mask = cv2.bitwise_or(mask, color_mask)
        
        return mask
    
    def detect_color_range(self, 
                           h_range: Tuple[int, int],
                           s_range: Tuple[int, int] = (50, 255),
                           v_range: Tuple[int, int] = (50, 255)) -> NDArray:
        """
        检测指定HSV范围的颜色
        
        参数:
            h_range: 色调范围 (min, max)
            s_range: 饱和度范围
            v_range: 明度范围
            
        返回:
            颜色掩码
        """
        lower = np.array([h_range[0], s_range[0], v_range[0]])
        upper = np.array([h_range[1], s_range[1], v_range[1]])
        
        return cv2.inRange(self.hsv_image, lower, upper)
    
    def adjust_saturation(self, factor: float) -> NDArray:
        """
        调整饱和度
        
        参数:
            factor: 饱和度调整因子,>1增加,<1减少
            
        返回:
            调整后的BGR图像
        """
        h, s, v = self.get_hsv_channels()
        s_adjusted = np.clip(s.astype(np.float32) * factor, 0, 255).astype(np.uint8)
        hsv_adjusted = cv2.merge([h, s_adjusted, v])
        return cv2.cvtColor(hsv_adjusted, cv2.COLOR_HSV2BGR)
    
    def adjust_brightness(self, factor: float) -> NDArray:
        """
        调整明度
        
        参数:
            factor: 明度调整因子
            
        返回:
            调整后的BGR图像
        """
        h, s, v = self.get_hsv_channels()
        v_adjusted = np.clip(v.astype(np.float32) * factor, 0, 255).astype(np.uint8)
        hsv_adjusted = cv2.merge([h, s, v_adjusted])
        return cv2.cvtColor(hsv_adjusted, cv2.COLOR_HSV2BGR)
    
    def shift_hue(self, shift: int) -> NDArray:
        """
        色调偏移
        
        参数:
            shift: 色调偏移量(0-179范围内)
            
        返回:
            色调偏移后的BGR图像
        """
        h, s, v = self.get_hsv_channels()
        h_shifted = (h.astype(np.int32) + shift) % 180
        hsv_shifted = cv2.merge([h_shifted.astype(np.uint8), s, v])
        return cv2.cvtColor(hsv_shifted, cv2.COLOR_HSV2BGR)
    
    def create_color_mask_visualization(self, mask: NDArray) -> NDArray:
        """
        创建颜色掩码的可视化图像
        
        参数:
            mask: 二值掩码
            
        返回:
            可视化图像(原图+掩码高亮)
        """
        # 创建红色高亮
        highlight = self.bgr_image.copy()
        highlight[mask > 0] = [0, 0, 255]
        
        # 混合原图和高亮
        result = cv2.addWeighted(self.bgr_image, 0.7, highlight, 0.3, 0)
        
        return result
    
    def extract_colored_objects(self, color_name: str) -> Tuple[NDArray, List[dict]]:
        """
        提取指定颜色的物体
        
        参数:
            color_name: 颜色名称
            
        返回:
            (提取结果图像, 物体信息列表)
        """
        mask = self.detect_color(color_name)
        
        # 形态学处理,去除噪声
        kernel = np.ones((5, 5), np.uint8)
        mask = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel)
        mask = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel)
        
        # 查找轮廓
        contours, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL, 
                                       cv2.CHAIN_APPROX_SIMPLE)
        
        # 提取物体信息
        objects_info = []
        result = self.bgr_image.copy()
        
        for i, contour in enumerate(contours):
            area = cv2.contourArea(contour)
            if area < 100:  # 过滤小区域
                continue
            
            # 计算边界框
            x, y, w, h = cv2.boundingRect(contour)
            
            # 计算中心点
            M = cv2.moments(contour)
            if M['m00'] > 0:
                cx = int(M['m10'] / M['m00'])
                cy = int(M['m01'] / M['m00'])
            else:
                cx, cy = x + w // 2, y + h // 2
            
            objects_info.append({
                'id': i,
                'area': area,
                'bbox': (x, y, w, h),
                'center': (cx, cy)
            })
            
            # 绘制边界框和中心点
            cv2.rectangle(result, (x, y), (x + w, y + h), (0, 255, 0), 2)
            cv2.circle(result, (cx, cy), 5, (0, 0, 255), -1)
        
        return result, objects_info
    
    def analyze_hsv_distribution(self) -> dict:
        """
        分析HSV分布
        
        返回:
            HSV分布统计信息
        """
        h, s, v = self.get_hsv_channels()
        
        # 计算色调直方图
        h_hist = cv2.calcHist([h], [0], None, [180], [0, 180])
        
        # 找到主要色调
        dominant_hue = int(np.argmax(h_hist))
        
        return {
            'hue': {
                'mean': float(h.mean()),
                'std': float(h.std()),
                'dominant': dominant_hue
            },
            'saturation': {
                'mean': float(s.mean()),
                'std': float(s.std())
            },
            'value': {
                'mean': float(v.mean()),
                'std': float(v.std())
            }
        }


def create_hsv_color_chart(size: int = 300) -> NDArray:
    """
    创建HSV颜色图表
    
    参数:
        size: 图表尺寸
        
    返回:
        HSV颜色图表(BGR格式)
    """
    # 创建HSV图像
    hsv = np.zeros((size, size * 2, 3), dtype=np.uint8)
    
    # 左半部分:色调-饱和度图(明度固定为255)
    for y in range(size):
        for x in range(size):
            h = int(x * 180 / size)
            s = int(255 - y * 255 / size)
            hsv[y, x] = [h, s, 255]
    
    # 右半部分:色调-明度图(饱和度固定为255)
    for y in range(size):
        for x in range(size):
            h = int(x * 180 / size)
            v = int(255 - y * 255 / size)
            hsv[y, size + x] = [h, 255, v]
    
    # 转换为BGR
    bgr = cv2.cvtColor(hsv, cv2.COLOR_HSV2BGR)
    
    return bgr


def demonstrate_hsv_operations():
    """
    演示HSV色彩空间操作
    """
    # 创建测试图像(包含多种颜色的图形)
    image = np.zeros((400, 600, 3), dtype=np.uint8)
    
    # 绘制不同颜色的圆形
    colors_bgr = {
        'red': [0, 0, 255],
        'green': [0, 255, 0],
        'blue': [255, 0, 0],
        'yellow': [0, 255, 255],
        'cyan': [255, 255, 0],
        'magenta': [255, 0, 255]
    }
    
    positions = [(100, 100), (300, 100), (500, 100),
                 (100, 300), (300, 300), (500, 300)]
    
    for (x, y), (name, color) in zip(positions, colors_bgr.items()):
        cv2.circle(image, (x, y), 60, color, -1)
    
    # 初始化HSV操作器
    hsv_ops = HSVColorSpace(image)
    
    # 检测红色
    red_mask = hsv_ops.detect_color('red')
    print(f"红色掩码形状: {red_mask.shape}")
    print(f"红色像素数: {np.sum(red_mask > 0)}")
    
    # 分析HSV分布
    distribution = hsv_ops.analyze_hsv_distribution()
    print(f"HSV分布: {distribution}")
    
    # 创建颜色图表
    color_chart = create_hsv_color_chart(300)
    print(f"颜色图表形状: {color_chart.shape}")
    
    return {
        'original': image,
        'red_mask': red_mask,
        'color_chart': color_chart
    }


if __name__ == "__main__":
    results = demonstrate_hsv_operations()
    print("\nHSV色彩空间操作演示完成")

2.3.3 LAB色彩空间

LAB色彩空间(也称为CIELAB)是一种设备无关的色彩空间,它将颜色表示为亮度(L)和两个颜色分量(a和b)。L分量表示亮度,取值范围0到100;a分量表示从绿色到红色的变化,取值范围-128到127;b分量表示从蓝色到黄色的变化,取值范围也是-128到127。

LAB色彩空间的设计目标是使颜色差异与人类感知的颜色差异一致。在LAB空间中,相同数值距离的颜色差异在视觉上也是相似的,这使得LAB空间在颜色比较、颜色差异计算等任务中非常有用。此外,LAB空间将亮度信息与颜色信息分离,这使得可以在不影响颜色的情况下调整图像亮度。

在OpenCV中,LAB空间的数值范围与标准定义有所不同。L分量被缩放到0-255范围,a和b分量被偏移到0-255范围(原始的-128到127加上128)。使用时需要注意这种转换关系。以下代码展示了LAB色彩空间的应用。

python 复制代码
"""
LAB色彩空间操作详解
演示LAB在颜色分析和图像处理中的应用
兼容Python 3.13
"""

import numpy as np
import cv2
from typing import Tuple, Optional
from numpy.typing import NDArray


class LABColorSpace:
    """
    LAB色彩空间操作类
    提供基于LAB的颜色分析和处理功能
    """
    
    def __init__(self, bgr_image: NDArray):
        """
        初始化LAB操作器
        
        参数:
            bgr_image: BGR格式的输入图像
        """
        self.bgr_image = bgr_image.copy()
        self.lab_image = cv2.cvtColor(bgr_image, cv2.COLOR_BGR2LAB)
        self.height, self.width = bgr_image.shape[:2]
    
    def get_lab_channels(self) -> Tuple[NDArray, NDArray, NDArray]:
        """
        分离LAB通道
        
        返回:
            (L, A, B)三个通道
        """
        l, a, b = cv2.split(self.lab_image)
        return l, a, b
    
    def get_lightness(self) -> NDArray:
        """
        获取亮度通道
        
        返回:
            亮度通道图像
        """
        return self.lab_image[:, :, 0].copy()
    
    def adjust_lightness(self, factor: float) -> NDArray:
        """
        调整亮度(不影响颜色)
        
        参数:
            factor: 亮度调整因子
            
        返回:
            调整后的BGR图像
        """
        l, a, b = self.get_lab_channels()
        l_adjusted = np.clip(l.astype(np.float32) * factor, 0, 255).astype(np.uint8)
        lab_adjusted = cv2.merge([l_adjusted, a, b])
        return cv2.cvtColor(lab_adjusted, cv2.COLOR_LAB2BGR)
    
    def color_transfer(self, target_image: NDArray) -> NDArray:
        """
        颜色迁移(将目标图像的颜色风格迁移到源图像)
        
        参数:
            target_image: 目标图像(BGR格式)
            
        返回:
            颜色迁移后的图像
        """
        # 转换目标图像到LAB空间
        target_lab = cv2.cvtColor(target_image, cv2.COLOR_BGR2LAB)
        
        # 计算源图像和目标图像的均值和标准差
        src_l, src_a, src_b = self.get_lab_channels()
        tgt_l, tgt_a, tgt_b = cv2.split(target_lab)
        
        # 对每个通道进行颜色迁移
        result_channels = []
        for src_ch, tgt_ch in [(src_l, tgt_l), (src_a, tgt_a), (src_b, tgt_b)]:
            src_mean, src_std = src_ch.mean(), src_ch.std()
            tgt_mean, tgt_std = tgt_ch.mean(), tgt_ch.std()
            
            # 迁移变换
            result = (src_ch - src_mean) * (tgt_std / (src_std + 1e-6)) + tgt_mean
            result = np.clip(result, 0, 255).astype(np.uint8)
            result_channels.append(result)
        
        # 合并通道并转换回BGR
        result_lab = cv2.merge(result_channels)
        return cv2.cvtColor(result_lab, cv2.COLOR_LAB2BGR)
    
    def compute_color_difference(self, other_image: NDArray) -> NDArray:
        """
        计算与另一张图像的颜色差异(CIEDE2000简化版)
        
        参数:
            other_image: 另一张图像
            
        返回:
            颜色差异图
        """
        other_lab = cv2.cvtColor(other_image, cv2.COLOR_BGR2LAB)
        
        # 计算欧氏距离(简化版颜色差异)
        diff = np.sqrt(np.sum((self.lab_image.astype(np.float32) - 
                               other_lab.astype(np.float32)) ** 2, axis=2))
        
        return diff
    
    def enhance_color_contrast(self, a_factor: float = 1.5, 
                                b_factor: float = 1.5) -> NDArray:
        """
        增强颜色对比度
        
        参数:
            a_factor: a通道增强因子
            b_factor: b通道增强因子
            
        返回:
            增强后的BGR图像
        """
        l, a, b = self.get_lab_channels()
        
        # 对a和b通道进行对比度增强
        a_enhanced = np.clip(128 + (a.astype(np.float32) - 128) * a_factor, 
                            0, 255).astype(np.uint8)
        b_enhanced = np.clip(128 + (b.astype(np.float32) - 128) * b_factor, 
                            0, 255).astype(np.uint8)
        
        lab_enhanced = cv2.merge([l, a_enhanced, b_enhanced])
        return cv2.cvtColor(lab_enhanced, cv2.COLOR_LAB2BGR)
    
    def analyze_color_properties(self) -> dict:
        """
        分析颜色属性
        
        返回:
            颜色属性统计信息
        """
        l, a, b = self.get_lab_channels()
        
        # 将a和b转换回原始范围
        a_original = a.astype(np.float32) - 128
        b_original = b.astype(np.float32) - 128
        
        return {
            'lightness': {
                'mean': float(l.mean()),
                'std': float(l.std()),
                'min': int(l.min()),
                'max': int(l.max())
            },
            'a_channel': {
                'mean': float(a_original.mean()),
                'std': float(a_original.std()),
                'range': 'green(-) to red(+)'
            },
            'b_channel': {
                'mean': float(b_original.mean()),
                'std': float(b_original.std()),
                'range': 'blue(-) to yellow(+)'
            },
            'color_cast': self._detect_color_cast(a_original, b_original)
        }
    
    def _detect_color_cast(self, a: NDArray, b: NDArray) -> str:
        """
        检测颜色偏色
        
        参数:
            a: a通道(原始范围)
            b: b通道(原始范围)
            
        返回:
            偏色描述
        """
        a_mean, b_mean = a.mean(), b.mean()
        
        if abs(a_mean) < 5 and abs(b_mean) < 5:
            return "无明显偏色"
        elif a_mean > 10 and b_mean > 10:
            return "偏暖色(红黄色调)"
        elif a_mean < -10 and b_mean < -10:
            return "偏冷色(绿蓝色调)"
        elif a_mean > 10:
            return "偏红色"
        elif a_mean < -10:
            return "偏绿色"
        elif b_mean > 10:
            return "偏黄色"
        elif b_mean < -10:
            return "偏蓝色"
        else:
            return "轻微偏色"


def demonstrate_lab_operations():
    """
    演示LAB色彩空间操作
    """
    # 创建测试图像
    image = np.random.randint(0, 256, (300, 400, 3), dtype=np.uint8)
    
    lab_ops = LABColorSpace(image)
    
    # 分析颜色属性
    properties = lab_ops.analyze_color_properties()
    print(f"颜色属性: {properties}")
    
    # 调整亮度
    brightened = lab_ops.adjust_lightness(1.3)
    print(f"提亮后图像形状: {brightened.shape}")
    
    # 增强颜色对比度
    enhanced = lab_ops.enhance_color_contrast(1.5, 1.5)
    print(f"增强后图像形状: {enhanced.shape}")
    
    return {
        'original': image,
        'brightened': brightened,
        'enhanced': enhanced
    }


if __name__ == "__main__":
    results = demonstrate_lab_operations()
    print("\nLAB色彩空间操作演示完成")

2.4 图像数据类型与转换

2.4.1 常用数据类型

在图像处理中,选择合适的数据类型对于保证处理精度和效率至关重要。不同的数据类型有不同的数值范围和内存占用,适用于不同的处理场景。以下表格总结了图像处理中常用的数据类型及其特点。

数据类型 数值范围 内存占用 适用场景
uint8 0-255 1字节 图像存储、显示
uint16 0-65535 2字节 医学图像、高动态范围
float32 约±3.4e38 4字节 图像计算、归一化
float64 约±1.8e308 8字节 高精度计算
bool True/False 1字节 二值图像、掩码

2.4.2 数据类型转换

在图像处理过程中,经常需要进行数据类型转换。例如,将uint8图像转换为float类型进行数学运算,然后将结果转换回uint8进行保存。以下代码展示了各种数据类型转换的方法和注意事项。

python 复制代码
"""
图像数据类型转换详解
演示各种数据类型之间的转换方法
兼容Python 3.13
"""

import numpy as np
import cv2
from typing import Union, Tuple
from numpy.typing import NDArray


class ImageDataTypeConverter:
    """
    图像数据类型转换器
    提供各种数据类型之间的转换方法
    """
    
    @staticmethod
    def uint8_to_float(image: NDArray[np.uint8], 
                       normalize: bool = True) -> NDArray[np.float32]:
        """
        将uint8图像转换为float32
        
        参数:
            image: uint8图像
            normalize: 是否归一化到[0, 1]范围
            
        返回:
            float32图像
        """
        if normalize:
            return image.astype(np.float32) / 255.0
        return image.astype(np.float32)
    
    @staticmethod
    def float_to_uint8(image: NDArray, 
                       denormalize: bool = True) -> NDArray[np.uint8]:
        """
        将float图像转换为uint8
        
        参数:
            image: float图像
            denormalize: 是否从[0, 1]范围反归一化
            
        返回:
            uint8图像
        """
        if denormalize:
            image = image * 255.0
        return np.clip(image, 0, 255).astype(np.uint8)
    
    @staticmethod
    def uint8_to_uint16(image: NDArray[np.uint8]) -> NDArray[np.uint16]:
        """
        将uint8图像转换为uint16
        
        参数:
            image: uint8图像
            
        返回:
            uint16图像(值范围扩展到0-65535)
        """
        return image.astype(np.uint16) * 257  # 65535 / 255 ≈ 257
    
    @staticmethod
    def uint16_to_uint8(image: NDArray[np.uint16]) -> NDArray[np.uint8]:
        """
        将uint16图像转换为uint8
        
        参数:
            image: uint16图像
            
        返回:
            uint8图像
        """
        return (image // 257).astype(np.uint8)
    
    @staticmethod
    def normalize_to_range(image: NDArray, 
                           target_range: Tuple[float, float] = (0.0, 1.0),
                           dtype: np.dtype = np.float32) -> NDArray:
        """
        将图像归一化到指定范围
        
        参数:
            image: 输入图像
            target_range: 目标范围 (min, max)
            dtype: 输出数据类型
            
        返回:
            归一化后的图像
        """
        image_float = image.astype(np.float64)
        min_val, max_val = image_float.min(), image_float.max()
        
        if max_val - min_val < 1e-10:
            return np.full_like(image_float, target_range[0], dtype=dtype)
        
        normalized = (image_float - min_val) / (max_val - min_val)
        result = normalized * (target_range[1] - target_range[0]) + target_range[0]
        
        return result.astype(dtype)
    
    @staticmethod
    def linear_transform(image: NDArray,
                         alpha: float = 1.0,
                         beta: float = 0.0,
                         dtype: np.dtype = np.uint8) -> NDArray:
        """
        线性变换:output = alpha * input + beta
        
        参数:
            image: 输入图像
            alpha: 缩放系数
            beta: 偏移量
            dtype: 输出数据类型
            
        返回:
            变换后的图像
        """
        result = image.astype(np.float64) * alpha + beta
        
        if dtype == np.uint8:
            return np.clip(result, 0, 255).astype(np.uint8)
        elif dtype == np.uint16:
            return np.clip(result, 0, 65535).astype(np.uint16)
        else:
            return result.astype(dtype)
    
    @staticmethod
    def gamma_correction(image: NDArray, 
                         gamma: float = 1.0,
                         dtype: np.dtype = np.uint8) -> NDArray:
        """
        Gamma校正
        
        参数:
            image: 输入图像
            gamma: Gamma值
            dtype: 输出数据类型
            
        返回:
            校正后的图像
        """
        # 归一化到[0, 1]
        normalized = image.astype(np.float64) / 255.0
        
        # 应用Gamma校正
        corrected = np.power(normalized, gamma)
        
        # 转换回原始范围
        result = corrected * 255.0
        
        if dtype == np.uint8:
            return np.clip(result, 0, 255).astype(np.uint8)
        return result.astype(dtype)
    
    @staticmethod
    def create_binary_image(image: NDArray, 
                            threshold: float = 127.0,
                            mode: str = 'binary') -> NDArray[np.uint8]:
        """
        创建二值图像
        
        参数:
            image: 输入图像
            threshold: 阈值
            mode: 二值化模式
                   'binary': 大于阈值为255,否则为0
                   'binary_inv': 大于阈值为0,否则为255
                   'truncate': 大于阈值设为阈值
                   'tozero': 大于阈值保持不变,否则为0
                   
        返回:
            二值图像
        """
        if len(image.shape) == 3:
            image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
        
        if mode == 'binary':
            _, result = cv2.threshold(image, threshold, 255, cv2.THRESH_BINARY)
        elif mode == 'binary_inv':
            _, result = cv2.threshold(image, threshold, 255, cv2.THRESH_BINARY_INV)
        elif mode == 'truncate':
            _, result = cv2.threshold(image, threshold, 255, cv2.THRESH_TRUNC)
        elif mode == 'tozero':
            _, result = cv2.threshold(image, threshold, 255, cv2.THRESH_TOZERO)
        else:
            raise ValueError(f"未知模式: {mode}")
        
        return result


def demonstrate_data_type_conversion():
    """
    演示数据类型转换
    """
    # 创建测试图像
    image_uint8 = np.random.randint(0, 256, (100, 100), dtype=np.uint8)
    
    converter = ImageDataTypeConverter()
    
    # uint8 -> float32
    image_float = converter.uint8_to_float(image_uint8)
    print(f"uint8转float32: 范围[{image_float.min():.3f}, {image_float.max():.3f}]")
    
    # float32 -> uint8
    image_back = converter.float_to_uint8(image_float)
    print(f"float32转uint8: 范围[{image_back.min()}, {image_back.max()}]")
    
    # Gamma校正
    gamma_corrected = converter.gamma_correction(image_uint8, gamma=0.5)
    print(f"Gamma校正后: 范围[{gamma_corrected.min()}, {gamma_corrected.max()}]")
    
    # 二值化
    binary = converter.create_binary_image(image_uint8, threshold=128)
    print(f"二值图像: 唯一值{np.unique(binary)}")
    
    return {
        'original': image_uint8,
        'float': image_float,
        'restored': image_back,
        'gamma_corrected': gamma_corrected,
        'binary': binary
    }


if __name__ == "__main__":
    results = demonstrate_data_type_conversion()
    print("\n数据类型转换演示完成")

2.5 本章小结

本章深入讲解了数字图像的基础概念,包括像素、分辨率、色彩空间等核心内容。通过NumPy数组的各种操作,读者应该已经掌握了如何在Python中表示和操作图像数据。RGB、HSV、LAB三种色彩空间各有特点,适用于不同的应用场景:RGB适合图像显示和存储,HSV适合颜色检测和分割,LAB适合颜色分析和迁移。

理解图像的数据类型和转换方法对于进行正确的图像处理至关重要。在进行数学运算时,通常需要将图像转换为浮点类型;在保存和显示时,需要转换回无符号整数类型。Gamma校正等非线性变换可以改变图像的亮度分布,是图像增强的重要工具。

下一章将介绍OpenCV的核心操作,包括图像的读写、裁剪、缩放、旋转等基本变换,为后续的高级图像处理技术打下基础。


GPT-5.4辅助编程提示词

text 复制代码
我正在学习Python图像处理,需要处理一个实际的图像数据类型转换问题:

问题描述:
我有一组医学图像,原始数据是uint16类型(值范围0-4095),我需要:
1. 将其转换为适合深度学习模型输入的格式
2. 应用窗口化技术(windowing)增强特定灰度范围
3. 归一化到[0, 1]范围
4. 转换为RGB三通道格式(通过复制灰度通道)

请提供完整的Python代码实现,要求:
- 兼容Python 3.13
- 使用NumPy进行高效数组操作
- 添加详细的注释说明
- 包含边界情况处理
相关推荐
Jasmine_llq2 小时前
《B3840 [GESP202306 二级] 找素数》
开发语言·c++·试除法·顺序输入输出算法·素数判定算法·枚举遍历算法·布尔标记算法
梁山好汉(Ls_man)3 小时前
鸿蒙_ArkTS解决Duplicate function implementation错误
开发语言·华为·typescript·harmonyos·鸿蒙
xiaoshuaishuai83 小时前
Git二分法定位Bug
开发语言·python
so2F32hj23 小时前
一款Go语言Gin框架DDD脚手架,适合快速搭建项目
开发语言·golang·gin
2401_835792543 小时前
FastAPI 速通
windows·python·fastapi
LJianK13 小时前
Java中的类、普通类,抽象类,接口的区别
java·开发语言
Dev7z3 小时前
基于MATLAB的5G物理层文本传输系统仿真与性能分析
开发语言·5g·matlab
小智社群3 小时前
贝壳获取小区的名称
开发语言·前端·javascript
YMWM_3 小时前
export MPLBACKEND=Agg命令使用
linux·python