WinClip非官方复现代码学习笔记12

该篇文章讨论了`visualization.py`中的代码,涉及图像样本与异常分数的可视化,以及特征降维(t-SNE和PCA)在理解数据模式和异常方面的应用。
摘要由CSDN通过智能技术生成

一、程序结构展示

上篇笔记讲解了training_utils.py文件,本篇笔记主要讲解visualization.py文件。

二、代码功能介绍

本篇代码主要用于图像和特征可视化。下面是各个函数的解释:

  1. plot_sample_cv2(names, imgs, scores_: dict, gts, save_folder=None): 这个函数用于绘制图像样本及其对应的真实标签和分数(或其他指标)的热力图。具体步骤包括:

    • 将分数进行归一化处理,以便与图像进行叠加显示。
    • 根据真实标签,在图像上标记出异常区域。
    • 将原始图像、标记的异常区域图像和叠加了热力图的图像保存到指定文件夹中。
  2. plot_anomaly_score_distributions(scores: dict, ground_truths_list, save_folder, class_name): 这个函数用于绘制异常分数的分布直方图。具体步骤包括:

    • 从给定的分数字典中提取分数。
    • 根据真实标签将分数分为正常样本和异常样本。
    • 绘制正常样本和异常样本的分布直方图,并将其保存到指定文件夹中。
  3. visualize_feature(features, labels, legends, n_components=3, method='TSNE'): 这个函数用于可视化特征。具体步骤包括:

    • 根据选择的方法(如 TSNE 或 PCA)将特征降维到 2 维或 3 维。
    • 根据标签对降维后的特征进行散点图可视化。
    • 可选地,将图例添加到图像中。
  4. scatter_3d(feat_proj, label): 这个函数用于绘制 3 维特征的散点图。它会创建一个 3D 散点图,并根据标签对特征进行着色。

  5. scatter_2d(feat_proj, label): 这个函数用于绘制 2 维特征的散点图。它会创建一个 2D 散点图,并根据标签对特征进行着色。

这些函数可以帮助分析图像数据和特征,发现其中的模式和异常,从而更好地理解数据和模型的表现。

三、代码逐行注释

import cv2  # 导入 OpenCV 库,用于图像处理和操作。
import matplotlib  # 导入 matplotlib 库,用于数据可视化,包括绘制图表和图像等

matplotlib.use("Agg")  # 设置 matplotlib 使用的后端为 "Agg",这是一个无界面的后端,用于在不显示图形的情况下保存图像
import matplotlib.pyplot as plt  # 导入 matplotlib 中的 pyplot 模块,提供了类似 MATLAB 风格的绘图函数,方便快速绘制各种图形
import numpy as np  # 导入 NumPy 库,用于数组操作和数学计算
import os  # 导入 os 模块,用于与操作系统进行交互,例如创建文件夹、读取文件等
import seaborn as sns  # 导入 seaborn 库,是 matplotlib 的一个高级接口,提供了更多样式和绘图选项,用于创建更漂亮的统计图表

##
from sklearn.manifold import TSNE  # 从 scikit-learn 库中导入 t-SNE(t-distributed Stochastic Neighbor Embedding)算法,用于降维和可视化高维数据
from sklearn.decomposition import PCA  # 从 scikit-learn 库中导入 PCA(Principal Component Analysis)算法,也用于降维和数据压缩

##
import matplotlib.ticker as mtick  # 导入 matplotlib 中的 ticker 模块,用于设置刻度标签的格式


