在这里插入图片描述

本节课你将学到

  • 理解NumPy在AI开发中的重要地位
  • 掌握多维数组的创建和基本属性
  • 学会数组的索引、切片和形状操作
  • 掌握数组运算和广播机制
  • 完成图像数据处理实战项目

开始之前

什么是NumPy?

NumPy(Numerical Python)是Python科学计算的基础库。想象一下:

  • Python原生列表:就像一个普通的购物袋,什么都能装,但计算很慢
  • NumPy数组:就像专业的工具箱,专门为数值计算设计,速度快、功能强

NumPy的核心是n维数组对象(ndarray),它是:

  • 同质的:数组中所有元素都是同一类型
  • 多维的:可以是1维、2维、3维…任意维度
  • 高效的:底层用C语言实现,比Python列表快10-100倍

为什么NumPy对AI如此重要?

1. 性能优势

# Python列表计算100万个数的平方
import time
python_list = list(range(1000000))
start = time.time()
result = [x**2 for x in python_list]
print(f"Python列表耗时: {time.time() - start:.4f}秒")

# NumPy数组计算
import numpy as np
numpy_array = np.arange(1000000)
start = time.time()
result = numpy_array ** 2
print(f"NumPy数组耗时: {time.time() - start:.4f}秒")

2. AI库的基础

  • Pandas:基于NumPy构建的数据分析库
  • Matplotlib:使用NumPy数组进行绘图
  • Scikit-learn:机器学习算法的输入都是NumPy数组
  • TensorFlow/PyTorch:深度学习框架底层都使用类似NumPy的结构

3. 数据表示

  • 图像:3维数组 (高度, 宽度, 颜色通道)
  • 音频:1维数组 (时间序列)
  • 视频:4维数组 (时间, 高度, 宽度, 颜色通道)
  • 机器学习数据:2维数组 (样本数, 特征数)

环境要求

  • 已完成前三讲的环境配置
  • NumPy已安装(在第1讲中已安装)
  • Matplotlib用于可视化演示

NumPy基础操作

导入NumPy

# 标准导入方式
import numpy as np

# 查看NumPy版本
print(f"NumPy版本: {np.__version__}")

# 查看NumPy配置信息
print(f"NumPy配置: {np.show_config()}")

创建NumPy数组

从Python列表创建
# 创建一维数组
arr_1d = np.array([1, 2, 3, 4, 5])
print("一维数组:", arr_1d)
print("数据类型:", arr_1d.dtype)
print("数组形状:", arr_1d.shape)
print("数组维度:", arr_1d.ndim)

# 创建二维数组
arr_2d = np.array([[1, 2, 3], [4, 5, 6]])
print("\n二维数组:")
print(arr_2d)
print("数组形状:", arr_2d.shape)  # (行数, 列数)
print("数组维度:", arr_2d.ndim)

# 创建三维数组
arr_3d = np.array([[[1, 2], [3, 4]], [[5, 6], [7, 8]]])
print("\n三维数组:")
print(arr_3d)
print("数组形状:", arr_3d.shape)  # (深度, 行数, 列数)
print("数组维度:", arr_3d.ndim)
使用内置函数创建
# 创建全零数组
zeros_arr = np.zeros((3, 4))  # 3行4列的全零数组
print("全零数组:")
print(zeros_arr)

# 创建全一数组
ones_arr = np.ones((2, 3, 4))  # 2×3×4的全一数组
print(f"\n全一数组形状: {ones_arr.shape}")

# 创建单位矩阵
identity_arr = np.eye(4)  # 4×4单位矩阵
print("\n单位矩阵:")
print(identity_arr)

# 创建等差数列
arange_arr = np.arange(0, 10, 2)  # 从0到10,步长为2
print(f"\n等差数列: {arange_arr}")

# 创建线性空间
linspace_arr = np.linspace(0, 1, 5)  # 0到1之间5个等间距的点
print(f"线性空间: {linspace_arr}")

# 创建随机数组
random_arr = np.random.random((3, 3))  # 3×3的随机数组(0-1之间)
print("\n随机数组:")
print(random_arr)

# 创建正态分布随机数
normal_arr = np.random.normal(0, 1, (2, 3))  # 均值0,标准差1的正态分布
print("\n正态分布随机数:")
print(normal_arr)

数组属性详解

# 创建示例数组
sample_arr = np.random.randint(0, 10, (3, 4, 5))

print("📊 数组属性详解")
print("=" * 30)
print(f"数组内容预览:")
print(sample_arr)

print(f"\n🔍 基本属性:")
print(f"  形状 (shape): {sample_arr.shape}")           # 各维度的大小
print(f"  维度数 (ndim): {sample_arr.ndim}")           # 数组的维度数
print(f"  元素总数 (size): {sample_arr.size}")         # 数组中元素的总数
print(f"  数据类型 (dtype): {sample_arr.dtype}")       # 元素的数据类型

print(f"\n💾 内存信息:")
print(f"  每个元素字节数 (itemsize): {sample_arr.itemsize}")
print(f"  总字节数 (nbytes): {sample_arr.nbytes}")
print(f"  内存布局 (flags): {sample_arr.flags.c_contiguous}")

