作者简介: 本文作者系大学统计学专业教师,多年从事统计学的教学科研工作,在随机过程、统计推 断、机器学习领域有深厚的理论积累与应用实践。个人主页
据统计,新型冠状病毒肺炎(COVID-19
)有显著的发病率,而且致死率是普通流感的五倍。从肺部X光图像看,新冠肺炎非常类似其它肺炎,例如,导致肺部发炎、积水。这增加了从胸片图像诊断新冠肺炎的难度。当前,新冠肺炎的诊断主要通过病毒学的聚合酶链式反应检测遗传物质,或肺部X光图像检查。分子试验结果需要几个小时甚至数天时间,而胸片图像只需要几分钟的时间。本研究将识别与定位X光图像的新冠肺炎异常。这是一个目标检测与分类问题。
本研究的数据集由6,334张DICOM格式的胸部X光图像组成。对于每一张检验图像,你将开发计算机视觉算法预测一个边界框和标签。如果预测结果没有发现异常,将用none 1 0 0 1 1
表示。
YOLO
是You only look once的缩写,它是一个目标检测算法,将检测图像分割成一个网格系统,网格里的每个细胞对应图像的一部分。YOLO将对象检测重新定义为一个回归问题。它将单个卷积神经网络(CNN)应用于整个图像,将图像分成网格,并预测每个网格的类概率和边界框。YOLO非常快。由于检测问题是一个回归问题,所以不需要复杂的管道。它比R-CNN快1000倍,比Fast R-CNN快100倍。YOLOv5是YOLO最新的版本。
# Download YOLOv5
!git clone https://github.com/ultralytics/yolov5 # clone repo
%cd yolov5
# Install dependencies
%pip install -qr requirements.txt # install dependencies
%cd ../
import torch
print(f"Setup complete. Using torch {torch.__version__} ({torch.cuda.get_device_properties(0).name if torch.cuda.is_available() else 'CPU'})")
# Install W&B
!pip install -q --upgrade wandb
# Login
import wandb
from kaggle_secrets import UserSecretsClient
user_secrets = UserSecretsClient()
# I have saved my API token with "wandb_api" as Label.
# If you use some other Label make sure to change the same below.
wandb_api = user_secrets.get_secret("wandb_api")
wandb.login(key=wandb_api)
# Necessary/extra dependencies.
import os
import gc
import cv2
import numpy as np
import pandas as pd
from tqdm import tqdm
from shutil import copyfile
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split
#customize iPython writefile so we can write variables
from IPython.core.magic import register_line_cell_magic
@register_line_cell_magic
def writetemplate(line, cell):
with open(line, 'w') as f:
f.write(cell.format(**globals()))
# Load image level csv file
df = pd.read_csv('input/siim-covid19-detection/train_image_level.csv')
# Modify values in the id column
df['id'] = df.apply(lambda row: row.id.split('_')[0], axis=1)
# Add absolute path
df['path'] = df.apply(lambda row: TRAIN_PATH+row.id+'.jpg', axis=1)
# Get image level labels
df['image_level'] = df.apply(lambda row: row.label.split(' ')[0], axis=1)
df.head(5)
# Create train and validation split.
train_df, valid_df = train_test_split(df, test_size=0.2, random_state=42, stratify=df.image_level.values)
train_df.loc[:, 'split'] = 'train'
valid_df.loc[:, 'split'] = 'valid'
df = pd.concat([train_df, valid_df]).reset_index(drop=True)
os.makedirs('tmp/covid/images/train', exist_ok=True)
os.makedirs('tmp/covid/images/valid', exist_ok=True)
os.makedirs('tmp/covid/labels/train', exist_ok=True)
os.makedirs('tmp/covid/labels/valid', exist_ok=True)
! ls tmp/covid/images
# Move the images to relevant split folder.
for i in tqdm(range(len(df))):
row = df.loc[i]
if row.split == 'train':
copyfile(row.path, f'tmp/covid/images/train/{row.id}.jpg')
else:
copyfile(row.path, f'tmp/covid/images/valid/{row.id}.jpg')
# Get the raw bounding box by parsing the row value of the label column.
# Ref: https://www.kaggle.com/yujiariyasu/plot-3positive-classes
def get_bbox(row):
bboxes = []
bbox = []
for i, l in enumerate(row.label.split(' ')):
if (i % 6 == 0) | (i % 6 == 1):
continue
bbox.append(float(l))
if i % 6 == 5:
bboxes.append(bbox)
bbox = []
return bboxes
# Scale the bounding boxes according to the size of the resized image.
def scale_bbox(row, bboxes):
# Get scaling factor
scale_x = IMG_SIZE/row.dim1
scale_y = IMG_SIZE/row.dim0
scaled_bboxes = []
for bbox in bboxes:
x = int(np.round(bbox[0]*scale_x, 4))
y = int(np.round(bbox[1]*scale_y, 4))
x1 = int(np.round(bbox[2]*(scale_x), 4))
y1= int(np.round(bbox[3]*scale_y, 4))
scaled_bboxes.append([x, y, x1, y1]) # xmin, ymin, xmax, ymax
return scaled_bboxes
# Convert the bounding boxes in YOLO format.
def get_yolo_format_bbox(img_w, img_h, bboxes):
yolo_boxes = []
for bbox in bboxes:
w = bbox[2] - bbox[0] # xmax - xmin
h = bbox[3] - bbox[1] # ymax - ymin
xc = bbox[0] + int(np.round(w/2)) # xmin + width/2
yc = bbox[1] + int(np.round(h/2)) # ymin + height/2
yolo_boxes.append([xc/img_w, yc/img_h, w/img_w, h/img_h]) # x_center y_center width height
return yolo_boxes
# Prepare the txt files for bounding box
for i in tqdm(range(len(df))):
row = df.loc[i]
# Get image id
img_id = row.id
# Get split
split = row.split
# Get image-level label
label = row.image_level
if row.split=='train':
file_name = f'tmp/covid/labels/train/{row.id}.txt'
else:
file_name = f'tmp/covid/labels/valid/{row.id}.txt'
if label=='opacity':
# Get bboxes
bboxes = get_bbox(row)
# Scale bounding boxes
scale_bboxes = scale_bbox(row, bboxes)
# Format for YOLOv5
yolo_bboxes = get_yolo_format_bbox(IMG_SIZE, IMG_SIZE, scale_bboxes)
with open(file_name, 'w') as f:
for bbox in yolo_bboxes:
bbox = [1]+bbox
bbox = [str(i) for i in bbox]
bbox = ' '.join(bbox)
f.write(bbox)
f.write('\n')
%cd tmp/yolov5/
/kaggle/tmp/yolov5
--img {IMG_SIZE} \ # Input image size.
--batch {BATCH_SIZE} \ # Batch size
--epochs {EPOCHS} \ # Number of epochs
--data data.yaml \ # Configuration file
--weights yolov5s.pt \ # Model name
--save_period 1\ # Save model after interval
--project kaggle-siim-covid # W&B project name
!python train.py --img {IMG_SIZE} \
--batch {BATCH_SIZE} \
--epochs {EPOCHS} \
--data data.yaml \
--weights yolov5s.pt \
--save-period 1 \
--project kaggle-siim-covid
TEST_PATH = '../input/siim-covid19-resized-to-256px-jpg/test/'
MODEL_PATH = 'siim-covid/exp/weights/best.pt'
--weights {MODEL_PATH} \ # path to the best model.
--source {TEST_PATH} \ # absolute path to the test images.
--img {IMG_SIZE} \ # Size of image
--conf 0.281 \ # Confidence threshold (default is 0.25)
--iou-thres 0.5 \ # IOU threshold (default is 0.45)
--max-det 3 \ # Number of detections per image (default is 1000)
--save-txt \ # Save predicted bounding box coordinates as txt files
--save-conf # Save the confidence of prediction for each bounding box
!python detect.py --weights {MODEL_PATH} \
--source {TEST_PATH} \
--img {IMG_SIZE} \
--conf 0.281 \
--iou-thres 0.5 \
--max-det 3 \
--save-txt \
--save-conf
'''from os.path import exists
import os
print(os.getcwd())
print(exists('runs/detect'))
with os.scandir('runs/detect') as it:
for entry in it:
print(entry.name, entry.path)'''
完