afm-analysis-web / utils /CNO_KDE_Integration.py
jenhung's picture
Initial commit
80eac1a
# Copyright 2024 Jen-Hung Wang, IDUN Section, Department of Health Technology, Technical University of Denmark (DTU)
import time
import sys
import warnings
import csv
import cv2
import math
from pathlib import Path
from utils.growcut import *
from ultralytics import YOLO
from sklearn.neighbors import KernelDensity
from sklearn.model_selection import GridSearchCV
warnings.filterwarnings('ignore')
DIR_NAME = Path(os.path.dirname(__file__)).parent
np.set_printoptions(threshold=sys.maxsize)
# Use GPU
# torch.cuda.set_device(0) # Set to your desired GPU number
# Model Path
DETECTION_MODEL_n = os.path.join(DIR_NAME, 'models', 'YOLOv8-N_CNO_Detection.pt')
DETECTION_MODEL_s = os.path.join(DIR_NAME, 'models', 'YOLOv8-S_CNO_Detection.pt')
DETECTION_MODEL_m = os.path.join(DIR_NAME, 'models', 'YOLOv8-M_CNO_Detection.pt')
DETECTION_MODEL_l = os.path.join(DIR_NAME, 'models', 'YOLOv8-L_CNO_Detection.pt')
DETECTION_MODEL_x = os.path.join(DIR_NAME, 'models', 'YOLOv8-X_CNO_Detection.pt')
# DETECTION_MODEL_c = os.path.join(DIR_NAME, 'models', 'YOLOv9-C_CNO_Detection.pt')
# DETECTION_MODEL_e = os.path.join(DIR_NAME, 'models', 'YOLOv9-E_CNO_Detection.pt')
def numcat(arr):
arr_size = arr.shape[0]
arr_cat = np.empty([arr_size, 1], dtype=np.int32)
for i in range(arr.shape[0]):
arr_cat[i] = arr[i][0] * 1000 + arr[i][1]
return arr_cat
def cno_detection(source, kde_dir, conf, cno_model, file_list, model_type):
# Declare Parameters
cno_col = []
total_layer_area = []
total_layer_cno = []
total_layer_density = []
avg_area_col = []
total_area_col = []
detection_results = cno_model.predict(source, save=False, save_txt=False, iou=0.5, conf=conf, max_det=1200)
# CNO Analysis
for idx, result in enumerate(detection_results):
CNO = len(result.boxes)
single_layer_area = []
single_layer_cno = []
single_layer_density = []
total_area = 0
if CNO < 5:
avg_area_col.append(np.nan)
total_area_col.append(np.nan)
nan_arr = np.empty([25])
nan_arr[:] = np.nan
total_layer_area.append(nan_arr)
total_layer_cno.append(nan_arr)
total_layer_density.append(nan_arr)
else:
CNO_coor = np.empty([CNO, 2], dtype=int)
for j in range(CNO):
w = result.boxes.xywh[j][2]
h = result.boxes.xywh[j][3]
area = (math.pi * w * h / 4) * 20 * 20 / (512 * 512)
total_area += area
bbox_img = result.orig_img
x = round(result.boxes.xywh[j][0].item())
y = round(result.boxes.xywh[j][1].item())
x1 = round(result.boxes.xyxy[j][0].item())
y1 = round(result.boxes.xyxy[j][1].item())
x2 = round(result.boxes.xyxy[j][2].item())
y2 = round(result.boxes.xyxy[j][3].item())
CNO_coor[j] = [x, y]
bbox_img = cv2.rectangle(bbox_img,
(x1, y1),
(x2, y2),
(0, 255, 0), 1)
avg_area = total_area / CNO
avg_area_col.append(round(avg_area.item(), 4))
total_area_col.append(round(total_area.item(), 4))
cv2.imwrite(os.path.join(kde_dir, '{}_{}_{}_bbox.png'.format(file_list[idx], model_type, conf)),
bbox_img)
kde = KernelDensity(metric='euclidean', kernel='gaussian', algorithm='ball_tree')
# Finding Optimal Bandwidth
ti = time.time()
if CNO < 7:
fold = CNO
else:
fold = 7
gs = GridSearchCV(kde, {'bandwidth': np.linspace(20, 60, 41)}, cv=fold)
cv = gs.fit(CNO_coor)
bw = cv.best_params_['bandwidth']
tf = time.time()
print("Finding optimal bandwidth={:.2f} ({:n}-fold cross-validation): {:.2f} secs".format(bw, cv.cv,
(tf - ti)))
kde.bandwidth = bw
_ = kde.fit(CNO_coor)
xgrid = np.arange(0, bbox_img.shape[1], 1)
ygrid = np.arange(0, bbox_img.shape[0], 1)
xv, yv = np.meshgrid(xgrid, ygrid)
xys = np.vstack([xv.ravel(), yv.ravel()]).T
gdim = xv.shape
zi = np.arange(xys.shape[0])
zXY = xys
z = np.exp(kde.score_samples(zXY))
zg = -9999 + np.zeros(xys.shape[0])
zg[zi] = z
xyz = np.hstack((xys[:, :2], zg[:, None]))
x = xyz[:, 0].reshape(gdim)
y = xyz[:, 1].reshape(gdim)
z = xyz[:, 2].reshape(gdim)
levels = np.linspace(0, z.max(), 26)
print("levels", levels)
for j in range(len(levels) - 1):
area = np.argwhere(z >= levels[j])
area_concatenate = numcat(area)
CNO_concatenate = numcat(CNO_coor)
ecno = np.count_nonzero(np.isin(area_concatenate, CNO_concatenate))
layer_area = area.shape[0]
if layer_area == 0:
density = np.round(0.0, 4)
else:
density = np.round((ecno / layer_area) * 512 * 512 / 400, 4)
print("Level {}: Area={}, CNO={}, density={}".format(j, layer_area, ecno, density))
single_layer_area.append(layer_area)
single_layer_cno.append(ecno)
single_layer_density.append(density)
total_layer_area.append(single_layer_area)
total_layer_cno.append(single_layer_cno)
total_layer_density.append(single_layer_density)
# Plot CNO Distribution
plt.contourf(x, y, z, levels=levels, cmap=plt.cm.bone)
plt.axis('off')
# plt.gcf().set_size_inches(8, 8)
plt.gcf().set_size_inches(8 * (gdim[1] / gdim[0]), 8)
plt.gca().invert_yaxis()
plt.xlim(0, gdim[1] - 1)
plt.ylim(gdim[0] - 1, 0)
plt.savefig(os.path.join(kde_dir, '{}_{}_{}_KDE.png'.format(file_list[idx], model_type, conf)),
bbox_inches='tight', pad_inches=0)
plt.clf()
plt.scatter(CNO_coor[:, 0], CNO_coor[:, 1], s=10)
plt.xlim(0, gdim[1] - 1)
plt.ylim(0, gdim[0] - 1)
plt.axis('off')
plt.gcf().set_size_inches(8, 8)
plt.gcf().set_size_inches(8 * (gdim[1] / gdim[0]), 8)
plt.gca().invert_yaxis()
plt.savefig(os.path.join(kde_dir, '{}_{}_{}_Spatial.png'.format(file_list[idx], model_type, conf)),
bbox_inches='tight', pad_inches=0)
plt.clf()
cno_col.append(CNO)
return cno_col, avg_area_col, total_area_col, total_layer_area, total_layer_cno, total_layer_density
def cno_detect(folder_dir, model, conf):
if model == 'YOLOv8-N':
CNO_model = YOLO(DETECTION_MODEL_n)
elif model == 'YOLOv8-S':
CNO_model = YOLO(DETECTION_MODEL_s)
elif model == 'YOLOv8-M':
CNO_model = YOLO(DETECTION_MODEL_m)
elif model == 'YOLOv8-L':
CNO_model = YOLO(DETECTION_MODEL_l)
else:
CNO_model = YOLO(DETECTION_MODEL_x)
"""
elif model == 'YOLOv9-C':
CNO_model = YOLO(DETECTION_MODEL_c)
else:
CNO_model = YOLO(DETECTION_MODEL_e)
"""
# Search folder path
folder = folder_dir.split(os.sep)[-1]
print("Analyzing Folder", folder)
# Extract folder information
folder_info = folder.split('_')
if folder_info[2][0:2] == "TL":
Country = folder_info[0]
AD_severity = folder_info[1]
TLSS = int(folder_info[2].strip("TL"))
if TLSS == 0:
lesional = False
else:
lesional = True
Number = int(folder_info[-1].strip("No."))
AD_group = AD_severity.strip("G")
else:
Country = None
TLSS = None
lesional = None
Number = None
AD_group = None
run_growcut = True
timestr = time.strftime("%Y%m%d-%H%M%S")
CNO_list = []
Area_sum = []
Area_avg = []
file_list = []
growcut_list = []
growcut_path = os.path.join(folder_dir, "CNO_Detection", "GrowCut")
original_png_path = os.path.join(folder_dir, "CNO_Detection", "Image", "Original")
enhanced_png_path = os.path.join(folder_dir, "CNO_Detection", "Image", "Enhanced")
kde_png_path = os.path.join(folder_dir, "CNO_Detection", "Image", "KDE")
save_dir = os.path.join(folder_dir, "CNO_Detection", "Result")
print("Save Path:", save_dir)
try:
os.makedirs(growcut_path, exist_ok=True)
os.makedirs(original_png_path, exist_ok=True)
os.makedirs(enhanced_png_path, exist_ok=True)
os.makedirs(kde_png_path, exist_ok=True)
if not os.listdir(enhanced_png_path):
print("Directory is empty")
run_growcut = True
else:
print("Directory is not empty")
run_growcut = False
os.makedirs(save_dir, exist_ok=True)
except OSError as error:
print("Directory can not be created")
encyc = []
walk = os.walk(folder_dir)
for d, sd, files in walk:
directory = d.split(os.sep)[-1]
for fn in files:
if fn[0:2] != "._" and fn[-10:].lower() == '_trace.bcr' and directory == folder:
encyc.append(d + os.sep + fn)
encyc.sort()
# GrowCut Detection
if run_growcut:
for i, fn in enumerate(encyc):
file, gc_CNO = treat_one_image(fn, growcut_path, original_png_path, enhanced_png_path)
file_list.append(file)
growcut_list.append(gc_CNO)
print(i, end=' ')
else:
for i, fn in enumerate(encyc):
file_list.append(os.path.split(fn)[1][0:-10])
# CNO Detection & AD Classification
print("Model", model)
print("Conf", conf)
# Make Function
cno_col, avg_area_col, total_area_col, layer_area, layer_cno, layer_density = cno_detection(enhanced_png_path,
kde_png_path,
conf, CNO_model,
file_list, model)
CNO_list.append(cno_col)
Area_sum.append(total_area_col)
Area_avg.append(avg_area_col)
Layer_area = layer_area
Layer_cno = layer_cno
Layer_density = layer_density
# Write CSV
# open the file in the write mode
f = open(save_dir + os.sep + '{}_{}_{}_{}_.csv'.format(folder, timestr, model, conf), 'w')
header = ['File', 'Country', 'Group', 'No.', 'TLSS', 'Lesional',
'Layer_Area_0', 'Layer_Area_1', 'Layer_Area_2', 'Layer_Area_3', 'Layer_Area_4',
'Layer_Area_5', 'Layer_Area_6', 'Layer_Area_7', 'Layer_Area_8', 'Layer_Area_9',
'Layer_Area_10', 'Layer_Area_11', 'Layer_Area_12', 'Layer_Area_13', 'Layer_Area_14',
'Layer_Area_15', 'Layer_Area_16', 'Layer_Area_17', 'Layer_Area_18', 'Layer_Area_19',
'Layer_Area_20', 'Layer_Area_21', 'Layer_Area_22', 'Layer_Area_23', 'Layer_Area_24',
'Layer_CNO_0', 'Layer_CNO_1', 'Layer_CNO_2', 'Layer_CNO_3', 'Layer_CNO_4',
'Layer_CNO_5', 'Layer_CNO_6', 'Layer_CNO_7', 'Layer_CNO_8', 'Layer_CNO_9',
'Layer_CNO_10', 'Layer_CNO_11', 'Layer_CNO_12', 'Layer_CNO_13', 'Layer_CNO_14',
'Layer_CNO_15', 'Layer_CNO_16', 'Layer_CNO_17', 'Layer_CNO_18', 'Layer_CNO_19',
'Layer_CNO_20', 'Layer_CNO_21', 'Layer_CNO_22', 'Layer_CNO_23', 'Layer_CNO_24',
'Layer_Density_0', 'Layer_Density_1', 'Layer_Density_2', 'Layer_Density_3',
'Layer_Density_4', 'Layer_Density_5', 'Layer_Density_6', 'Layer_Density_7',
'Layer_Density_8', 'Layer_Density_9', 'Layer_Density_10', 'Layer_Density_11',
'Layer_Density_12', 'Layer_Density_13', 'Layer_Density_14', 'Layer_Density_15',
'Layer_Density_16', 'Layer_Density_17', 'Layer_Density_18', 'Layer_Density_19',
'Layer_Density_20', 'Layer_Density_21', 'Layer_Density_22', 'Layer_Density_23',
'Layer_Density_24',
'AVG_Area', 'AVG_Size']
writer = csv.writer(f)
writer.writerow(header)
for i in range(len(file_list)):
data = [file_list[i], Country, AD_group, Number, TLSS, lesional,
Layer_area[i][0], Layer_area[i][1], Layer_area[i][2], Layer_area[i][3], Layer_area[i][4],
Layer_area[i][5], Layer_area[i][6], Layer_area[i][7], Layer_area[i][8], Layer_area[i][9],
Layer_area[i][10], Layer_area[i][11], Layer_area[i][12], Layer_area[i][13],
Layer_area[i][14], Layer_area[i][15], Layer_area[i][16], Layer_area[i][17],
Layer_area[i][18], Layer_area[i][19], Layer_area[i][20], Layer_area[i][21],
Layer_area[i][22], Layer_area[i][23], Layer_area[i][24],
Layer_cno[i][0], Layer_cno[i][1], Layer_cno[i][2], Layer_cno[i][3], Layer_cno[i][4],
Layer_cno[i][5], Layer_cno[i][6], Layer_cno[i][7], Layer_cno[i][8], Layer_cno[i][9],
Layer_cno[i][10], Layer_cno[i][11], Layer_cno[i][12], Layer_cno[i][13], Layer_cno[i][14],
Layer_cno[i][15], Layer_cno[i][16], Layer_cno[i][17], Layer_cno[i][18], Layer_cno[i][19],
Layer_cno[i][20], Layer_cno[i][21], Layer_cno[i][22], Layer_cno[i][23], Layer_cno[i][24],
Layer_density[i][0], Layer_density[i][1], Layer_density[i][2], Layer_density[i][3],
Layer_density[i][4], Layer_density[i][5], Layer_density[i][6], Layer_density[i][7],
Layer_density[i][8], Layer_density[i][9], Layer_density[i][10], Layer_density[i][11],
Layer_density[i][12], Layer_density[i][13], Layer_density[i][14], Layer_density[i][15],
Layer_density[i][16], Layer_density[i][17], Layer_density[i][18], Layer_density[i][19],
Layer_density[i][20], Layer_density[i][21], Layer_density[i][22], Layer_density[i][23],
Layer_density[i][24],
Area_sum[0][i], Area_avg[0][i]]
writer.writerow(data)
f.close()