print(f"\n📏 形状解释:")
print(f"  第0维(深度): {sample_arr.shape[0]} 个二维数组")
print(f"  第1维(行数): {sample_arr.shape[1]} 行")  
print(f"  第2维(列数): {sample_arr.shape[2]} 列")
print(f"  总计: {sample_arr.shape[0]} × {sample_arr.shape[1]} × {sample_arr.shape[2]} = {sample_arr.size} 个元素")

数组索引和切片

一维数组索引

# 创建一维数组进行索引演示
arr_1d = np.array([10, 20, 30, 40, 50, 60, 70, 80, 90])

print("📍 一维数组索引")
print("=" * 25)
print(f"原数组: {arr_1d}")

# 正向索引(从0开始)
print(f"\n🔹 正向索引:")
print(f"  第1个元素 arr[0]: {arr_1d[0]}")
print(f"  第3个元素 arr[2]: {arr_1d[2]}")
print(f"  最后一个元素 arr[8]: {arr_1d[8]}")

# 负向索引(从-1开始)
print(f"\n🔸 负向索引:")
print(f"  最后一个元素 arr[-1]: {arr_1d[-1]}")
print(f"  倒数第二个 arr[-2]: {arr_1d[-2]}")
print(f"  倒数第三个 arr[-3]: {arr_1d[-3]}")

# 切片操作
print(f"\n✂️ 切片操作:")
print(f"  前3个元素 arr[:3]: {arr_1d[:3]}")
print(f"  第3到第6个 arr[2:6]: {arr_1d[2:6]}")
print(f"  最后3个元素 arr[-3:]: {arr_1d[-3:]}")
print(f"  每隔2个取一个 arr[::2]: {arr_1d[::2]}")
print(f"  倒序 arr[::-1]: {arr_1d[::-1]}")

# 条件索引
print(f"\n🎯 条件索引:")
condition = arr_1d > 50  # 创建布尔数组
print(f"  大于50的条件: {condition}")
print(f"  大于50的元素: {arr_1d[condition]}")
print(f"  大于50的元素: {arr_1d[arr_1d > 50]}")  # 简写形式

二维数组索引

# 创建二维数组进行索引演示
arr_2d = np.array([[10, 20, 30, 40],
                   [50, 60, 70, 80], 
                   [90, 100, 110, 120]])

print("📍 二维数组索引")
print("=" * 25)
print("原数组:")
print(arr_2d)

# 单个元素索引
print(f"\n🔹 单个元素索引:")
print(f"  第1行第1列 arr[0, 0]: {arr_2d[0, 0]}")
print(f"  第2行第3列 arr[1, 2]: {arr_2d[1, 2]}")
print(f"  最后一行最后一列 arr[-1, -1]: {arr_2d[-1, -1]}")

# 行和列的选择
print(f"\n📏 行和列选择:")
print(f"  第1行 arr[0, :]: {arr_2d[0, :]}")
print(f"  第3列 arr[:, 2]: {arr_2d[:, 2]}")
print(f"  第2-3行: \n{arr_2d[1:3, :]}")
print(f"  第2-4列: \n{arr_2d[:, 1:4]}")

# 子矩阵选择
print(f"\n🔲 子矩阵选择:")
print(f"  左上角2×2矩阵:")
print(arr_2d[:2, :2])
print(f"  右下角2×2矩阵:")
print(arr_2d[-2:, -2:])

# 花式索引
print(f"\n🎨 花式索引:")
row_indices = [0, 2]      # 选择第1行和第3行
col_indices = [1, 3]      # 选择第2列和第4列
print(f"  选择特定行列的交叉点:")
print(arr_2d[np.ix_(row_indices, col_indices)])

# 条件索引
print(f"\n🎯 条件索引:")
print(f"  大于60的元素: {arr_2d[arr_2d > 60]}")
print(f"  大于60的元素位置:")
positions = np.where(arr_2d > 60)
print(f"    行索引: {positions[0]}")
print(f"    列索引: {positions[1]}")

数组形状操作

# 创建示例数组
original_arr = np.arange(24)  # 创建0到23的数组
print("🔄 数组形状操作")
print("=" * 25)
print(f"原始数组: {original_arr}")
print(f"原始形状: {original_arr.shape}")

# reshape:改变数组形状
print(f"\n📐 reshape操作:")
reshaped_2d = original_arr.reshape(4, 6)  # 转换为4×6的二维数组
print(f"  reshape为4×6:")
print(reshaped_2d)

reshaped_3d = original_arr.reshape(2, 3, 4)  # 转换为2×3×4的三维数组
print(f"\n  reshape为2×3×4:")
print(reshaped_3d)

# 自动计算维度
auto_reshape = original_arr.reshape(4, -1)  # -1表示自动计算该维度
print(f"\n  自动计算维度 reshape(4, -1):")
print(auto_reshape)
print(f"  结果形状: {auto_reshape.shape}")

# flatten:展平数组
print(f"\n🔽 flatten操作:")
flattened = reshaped_3d.flatten()  # 将多维数组展平为一维
print(f"  展平后: {flattened}")
print(f"  展平后形状: {flattened.shape}")

# ravel:另一种展平方式
print(f"\n🔽 ravel操作:")
raveled = reshaped_3d.ravel()  # 功能类似flatten,但返回视图而非副本
print(f"  ravel后: {raveled}")

