摘要 :本文深入讲解数字图像的基础概念,包括像素、分辨率、色彩空间等核心概念,以及如何在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进行高效数组操作
- 添加详细的注释说明
- 包含边界情况处理