def plot_sample_cv2(names, imgs, scores_: dict, gts, save_folder=None):  # names: 图像名称的列表。imgs: 图像数据的列表。scores_: 一个字典,包含了每个图像的分数信息。gts: 图像的真实标签。save_folder: 可选参数,指定保存结果图像的文件夹路径
    # get subplot number
    total_number = len(imgs)  # 获取图像数量,即图像列表 imgs 的长度

    scores = scores_.copy()  # 复制分数字典,以便后续对分数进行归一化处理而不影响原始数据
    # normarlisze anomalies
    for k, v in scores.items():  # 遍历分数字典中的每个项目
        max_value = np.max(v)  # 获取当前分数数组中的最大值
        min_value = np.min(v)  #  获取当前分数数组中的最小值

        scores[k] = (scores[k] - min_value) / max_value * 255  # 对当前分数数组进行归一化处理,将分数值缩放到 0 到 255 的范围内
        scores[k] = scores[k].astype(np.uint8)  #  将归一化后的分数数组转换为 np.uint8 数据类型,以便后续图像处理
    # draw gts
    mask_imgs = []  # 初始化一个空列表,用于存储带有标签的图像
    for idx in range(total_number):  # 遍历每张图像的索引
        gts_ = gts[idx]  # 获取当前图像的真实标签
        mask_imgs_ = imgs[idx].copy()  # 复制当前图像的数据,以便后续绘制标签
        mask_imgs_[gts_ > 0.5] = (0, 0, 255)  # 将真实标签值大于 0.5 的像素点设为红色(0, 0, 255)
        mask_imgs.append(mask_imgs_)  # 将带有标签的图像添加到 mask_imgs 列表中

    # save imgs
    for idx in range(total_number):  # 再次遍历每张图像的索引,用于保存图像结果
        cv2.imwrite(os.path.join(save_folder, f'{names[idx]}_ori.jpg'), imgs[idx])  # 将原始图像保存为文件,文件名包含图像名称和 _ori.jpg 后缀
        cv2.imwrite(os.path.join(save_folder, f'{names[idx]}_gt.jpg'), mask_imgs[idx])  # 将带有标签的图像保存为文件,文件名包含图像名称和 _gt.jpg 后缀

        for key in scores:  #  遍历每个分数对应的键
            heat_map = cv2.applyColorMap(scores[key][idx], cv2.COLORMAP_JET)  # 将当前图像的分数数组转换为热力图
            visz_map = cv2.addWeighted(heat_map, 0.5, imgs[idx], 0.5, 0)  # 将热力图与原始图像进行加权叠加
            cv2.imwrite(os.path.join(save_folder, f'{names[idx]}_{key}.jpg'),  # 将叠加后的可视化图像保存为文件,文件名包含图像名称和对应分数的键
                        visz_map)


def plot_anomaly_score_distributions(scores: dict, ground_truths_list, save_folder, class_name):  # 接受参数:scores: 一个字典,包含了不同类别的异常分数;ground_truths_list: 真实标签的列表
    ground_truths = np.stack(ground_truths_list, axis=0)  # 将真实标签列表堆叠成一个 numpy 数组,用于后续计算

    N_COUNT = 100000  # 定义一个常量,表示每个分布样本的数量

    for k, v in scores.items():  # 遍历异常分数字典中的每一项
        layer_score = np.stack(v, axis=0)  # 将当前类别的异常分数列表堆叠成一个 numpy 数组
        normal_score = layer_score[ground_truths == 0]  # 从当前类别的异常分数中提取真实标签为正常的分数
        abnormal_score = layer_score[ground_truths != 0]  # 从当前类别的异常分数中提取真实标签为异常的分数

        plt.clf()  # 清空当前图形
        plt.figure(figsize=(4, 3))  # 创建一个新的图形,指定图形大小为 4x3
        ax = plt.gca()  # 获取当前图形的坐标轴
        ax.yaxis.set_major_formatter(mtick.FormatStrFormatter('%.2f'))  # 设置坐标轴的刻度格式为保留两位小数
        ax.xaxis.set_major_formatter(mtick.FormatStrFormatter('%.2f'))

        # with plt.style.context(['science', 'ieee', 'no-latex']):
        sns.histplot(np.random.choice(normal_score, N_COUNT), color="green", bins=50, label='${d(p_n)}$',
                     stat='probability', alpha=.75)  # 绘制正常分数的直方图,并指定颜色、分组数、标签等参数
        sns.histplot(np.random.choice(abnormal_score, N_COUNT), color="red", bins=50, label='${d(p_a)}$',
                     stat='probability', alpha=.75)  # 绘制异常分数的直方图

        plt.xlim([0, 3])  # 设置 x 轴的范围为 0 到 3

        save_path = os.path.join(save_folder, f'distributions_{class_name}_{k}.jpg')  # 构建保存结果图像的文件路径,文件名包含类别名称和分数键

        plt.savefig(save_path, bbox_inches='tight', dpi=300)  # 将当前图形保存为图像文件