# transpose:转置
print(f"\n🔀 transpose操作:")
matrix = np.array([[1, 2, 3], [4, 5, 6]])
print(f"  原矩阵:")
print(matrix)
print(f"  转置后:")
print(matrix.T)  # 或者 matrix.transpose()

# squeeze:移除长度为1的维度
print(f"\n🗜️ squeeze操作:")
arr_with_single_dims = np.array([[[1], [2], [3]]])  # 形状为(1, 3, 1)
print(f"  原形状: {arr_with_single_dims.shape}")
squeezed = np.squeeze(arr_with_single_dims)
print(f"  squeeze后形状: {squeezed.shape}")
print(f"  squeeze后内容: {squeezed}")

# expand_dims:增加维度
print(f"\n📈 expand_dims操作:")
arr_1d = np.array([1, 2, 3])
print(f"  原数组形状: {arr_1d.shape}")
expanded = np.expand_dims(arr_1d, axis=0)  # 在第0维增加维度
print(f"  在第0维扩展后形状: {expanded.shape}")
print(f"  扩展后内容: {expanded}")

数组运算

基本数学运算

# 创建示例数组
arr1 = np.array([[1, 2, 3], [4, 5, 6]])
arr2 = np.array([[7, 8, 9], [10, 11, 12]])

print("🧮 数组基本运算")
print("=" * 25)
print("数组1:")
print(arr1)
print("数组2:")
print(arr2)

# 元素级运算(逐元素运算)
print(f"\n➕ 加法运算:")
addition = arr1 + arr2
print(addition)

print(f"\n➖ 减法运算:")
subtraction = arr2 - arr1
print(subtraction)

print(f"\n✖️ 乘法运算(逐元素):")
multiplication = arr1 * arr2
print(multiplication)

print(f"\n➗ 除法运算:")
division = arr2 / arr1
print(division)

print(f"\n🔢 幂运算:")
power = arr1 ** 2
print(power)

print(f"\n📊 平方根:")
sqrt_result = np.sqrt(arr1)
print(sqrt_result)

# 标量运算
print(f"\n🔢 标量运算:")
scalar_add = arr1 + 10  # 每个元素都加10
print(f"  数组1 + 10:")
print(scalar_add)

scalar_mult = arr1 * 3  # 每个元素都乘以3
print(f"  数组1 × 3:")
print(scalar_mult)

# 比较运算
print(f"\n🎯 比较运算:")
comparison = arr1 > 3
print(f"  arr1 > 3:")
print(comparison)

equal_comparison = arr1 == arr2
print(f"  arr1 == arr2:")
print(equal_comparison)

聚合函数

# 创建示例数组
data_arr = np.array([[10, 20, 30, 40],
                     [50, 60, 70, 80],
                     [90, 100, 110, 120]])

print("📊 聚合函数")
print("=" * 20)
print("数据数组:")
print(data_arr)

# 基本聚合
print(f"\n🔢 基本聚合:")
print(f"  总和: {np.sum(data_arr)}")
print(f"  平均值: {np.mean(data_arr)}")
print(f"  中位数: {np.median(data_arr)}")
print(f"  最大值: {np.max(data_arr)}")
print(f"  最小值: {np.min(data_arr)}")
print(f"  标准差: {np.std(data_arr):.2f}")
print(f"  方差: {np.var(data_arr):.2f}")

# 按轴聚合
print(f"\n📏 按轴聚合:")
print(f"  按行求和 (axis=1): {np.sum(data_arr, axis=1)}")
print(f"  按列求和 (axis=0): {np.sum(data_arr, axis=0)}")
print(f"  按行求平均 (axis=1): {np.mean(data_arr, axis=1)}")
print(f"  按列求平均 (axis=0): {np.mean(data_arr, axis=0)}")

# 最值位置
print(f"\n🎯 最值位置:")
print(f"  最大值位置: {np.argmax(data_arr)}")  # 展平后的位置
print(f"  最小值位置: {np.argmin(data_arr)}")  # 展平后的位置
print(f"  按行最大值位置: {np.argmax(data_arr, axis=1)}")
print(f"  按列最大值位置: {np.argmax(data_arr, axis=0)}")

# 累积计算
print(f"\n📈 累积计算:")
print(f"  累积求和:")
print(np.cumsum(data_arr, axis=1))  # 按行累积求和
print(f"  累积乘积:")
print(np.cumprod(data_arr, axis=0))  # 按列累积乘积

线性代数运算

# 创建矩阵
matrix_a = np.array([[1, 2], [3, 4]])
matrix_b = np.array([[5, 6], [7, 8]])
vector = np.array([1, 2])

print("🔢 线性代数运算")
print("=" * 25)
print("矩阵A:")
print(matrix_a)
print("矩阵B:")
print(matrix_b)
print(f"向量: {vector}")

# 矩阵乘法
print(f"\n✖️ 矩阵乘法:")
matrix_mult = np.dot(matrix_a, matrix_b)  # 或使用 matrix_a @ matrix_b
print(matrix_mult)

# 矩阵与向量乘法
print(f"\n🔢 矩阵向量乘法:")
mv_mult = np.dot(matrix_a, vector)
print(mv_mult)

