chanelisa/objectdetectionhw
0
1# Plotting utils2 3import glob4import math5import os6import random7from copy import copy8from pathlib import Path9 10import cv211import matplotlib12import matplotlib.pyplot as plt13import numpy as np14import pandas as pd15import seaborn as sns16import torch17import yaml18from PIL import Image, ImageDraw, ImageFont19from scipy.signal import butter, filtfilt20 21from utils.general import xywh2xyxy, xyxy2xywh22from utils.metrics import fitness23 24# Settings25matplotlib.rc('font', **{'size': 11})26matplotlib.use('Agg') # for writing to files only27 28 29def color_list():30 # Return first 10 plt colors as (r,g,b) https://stackoverflow.com/questions/51350872/python-from-color-name-to-rgb31 def hex2rgb(h):32 return tuple(int(h[1 + i:1 + i + 2], 16) for i in (0, 2, 4))33 34 return [hex2rgb(h) for h in matplotlib.colors.TABLEAU_COLORS.values()] # or BASE_ (8), CSS4_ (148), XKCD_ (949)35 36 37def hist2d(x, y, n=100):38 # 2d histogram used in labels.png and evolve.png39 xedges, yedges = np.linspace(x.min(), x.max(), n), np.linspace(y.min(), y.max(), n)40 hist, xedges, yedges = np.histogram2d(x, y, (xedges, yedges))41 xidx = np.clip(np.digitize(x, xedges) - 1, 0, hist.shape[0] - 1)42 yidx = np.clip(np.digitize(y, yedges) - 1, 0, hist.shape[1] - 1)43 return np.log(hist[xidx, yidx])44 45 46def butter_lowpass_filtfilt(data, cutoff=1500, fs=50000, order=5):47 # https://stackoverflow.com/questions/28536191/how-to-filter-smooth-with-scipy-numpy48 def butter_lowpass(cutoff, fs, order):49 nyq = 0.5 * fs50 normal_cutoff = cutoff / nyq51 return butter(order, normal_cutoff, btype='low', analog=False)52 53 b, a = butter_lowpass(cutoff, fs, order=order)54 return filtfilt(b, a, data) # forward-backward filter55 56 57def plot_one_box(x, img, color=None, label=None, line_thickness=3):58 # Plots one bounding box on image img59 tl = line_thickness or round(0.002 * (img.shape[0] + img.shape[1]) / 2) + 1 # line/font thickness60 color = color or [random.randint(0, 255) for _ in range(3)]61 c1, c2 = (int(x[0]), int(x[1])), (int(x[2]), int(x[3]))62 cv2.rectangle(img, c1, c2, color, thickness=tl, lineType=cv2.LINE_AA)63 if label:64 tf = max(tl - 1, 1) # font thickness65 t_size = cv2.getTextSize(label, 0, fontScale=tl / 3, thickness=tf)[0]66 c2 = c1[0] + t_size[0], c1[1] - t_size[1] - 367 cv2.rectangle(img, c1, c2, color, -1, cv2.LINE_AA) # filled68 cv2.putText(img, label, (c1[0], c1[1] - 2), 0, tl / 3, [225, 255, 255], thickness=tf, lineType=cv2.LINE_AA)69 70 71def plot_one_box_PIL(box, img, color=None, label=None, line_thickness=None):72 img = Image.fromarray(img)73 draw = ImageDraw.Draw(img)74 line_thickness = line_thickness or max(int(min(img.size) / 200), 2)75 draw.rectangle(box, width=line_thickness, outline=tuple(color)) # plot76 if label:77 fontsize = max(round(max(img.size) / 40), 12)78 font = ImageFont.truetype("Arial.ttf", fontsize)79 txt_width, txt_height = font.getsize(label)80 draw.rectangle([box[0], box[1] - txt_height + 4, box[0] + txt_width, box[1]], fill=tuple(color))81 draw.text((box[0], box[1] - txt_height + 1), label, fill=(255, 255, 255), font=font)82 return np.asarray(img)83 84 85def plot_wh_methods(): # from utils.plots import *; plot_wh_methods()86 # Compares the two methods for width-height anchor multiplication87 # https://github.com/ultralytics/yolov3/issues/16888 x = np.arange(-4.0, 4.0, .1)89 ya = np.exp(x)90 yb = torch.sigmoid(torch.from_numpy(x)).numpy() * 291 92 fig = plt.figure(figsize=(6, 3), tight_layout=True)93 plt.plot(x, ya, '.-', label='YOLOv3')94 plt.plot(x, yb ** 2, '.-', label='YOLOR ^2')95 plt.plot(x, yb ** 1.6, '.-', label='YOLOR ^1.6')96 plt.xlim(left=-4, right=4)97 plt.ylim(bottom=0, top=6)98 plt.xlabel('input')99 plt.ylabel('output')100 plt.grid()101 plt.legend()102 fig.savefig('comparison.png', dpi=200)103 104 105def output_to_target(output):106 # Convert model output to target format [batch_id, class_id, x, y, w, h, conf]107 targets = []108 for i, o in enumerate(output):109 for *box, conf, cls in o.cpu().numpy():110 targets.append([i, cls, *list(*xyxy2xywh(np.array(box)[None])), conf])111 return np.array(targets)112 113 114def plot_images(images, targets, paths=None, fname='images.jpg', names=None, max_size=640, max_subplots=16):115 # Plot image grid with labels116 117 if isinstance(images, torch.Tensor):118 images = images.cpu().float().numpy()119 if isinstance(targets, torch.Tensor):120 targets = targets.cpu().numpy()121 122 # un-normalise123 if np.max(images[0]) <= 1:124 images *= 255125 126 tl = 3 # line thickness127 tf = max(tl - 1, 1) # font thickness128 bs, _, h, w = images.shape # batch size, _, height, width129 bs = min(bs, max_subplots) # limit plot images130 ns = np.ceil(bs ** 0.5) # number of subplots (square)131 132 # Check if we should resize133 scale_factor = max_size / max(h, w)134 if scale_factor < 1:135 h = math.ceil(scale_factor * h)136 w = math.ceil(scale_factor * w)137 138 colors = color_list() # list of colors139 mosaic = np.full((int(ns * h), int(ns * w), 3), 255, dtype=np.uint8) # init140 for i, img in enumerate(images):141 if i == max_subplots: # if last batch has fewer images than we expect142 break143 144 block_x = int(w * (i // ns))145 block_y = int(h * (i % ns))146 147 img = img.transpose(1, 2, 0)148 if scale_factor < 1:149 img = cv2.resize(img, (w, h))150 151 mosaic[block_y:block_y + h, block_x:block_x + w, :] = img152 if len(targets) > 0:153 image_targets = targets[targets[:, 0] == i]154 boxes = xywh2xyxy(image_targets[:, 2:6]).T155 classes = image_targets[:, 1].astype('int')156 labels = image_targets.shape[1] == 6 # labels if no conf column157 conf = None if labels else image_targets[:, 6] # check for confidence presence (label vs pred)158 159 if boxes.shape[1]:160 if boxes.max() <= 1.01: # if normalized with tolerance 0.01161 boxes[[0, 2]] *= w # scale to pixels162 boxes[[1, 3]] *= h163 elif scale_factor < 1: # absolute coords need scale if image scales164 boxes *= scale_factor165 boxes[[0, 2]] += block_x166 boxes[[1, 3]] += block_y167 for j, box in enumerate(boxes.T):168 cls = int(classes[j])169 color = colors[cls % len(colors)]170 cls = names[cls] if names else cls171 if labels or conf[j] > 0.25: # 0.25 conf thresh172 label = '%s' % cls if labels else '%s %.1f' % (cls, conf[j])173 plot_one_box(box, mosaic, label=label, color=color, line_thickness=tl)174 175 # Draw image filename labels176 if paths:177 label = Path(paths[i]).name[:40] # trim to 40 char178 t_size = cv2.getTextSize(label, 0, fontScale=tl / 3, thickness=tf)[0]179 cv2.putText(mosaic, label, (block_x + 5, block_y + t_size[1] + 5), 0, tl / 3, [220, 220, 220], thickness=tf,180 lineType=cv2.LINE_AA)181 182 # Image border183 cv2.rectangle(mosaic, (block_x, block_y), (block_x + w, block_y + h), (255, 255, 255), thickness=3)184 185 if fname:186 r = min(1280. / max(h, w) / ns, 1.0) # ratio to limit image size187 mosaic = cv2.resize(mosaic, (int(ns * w * r), int(ns * h * r)), interpolation=cv2.INTER_AREA)188 # cv2.imwrite(fname, cv2.cvtColor(mosaic, cv2.COLOR_BGR2RGB)) # cv2 save189 Image.fromarray(mosaic).save(fname) # PIL save190 return mosaic191 192 193def plot_lr_scheduler(optimizer, scheduler, epochs=300, save_dir=''):194 # Plot LR simulating training for full epochs195 optimizer, scheduler = copy(optimizer), copy(scheduler) # do not modify originals196 y = []197 for _ in range(epochs):198 scheduler.step()199 y.append(optimizer.param_groups[0]['lr'])200 plt.plot(y, '.-', label='LR')201 plt.xlabel('epoch')202 plt.ylabel('LR')203 plt.grid()204 plt.xlim(0, epochs)205 plt.ylim(0)206 plt.savefig(Path(save_dir) / 'LR.png', dpi=200)207 plt.close()208 209 210def plot_test_txt(): # from utils.plots import *; plot_test()211 # Plot test.txt histograms212 x = np.loadtxt('test.txt', dtype=np.float32)213 box = xyxy2xywh(x[:, :4])214 cx, cy = box[:, 0], box[:, 1]215 216 fig, ax = plt.subplots(1, 1, figsize=(6, 6), tight_layout=True)217 ax.hist2d(cx, cy, bins=600, cmax=10, cmin=0)218 ax.set_aspect('equal')219 plt.savefig('hist2d.png', dpi=300)220 221 fig, ax = plt.subplots(1, 2, figsize=(12, 6), tight_layout=True)222 ax[0].hist(cx, bins=600)223 ax[1].hist(cy, bins=600)224 plt.savefig('hist1d.png', dpi=200)225 226 227def plot_targets_txt(): # from utils.plots import *; plot_targets_txt()228 # Plot targets.txt histograms229 x = np.loadtxt('targets.txt', dtype=np.float32).T230 s = ['x targets', 'y targets', 'width targets', 'height targets']231 fig, ax = plt.subplots(2, 2, figsize=(8, 8), tight_layout=True)232 ax = ax.ravel()233 for i in range(4):234 ax[i].hist(x[i], bins=100, label='%.3g +/- %.3g' % (x[i].mean(), x[i].std()))235 ax[i].legend()236 ax[i].set_title(s[i])237 plt.savefig('targets.jpg', dpi=200)238 239 240def plot_study_txt(path='', x=None): # from utils.plots import *; plot_study_txt()241 # Plot study.txt generated by test.py242 fig, ax = plt.subplots(2, 4, figsize=(10, 6), tight_layout=True)243 # ax = ax.ravel()244 245 fig2, ax2 = plt.subplots(1, 1, figsize=(8, 4), tight_layout=True)246 # for f in [Path(path) / f'study_coco_{x}.txt' for x in ['yolor-p6', 'yolor-w6', 'yolor-e6', 'yolor-d6']]:247 for f in sorted(Path(path).glob('study*.txt')):248 y = np.loadtxt(f, dtype=np.float32, usecols=[0, 1, 2, 3, 7, 8, 9], ndmin=2).T249 x = np.arange(y.shape[1]) if x is None else np.array(x)250 s = ['P', 'R', 'mAP@.5', 'mAP@.5:.95', 't_inference (ms/img)', 't_NMS (ms/img)', 't_total (ms/img)']251 # for i in range(7):252 # ax[i].plot(x, y[i], '.-', linewidth=2, markersize=8)253 # ax[i].set_title(s[i])254 255 j = y[3].argmax() + 1256 ax2.plot(y[6, 1:j], y[3, 1:j] * 1E2, '.-', linewidth=2, markersize=8,257 label=f.stem.replace('study_coco_', '').replace('yolo', 'YOLO'))258 259 ax2.plot(1E3 / np.array([209, 140, 97, 58, 35, 18]), [34.6, 40.5, 43.0, 47.5, 49.7, 51.5],260 'k.-', linewidth=2, markersize=8, alpha=.25, label='EfficientDet')261 262 ax2.grid(alpha=0.2)263 ax2.set_yticks(np.arange(20, 60, 5))264 ax2.set_xlim(0, 57)265 ax2.set_ylim(30, 55)266 ax2.set_xlabel('GPU Speed (ms/img)')267 ax2.set_ylabel('COCO AP val')268 ax2.legend(loc='lower right')269 plt.savefig(str(Path(path).name) + '.png', dpi=300)270 271 272def plot_labels(labels, names=(), save_dir=Path(''), loggers=None):273 # plot dataset labels274 print('Plotting labels... ')275 c, b = labels[:, 0], labels[:, 1:].transpose() # classes, boxes276 nc = int(c.max() + 1) # number of classes277 colors = color_list()278 x = pd.DataFrame(b.transpose(), columns=['x', 'y', 'width', 'height'])279 280 # seaborn correlogram281 sns.pairplot(x, corner=True, diag_kind='auto', kind='hist', diag_kws=dict(bins=50), plot_kws=dict(pmax=0.9))282 plt.savefig(save_dir / 'labels_correlogram.jpg', dpi=200)283 plt.close()284 285 # matplotlib labels286 matplotlib.use('svg') # faster287 ax = plt.subplots(2, 2, figsize=(8, 8), tight_layout=True)[1].ravel()288 ax[0].hist(c, bins=np.linspace(0, nc, nc + 1) - 0.5, rwidth=0.8)289 ax[0].set_ylabel('instances')290 if 0 < len(names) < 30:291 ax[0].set_xticks(range(len(names)))292 ax[0].set_xticklabels(names, rotation=90, fontsize=10)293 else:294 ax[0].set_xlabel('classes')295 sns.histplot(x, x='x', y='y', ax=ax[2], bins=50, pmax=0.9)296 sns.histplot(x, x='width', y='height', ax=ax[3], bins=50, pmax=0.9)297 298 # rectangles299 labels[:, 1:3] = 0.5 # center300 labels[:, 1:] = xywh2xyxy(labels[:, 1:]) * 2000301 img = Image.fromarray(np.ones((2000, 2000, 3), dtype=np.uint8) * 255)302 for cls, *box in labels[:1000]:303 ImageDraw.Draw(img).rectangle(box, width=1, outline=colors[int(cls) % 10]) # plot304 ax[1].imshow(img)305 ax[1].axis('off')306 307 for a in [0, 1, 2, 3]:308 for s in ['top', 'right', 'left', 'bottom']:309 ax[a].spines[s].set_visible(False)310 311 plt.savefig(save_dir / 'labels.jpg', dpi=200)312 matplotlib.use('Agg')313 plt.close()314 315 # loggers316 for k, v in loggers.items() or {}:317 if k == 'wandb' and v:318 v.log({"Labels": [v.Image(str(x), caption=x.name) for x in save_dir.glob('*labels*.jpg')]}, commit=False)319 320 321def plot_evolution(yaml_file='data/hyp.finetune.yaml'): # from utils.plots import *; plot_evolution()322 # Plot hyperparameter evolution results in evolve.txt323 with open(yaml_file) as f:324 hyp = yaml.load(f, Loader=yaml.SafeLoader)325 x = np.loadtxt('evolve.txt', ndmin=2)326 f = fitness(x)327 # weights = (f - f.min()) ** 2 # for weighted results328 plt.figure(figsize=(10, 12), tight_layout=True)329 matplotlib.rc('font', **{'size': 8})330 for i, (k, v) in enumerate(hyp.items()):331 y = x[:, i + 7]332 # mu = (y * weights).sum() / weights.sum() # best weighted result333 mu = y[f.argmax()] # best single result334 plt.subplot(6, 5, i + 1)335 plt.scatter(y, f, c=hist2d(y, f, 20), cmap='viridis', alpha=.8, edgecolors='none')336 plt.plot(mu, f.max(), 'k+', markersize=15)337 plt.title('%s = %.3g' % (k, mu), fontdict={'size': 9}) # limit to 40 characters338 if i % 5 != 0:339 plt.yticks([])340 print('%15s: %.3g' % (k, mu))341 plt.savefig('evolve.png', dpi=200)342 print('\nPlot saved as evolve.png')343 344 345def profile_idetection(start=0, stop=0, labels=(), save_dir=''):346 # Plot iDetection '*.txt' per-image logs. from utils.plots import *; profile_idetection()347 ax = plt.subplots(2, 4, figsize=(12, 6), tight_layout=True)[1].ravel()348 s = ['Images', 'Free Storage (GB)', 'RAM Usage (GB)', 'Battery', 'dt_raw (ms)', 'dt_smooth (ms)', 'real-world FPS']349 files = list(Path(save_dir).glob('frames*.txt'))350 for fi, f in enumerate(files):351 try:352 results = np.loadtxt(f, ndmin=2).T[:, 90:-30] # clip first and last rows353 n = results.shape[1] # number of rows354 x = np.arange(start, min(stop, n) if stop else n)355 results = results[:, x]356 t = (results[0] - results[0].min()) # set t0=0s357 results[0] = x358 for i, a in enumerate(ax):359 if i < len(results):360 label = labels[fi] if len(labels) else f.stem.replace('frames_', '')361 a.plot(t, results[i], marker='.', label=label, linewidth=1, markersize=5)362 a.set_title(s[i])363 a.set_xlabel('time (s)')364 # if fi == len(files) - 1:365 # a.set_ylim(bottom=0)366 for side in ['top', 'right']:367 a.spines[side].set_visible(False)368 else:369 a.remove()370 except Exception as e:371 print('Warning: Plotting error for %s; %s' % (f, e))372 373 ax[1].legend()374 plt.savefig(Path(save_dir) / 'idetection_profile.png', dpi=200)375 376 377def plot_results_overlay(start=0, stop=0): # from utils.plots import *; plot_results_overlay()378 # Plot training 'results*.txt', overlaying train and val losses379 s = ['train', 'train', 'train', 'Precision', 'mAP@0.5', 'val', 'val', 'val', 'Recall', 'mAP@0.5:0.95'] # legends380 t = ['Box', 'Objectness', 'Classification', 'P-R', 'mAP-F1'] # titles381 for f in sorted(glob.glob('results*.txt') + glob.glob('../../Downloads/results*.txt')):382 results = np.loadtxt(f, usecols=[2, 3, 4, 8, 9, 12, 13, 14, 10, 11], ndmin=2).T383 n = results.shape[1] # number of rows384 x = range(start, min(stop, n) if stop else n)385 fig, ax = plt.subplots(1, 5, figsize=(14, 3.5), tight_layout=True)386 ax = ax.ravel()387 for i in range(5):388 for j in [i, i + 5]:389 y = results[j, x]390 ax[i].plot(x, y, marker='.', label=s[j])391 # y_smooth = butter_lowpass_filtfilt(y)392 # ax[i].plot(x, np.gradient(y_smooth), marker='.', label=s[j])393 394 ax[i].set_title(t[i])395 ax[i].legend()396 ax[i].set_ylabel(f) if i == 0 else None # add filename397 fig.savefig(f.replace('.txt', '.png'), dpi=200)398 399 400def plot_results(start=0, stop=0, bucket='', id=(), labels=(), save_dir=''):401 # Plot training 'results*.txt'. from utils.plots import *; plot_results(save_dir='runs/train/exp')402 fig, ax = plt.subplots(2, 5, figsize=(12, 6), tight_layout=True)403 ax = ax.ravel()404 s = ['Box', 'Objectness', 'Classification', 'Precision', 'Recall',405 'val Box', 'val Objectness', 'val Classification', 'mAP@0.5', 'mAP@0.5:0.95']406 if bucket:407 # files = ['https://storage.googleapis.com/%s/results%g.txt' % (bucket, x) for x in id]408 files = ['results%g.txt' % x for x in id]409 c = ('gsutil cp ' + '%s ' * len(files) + '.') % tuple('gs://%s/results%g.txt' % (bucket, x) for x in id)410 os.system(c)411 else:412 files = list(Path(save_dir).glob('results*.txt'))413 assert len(files), 'No results.txt files found in %s, nothing to plot.' % os.path.abspath(save_dir)414 for fi, f in enumerate(files):415 try:416 results = np.loadtxt(f, usecols=[2, 3, 4, 8, 9, 12, 13, 14, 10, 11], ndmin=2).T417 n = results.shape[1] # number of rows418 x = range(start, min(stop, n) if stop else n)419 for i in range(10):420 y = results[i, x]421 if i in [0, 1, 2, 5, 6, 7]:422 y[y == 0] = np.nan # don't show zero loss values423 # y /= y[0] # normalize424 label = labels[fi] if len(labels) else f.stem425 ax[i].plot(x, y, marker='.', label=label, linewidth=2, markersize=8)426 ax[i].set_title(s[i])427 # if i in [5, 6, 7]: # share train and val loss y axes428 # ax[i].get_shared_y_axes().join(ax[i], ax[i - 5])429 except Exception as e:430 print('Warning: Plotting error for %s; %s' % (f, e))431 432 ax[1].legend()433 fig.savefig(Path(save_dir) / 'results.png', dpi=200)434 435 436def output_to_keypoint(output):437 # Convert model output to target format [batch_id, class_id, x, y, w, h, conf]438 targets = []439 for i, o in enumerate(output):440 kpts = o[:,6:]441 o = o[:,:6]442 for index, (*box, conf, cls) in enumerate(o.detach().cpu().numpy()):443 targets.append([i, cls, *list(*xyxy2xywh(np.array(box)[None])), conf, *list(kpts.detach().cpu().numpy()[index])])444 return np.array(targets)445 446 447def plot_skeleton_kpts(im, kpts, steps, orig_shape=None):448 #Plot the skeleton and keypointsfor coco datatset449 palette = np.array([[255, 128, 0], [255, 153, 51], [255, 178, 102],450 [230, 230, 0], [255, 153, 255], [153, 204, 255],451 [255, 102, 255], [255, 51, 255], [102, 178, 255],452 [51, 153, 255], [255, 153, 153], [255, 102, 102],453 [255, 51, 51], [153, 255, 153], [102, 255, 102],454 [51, 255, 51], [0, 255, 0], [0, 0, 255], [255, 0, 0],455 [255, 255, 255]])456 457 skeleton = [[16, 14], [14, 12], [17, 15], [15, 13], [12, 13], [6, 12],458 [7, 13], [6, 7], [6, 8], [7, 9], [8, 10], [9, 11], [2, 3],459 [1, 2], [1, 3], [2, 4], [3, 5], [4, 6], [5, 7]]460 461 pose_limb_color = palette[[9, 9, 9, 9, 7, 7, 7, 0, 0, 0, 0, 0, 16, 16, 16, 16, 16, 16, 16]]462 pose_kpt_color = palette[[16, 16, 16, 16, 16, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9]]463 radius = 5464 num_kpts = len(kpts) // steps465 466 for kid in range(num_kpts):467 r, g, b = pose_kpt_color[kid]468 x_coord, y_coord = kpts[steps * kid], kpts[steps * kid + 1]469 if not (x_coord % 640 == 0 or y_coord % 640 == 0):470 if steps == 3:471 conf = kpts[steps * kid + 2]472 if conf < 0.5:473 continue474 cv2.circle(im, (int(x_coord), int(y_coord)), radius, (int(r), int(g), int(b)), -1)475 476 for sk_id, sk in enumerate(skeleton):477 r, g, b = pose_limb_color[sk_id]478 pos1 = (int(kpts[(sk[0]-1)*steps]), int(kpts[(sk[0]-1)*steps+1]))479 pos2 = (int(kpts[(sk[1]-1)*steps]), int(kpts[(sk[1]-1)*steps+1]))480 if steps == 3:481 conf1 = kpts[(sk[0]-1)*steps+2]482 conf2 = kpts[(sk[1]-1)*steps+2]483 if conf1<0.5 or conf2<0.5:484 continue485 if pos1[0]%640 == 0 or pos1[1]%640==0 or pos1[0]<0 or pos1[1]<0:486 continue487 if pos2[0] % 640 == 0 or pos2[1] % 640 == 0 or pos2[0]<0 or pos2[1]<0:488 continue489 cv2.line(im, pos1, pos2, (int(r), int(g), int(b)), thickness=2)490 