valid_feature_visualization_methods = ['TSNE', 'PCA']  # 定义了一个列表,包含了有效的特征可视化方法,即 t-SNE 和 PCA


def visualize_feature(features, labels, legends, n_components=3, method='TSNE'):  # 接受参数为features: 特征数据,即待可视化的数据;labels: 数据对应的标签;legends: 图例;n_components: 降维后的维度,默认为 3;method: 使用的降维方法,默认为 t-SNE
    assert method in valid_feature_visualization_methods  # 断言降维方法是否在有效的可视化方法列表中
    assert n_components in [2, 3]  # 断言降维后的维度是否为 2 或 3

    if method == 'TSNE':  # 根据指定的方法选择降维模型,若方法为 t-SNE,则使用 t-SNE 模型;若方法为 PCA,则使用 PCA 模型
        model = TSNE(n_components=n_components)  # 创建相应的 t-SNE 或 PCA 模型对象,并指定降维后的维度
    elif method == 'PCA':
        model = PCA(n_components=n_components)

    else:
        raise NotImplementedError

    feat_proj = model.fit_transform(features)  # 使用特征数据拟合降维模型,并进行降维操作,得到降维后的特征表示

    if n_components == 2:
        ax = scatter_2d(feat_proj, labels)  # 调用 scatter_2d 函数绘制二维散点图,传入降维后的特征表示和对应的标签
    elif n_components == 3:
        ax = scatter_3d(feat_proj, labels)  # 调用 scatter_3d 函数绘制三维散点图,传入降维后的特征表示和对应的标签
    else:
        raise NotImplementedError

    plt.legend(legends)  # 添加图例
    plt.axis('off')  # 关闭坐标轴


def scatter_3d(feat_proj, label):  # 定义了一个名为 scatter_3d 的函数,接受特征投影和标签作为参数
    plt.clf()  # 清空当前图形
    ax1 = plt.axes(projection='3d')  # 创建一个三维坐标轴

    label_unique = np.unique(label)  # 获取标签中的唯一值

    for l in label_unique:  # 遍历标签中的每一个唯一值
        ax1.scatter3D(feat_proj[label == l, 0],
                      feat_proj[label == l, 1],
                      feat_proj[label == l, 2], s=5)  # 绘制三维散点图,其中 feat_proj[label == l, 0] 表示特征投影中标签为 l 的样本在第一个特征维度上的值,feat_proj[label == l, 1] 和 feat_proj[label == l, 2] 分别表示第二个和第三个特征维度上的值,s=5 表示散点的大小为 5

    return ax1


def scatter_2d(feat_proj, label):  # 定义了一个名为 scatter_2d 的函数,与 scatter_3d 类似,用于绘制二维散点图
    plt.clf()  # 清空当前图形
    ax1 = plt.axes()  # 创建一个二维坐标轴

    label_unique = np.unique(label)  # 获取标签中的唯一值

    for l in label_unique:  # 遍历标签中的每一个唯一值
        ax1.scatter(feat_proj[label == l, 0],
                    feat_proj[label == l, 1], s=5)  # 绘制二维散点图,其中 feat_proj[label == l, 0] 表示特征投影中标签为 l 的样本在第一个特征维度上的值,feat_proj[label == l, 1] 表示第二个特征维度上的值,s=5 表示散点的大小为 5

    return ax1

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值