# 矩阵的逆
print(f"\n🔄 矩阵的逆:")
try:
    inverse_a = np.linalg.inv(matrix_a)
    print(inverse_a)
    
    # 验证逆矩阵
    identity_check = np.dot(matrix_a, inverse_a)
    print(f"验证A × A^(-1) = I:")
    print(np.round(identity_check, 10))  # 四舍五入以避免浮点误差
except np.linalg.LinAlgError:
    print("矩阵不可逆")

# 矩阵的行列式
print(f"\n📐 矩阵的行列式:")
det_a = np.linalg.det(matrix_a)
print(f"det(A) = {det_a}")

# 特征值和特征向量
print(f"\n🎯 特征值和特征向量:")
eigenvalues, eigenvectors = np.linalg.eig(matrix_a)
print(f"特征值: {eigenvalues}")
print(f"特征向量:")
print(eigenvectors)

# 矩阵的迹(对角线元素之和)
print(f"\n📊 矩阵的迹:")
trace_a = np.trace(matrix_a)
print(f"trace(A) = {trace_a}")

广播机制

广播原理

# 广播机制演示
print("📡 NumPy广播机制")
print("=" * 30)

# 示例1:标量与数组
arr = np.array([[1, 2, 3], [4, 5, 6]])
scalar = 10

print("数组:")
print(arr)
print(f"标量: {scalar}")

result1 = arr + scalar
print("数组 + 标量 (广播):")
print(result1)

# 示例2:不同形状的数组
arr_2d = np.array([[1, 2, 3], [4, 5, 6]])      # 形状: (2, 3)
arr_1d = np.array([10, 20, 30])                 # 形状: (3,)

print(f"\n2D数组形状: {arr_2d.shape}")
print(arr_2d)
print(f"1D数组形状: {arr_1d.shape}")
print(arr_1d)

result2 = arr_2d + arr_1d  # 1D数组会广播到每一行
print("2D数组 + 1D数组 (广播):")
print(result2)

# 示例3:列向量与行向量
col_vector = np.array([[1], [2], [3]])          # 形状: (3, 1)
row_vector = np.array([10, 20])                 # 形状: (2,)

print(f"\n列向量形状: {col_vector.shape}")
print(col_vector)
print(f"行向量形状: {row_vector.shape}")
print(row_vector)

result3 = col_vector + row_vector  # 广播成 (3, 2)
print("列向量 + 行向量 (广播):")
print(result3)
print(f"结果形状: {result3.shape}")

广播规则详解

# 广播规则说明和示例
print("📏 广播规则详解")
print("=" * 30)

def check_broadcast_compatibility(shape1, shape2):
    """检查两个形状是否可以广播"""
    print(f"检查形状 {shape1}{shape2} 的广播兼容性:")
    
    # 从右到左比较维度
    max_ndim = max(len(shape1), len(shape2))
    shape1_padded = [1] * (max_ndim - len(shape1)) + list(shape1)
    shape2_padded = [1] * (max_ndim - len(shape2)) + list(shape2)
    
    result_shape = []
    compatible = True
    
    for i in range(max_ndim):
        dim1, dim2 = shape1_padded[i], shape2_padded[i]
        if dim1 == 1:
            result_shape.append(dim2)
        elif dim2 == 1:
            result_shape.append(dim1)
        elif dim1 == dim2:
            result_shape.append(dim1)
        else:
            compatible = False
            break
    
    if compatible:
        print(f"  ✅ 兼容,结果形状: {tuple(result_shape)}")
    else:
        print(f"  ❌ 不兼容")
    
    return compatible

# 测试各种广播情况
test_cases = [
    ((3, 4), (4,)),      # 兼容
    ((3, 4), (3, 1)),    # 兼容
    ((3, 4), (1, 4)),    # 兼容
    ((3, 4), (3, 4)),    # 兼容
    ((3, 4), (2, 4)),    # 不兼容
    ((2, 3, 4), (4,)),   # 兼容
    ((2, 3, 4), (3, 4)), # 兼容
    ((2, 3, 4), (2, 1, 4)),  # 兼容
]

for shape1, shape2 in test_cases:
    check_broadcast_compatibility(shape1, shape2)
    print()

# 实际广播示例
print("🔬 实际广播示例:")

# 图像处理中的常见广播
image = np.random.randint(0, 255, (100, 100, 3))  # 100×100的RGB图像
brightness_adjustment = np.array([20, -10, 5])     # 对RGB三个通道的调整

print(f"图像形状: {image.shape}")
print(f"亮度调整数组形状: {brightness_adjustment.shape}")

# 广播应用亮度调整
adjusted_image = image + brightness_adjustment
print(f"调整后图像形状: {adjusted_image.shape}")
print("✅ 每个像素的RGB值都被相应调整")

完整项目:图像数据处理

让我们创建一个完整的图像处理项目,展示NumPy在实际AI应用中的使用:

# image_processor.py - 图像数据处理项目
"""
使用NumPy进行图像数据处理
演示NumPy在计算机视觉中的应用
"""

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import ListedColormap

