【零基础学AI】第4讲:NumPy数组操作
本文介绍了NumPy在AI开发中的核心作用及其基础操作。NumPy作为Python科学计算的基础库,其多维数组对象(ndarray)具有高效、同质、多维的特点,比Python原生列表快10-100倍,是Pandas、Scikit-learn等AI库的底层基础。文章详细讲解了NumPy数组的创建方法(从列表生成/内置函数生成)、关键属性(形状、维度、数据类型等)以及核心操作技巧(索引、切片、条件筛选
·

本节课你将学到
- 理解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学习之旅。现在你可以处理各种数值数据,为后续的数据分析和机器学习打下了坚实基础!
更多推荐



所有评论(0)