涵盖 NumPy 核心矩阵操作与 OpenCV/Matplotlib 图像读取显示。
1. 矩阵创建与基本运算
创建以下两个矩阵并完成运算:
A = [[1, 2, 3], B = [[9, 8, 7],
[4, 5, 6], [6, 5, 4],
[7, 8, 9]] [3, 2, 1]]
要求:
-
计算 A + B、A × B(矩阵乘法)、A 的转置
-
求 A 的行列式和逆矩阵(若存在)
-
找出 A 中大于 5 的所有元素,并将其替换为 0
pythonimport numpy as np A = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) B = np.array([[9, 8, 7], [6, 5, 4], [3, 2, 1]]) print("A+B:\n",A+B) print("A*B:\n",A@B) print("AT:\n",A.T) print("行列式:", np.linalg.det(A)) #print("逆矩阵:\n", np.linalg.inv(A)) print("特征值:", np.linalg.eigvals(A)) print("求和:", A.sum()) # 21 print("均值:", A.mean()) # 3.5 mask=A>5 print(mask) A[mask]=0 print(A)
2. 矩阵统计与切片
用 NumPy 生成一个 6×6 的随机整数矩阵(范围 0~100),完成:
- 计算每行的均值、每列的最大值
- 提取中间 4×4 的子矩阵
- 将矩阵按主对角线翻转(转置),并对比翻转前后的形状
python
import numpy as np
# 1. 生成 6×6 随机整数矩阵(0~100)
#np.random.seed(42) # 固定种子,结果可复现
A = np.random.randint(0, 101, size=(6, 6))
print("=" * 50)
print("原始矩阵 A (6×6):")
print(A)
print(f"形状: {A.shape}")
print("\n" + "=" * 50)
print("2. 每行的均值:")
row_means = A.mean(axis=1)
print(row_means)
print(f"形状: {row_means.shape}") # (6,)
print("\n" + "=" * 50)
print("3. 每列的最大值:")
col_max = A.max(axis=0)
print(col_max)
print(f"形状: {col_max.shape}") # (6,)
print("\n" + "=" * 50)
print("4. 提取中间 4×4 子矩阵:")
# 方法1:直接切片 [1:5, 1:5] 去掉第0行/列和第5行/列
sub_matrix = A[1:5, 1:5]
print(sub_matrix)
print(f"形状: {sub_matrix.shape}")
print("\n" + "=" * 50)
print("5. 主对角线翻转(转置):")
A_T = A.T # 或 A.transpose()
print(A_T)
print(f"转置后形状: {A_T.shape}")
print("\n" + "=" * 50)
print("对比:")
print(f"原矩阵形状: {A.shape} → 转置后形状: {A_T.shape}")
print("形状不变!都是 (6, 6)")
3. 图像读取与通道分离
读取一张彩色图片,完成:
-
打印图像的 shape、dtype 和像素值范围
-
将 RGB 三个通道分离,分别单独显示(其余通道置 0)
-
用 Matplotlib 在一张图中以 2×2 布局显示:原图、R通道、G通道、B通道
pythonimport cv2 import matplotlib.pyplot as plt import numpy as np # -------------------------- 1. 自动生成彩色测试图片 -------------------------- # 生成尺寸为 400x600 的彩色渐变图(避免纯色图,能更清晰看到通道效果) height, width = 400, 600 # 创建渐变矩阵:R通道从左到右渐变,G通道从上到下渐变,B通道对角线渐变 x = np.linspace(0, 255, width, dtype=np.uint8) y = np.linspace(0, 255, height, dtype=np.uint8) xx, yy = np.meshgrid(x, y) # 构建RGB三通道 r_channel_gen = xx # 红色通道:左→右 0→255 g_channel_gen = yy # 绿色通道:上→下 0→255 b_channel_gen = (xx + yy) // 2 # 蓝色通道:对角线渐变 # 合并为RGB图片 img_rgb = np.stack([r_channel_gen, g_channel_gen, b_channel_gen], axis=-1) # -------------------------- 2. 打印图片基本信息 -------------------------- print("=== 图片基本信息 ===") print(f"图像shape (高度, 宽度, 通道数): {img_rgb.shape}") print(f"图像数据类型 dtype: {img_rgb.dtype}") print(f"像素值范围: [{np.min(img_rgb)}, {np.max(img_rgb)}]") # -------------------------- 3. 分离RGB通道(其余通道置0) -------------------------- # R通道(仅红色保留,绿、蓝置0) r_channel = np.zeros_like(img_rgb) r_channel[:, :, 0] = img_rgb[:, :, 0] # G通道(仅绿色保留,红、蓝置0) g_channel = np.zeros_like(img_rgb) g_channel[:, :, 1] = img_rgb[:, :, 1] # B通道(仅蓝色保留,红、绿置0) b_channel = np.zeros_like(img_rgb) b_channel[:, :, 2] = img_rgb[:, :, 2] # -------------------------- 4. 2×2布局显示所有图像 -------------------------- plt.figure(figsize=(12, 10)) # 子图1:原图 plt.subplot(2, 2, 1) plt.imshow(img_rgb) plt.title("Original Image (RGB Gradient)", fontsize=12) plt.axis("off") # 子图2:R通道 plt.subplot(2, 2, 2) plt.imshow(r_channel) plt.title("R Channel (Red Only)", fontsize=12) plt.axis("off") # 子图3:G通道 plt.subplot(2, 2, 3) plt.imshow(g_channel) plt.title("G Channel (Green Only)", fontsize=12) plt.axis("off") # 子图4:B通道 plt.subplot(2, 2, 4) plt.imshow(b_channel) plt.title("B Channel (Blue Only)", fontsize=12) plt.axis("off") plt.tight_layout() plt.show()
4. 图像的矩阵变换
对一张图片进行以下纯 NumPy 矩阵操作(不使用 OpenCV 的变换函数):
-
水平翻转 :利用切片
[:, ::-1] -
垂直翻转 :利用切片
[::-1, :] -
亮度调整:将所有像素值乘以 1.5,注意截断到 255 并转换类型
-
裁剪 ROI:截取图像中心 50% 区域
-
将以上 5 张图(含原图)用 Matplotlib 排列展示
pythonimport numpy as np import matplotlib.pyplot as plt # ============================================================================= # 生成测试图片(不用外部图片) # ============================================================================= # 创建 400×600 的彩色图像 h, w = 400, 600 img = np.zeros((h, w, 3), dtype=np.uint8) # 绘制彩色条纹(方便观察翻转效果) for i in range(0, w, 100): color = [(255, 0, 0), (0, 255, 0), (0, 0, 255), (255, 255, 0), (255, 0, 255), (0, 255, 255)][(i//100) % 6] img[:, i:i+100] = color # 添加文字区域(黑色矩形+白色文字模拟) img[50:150, 200:400] = [0, 0, 0] # 黑底 img[70:130, 220:380] = [255, 255, 255] # 白字区域 # 添加渐变(方便观察亮度调整) for i in range(h): img[i, :, 0] = int(255 * i / h) # 红色渐变 img[i, :, 2] = int(255 * (1 - i / h)) # 蓝色渐变 print(f"原图形状: {img.shape}") # ============================================================================= # 1. 原图 # ============================================================================= original = img.copy() # ============================================================================= # 2. 水平翻转(左右镜像) # ============================================================================= # [:, ::-1, :] # 第0维: : → 所有行(高度不变) # 第1维: ::-1 → 列反向(左右翻转) # 第2维: : → 所有通道 horizontal_flip = img[:, ::-1, :] # ============================================================================= # 3. 垂直翻转(上下镜像) # ============================================================================= # [::-1, :, :] # 第0维: ::-1 → 行反向(上下翻转) # 第1维: : → 所有列(宽度不变) # 第2维: : → 所有通道 vertical_flip = img[::-1, :, :] # ============================================================================= # 4. 亮度调整(×1.5,截断到255) # ============================================================================= # 必须转float,否则uint8溢出 brightness = img.astype(np.float32) # Step1: 转float32 brightness = brightness * 1.5 # Step2: 乘1.5 brightness = np.clip(brightness, 0, 255) # Step3: 截断超出的值 brightness = brightness.astype(np.uint8) # Step4: 转回uint8 # ============================================================================= # 5. 裁剪ROI(中心50%区域) # ============================================================================= h, w = img.shape[:2] # 计算中心区域(各去掉25%边距) h_start, h_end = h // 4, h * 3 // 4 # 100~300(共200像素) w_start, w_end = w // 4, w * 3 // 4 # 150~450(共300像素) roi = img[h_start:h_end, w_start:w_end, :] print(f"ROI形状: {roi.shape}") # (200, 300, 3) # ============================================================================= # Matplotlib展示(2×3布局) # ============================================================================= fig, axes = plt.subplots(2, 3, figsize=(15, 10)) fig.suptitle('NumPy Image Operations (Code Generated Image)', fontsize=14) # 子图1:原图 axes[0, 0].imshow(original) axes[0, 0].set_title(f'Original\n{original.shape}', color='blue') axes[0, 0].axis('off') # 子图2:水平翻转 axes[0, 1].imshow(horizontal_flip) axes[0, 1].set_title(f'Horizontal Flip [:, ::-1, :]\n{horizontal_flip.shape}', color='red') axes[0, 1].axis('off') # 子图3:垂直翻转 axes[0, 2].imshow(vertical_flip) axes[0, 2].set_title(f'Vertical Flip [::-1, :, :]\n{vertical_flip.shape}', color='green') axes[0, 2].axis('off') # 子图4:亮度调整 axes[1, 0].imshow(brightness) axes[1, 0].set_title(f'Brightness ×1.5 (clip 0-255)\n{brightness.shape}', color='orange') axes[1, 0].axis('off') # 子图5:ROI裁剪 axes[1, 1].imshow(roi) axes[1, 1].set_title(f'Center ROI 50%\n[{h_start}:{h_end}, {w_start}:{w_end}]\n{roi.shape}', color='purple') axes[1, 1].axis('off') # 子图6:显示原图+ROI框 img_with_box = original.copy() # 画红色框标记ROI img_with_box[h_start, w_start:w_end] = [255, 0, 0] # 上边框 img_with_box[h_end-1, w_start:w_end] = [255, 0, 0] # 下边框 img_with_box[h_start:h_end, w_start] = [255, 0, 0] # 左边框 img_with_box[h_start:h_end, w_end-1] = [255, 0, 0] # 右边框 axes[1, 2].imshow(img_with_box) axes[1, 2].set_title(f'Original with ROI Box\n(red border)', color='brown') axes[1, 2].axis('off') plt.tight_layout() plt.savefig('numpy_image_operations.png', dpi=150, bbox_inches='tight') plt.show() print("完成!5张图已生成")
5. 图像灰度化与直方图
读取一张彩色图像,完成:
-
用 NumPy 手动实现灰度转换(公式:
Gray = 0.299R + 0.587G + 0.114B),不使用cv2.cvtColor -
统计灰度图的像素值分布,绘制灰度直方图(横轴 0~255,纵轴像素频次)
-
将灰度值归一化到 0~1 的浮点范围,再还原回 0~255,验证两次转换前后的最大误差
pythonimport numpy as np import matplotlib.pyplot as plt # ============================================================================= # 1. 生成测试图像(彩色) # ============================================================================= np.random.seed(42) h, w = 200, 300 # 创建渐变+噪声图像 img = np.zeros((h, w, 3), dtype=np.uint8) for i in range(h): img[i, :, 0] = int(255 * i / h) # 红:从上到下的渐变 img[i, :, 1] = int(255 * (1 - i / h)) # 绿:从下到上的渐变 img[:, :, 2] = np.random.randint(0, 256, (h, w)) # 蓝:随机噪声 print(f"原图形状: {img.shape}") # (200, 300, 3) # ============================================================================= # 2. 手动实现灰度转换(加权平均法) # ============================================================================= # 提取三个通道(二维数组) R = img[:, :, 0].astype(np.float32) # 转float避免uint8溢出 G = img[:, :, 1].astype(np.float32) B = img[:, :, 2].astype(np.float32) # 应用公式:Gray = 0.299R + 0.587G + 0.114B # 权重依据人眼敏感度:绿色最敏感,蓝色最不敏感 gray = 0.299 * R + 0.587 * G + 0.114 * B # 四舍五入并转回uint8 gray = np.round(gray).astype(np.uint8) print(f"灰度图形状: {gray.shape}") # (200, 300) # ============================================================================= # 3. 统计像素值分布,绘制直方图 # ============================================================================= # 方法1:手动统计(纯NumPy) hist_manual = np.zeros(256, dtype=np.int32) for val in range(256): hist_manual[val] = np.sum(gray == val) # 方法2:使用np.bincount(更快) hist_np = np.bincount(gray.flatten(), minlength=256) # 验证两种方法结果相同 print(f"手动统计与np.bincount结果相同: {np.array_equal(hist_manual, hist_np)}") # ============================================================================= # 4. 绘制原图、灰度图、直方图 # ============================================================================= fig, axes = plt.subplots(2, 2, figsize=(14, 10)) fig.suptitle('Grayscale Conversion & Histogram (Pure NumPy)', fontsize=14) # 子图1:原彩色图 axes[0, 0].imshow(img) axes[0, 0].set_title('Original Color Image') axes[0, 0].axis('off') # 子图2:灰度图(需要指定cmap='gray') axes[0, 1].imshow(gray, cmap='gray', vmin=0, vmax=255) axes[0, 1].set_title(f'Grayscale (0.299R+0.587G+0.114B)\nShape: {gray.shape}') axes[0, 1].axis('off') # 子图3:灰度直方图 axes[1, 0].bar(range(256), hist_np, width=1, color='gray', edgecolor='black') axes[1, 0].set_xlim(0, 255) axes[1, 0].set_xlabel('Pixel Value (0-255)') axes[1, 0].set_ylabel('Frequency') axes[1, 0].set_title(f'Grayscale Histogram\nTotal pixels: {gray.size}') axes[1, 0].grid(True, alpha=0.3) # 子图4:归一化验证对比 axes[1, 1].axis('off') # 先关闭,后面单独画 # ============================================================================= # 5. 归一化到0~1,再还原,验证误差 # ============================================================================= # Step 1: 归一化到 0~1 # 方法:除以255 gray_normalized = gray.astype(np.float32) / 255.0 print(f"\n归一化后范围: [{gray_normalized.min():.4f}, {gray_normalized.max():.4f}]") # Step 2: 还原回 0~255 # 方法:乘以255,四舍五入 gray_restored = np.round(gray_normalized * 255).astype(np.uint8) # Step 3: 计算最大误差 # 误差 = |原值 - 还原值| error = np.abs(gray.astype(np.int16) - gray_restored.astype(np.int16)) max_error = np.max(error) mean_error = np.mean(error) print(f"还原后范围: [{gray_restored.min()}, {gray_restored.max()}]") print(f"最大误差: {max_error}") print(f"平均误差: {mean_error:.4f}") # 显示误差分布 error_positions = np.where(error > 0) print(f"有误差的位置数量: {len(error_positions[0])}") # 在子图4显示误差分析 axes[1, 1].hist(error.flatten(), bins=range(0, max_error+2), color='red', edgecolor='black', alpha=0.7) axes[1, 1].set_xlabel('Error (|original - restored|)') axes[1, 1].set_ylabel('Number of Pixels') axes[1, 1].set_title(f'Conversion Error Distribution\nMax Error: {max_error}, Mean: {mean_error:.4f}') axes[1, 1].grid(True, alpha=0.3) plt.tight_layout() plt.savefig('grayscale_analysis.png', dpi=150, bbox_inches='tight') plt.show() # ============================================================================= # 6. 额外:展示有误差的具体像素(调试用) # ============================================================================= if max_error > 0: print("\n部分误差示例(原值, 还原值, 误差):") idx = np.where(error > 0) for i in range(min(5, len(idx[0]))): y, x = idx[0][i], idx[1][i] orig = gray[y, x] rest = gray_restored[y, x] err = error[y, x] print(f" 位置({y},{x}): {orig} → {rest}, 误差={err}")