目录
前言
Datawhale 2024.10 组队学习来了!这次选择的是动手实践专区——CV方向——“全球AI攻防挑战赛—赛道二:AI核身之金融场景凭证篡改检测”。
Baseline代码解读
1、读取数据集
!apt update > /dev/null;aptinstall aria2 git-lfs axel -y > /dev/null
!pip installultralytics==8.2.0 numpy pandas opencv-python Pillow matplotlib > /dev/null
!axel -n 12 -a http://mirror.coggle.club/seg_risky_testing_data.zip;unzip -q seg_risky_testing_data.zip
!axel -n 12 -a http://mirror.coggle.club/seg_risky_training_data_00.zip;unzip -q seg_risky_training_data_00.zip
(1)
!apt update > /dev/null; apt install aria2 git-lfs axel -y > /dev/null
!
: 告诉 Jupyter Notebook (或 Google Colab),运行的是 apt update 这个shell 命令,而不是 Python 代码
apt update
:更新Ubuntu的包管理器APT的包列表
-y
:自动同意安装提示,省去手动确认。
> /dev/null
:将输出重定向到/dev/null,相当于忽略输出日志。
安装用于下载文件的工具:
aria2:支持多源下载
git-lfs:用于处理大文件
axel:一个多线程下载工具
(2)
!pip install ultralytics==8.2.0 numpy pandas opencv-python Pillow matplotlib > /dev/null
安装一些Python库:
ultralytics==8.2.0:一个开源的YOLOv8框架,用于目标检测、分割等任务。
numpy:用于数值计算的库。
pandas:用于数据处理和分析的库。
opencv-python:用于图像处理的库。
Pillow:用于图像处理的Python库。
matplotlib:用于绘制图形的库。
(3)
!axel -n 12 -a http://mirror.coggle.club/seg_risky_testing_data.zip; unzip -q seg_risky_testing_data.zip
使用axel以12个线程并行下载名为seg_risky_testing_data.zip的文件。下载完成后,使用unzip命令解压该压缩文件。
-n 12
:指定12个线程进行下载
-a
:显示下载进度
-q
:以安静模式解压(即不显示解压过程的详细信息)
(4)
!axel -n 12 -a http://mirror.coggle.club/seg_risky_training_data_00.zip; unzip -q seg_risky_training_data_00.zip
与(3)类似,使用axel以12个线程下载名为seg_risky_training_data_00.zip的文件,并解压。
import os, shutil
import cv2
import glob
import json
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
training_anno = pd.read_csv('http://mirror.coggle.club/seg_risky_training_anno.csv')
train_jpgs =[x.replace('./', '')forxin glob.glob('./0/*.jpg')]
training_anno = training_anno[training_anno['Path'].isin(train_jpgs)]
training_anno['Polygons']= training_anno['Polygons'].apply(json.loads)
training_anno.head()
(1)
import *
导入库
os 和 shutil:用于文件和目录操作。
cv2:OpenCV库,用于计算机视觉任务(如图像处理)。
glob:用于查找符合特定规则的文件路径名。
json:用于解析和处理JSON格式的数据。
pandas:用于数据处理和分析。
numpy:用于数值计算,提供高效的数组操作。
matplotlib.pyplot:用于绘制图形和可视化数据。
(2)
training_anno = pd.read_csv('http://mirror.coggle.club/seg_risky_training_anno.csv')
加载篡改后的凭证图像的位置标注,标注文件以csv格式给出(seg_risky_training_anno.csv),csv文件中包括两列,Path列内容为篡改凭证图像的名称,Polygons列内容采用轮廓点的方式存储每个篡改区域的位置;
使用 pandas 的** read_csv** 函数从指定的 URL 加载 CSV 文件,创建一个 DataFrame,名为training_anno。
(3)
train_jpgs = [x.replace('./', '') for x in glob.glob('./0/*.jpg')]
获取训练图像文件列表:glob.glob(‘./0/*.jpg’) 会返回 ./0 目录下所有 JPG 文件的路径。
列表推导式将这些路径中的 ‘./’ 替换为空字符串,生成 train_jpgs 列表,包含相对路径的 JPG 文件名。
(4)
training_anno = training_anno[training_anno['Path'].isin(train_jpgs)]
条件过滤,保留 training_anno 中仅与训练图像相对应的行。
isin(train_jpgs)
:检查 training_anno 中的 ‘Path’ 列是否在 train_jpgs 列表中。
(5)
training_anno['Polygons'] = training_anno['Polygons'].apply(json.loads)
解析 ‘Polygons’ 列
apply()
: 是 pandas DataFrame 或 Series 的方法,它允许对列中的每个元素应用一个函数。这里对 Polygons 列的每个元素应用了 json.loads 函数。
json.loads
:将 training_anno 中的 ‘Polygons’ 列应用 json.loads 函数,以将存储为字符串格式的 JSON 数据(字符串,表示存储Polygons坐标的JSON对象)解析为 Python 对象(如字典或列表),为后续的图像处理或模型训练做准备。假如 Polygons 列的某一单元格是 “[ [10, 20], [30, 40], [50, 60] ]”,它会将其转换为 Python 列表:[[10, 20], [30, 40], [50, 60]]。
training_anno.shape
(63785, 2)
打印 training_anno DataFrame 的形状👆
np.array(training_anno['Polygons'].iloc[4], dtype=np.int32)
将 training_anno DataFrame 中第 5 行(索引为 4)的 Polygons 列的数据转换为一个 NumPy 数组,并指定数组的数据类型为 np.int32(32位整数),作用是将多边形的顶点坐标(例如,图像分割任务中的标注)转化为 NumPy 数组,以便进行进一步的处理、计算或用于模型训练。👆
training_anno['Polygons']
:选择 training_anno DataFrame 中名为 ‘Polygons’ 的列,该列包含多边形的标注数据,通常是一个表示多边形顶点坐标的列表或数组。
.iloc[4]
:使用 iloc 方法按位置索引选择 DataFrame 的第 5 行(因为索引从 0 开始,4 表示第 5 行)。这行数据的 Polygons 列可能包含多边形的顶点坐标。
np.array(..., dtype=np.int32)
:将选择的数据转换为 NumPy 数组,并指定其数据类型为 np.int32。这是为了确保数组中的元素都是 32 位整数,这在处理数值计算时可能有助于节省内存或确保兼容性。
idx =23
img = cv2.imread(training_anno['Path'].iloc[idx])
plt.figure(figsize=(12, 6))
plt.subplot(121)
plt.imshow(img)
plt.title("Original Image")
plt.axis('off')
plt.subplot(122)
img = cv2.imread(training_anno['Path'].iloc[idx])
polygon_coords = np.array(training_anno['Polygons'].iloc[idx], dtype=np.int32)forpolygon_coordin polygon_coords:
cv2.polylines(img, np.expand_dims(polygon_coord, 0), isClosed=True, color=(0, 255, 0), thickness=2)img= cv2.fillPoly(img, np.expand_dims(polygon_coord, 0), color=(255, 0, 0, 0.5))
plt.imshow(img)
plt.title("Image with Polygons")
plt.axis('off')
(1)读取图像
idx = 23
:设置一个索引 idx,指向你要处理的图像的行号。
img =cv2.imread(training_anno['Path'].iloc[idx])
:使用 OpenCV 的 imread 函数读取 training_anno DataFrame 中第 24 行(索引为 23)对应路径的图像。
(2)设置绘图
plt.figure(figsize=(12, 6))
:设置图形的大小为 12x6 英寸。
(3)绘制原始图像
plt.subplot(121)
:创建一个 1 行 2 列的子图,当前选择第 1 个子图。
plt.imshow(img)
:显示原始图像。
plt.title("Original Image")
:设置当前子图的标题。
plt.axis('off')
:关闭坐标轴显示。
(4)读取并绘制多边形
plt.subplot(122)
:选择第 2 个子图。
img = cv2.imread(training_anno['Path'].iloc[idx])
:再次读取图像,以确保与绘制多边形时使用的图像是同一张(如果进行了修改,确保是原始图像)。
polygon_coords = np.array(training_anno['Polygons'].iloc[idx], dtype=np.int32)
:将第 24 行的多边形坐标解析为 NumPy 数组。
(5)绘制多边形
for polygon_coord in polygon_coords:
:遍历每个多边形坐标polygon_coord。
cv2.polylines
:在图像上绘制多边形的边界。np.expand_dims(polygon_coord, 0) 用于在 NumPy 数组中插入一个新的维度,这里的 0 表示在第一个轴(即行的前面)插入一个新维度,以符合 OpenCV 绘制多边形函数的输入要求。isClosed=True 表示将边界闭合,color=(0, 255, 0) 表示多边形边界为绿色,thickness=2 表示边界的线宽。
cv2.fillPoly
:将多边形内部填充为红色(RGB (255, 0, 0)),并设置透明度为 0.5。
(6)显示带多边形的图像
plt.imshow(img)
:显示带有多边形标注的图像。
plt.title("Image with Polygons")
:设置当前子图的标题。
plt.axis('off')
:关闭坐标轴显示。
2、构建YOLO模型
training_anno.info()
training_anno.info() 是 pandas 中用来获取 training_anno DataFrame 的简要信息的函数。它会输出数据框的行数、列数、每列的数据类型、非空值数量等信息
if os.path.exists('yolo_seg_dataset'):
shutil.rmtree('yolo_seg_dataset')
os.makedirs('yolo_seg_dataset/train')
os.makedirs('yolo_seg_dataset/valid')
if os.path.exists('yolo_seg_dataset')
:
判断名为 ‘yolo_seg_dataset’ 的文件夹或文件是否存;如果存在,则执行下面的代码;
shutil.rmtree('yolo_seg_dataset')
删除名为 ‘yolo_seg_dataset’ 的文件夹及其所有内容(包括文件和子文件夹)。**shutil.rmtree()**函数可以递归地删除整个文件夹树。
os.makedirs('yolo_seg_dataset/train')
:创建 ‘yolo_seg_dataset’ 文件夹及其子文件夹 ‘train’。如果父文件夹 ‘yolo_seg_dataset’ 不存在,会自动创建它。
def normalize_polygon(polygon, img_width, img_height):
return[(x / img_width, y / img_height)for x, y in polygon]forrowin training_anno.iloc[:10000].iterrows():
shutil.copy(row[1].Path, 'yolo_seg_dataset/train')
img = cv2.imread(row[1].Path)
img_height, img_width = img.shape[:2]
txt_filename = os.path.join('yolo_seg_dataset/train/' + row[1].Path.split('/')[-1][:-4] + '.txt')
with open(txt_filename, 'w') as up:
forpolygonin row[1].Polygons:
normalized_polygon = normalize_polygon(polygon, img_width, img_height)
normalized_coords =' '.join([f'{coord[0]:.3f} {coord[1]:.3f}'forcoordin normalized_polygon])
up.write(f'0 {normalized_coords}\n')
(1)normalize_polygon:该函数将多边形中的每个点的 x 坐标除以图像的宽度,y 坐标除以图像的高度,使坐标值转换到 [0, 1] 范围内。
polygon
:表示多边形的顶点坐标(一个坐标点的列表,格式如 [(x1, y1), (x2, y2), …])。
img_width, img_height
:图像的宽度和高度,用于将坐标归一化。
(2)遍历训练集数据并进行操作
training_anno.iloc[:10000].iterrows()
:遍历 training_anno 数据框中的前 10,000 行数据,每次迭代返回一行数据。row[1] 是当前行的实际数据。
(3)复制图像
shutil.copy
:将当前行对应的图像文件从源路径 row[1].Path 复制到 ‘yolo_seg_dataset/train’ 文件夹中。
(4)读取图像并获取尺寸
cv2.imread
:使用 OpenCV 读取图像文件。
img.shape[:2]
:返回图像的高度和宽度。
(5)创建并写入归一化坐标文件
txt_filename
:构建要保存的 .txt 文件的路径,该文件用于存储归一化后的多边形坐标。通过 Path.split(‘/’)[-1][:-4] 从图像路径中提取文件名(去掉扩展名),然后生成相应的 .txt 文件名。
open(txt_filename, 'w')
:以写入模式打开该 .txt 文件。
(6)归一化多边形坐标并保存
for polygon in row[1].Polygons
:遍历当前图像的所有多边形。
normalize_polygon
:将每个多边形的顶点坐标归一化。
normalized_coords
:将归一化后的坐标转换为字符串,每个坐标保留三位小数。
up.write(f'0 {normalized_coords}\n')
:将归一化的坐标写入 .txt 文件。0 表示类别标签(通常代表某一类对象)。
总结:将图像和对应的多边形标注复制并归一化处理,归一化后的坐标和类别标签被保存到 .txt 文件中,便于后续进行目标检测或分割任务。
forrowin training_anno.iloc[10000:10150].iterrows():
shutil.copy(row[1].Path, 'yolo_seg_dataset/valid')
img = cv2.imread(row[1].Path)
img_height, img_width = img.shape[:2]
txt_filename = os.path.join('yolo_seg_dataset/valid/' + row[1].Path.split('/')[-1][:-4] + '.txt')
with open(txt_filename, 'w') as up:
forpolygonin row[1].Polygons:
normalized_polygon = normalize_polygon(polygon, img_width, img_height)
normalized_coords =' '.join([f'{coord[0]:.3f} {coord[1]:.3f}'forcoordin normalized_polygon])
up.write(f'0 {normalized_coords}\n')
(1)遍历验证集数据
training_anno.iloc[10000:10150].iterrows()
:遍历 training_anno 数据框的第 10,000 到第 10,150 行(总共 150 行数据),每次迭代返回一行数据,row[1] 是当前行的数据
(2)复制图像文件到验证集文件夹
shutil.copy
:将当前行对应的图像文件从源路径 row[1].Path 复制到 ‘yolo_seg_dataset/valid’ 文件夹中。
(3)读取图像并获取尺寸
cv2.imread
:读取图像文件并获取图像的宽度和高度。
(4)构建并写入归一化坐标文件
txt_filename
:创建相应的 .txt 文件路径。将原图像文件名(去除扩展名)用于 .txt 文件名,保存到 ‘yolo_seg_dataset/valid’ 文件夹中。
open(txt_filename, 'w')
:以写入模式打开 .txt 文件。
(5)归一化多边形坐标并保存
for polygon in row[1].Polygons
:遍历图像中所有的多边形。
normalize_polygon
:将每个多边形的顶点坐标归一化,坐标值被缩放到 [0, 1] 之间。
normalized_coords
:将归一化的多边形坐标转换为字符串,每个坐标保留三位小数。
up.write(f'0 {normalized_coords}\n')
:将归一化的坐标和类别标签 0 写入 .txt 文件中。
总结这段代码的目的是将 training_anno 数据框中第 10,000 行到第 10,150 行的图像文件及其多边形标注数据,复制到验证集目录 ‘yolo_seg_dataset/valid’ 中,并将每个多边形的坐标归一化保存为 .txt 文件。这些数据可以用于验证模型的性能
with open('yolo_seg_dataset/data.yaml', 'w') as up:
data_root = os.path.abspath('yolo_seg_dataset/')
up.write(f'''
path: {data_root}
train: train
val: valid
names:
0: alter
''')
(1)打开并创建 data.yaml 文件
open('yolo_seg_dataset/data.yaml', 'w')
:在 ‘yolo_seg_dataset/’ 文件夹中创建或打开一个名为
data.yaml
的文件,并以写入模式 (
'w'
) 打开。
as up
:将文件对象赋值给变量
up
,可以通过
up
来操作该文件。
(2)获取数据集的绝对路径
os.path.abspath('yolo_seg_dataset/')
:获取 ‘yolo_seg_dataset/’ 文件夹的绝对路径,存储在变量 data_root 中。这样做的目的是确保在使用相对路径时避免错误,使用绝对路径可以确保路径的一致性
(3)写入配置信息
up.write(f''' ... ''')
:通过文件对象 up 将配置内容写入到 data.yaml 文件中。使用 f 字符串插入变量 data_root。
配置文件内容解释:
path: {data_root}
:这是数据集的根路径。使用绝对路径 data_root 作为数据集根目录,方便后续训练过程中引用数据。
train: train
训练集所在文件夹的路径,相对于 data_root。在这里,训练集位于 ‘yolo_seg_dataset/train’。
val: valid
:验证集所在文件夹的路径,相对于 data_root。验证集位于 ‘yolo_seg_dataset/valid’。
names: 0: alter
:这是类别名称的定义。在目标检测或图像分割中,names 定义了类别编号及其对应的名称。
0: alter:表示类别 0 对应的名称是 alter(可以是任何自定义的类别名称,具体根据任务的需求设置)。
总结:这段代码生成了一个用于 YOLO 模型训练的数据集配置文件 data.yaml,其中包括数据集的路径、训练集和验证集的文件夹信息,以及类别名称的定义。这个配置文件在训练模型时会被用来指定数据集和类别标签。
!mkdir -p /root/.config/Ultralytics/
!wget http://mirror.coggle.club/yolo/Arial.ttf -O /root/.config/Ultralytics/Arial.ttf
!wget http://mirror.coggle.club/yolo/yolov8n-v8.2.0.pt -O yolov8n.pt
!wget http://mirror.coggle.club/yolo/yolov8n-seg-v8.2.0.pt -O yolov8n-seg.pt
(1)创建配置文件目录
mkdir -p
:创建目录。
/root/.config/Ultralytics/
:这是将要创建的目录路径。它会创建一个名为 Ultralytics 的文件夹,位于 /root/.config/ 路径下。
-p
选项表示,如果中间的目录(如 /root/.config)不存在,它会自动创建这些父目录。该目录通常是 Ultralytics(YOLO 系列模型的库)使用的配置文件保存路径。
(2)下载字体文件
wget
:这是一个从网络上下载文件的命令。
http://mirror.coggle.club/yolo/Arial.ttf
:这是下载文件的 URL,链接到一个字体文件 Arial.ttf。
-O /root/.config/Ultralytics/Arial.ttf
:指定将下载的文件保存到 /root/.config/Ultralytics/ 目录下,并命名为 Arial.ttf。
该字体文件可能用于在训练过程中对图像上的检测结果进行标注和显示。
(3)下载 YOLOv8n 模型权重文件
wget
:用于下载文件。
http://mirror.coggle.club/yolo/yolov8n-v8.2.0.pt
:这是链接到一个 YOLOv8n 模型的预训练权重文件。
-O yolov8n.pt
:将下载的文件保存为 yolov8n.pt,这是
YOLOv8n(YOLOv8 nano)
的权重文件,通常用于目标检测任务。
(4)下载 YOLOv8n-seg 模型权重文件
wget
:用于下载文件。
http://mirror.coggle.club/yolo/yolov8n-seg-v8.2.0.pt
:链接到一个 YOLOv8n-seg(YOLOv8 nano 分割模型)的预训练权重文件。
-O yolov8n-seg.pt
:将文件保存为 yolov8n-seg.pt,这是 YOLOv8n 图像分割任务使用的权重文件。
总结这段代码使用了一些 shell 命令来创建目录,并下载文件到指定的路径,为后续使用 Ultralytics YOLOv8 模型(检测和分割任务)做准备。
3、训练模型
from ultralytics import YOLO
model = YOLO("./yolov8n-seg.pt")
results = model.train(data="./yolo_seg_dataset/data.yaml", epochs=10, imgsz=640)
(1)导入 YOLO 模块
from ultralytics import YOLO
:从 ultralytics 库中导入 YOLO 模型类。这个类提供了 YOLOv8 模型的加载和训练等功能。
(2)加载 YOLOv8 分割模型
YOLO("./yolov8n-seg.pt")
:实例化一个 YOLO 模型对象,并加载 yolov8n-seg.pt 预训练权重文件。
"./yolov8n-seg.pt"
是之前下载的 YOLOv8n 分割模型权重文件,YOLOv8n 表示这个模型是 YOLOv8 的 nano 版本,体积小,适合于轻量级任务。
(3)训练模型
model.train()
:调用 YOLO 模型的 train 方法来进行训练。
data="./yolo_seg_dataset/data.yaml"
:指定数据集的配置文件路径,这个配置文件描述了训练集和验证集的位置(文件夹),以及类别信息。你之前生成了这个 data.yaml 文件。
epochs=10
:设置训练的迭代次数,即模型会在整个数据集上训练 10 次。
imgsz=640
:设置图像的输入尺寸。YOLOv8 模型将调整输入图像的大小为 640×640 像素。
results
:训练的结果会存储在 results 变量中,包括损失值、精度、召回率等评估指标。这些信息可以用来跟踪模型训练的进展。
总结:这段代码(加载模型过程)使用了 Ultralytics YOLOv8 库来加载预训练的 YOLOv8 分割模型,可以在一个轻量级的模型上进行训练;(训练过程)模型使用你定义的数据集 yolo_seg_dataset/data.yaml 进行训练,训练 10 个 epochs,每个输入图像被调整为 640×640 像素大小;(结果输出)训练的结果会保存在 results 中,可以用来进一步分析模型的表现。
4、预测测试集
from ultralytics import YOLO
import glob
from tqdm import tqdm
model = YOLO("./runs/segment/train/weights/best.pt")
test_imgs = glob.glob('./test_set_A_rename/*/*')
(1)导入YOLO模块和其他库
from ultralytics import YOLO
:从 ultralytics 库中导入 YOLO 类,用于加载和使用 YOLO 模型。
import glob
:导入 glob 模块,用于查找符合特定规则的文件路径名。
from tqdm import tqdm
:导入 tqdm 库,用于显示进度条,方便监控处理过程
(2)加载训练好的模型
YOLO("./runs/segment/train/weights/best.pt")
:实例化一个 YOLO 模型对象,并加载在之前训练过程中保存的最佳权重文件 best.pt。
best.pt
通常是训练过程中性能最佳的模型权重。
(3)准备测试图像列表
glob.glob('./test_set_A_rename/*/*')
:使用 glob 模块查找符合路径模式的所有文件。
'./test_set_A_rename/*/*'
表示匹配 test_set_A_rename 目录下的所有子目录和子目录中的所有文件。
这将生成一个包含所有测试图像路径的列表,存储在 test_imgs 变量中。
总结:这段代码的作用是加载一个训练好的 YOLOv8 分割模型,并准备测试图像进行预测。(导入所需库)导入 YOLO 模型、文件路径查找模块 glob 和进度条显示模块 tqdm。(加载模型):实例化 YOLO 模型对象并加载之前训练得到的最佳模型权重文件,以便后续进行图像预测。(准备测试图像):使用 glob 查找所有测试图像文件的路径,并将其存储在 test_imgs 列表中,以便进行后续处理(如预测)。
接下来,通常会进行模型预测操作,处理 test_imgs 中的图像数据。
Polygon =[]forpathin tqdm(test_imgs[:10000]):
results = model(path, verbose=False)
result = results[0]if result.masks is None:
Polygon.append([])
else:
Polygon.append([mask.astype(int).tolist()formaskin result.masks.xy])
(1)初始化多边形列表
Polygon = []
:初始化一个空列表 Polygon,用于存储每张测试图像的分割结果(多边形坐标)。
(2)遍历测试图像并进行推理
for path in tqdm(test_imgs[:10000])
:遍历 test_imgs 列表中的前 10000 个图像路径。
tqdm(...)
:将迭代器包裹在 tqdm 中,显示进度条,以便跟踪处理进度。
(3)对每张图像进行推理
results = model(path, verbose=False)
:调用 YOLO 模型对当前图像路径 path 进行推理,返回结果 results。
verbose=False
:设置为 False,表示不打印详细的推理信息。
(4) 处理推理结果
result = results[0]
:获取推理结果的第一个(通常是唯一的)结果,存储在 result 变量中。
(5)提取多边形坐标
if result.masks is None:
:检查 result 中是否存在 masks 属性。如果没有分割结果(即没有检测到任何对象),则执行以下操作:
Polygon.append([])
:在 Polygon 列表中添加一个空列表,表示该图像没有分割结果。
else:
:如果存在分割结果,则执行以下操作:
Polygon.append([mask.astype(int).tolist() for mask in result.masks.xy])
:
result.masks.xy
:这是分割结果的坐标,通常是以浮点数表示的多边形(mask)顶点的坐标。
mask.astype(int).tolist()
:将每个坐标数组转换为整数,并转化为 Python 列表。
[mask.astype(int).tolist() for mask in result.masks.xy]
:对每个 mask 的坐标进行处理,最终得到一个包含所有 mask 坐标的列表,然后将其添加到 Polygon 列表中。
总结主要目的是使用训练好的 YOLO 模型对测试图像进行推理,并提取每个图像中的多边形(mask)坐标初始化列表:创建一个空列表 Polygon 用于存储每张图像的分割结果。
遍历测试图像:使用 tqdm 显示处理进度,遍历前 10000 张测试图像路径。
模型推理:对每张图像调用 YOLO 模型进行推理,获取分割结果。
提取多边形坐标:
如果没有检测到任何分割结果,则在 Polygon 中添加空列表。
如果有检测到的分割结果,则提取所有 mask 的坐标,将其转换为整数列表并添加到 Polygon 列表中。
最终,Polygon 列表将包含每张图像的分割结果(如果有),并以列表的形式存储多边形的坐标
import pandas as pd
submit = pd.DataFrame({'Path':[x.split('/')[-1]forxin test_imgs[:10000]],
'Polygon': Polygon
})
(1)导入 Pandas 库
import pandas as pd
:导入 Pandas 库,并使用别名 pd,以便后续使用。
(2)创建 DataFrame
pd.DataFrame({...})
:创建一个新的 DataFrame,作为 submit 变量,包含两个列:
'Path'
:包含每个图像的文件名。
[x.split('/')[-1] for x in test_imgs[:10000]]
:这是一个列表推导式,用于生成文件名列表。
x.split('/')[-1]
:对于每个图像路径 x,使用 split(‘/’) 方法分割路径字符串,并取最后一个元素(即文件名),因此只保留文件名部分。
test_imgs[:10000]
:表示只处理前 10000 张测试图像。
'Polygon'
:包含与每张图像对应的多边形坐标。
Polygon
:这是前面提取的多边形坐标列表,每个元素对应于一张图像的分割结果。
总结:这段代码的目的是创建一个 Pandas DataFrame,用于保存推理结果,包含每张测试图像的文件名和对应的多边形(mask)坐标。导入 Pandas:导入 Pandas 库以便使用 DataFrame 数据结构。
创建 DataFrame:构建一个名为 submit 的 DataFrame,包含:
Path 列,存储每张图像的文件名。
Polygon 列,存储与每张图像对应的多边形坐标。
最终,submit DataFrame 将包含 10000 行数据,每行对应一个测试图像的文件名及其分割结果(多边形坐标)。这通常用于后续的结果保存或提交。
submit = pd.merge(submit, pd.DataFrame({'Path':[x.split('/')[-1]forxin test_imgs[:]]}), on='Path', how='right')
(1) 创建新的 DataFrame
pd.DataFrame({...})
:创建一个新的 DataFrame,用于包含所有测试图像的文件名。
'Path'
: [x.split(‘/’)[-1] for x in test_imgs[:]]:
这是一个列表推导式,类似于之前创建的 submit DataFrame 中的 Path 列。
test_imgs[:]
:表示处理所有的测试图像路径,而不是仅限于前 10000 张。
每个图像路径 x 通过 split(‘/’) 方法提取最后一个部分(文件名)。
(2) 合并 DataFrame
pd.merge(...)
:使用 Pandas 的 merge 函数将两个 DataFrame 合并。
submit
:第一个 DataFrame,包含之前的结果(即图像文件名及对应的多边形坐标)。
...
:第二个 DataFrame 是刚刚创建的包含所有图像文件名的新 DataFrame。
on='Path'
:指定以 ‘Path’ 列为合并的键。
how='right'
:指定合并的方式为右连接(right join),这意味着合并的结果将包括右边 DataFrame 中的所有行,且与左边 DataFrame 中的匹配行,如果没有匹配则相应的值将为 NaN。
总结:这段代码的目的是将之前创建的 submit DataFrame 与一个新的 DataFrame 进行合并,确保在合并后包含所有测试图像的文件名,尽管这些图像可能没有相应的多边形坐标。创建新的 DataFrame:生成一个包含所有测试图像文件名的 DataFrame。
合并 DataFrame:将 submit DataFrame 与新创建的 DataFrame 合并,以确保 submit 包含所有测试图像的文件名,即使某些图像没有对应的多边形坐标。
结果中的 Polygon 列将包含原 submit 中的多边形坐标,未匹配的图像将显示为 NaN。
合并后的 submit DataFrame 将包含所有测试图像的文件名和对应的多边形坐标,这样可以确保每个图像都有一个条目,即使没有检测到多边形。
submit = submit.fillna('[]')
fillna('[]')
:使用 Pandas 的 fillna 方法,将 DataFrame 中的所有 NaN 值替换为指定的值,这里是 ‘[]’。
NaN
:在 Pandas 中,缺失值通常用 NaN 表示。这可能是由于合并操作中某些图像没有找到相应的多边形坐标(即原 submit DataFrame 中的某些行在合并后没有匹配的记录)。
'[]'
:表示一个空的 JSON 数组字符串,通常用于表示没有多边形坐标的情况。
submit.to_csv('track2_submit.csv', index=None)
submit.to_csv(...)
:这是 Pandas DataFrame 提供的一个方法,用于将 DataFrame 导出为 CSV 格式的文件。
'track2_submit.csv'
:指定要保存的文件名。在当前工作目录下,将生成一个名为 track2_submit.csv 的文件。
index=None
:设置为 None,表示在保存时不包含行索引。默认情况下,to_csv 方法会将 DataFrame 的行索引也写入 CSV 文件。如果设置为 False,将不写入索引,使得输出的 CSV 文件只包含数据列,而不包含索引列。
5、优化
(1)测试集数量:由原来
[:10000]
改为
:
(2)训练集样本数:由原来的训练集、验证集
[:10000]、[10000:10150]
改为
[10000:]、[:10000]
(3)模型预测(修改为与官方一致的格式):
Baseline:
Polygon =[]forpathin tqdm(test_imgs[:]):
results = model(path, verbose=False)
result = results[0]if result.masks is None:
Polygon.append([])
else:
Polygon.append([mask.astype(int).tolist()formaskin result.masks.xy])
优化:
Polygon =[]forpathin tqdm(test_imgs[:]):
results = model(path, verbose=False)
result = results[0]if result.masks is None:
Polygon.append([])
else:
#Polygon.append([mask.astype(int).tolist() for mask in result.masks.xy])
image_polygons =[]formaskin result.masks.xy:
mask_coords = np.array(mask)
x_min = np.min(mask_coords[:, 0])
x_max = np.max(mask_coords[:, 0])
y_min = np.min(mask_coords[:, 1])
y_max = np.max(mask_coords[:, 1])
bounding_box =[[x_min, y_min],
[x_min, y_max],
[x_max, y_max],
[x_max, y_min]]
image_polygons.append(bounding_box)
Polygon.append(image_polygons)
image_polygons = []
: 为每张图像初始化一个空列表,用于存储所有 mask 的边界框。
遍历result.masks.xy:
对于每个 mask,转换为 NumPy 数组:
mask_coords = np.array(mask)
。
计算最小和最大 x, y 坐标:
x_min, x_max
: mask 的 x 坐标的最小和最大值。
y_min, y_max
: mask 的 y 坐标的最小和最大值。
定义边界框:创建一个包含四个点的边界框:
bounding_box
,这些点表示矩形的四个角。
将边界框添加到·image_polygons·列表:
image_polygons.append(bounding_box)
:为每个 mask 添加计算出的边界框。
将所有边界框添加到Polygon列表:
Polygon.append(image_polygons)
:将当前图像的所有边界框添加到 Polygon 列表中。
(4)模型:改用yolov8n-seg、yolov8s-seg、yolov8m-seg、yolov8l-seg和yolov8x-seg,模型的大小依次的递增(建议只是用前三个,后面两个模型过大,训练时间可能会比较长,也会有更高的过拟合风险
版权归原作者 yep吖 所有, 如有侵权,请联系我们删除。