class ImageProcessor:
    """图像处理器类"""
    
    def __init__(self):
        """初始化图像处理器"""
        # 设置随机种子以确保可重现性
        np.random.seed(42)
        print("🖼️ 图像处理器初始化完成")
    
    def create_sample_image(self, height=100, width=100):
        """
        创建示例图像
        
        参数:
            height: 图像高度
            width: 图像宽度
            
        返回:
            RGB图像数组,形状为(height, width, 3)
        """
        print(f"🎨 创建 {height}×{width} 像素的示例图像...")
        
        # 创建彩色渐变图像
        # 红色通道:水平渐变
        red_channel = np.linspace(0, 255, width).reshape(1, width)
        red_channel = np.repeat(red_channel, height, axis=0)
        
        # 绿色通道:垂直渐变
        green_channel = np.linspace(0, 255, height).reshape(height, 1)
        green_channel = np.repeat(green_channel, width, axis=1)
        
        # 蓝色通道:对角渐变
        x, y = np.meshgrid(np.linspace(0, 1, width), np.linspace(0, 1, height))
        blue_channel = 255 * (x + y) / 2
        
        # 组合RGB通道
        image = np.stack([red_channel, green_channel, blue_channel], axis=2)
        
        # 确保像素值在0-255范围内,并转换为整数
        image = np.clip(image, 0, 255).astype(np.uint8)
        
        print(f"✅ 图像创建完成,形状: {image.shape}")
        return image
    
    def add_noise(self, image, noise_type='gaussian', intensity=0.1):
        """
        为图像添加噪声
        
        参数:
            image: 输入图像
            noise_type: 噪声类型 ('gaussian', 'salt_pepper', 'uniform')
            intensity: 噪声强度
            
        返回:
            添加噪声后的图像
        """
        print(f"🔊 添加 {noise_type} 噪声,强度: {intensity}")
        
        noisy_image = image.copy().astype(np.float64)
        
        if noise_type == 'gaussian':
            # 高斯噪声
            noise = np.random.normal(0, intensity * 255, image.shape)
            noisy_image += noise
            
        elif noise_type == 'salt_pepper':
            # 椒盐噪声
            noise_mask = np.random.random(image.shape[:2]) < intensity
            salt_mask = np.random.random(image.shape[:2]) < 0.5
            
            # 盐噪声(白点)
            noisy_image[noise_mask & salt_mask] = 255
            # 椒噪声(黑点)
            noisy_image[noise_mask & ~salt_mask] = 0
            
        elif noise_type == 'uniform':
            # 均匀噪声
            noise = np.random.uniform(-intensity * 255, intensity * 255, image.shape)
            noisy_image += noise
        
        # 确保像素值在有效范围内
        noisy_image = np.clip(noisy_image, 0, 255).astype(np.uint8)
        
        print(f"✅ 噪声添加完成")
        return noisy_image
    
    def apply_filter(self, image, filter_type='blur'):
        """
        应用图像滤波器
        
        参数:
            image: 输入图像
            filter_type: 滤波器类型 ('blur', 'sharpen', 'edge')
            
        返回:
            滤波后的图像
        """
        print(f"🔧 应用 {filter_type} 滤波器...")
        
        # 定义不同类型的滤波核
        if filter_type == 'blur':
            # 均值滤波核(模糊效果)
            kernel = np.ones((3, 3)) / 9
            
        elif filter_type == 'sharpen':
            # 锐化滤波核
            kernel = np.array([[-1, -1, -1],
                              [-1,  9, -1],
                              [-1, -1, -1]])
            
        elif filter_type == 'edge':
            # 边缘检测滤波核
            kernel = np.array([[-1, -1, -1],
                              [-1,  8, -1],
                              [-1, -1, -1]])
        else:
            print(f"❌ 未知的滤波器类型: {filter_type}")
            return image
        
        # 应用卷积操作(简化版本)
        filtered_image = self._apply_convolution(image, kernel)
        
        print(f"✅ {filter_type} 滤波完成")
        return filtered_image
    
    def _apply_convolution(self, image, kernel):
        """
        应用卷积操作
        
        参数:
            image: 输入图像
            kernel: 卷积核
            
        返回:
            卷积后的图像
        """
        # 获取图像和核的尺寸
        img_height, img_width = image.shape[:2]
        kernel_size = kernel.shape[0]
        pad_size = kernel_size // 2
        
        # 为每个颜色通道分别处理
        if len(image.shape) == 3:
            channels = image.shape[2]
            result = np.zeros_like(image, dtype=np.float64)
            
            for c in range(channels):
                # 对图像进行边界填充
                padded = np.pad(image[:, :, c], pad_size, mode='edge')
                
                # 应用卷积
                for i in range(img_height):
                    for j in range(img_width):
                        region = padded[i:i+kernel_size, j:j+kernel_size]
                        result[i, j, c] = np.sum(region * kernel)
        else:
            # 灰度图像处理
            padded = np.pad(image, pad_size, mode='edge')
            result = np.zeros_like(image, dtype=np.float64)
            
            for i in range(img_height):
                for j in range(img_width):
                    region = padded[i:i+kernel_size, j:j+kernel_size]
                    result[i, j] = np.sum(region * kernel)
        
        # 确保像素值在有效范围内
        result = np.clip(result, 0, 255).astype(np.uint8)
        return result
    
    def rgb_to_grayscale(self, image):
        """
        将RGB图像转换为灰度图像
        
        参数:
            image: RGB图像
            
        返回:
            灰度图像
        """
        print("🎨 转换为灰度图像...")
        
        if len(image.shape) != 3 or image.shape[2] != 3:
            print("❌ 输入必须是RGB图像")
            return image
        
        # 使用标准的RGB到灰度转换权重
        # 人眼对绿色最敏感,对蓝色最不敏感
        weights = np.array([0.299, 0.587, 0.114])
        
        # 使用NumPy的广播机制应用权重
        grayscale = np.dot(image, weights)
        
        # 转换为整数类型
        grayscale = grayscale.astype(np.uint8)
        
        print(f"✅ 灰度转换完成,形状: {grayscale.shape}")
        return grayscale
    
    def resize_image(self, image, new_height, new_width):
        """
        调整图像大小(简单的最近邻插值)
        
        参数:
            image: 输入图像
            new_height: 新的高度
            new_width: 新的宽度
            
        返回:
            调整大小后的图像
        """
        print(f"📏 调整图像大小到 {new_height}×{new_width}...")
        
        old_height, old_width = image.shape[:2]
        
        # 计算缩放比例
        scale_y = old_height / new_height
        scale_x = old_width / new_width
        
        # 创建新图像
        if len(image.shape) == 3:
            new_image = np.zeros((new_height, new_width, image.shape[2]), dtype=image.dtype)
        else:
            new_image = np.zeros((new_height, new_width), dtype=image.dtype)
        
        # 最近邻插值
        for i in range(new_height):
            for j in range(new_width):
                # 计算原图像中对应的坐标
                old_i = int(i * scale_y)
                old_j = int(j * scale_x)
                
                # 确保坐标在有效范围内
                old_i = min(old_i, old_height - 1)
                old_j = min(old_j, old_width - 1)
                
                new_image[i, j] = image[old_i, old_j]
        
        print(f"✅ 图像调整完成")
        return new_image
    
    def calculate_histogram(self, image):
        """
        计算图像直方图
        
        参数:
            image: 输入图像
            
        返回:
            直方图数据
        """
        print("📊 计算图像直方图...")
        
        if len(image.shape) == 3:
            # RGB图像,计算每个通道的直方图
            histograms = {}
            colors = ['Red', 'Green', 'Blue']
            
            for i, color in enumerate(colors):
                hist, _ = np.histogram(image[:, :, i], bins=256, range=(0, 256))
                histograms[color] = hist
                
        else:
            # 灰度图像
            hist, _ = np.histogram(image, bins=256, range=(0, 256))
            histograms = {'Gray': hist}
        
        print(f"✅ 直方图计算完成")
        return histograms
    
    def enhance_contrast(self, image, factor=1.5):
        """
        增强图像对比度
        
        参数:
            image: 输入图像
            factor: 对比度增强因子
            
        返回:
            对比度增强后的图像
        """
        print(f"🔆 增强对比度,因子: {factor}")
        
        # 转换为浮点数进行计算
        enhanced = image.astype(np.float64)
        
        # 计算图像的平均值
        mean_value = np.mean(enhanced)
        
        # 应用对比度增强公式:new_pixel = mean + factor * (old_pixel - mean)
        enhanced = mean_value + factor * (enhanced - mean_value)
        
        # 确保像素值在有效范围内
        enhanced = np.clip(enhanced, 0, 255).astype(np.uint8)
        
        print(f"✅ 对比度增强完成")
        return enhanced
    
    def visualize_processing_pipeline(self, image):
        """
        可视化完整的图像处理流程
        
        参数:
            image: 输入图像
        """
        print("🎬 开始图像处理流程可视化...")
        
        # 处理步骤
        steps = []
        titles = []
        
        # 1. 原始图像
        steps.append(image)
        titles.append('原始图像')
        
        # 2. 添加噪声
        noisy = self.add_noise(image, 'gaussian', 0.05)
        steps.append(noisy)
        titles.append('添加高斯噪声')
        
        # 3. 模糊滤波
        blurred = self.apply_filter(noisy, 'blur')
        steps.append(blurred)
        titles.append('模糊滤波')
        
        # 4. 锐化滤波
        sharpened = self.apply_filter(image, 'sharpen')
        steps.append(sharpened)
        titles.append('锐化滤波')
        
        # 5. 边缘检测
        edges = self.apply_filter(image, 'edge')
        steps.append(edges)
        titles.append('边缘检测')
        
        # 6. 灰度转换
        gray = self.rgb_to_grayscale(image)
        steps.append(gray)
        titles.append('灰度转换')
        
        # 7. 对比度增强
        enhanced = self.enhance_contrast(image, 1.8)
        steps.append(enhanced)
        titles.append('对比度增强')
        
        # 8. 调整大小
        resized = self.resize_image(image, 50, 50)
        steps.append(resized)
        titles.append('尺寸调整(50×50)')
        
        # 创建可视化
        fig, axes = plt.subplots(2, 4, figsize=(20, 10))
        fig.suptitle('图像处理流程展示', fontsize=16, fontweight='bold')
        
        for i, (step, title) in enumerate(zip(steps, titles)):
            row = i // 4
            col = i % 4
            
            if len(step.shape) == 3:
                axes[row, col].imshow(step)
            else:
                axes[row, col].imshow(step, cmap='gray')
            
            axes[row, col].set_title(title, fontsize=12)
            axes[row, col].axis('off')
        
        plt.tight_layout()
        plt.show()
        
        print("✅ 图像处理流程可视化完成")
    
    def demonstrate_array_operations(self):
        """演示NumPy数组操作在图像处理中的应用"""
        print("\n🔬 NumPy数组操作演示")
        print("=" * 40)
        
        # 创建示例图像
        image = self.create_sample_image(80, 80)
        
        print(f"\n📊 图像信息:")
        print(f"  形状: {image.shape}")
        print(f"  数据类型: {image.dtype}")
        print(f"  最小值: {np.min(image)}")
        print(f"  最大值: {np.max(image)}")
        print(f"  平均值: {np.mean(image):.2f}")
        
        # 数组切片操作
        print(f"\n✂️ 数组切片操作:")
        top_left = image[:20, :20]  # 左上角20×20区域
        print(f"  左上角区域形状: {top_left.shape}")
        
        # 通道分离
        print(f"\n🎨 通道分离:")
        red_channel = image[:, :, 0]
        green_channel = image[:, :, 1]
        blue_channel = image[:, :, 2]
        print(f"  红色通道形状: {red_channel.shape}")
        print(f"  绿色通道平均值: {np.mean(green_channel):.2f}")
        print(f"  蓝色通道最大值: {np.max(blue_channel)}")
        
        # 数组运算
        print(f"\n🧮 数组运算:")
        brightened = image + 50  # 增加亮度
        brightened = np.clip(brightened, 0, 255)  # 确保在有效范围
        print(f"  亮度增加后平均值: {np.mean(brightened):.2f}")
        
        darkened = image * 0.7  # 降低亮度
        print(f"  亮度降低后平均值: {np.mean(darkened):.2f}")
        
        # 条件索引
        print(f"\n🎯 条件索引:")
        bright_pixels = image > 200  # 找出亮像素
        print(f"  亮像素数量: {np.sum(bright_pixels)}")
        
        # 统计分析
        print(f"\n📈 统计分析:")
        print(f"  每通道平均值: {np.mean(image, axis=(0, 1))}")
        print(f"  每通道标准差: {np.std(image, axis=(0, 1))}")
        
        # 形状操作
        print(f"\n📐 形状操作:")
        flattened = image.flatten()
        print(f"  展平后形状: {flattened.shape}")
        
        reshaped = image.reshape(-1, 3)  # 重塑为N×3(每行一个像素的RGB值)
        print(f"  重塑后形状: {reshaped.shape}")
        
        return image

