NumPy 矩阵操作 + 图像处理

涵盖 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

    python 复制代码
    import 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通道

    python 复制代码
    import 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 排列展示

    python 复制代码
    import 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,验证两次转换前后的最大误差

    python 复制代码
    import 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}")
相关推荐
超级学长4 小时前
Real-ESRGAN:用纯合成数据训练真实世界盲超分辨率模型
图像处理·深度学习·图像超分辨·超分辨
SJLoveIT4 小时前
手写transformer中自注意力机制,并解释每个矩阵及其运算的含义
深度学习·矩阵·transformer
Sagittarius_A*4 小时前
霍夫变换:几何特征检测与量化验证【计算机视觉】
图像处理·人工智能·opencv·算法·计算机视觉·霍夫变换
Dfreedom.4 小时前
从像素到智能:图像处理与计算机视觉全景解析
图像处理·人工智能·计算机视觉·视觉智能
Tisfy5 小时前
LeetCode 1878.矩阵中最大的三个菱形和:斜向前缀和
算法·leetcode·矩阵
张李浩13 小时前
Leetcode 054螺旋矩阵 采用方向数组解决
算法·leetcode·矩阵
李昊哲小课1 天前
NumPy 完整学习笔记
笔记·python·学习·数据分析·numpy
嵌入式-老费1 天前
Linux camera驱动开发(vivado hls不能导出ip的问题)
图像处理·fpga开发
sali-tec1 天前
C# 基于OpenCv的视觉工作流-章36-骨架提取
图像处理·人工智能·opencv·算法·计算机视觉