def demo_image_processing():
    """图像处理完整演示"""
    print("🖼️ 图像处理完整演示")
    print("=" * 50)
    
    # 创建图像处理器
    processor = ImageProcessor()
    
    # 演示NumPy数组操作
    image = processor.demonstrate_array_operations()
    
    # 可视化处理流程
    processor.visualize_processing_pipeline(image)
    
    # 计算和显示直方图
    histograms = processor.calculate_histogram(image)
    
    plt.figure(figsize=(12, 8))
    
    # 显示原图像和直方图
    plt.subplot(2, 2, 1)
    plt.imshow(image)
    plt.title('原始图像')
    plt.axis('off')
    
    # 显示RGB直方图
    plt.subplot(2, 2, 2)
    colors = ['red', 'green', 'blue']
    for i, (color_name, color) in enumerate(zip(['Red', 'Green', 'Blue'], colors)):
        if color_name in histograms:
            plt.plot(histograms[color_name], color=color, alpha=0.7, label=color_name)
    plt.title('RGB直方图')
    plt.xlabel('像素值')
    plt.ylabel('频次')
    plt.legend()
    
    # 显示灰度图像
    gray_image = processor.rgb_to_grayscale(image)
    plt.subplot(2, 2, 3)
    plt.imshow(gray_image, cmap='gray')
    plt.title('灰度图像')
    plt.axis('off')
    
    # 显示灰度直方图
    gray_hist = processor.calculate_histogram(gray_image)
    plt.subplot(2, 2, 4)
    plt.plot(gray_hist['Gray'], color='gray')
    plt.title('灰度直方图')
    plt.xlabel('像素值')
    plt.ylabel('频次')
    
    plt.tight_layout()
    plt.show()
    
    print("\n🎉 图像处理演示完成!")

if __name__ == "__main__":
    demo_image_processing()

NumPy性能优化技巧

向量化操作

# 性能比较:循环 vs 向量化
import time

print("⚡ NumPy性能优化技巧")
print("=" * 30)

# 创建大型数组
size = 1000000
arr1 = np.random.random(size)
arr2 = np.random.random(size)

print(f"数组大小: {size:,} 个元素")

# 方法1:Python循环(慢)
start_time = time.time()
result_loop = []
for i in range(size):
    result_loop.append(arr1[i] * arr2[i] + np.sin(arr1[i]))
result_loop = np.array(result_loop)
loop_time = time.time() - start_time

# 方法2:NumPy向量化操作(快)
start_time = time.time()
result_vectorized = arr1 * arr2 + np.sin(arr1)
vectorized_time = time.time() - start_time

print(f"\n⏱️ 性能比较:")
print(f"  Python循环耗时: {loop_time:.4f} 秒")
print(f"  NumPy向量化耗时: {vectorized_time:.4f} 秒")
print(f"  性能提升: {loop_time/vectorized_time:.1f} 倍")

# 验证结果一致性
print(f"  结果是否一致: {np.allclose(result_loop, result_vectorized)}")

内存效率优化

# 内存使用优化
print(f"\n💾 内存使用优化:")

# 选择合适的数据类型
large_int_array = np.arange(1000000, dtype=np.int64)
small_int_array = np.arange(1000000, dtype=np.int32)

print(f"  int64数组内存: {large_int_array.nbytes / 1024 / 1024:.2f} MB")
print(f"  int32数组内存: {small_int_array.nbytes / 1024 / 1024:.2f} MB")
print(f"  内存节省: {(1 - small_int_array.nbytes/large_int_array.nbytes)*100:.1f}%")

# 使用视图而非副本
original = np.arange(1000000)
view = original[::2]        # 视图:不复制数据
copy = original[::2].copy() # 副本:复制数据

print(f"\n📋 视图 vs 副本:")
print(f"  原数组内存: {original.nbytes / 1024 / 1024:.2f} MB")
print(f"  视图是否共享内存: {np.shares_memory(original, view)}")
print(f"  副本是否共享内存: {np.shares_memory(original, copy)}")

# 就地操作
arr = np.random.random(1000000)
print(f"\n🔄 就地操作:")
print(f"  修改前数组ID: {id(arr)}")
arr += 1  # 就地操作,不创建新数组
print(f"  修改后数组ID: {id(arr)}")
print(f"  内存地址未变,节省内存")

常见问题和解决方案

问题1:维度不匹配

# 解决维度不匹配问题
print("🚨 常见问题解决")
print("=" * 25)

print("问题1: 维度不匹配")
try:
    a = np.array([1, 2, 3])
    b = np.array([[1, 2], [3, 4]])
    result = a + b  # 这会失败
except ValueError as e:
    print(f"  错误: {e}")
    
    # 解决方案:重塑数组
    a_reshaped = a.reshape(3, 1)
    print(f"  原数组a形状: {a.shape}")
    print(f"  重塑后形状: {a_reshaped.shape}")
    print("  现在可以广播了")

问题2:数据类型转换

# 数据类型问题
print(f"\n问题2: 数据类型转换")

int_arr = np.array([1, 2, 3])
float_result = int_arr / 2

print(f"  整数数组: {int_arr}, 类型: {int_arr.dtype}")
print(f"  除法结果: {float_result}, 类型: {float_result.dtype}")

# 显式类型转换
converted = int_arr.astype(np.float32)
print(f"  转换后: {converted}, 类型: {converted.dtype}")

问题3:内存不足

# 处理大数组的策略
print(f"\n问题3: 处理大数组")

def process_large_array_safely(size):
    """安全处理大数组的策略"""
    try:
        # 尝试创建大数组
        large_arr = np.zeros(size)
        print(f"  成功创建大小为 {size:,} 的数组")
        del large_arr  # 释放内存
        
    except MemoryError:
        print(f"  内存不足,无法创建大小为 {size:,} 的数组")
        print(f"  建议使用分块处理或内存映射")

# 测试不同大小
for size in [10**6, 10**8, 10**9]:
    process_large_array_safely(size)

学习总结

通过本节课的学习,你已经掌握了:

✅ NumPy基础概念

  • 理解了NumPy的重要性和在AI中的地位
  • 掌握了ndarray的核心概念和属性
  • 学会了多种创建数组的方法

✅ 数组操作技能

  • 熟练使用索引和切片访问数组元素
  • 掌握了数组形状操作和维度变换
  • 学会了各种数学运算和聚合函数

✅ 高级特性

  • 理解了广播机制的原理和应用
  • 掌握了向量化操作提高性能
  • 学会了内存优化技巧

✅ 实际应用

  • 完成了完整的图像处理项目
  • 体验了NumPy在计算机视觉中的应用
  • 学会了解决常见问题的方法

✅ 性能优化

  • 了解了向量化vs循环的性能差异
  • 掌握了内存效率优化方法
  • 学会了选择合适的数据类型

下节课预告

第5讲我们将学习Pandas数据处理,这是数据分析的核心工具:

  • Pandas的核心数据结构:Series和DataFrame
  • 数据读取、清洗和预处理
  • 数据分组、聚合和透视表
  • 时间序列数据处理
  • 销售数据分析实战项目

Pandas基于NumPy构建,是数据科学工作流中不可缺少的工具!


恭喜完成第4讲!
你已经掌握了NumPy这个AI开发的基石。NumPy的高效数组操作和数学计算能力将伴随你整个AI学习之旅。现在你可以处理各种数值数据,为后续的数据分析和机器学习打下了坚实基础!

Logo

欢迎加入DeepSeek 技术社区。在这里,你可以找到志同道合的朋友,共同探索AI技术的奥秘。

更多推荐