dataset.py: align test() loader to train

scaling before mosaic with predefined filling_value according to img precision chnaged by scaling before mosaic. filling uint16 with image.mean()
detect.py : align to latest changes as test.py
test.py : support in large object size metrices stats_all_large
plots.py : plot detections with th>0.1 (not 0.25)
pull/2109/head
hanoch 2024-11-04 10:08:35 +02:00
parent c040cf8265
commit 15755c31b9
7 changed files with 216 additions and 62 deletions

View File

@ -0,0 +1,37 @@
lr0: 0.001 #0.001 # initial learning rate (SGD=1E-2, Adam=1E-3)
lrf: 0.01 # final OneCycleLR learning rate (lr0 * lrf)
momentum: 0.937 # SGD momentum/Adam beta1
weight_decay: 0.005 # optimizer weight decay 5e-4 It resolve mAP of overfitting test
warmup_epochs: 3.0 # warmup epochs (fractions ok)
warmup_momentum: 0.8 # warmup initial momentum
warmup_bias_lr: 0.001 #0.001 # warmup initial bias lr
box: 0.05 # box loss gain
cls: 0.5 # cls loss gain
cls_pw: 1.0 # cls BCELoss positive_weight
obj: 1.0 # obj loss gain (scale with pixels)
obj_pw: 1.0 # obj BCELoss positive_weight
iou_t: 0.60 # like the default in the code was 0.2 IoU training threshold
anchor_t: 4.0 # anchor-multiple threshold
anchors: 2 # anchors per output layer (0 to ignore) @@HK was 3
fl_gamma: 1.5 #1.5 # focal loss gamma (efficientDet default gamma=1.5)
hsv_h: 0.0 # image HSV-Hue augmentation (fraction)
hsv_s: 0.0 # image HSV-Saturation augmentation (fraction)
hsv_v: 0.0 # image HSV-Value augmentation (fraction)
degrees: 0 # image rotation (+/- deg)
translate: 0 # image translation (+/- fraction)
scale: 0 # image scale (+/- gain)
shear: 0.0 # image shear (+/- deg)
perspective: 0.0 # image perspective (+/- fraction), range 0-0.001
flipud: 0.3 # image flip up-down (probability)
fliplr: 0.5 # image flip left-right (probability)
mosaic: 0.5 # image mosaic (probability)
mixup: 0.15 # image mixup (probability)
copy_paste: 0.0 # image copy paste (probability)
paste_in: 0.0 # image copy paste (probability), use 0 for faster training : cutout
loss_ota: 0 #1 # use ComputeLossOTA, use 0 for faster training
inversion: 0.5 #opposite temperature
img_percentile_removal: 0.3
beta : 0.3
random_perspective : 0
scaling_before_mosaic : 1
gamma : 80 # percent

View File

@ -40,7 +40,7 @@ def detect(save_img=False):
imgsz = check_img_size(imgsz, s=stride) # check img_size
if trace:
model = TracedModel(model, device, opt.img_size)
model = TracedModel(model, device, opt.img_size, opt.input_channels)
if half:
model.half() # to FP16
@ -58,7 +58,10 @@ def detect(save_img=False):
cudnn.benchmark = True # set True to speed up constant image size inference
dataset = LoadStreams(source, img_size=imgsz, stride=stride)
else:
dataset = LoadImages(source, img_size=imgsz, stride=stride, scaling_type=opt.norm_type)
dataset = LoadImages(source, img_size=imgsz, stride=stride,
scaling_type=opt.norm_type, input_channels=opt.input_channels,
no_tir_signal=opt.no_tir_signal,
tir_channel_expansion=opt.tir_channel_expansion)
# Get names and colors
names = model.module.names if hasattr(model, 'module') else model.names
@ -66,7 +69,7 @@ def detect(save_img=False):
# Run inference
if device.type != 'cpu':
model(torch.zeros(1, 3, imgsz, imgsz).to(device).type_as(next(model.parameters()))) # run once
model(torch.zeros(1, opt.input_channels, imgsz, imgsz).to(device).type_as(next(model.parameters()))) # run once
old_img_w = old_img_h = imgsz
old_img_b = 1
@ -207,7 +210,23 @@ if __name__ == '__main__':
'single_image_percentile_0_1', 'remove+global_outlier_0_1'],
help='Normalization approach')
parser.add_argument('--no-tir-signal', action='store_true', help='')
parser.add_argument('--tir-channel-expansion', action='store_true', help='drc_per_ch_percentile')
parser.add_argument('--input-channels', type=int, default=3, help='')
parser.add_argument('--save-path', default='', help='save to project/name')
opt = parser.parse_args()
if opt.tir_channel_expansion: # operates over 3 channels
opt.input_channels = 3
if opt.tir_channel_expansion and opt.norm_type != 'single_image_percentile_0_1': # operates over 3 channels
print('Not a good combination')
print(opt)
#check_requirements(exclude=('pycocotools', 'thop'))
@ -226,4 +245,10 @@ python -u ./yolov7/detect.py --weights ./yolov7/yolov7.pt --conf 0.25 --img-size
--weights ./yolov7/yolov7.pt --conf 0.25 --img-size 640 --device 0 --save-txt --norm-type single_image_percentile_0_1 --source /home/hanoch/projects/tir_frames_rois/yolo7_tir_data_all/TIR10_v20_Dec18_Test22C_20181127_223533_FS_210F_0001_5500_ROTEM_left_roi_220_4707.tiff
--weights ./yolov7/yolov7.pt --conf 0.25 --img-size 640 --device 0 --save-txt --norm-type single_image_percentile_0_1 --source /home/hanoch/projects/tir_frames_rois/yolo7_tir_data_all/TIR10_V50_OCT21_Test46A_ML_RD_IL_2021_08_05_14_48_05_FS_210_XGA_630_922_DENIS_right_roi_210_881.tiff
--weights ./yolov7/yolov7.pt --conf 0.25 --img-size 640 --device 0 --save-txt --norm-type single_image_percentile_0_1 --source /home/hanoch/projects/tir_frames_rois/yolo7_tir_data_all/TIR135_V80_JUL23_Test55A_SY_RD_US_2023_01_18_07_29_38_FS_50_XGA_0001_3562_Shahar_left_roi_50_1348.tiff
YOLO model
--weights ./yolov7/yolov7.pt --conf 0.25 --img-size 640 --device 0 --save-txt --norm-type single_image_percentile_0_1 --source /home/hanoch/projects/tir_od/Snipaste_2024-09-15_09-00-58_tir_135_TIR135_V80_JUL23_Test55A_SY_RD_US_2023_01_18_07_29_38_FS_50_XGA_0001_3562_Shahar_left_roi_50_1348.png
--weights /mnt/Data/hanoch/runs/train/yolov7575/weights/best.pt --conf 0.01 --img-size 640 --input-channels 1 --device 0 --save-txt --norm-type single_image_percentile_0_1 --source /home/hanoch/projects/tir_frames_rois/tir_tiff_tiff_files/TIR8_V50_Test19G_Jul20_2018-12-06_13-39-17_FS_50F_0114_6368_ROTEM_right_roi_50_345.tiff
"""

55
test.py
View File

@ -40,8 +40,11 @@ def test(data,
half_precision=True,
trace=False,
is_coco=False,
v5_metric=False):
v5_metric=False,
**kwargs):
# Initialize/load model and set device
hyp = kwargs['hyp']
training = model is not None
if training: # called by train.py
device = next(model.parameters()).device # get model device
@ -93,14 +96,15 @@ def test(data,
log_imgs = min(wandb_logger.log_imgs, 100)
# Dataloader
hyp = dict()
hyp['person_size_small_medium_th'] = 32 * 32
hyp['car_size_small_medium_th'] = 44 * 44
if not training:
if device.type != 'cpu':
model(torch.zeros(1, opt.input_channels, imgsz, imgsz).to(device).type_as(next(model.parameters()))) # run once
task = opt.task if opt.task in ('train', 'val', 'test') else 'val' # path to train/val/test images
hyp = dict()
hyp['person_size_small_medium_th'] = 32 * 32
hyp['car_size_small_medium_th'] = 44 * 44
hyp['img_percentile_removal'] = 0.3
hyp['beta'] = 0.3
hyp['gamma'] = 80 # dummy anyway augmentation is disabled
@ -113,14 +117,14 @@ def test(data,
print("Testing with YOLOv5 AP metric...")
seen = 0
confusion_matrix = ConfusionMatrix(nc=nc, conf=0.25) # HK todo: change per conf threshold given by user
confusion_matrix = ConfusionMatrix(nc=nc, conf=conf_thres, iou_thres=iou_thres) # HK per conf per iou_thresh
names = {k: v for k, v in enumerate(model.names if hasattr(model, 'names') else model.module.names)}
coco91class = coco80_to_coco91_class()
s = ('%20s' + '%12s' * 6) % ('Class', 'Images', 'Labels', 'P', 'R', 'mAP@.5', 'mAP@.5:.95')
p, r, f1, mp, mr, map50, map, t0, t1 = 0., 0., 0., 0., 0., 0., 0., 0., 0.
loss = torch.zeros(3, device=device)
jdict, stats, ap, ap_class, wandb_images = [], [], [], [], []
stats_person_small, stats_person_medium = [], []
stats_all_large, stats_person_medium = [], []
for batch_i, (img, targets, paths, shapes) in enumerate(tqdm(dataloader, desc=s)):
img = img.to(device, non_blocking=True)
img = img.half() if half else img.float()
@ -144,6 +148,7 @@ def test(data,
lb = [targets[targets[:, 0] == i, 1:] for i in range(nb)] if save_hybrid else [] # for autolabelling
t = time_synchronized()
out = non_max_suppression(out, conf_thres=conf_thres, iou_thres=iou_thres, labels=lb, multi_label=True) # Does thresholding for class : list of detections, on (n,6) tensor per image [xyxy, conf, cls]
# out = non_max_suppression(out, conf_thres=conf_thres, iou_thres=iou_thres, labels=lb, multi_label=False) # Does thresholding for class : list of detections, on (n,6) tensor per image [xyxy, conf, cls]
t1 += time_synchronized() - t
# Statistics per image
@ -157,7 +162,7 @@ def test(data,
if len(pred) == 0:
if nl:
stats.append((torch.zeros(0, niou, dtype=torch.bool), torch.Tensor(), torch.Tensor(), tcls)) #niou for COCO 0.5:0.05:1
stats_person_small.append((torch.zeros(0, niou, dtype=torch.bool), torch.Tensor(), torch.Tensor(), tcls))
stats_all_large.append((torch.zeros(0, niou, dtype=torch.bool), torch.Tensor(), torch.Tensor(), tcls))
stats_person_medium.append((torch.zeros(0, niou, dtype=torch.bool), torch.Tensor(), torch.Tensor(), tcls))
continue
@ -208,7 +213,7 @@ def test(data,
tbox = xywh2xyxy(labels[:, 1:5])
scale_coords(img[si].shape[1:], tbox, shapes[si][0], shapes[si][1]) # native-space labels
if plots:
confusion_matrix.process_batch(predn, torch.cat((labels[:, 0:1], tbox), 1))
confusion_matrix.process_batch(predn, torch.cat((labels[:, 0:1], tbox), 1))
# Per target class
for cls in torch.unique(tcls_tensor):
@ -236,13 +241,14 @@ def test(data,
if not training or 1:
# assert len(pred[:, :4]) == 1
x, y, w, h = xyxy2xywh(pred[:, :4])[0]
if w * h < hyp['person_size_small_medium_th']:
stats_person_small.append((correct.cpu(), pred[:, 4].cpu(), pred[:, 5].cpu(), tcls))
else:
if w * h > hyp['person_size_small_medium_th']:
stats_person_medium.append((correct.cpu(), pred[:, 4].cpu(), pred[:, 5].cpu(), tcls))
if w * h > hyp['car_size_small_medium_th']:
stats_all_large.append((correct.cpu(), pred[:, 4].cpu(), pred[:, 5].cpu(), tcls))
# Plot images aa = np.repeat(img[0,:,:,:].cpu().permute(1,2,0).numpy(), 3, axis=2).astype('float32') cv2.imwrite('test/exp40/test_batch88_labels__.jpg', aa*255)
if plots and batch_i < 10 or 1:
# conf_thresh_plot = 0.1
f = save_dir / f'test_batch{batch_i}_labels.jpg' # labels
Thread(target=plot_images, args=(img, targets, paths, f, names), daemon=True).start()
f = save_dir / f'test_batch{batch_i}_pred.jpg' # predictions
@ -253,7 +259,7 @@ def test(data,
if not training or 1:
stats_person_medium = [np.concatenate(x, 0) for x in zip(*stats_person_medium)] # to numpy
stats_person_small = [np.concatenate(x, 0) for x in zip(*stats_person_small)] # to numpy
stats_all_large = [np.concatenate(x, 0) for x in zip(*stats_all_large)] # to numpy
if len(stats) and stats[0].any(): # P, R @ # max F1 index
p, r, ap, f1, ap_class = ap_per_class(*stats, plot=plots, v5_metric=v5_metric, save_dir=save_dir, names=names) #based on correct @ IOU=0.5 of pred box with target
@ -263,6 +269,11 @@ def test(data,
ap50_med, ap_med = ap_med[:, 0], ap_med.mean(1) # AP@0.5, AP@0.5:0.95
mp_med, mr_med, map50_med, map_med = p_med.mean(), r_med.mean(), ap50_med.mean(), ap_med.mean()
nt_med = np.bincount(stats_person_medium[3].astype(np.int64), minlength=nc) # number of targets per class
if bool(stats_all_large):
p_large, r_large, ap_large, f1_large, ap_class_large = ap_per_class(*stats_all_large, plot=plots, v5_metric=v5_metric, save_dir=save_dir, names=names)
ap50_large, ap_large = ap_large[:, 0], ap_large.mean(1) # AP@0.5, AP@0.5:0.95
mp_large, mr_large, map50_large, map_large = p_large.mean(), r_large.mean(), ap50_large.mean(), ap_large.mean()
nt_large = np.bincount(stats_all_large[3].astype(np.int64), minlength=nc) # number of targets per class
ap50, ap = ap[:, 0], ap.mean(1) # AP@0.5, AP@0.5:0.95
mp, mr, map50, map = p.mean(), r.mean(), ap50.mean(), ap.mean()
@ -271,6 +282,7 @@ def test(data,
else:
nt = torch.zeros(1)
nt_med = torch.zeros(1)
nt_large = torch.zeros(1)
# Print results
pf = '%20s' + '%12i' * 2 + '%12.3g' * 4 # print format
@ -282,6 +294,12 @@ def test(data,
except Exception as e:
print(e)
if bool(stats_all_large):
try:
print(pf % ('all_large', seen, nt_large.sum(), mp_large, mr_large, map50_large, map_large))
except Exception as e:
print(e)
file_path = os.path.join(save_dir, 'class_stats.txt') #'Class', 'Images', 'Labels', 'P', 'R', 'mAP@.5', 'mAP@.5:.95'
append_to_txt(file_path, 'all', seen, nt.sum(), mp, mr, map50, map)
@ -298,6 +316,14 @@ def test(data,
except Exception as e:
print(e)
try:
if bool(stats_all_large):
for i, c in enumerate(ap_class_large):
print(pf % (names[c]+ '_large', seen, nt_large[c], p_large[i], r_large[i], ap50_large[i], ap_large[i]))
append_to_txt(file_path, names[c] + '_large', seen, nt_large[c], p_large[i], r_large[i], ap50_large[i], ap_large[i])
except Exception as e:
print(e)
# Print speeds
t = tuple(x / seen * 1E3 for x in (t0, t1, t0 + t1)) + (imgsz, imgsz, batch_size) # tuple
if not training:
@ -395,6 +421,7 @@ if __name__ == '__main__':
opt.data = check_file(opt.data) # check file
print(opt)
#check_requirements()
hyp = dict()
if opt.task in ('train', 'val', 'test'): # run normally
test(opt.data,
@ -411,8 +438,8 @@ if __name__ == '__main__':
save_hybrid=opt.save_hybrid,
save_conf=opt.save_conf,
trace=not opt.no_trace,
v5_metric=opt.v5_metric
)
v5_metric=opt.v5_metric,
hyp=hyp)
elif opt.task == 'speed': # speed benchmarks
for w in opt.weights:

View File

@ -567,6 +567,8 @@ def train(hyp, opt, device, tb_writer=None):
# mAP
ema.update_attr(model, include=['yaml', 'nc', 'hyp', 'gr', 'names', 'stride', 'class_weights'])
final_epoch = epoch + 1 == epochs
if not opt.notest or final_epoch: # Calculate mAP
wandb_logger.current_epoch = epoch + 1
results, maps, times = test.test(data_dict,
@ -583,7 +585,8 @@ def train(hyp, opt, device, tb_writer=None):
wandb_logger=wandb_logger,
compute_loss=compute_loss,
is_coco=is_coco,
v5_metric=opt.v5_metric)
v5_metric=opt.v5_metric,
hyp=hyp)
# Write
with open(results_file, 'a') as f:
@ -803,6 +806,9 @@ if __name__ == '__main__':
# Hyperparameters
with open(opt.hyp) as f:
hyp = yaml.load(f, Loader=yaml.SafeLoader) # load hyps
#defaults for backward compatible hyp files whree not set
hyp['person_size_small_medium_th'] = hyp.get('person_size_small_medium_th', 32 * 32)
hyp['car_size_small_medium_th'] = hyp.get('car_size_small_medium_th', 44 * 44)
# Train
logger.info(opt)

View File

@ -124,6 +124,8 @@ def create_dataloader(path, imgsz, batch_size, stride, opt, hyp=None, augment=Fa
print('gamma_liklihood was overriden by optional value ', opt.gamma_aug_prob)
with torch_distributed_zero_first(rank):
scaling_before_mosaic = bool(hyp.get('scaling_before_mosaic', False))
dataset = LoadImagesAndLabels(path, imgsz, batch_size,
augment=augment, # augment images
hyp=hyp, # augmentation hyperparameters
@ -139,7 +141,8 @@ def create_dataloader(path, imgsz, batch_size, stride, opt, hyp=None, augment=Fa
input_channels=opt.input_channels,
num_cls=num_cls,
tir_channel_expansion=opt.tir_channel_expansion,
no_tir_signal=opt.no_tir_signal)
no_tir_signal=opt.no_tir_signal,
scaling_before_mosaic=scaling_before_mosaic)
batch_size = min(batch_size, len(dataset))
nw = min([os.cpu_count() // world_size, batch_size if batch_size > 1 else 0, workers]) # number of workers
@ -191,7 +194,8 @@ class _RepeatSampler(object):
class LoadImages: # for inference
def __init__(self, path, img_size=640, stride=32,
scaling_type='standardization', img_percentile_removal=0.3, beta=0.3, input_channels=3):
scaling_type='standardization', img_percentile_removal=0.3, beta=0.3, input_channels=3,
tir_channel_expansion=False, no_tir_signal=False):
p = str(Path(path).absolute()) # os-agnostic absolute path
if '*' in p:
@ -224,6 +228,9 @@ class LoadImages: # for inference
self.percentile = img_percentile_removal
self.beta = beta
self.input_channels = input_channels
self.tir_channel_expansion = tir_channel_expansion
self.is_tir_signal = not (no_tir_signal)
def __iter__(self):
self.count = 0
@ -267,6 +274,23 @@ class LoadImages: # for inference
# Padded resize
img = letterbox(img0, self.img_size, stride=self.stride)[0]
if self.tir_channel_expansion: # HK @@ according to the paper this CE is a sort of augmentation hence no need to preliminary augment. One of the channels are inversion hence avoid channel inversion aug
img = np.repeat(img[np.newaxis, :, :], 3, axis=0) # convert GL to RGB by replication
img_ce = np.zeros_like(img).astype('float64')
# CH1 hist equalization
img_chan = scaling_image(img[0, :, :], scaling_type=self.scaling_type,
percentile=0, beta=self.beta)
img_ce[0, :, :] = img_chan.astype('float64')
img_chan = scaling_image(img[1, :, :], scaling_type=self.scaling_type,
percentile=self.percentile, beta=self.beta)
img_ce[1, :, :] = img_chan.astype('float64')
img_chan = inversion_aug(img_ce[1, :, :]) # invert the DRC one
img_ce[2, :, :] = img_chan.astype('float64')
img = img_ce
if not self.tir_channel_expansion:
if self.is_tir_signal:
@ -454,8 +478,9 @@ class LoadImagesAndLabels(Dataset): # for training/testing
def __init__(self, path, img_size=640, batch_size=16, augment=False, hyp=None, rect=False, image_weights=False,
cache_images=False, single_cls=False, stride=32, pad=0.0, prefix='', rel_path_images='',
scaling_type='standardization', input_channels=3,
num_cls=-1, tir_channel_expansion=False, no_tir_signal=False):
num_cls=-1, tir_channel_expansion=False, no_tir_signal=False, scaling_before_mosaic=False):
self.scaling_before_mosaic = scaling_before_mosaic
self.img_size = img_size
self.augment = augment
self.hyp = hyp
@ -655,36 +680,41 @@ class LoadImagesAndLabels(Dataset): # for training/testing
def __getitem__(self, index):
index = self.indices[index] # linear, shuffled, or image_weights
if self.is_tir_signal:
if self.scaling_before_mosaic:
filling_value = 0.5 # on borders or after perspective fill with 0.5 in [0 1] equals to 114 in [0 255]
else:
filling_value = 0 # on borders or after perspective better to have 0 thermal profile uint16 based on the DR of the image which is unknown TODO: better find an elegent way
else:
filling_value = 114
hyp = self.hyp
mosaic = self.mosaic and random.random() < hyp['mosaic'] and not(self.tir_channel_expansion)
if mosaic:
# Load mosaic
if random.random() < 0.8:
img, labels = load_mosaic(self, index)
img, labels = load_mosaic(self, index, filling_value=filling_value)
else:
img, labels = load_mosaic9(self, index)
img, labels = load_mosaic9(self, index, filling_value=filling_value)
shapes = None
# MixUp https://arxiv.org/pdf/1710.09412.pdf
if random.random() < hyp['mixup']:
if random.random() < 0.8:
img2, labels2 = load_mosaic(self, random.randint(0, len(self.labels) - 1))
img2, labels2 = load_mosaic(self, random.randint(0, len(self.labels) - 1), filling_value=filling_value)
else:
img2, labels2 = load_mosaic9(self, random.randint(0, len(self.labels) - 1))
img2, labels2 = load_mosaic9(self, random.randint(0, len(self.labels) - 1), filling_value=filling_value)
r = np.random.beta(8.0, 8.0) # mixup ratio, alpha=beta=8.0
img = (img * r + img2 * (1 - r)).astype(img.dtype)#.astype(np.uint8)
labels = np.concatenate((labels, labels2), 0)
# if np.isnan(img).any():
# print('img is nan mosaic')
else:
# Load image
img, (h0, w0), (h, w) = load_image(self, index)
# Letterbox
shape = self.batch_shapes[self.batch[index]] if self.rect else self.img_size # final letterboxed shape
img, ratio, pad = letterbox(img, shape, auto=False, scaleup=self.augment)
img, ratio, pad = letterbox(img, shape, color=(filling_value, filling_value, filling_value), auto=False, scaleup=self.augment)
shapes = (h0, w0), ((h / h0, w / w0), pad) # for COCO mAP rescaling
labels = self.labels[index].copy()
@ -718,20 +748,21 @@ class LoadImagesAndLabels(Dataset): # for training/testing
translate=hyp['translate'],
scale=hyp['scale'],
shear=hyp['shear'],
perspective=hyp['perspective'])
perspective=hyp['perspective'],
filling_value=filling_value)
if random.random() < hyp['inversion']:
img = inversion_aug(img)
# GL gain/attenuation
# Squeeze pdf (x-mu)*scl+mu
# GL gain/attenuation
# Squeeze pdf (x-mu)*scl+mu
#img, labels = self.albumentations(img, labels)
img = self.albumentations_gamma_contrast(img)
# if np.isnan(img).any():
# print('img is nan gamma')
if hyp['hsv_h'] >0 or hyp['hsv_s'] >0 or hyp['hsv_v'] >0 :
if hyp['hsv_h'] > 0 or hyp['hsv_s'] > 0 or hyp['hsv_v'] > 0:
# Augment colorspace
augment_hsv(img, hgain=hyp['hsv_h'], sgain=hyp['hsv_s'], vgain=hyp['hsv_v'])
@ -781,17 +812,16 @@ class LoadImagesAndLabels(Dataset): # for training/testing
else:
# Convert
img = img[:, :, ::-1].transpose(2, 0, 1) # BGR to RGB, to 3x416x416
# else:
# # img = img[np.newaxis,...] # unsqueeze
# img = np.repeat(img[np.newaxis, :, :], self.input_channels, axis=0) #convert GL to RGB by replication
# print('\n image file', self.img_files[index])
if 0:
import matplotlib.pyplot as plt
plt.figure()
plt.hist(img.ravel(), bins=128)
plt.savefig(os.path.join('/home/hanoch/projects/tir_od/outputs', os.path.basename(self.img_files[index]).split('.')[0]+ 'pre_' +str(self.scaling_type)))
# tifffile.imwrite(os.path.join('/home/hanoch/projects/tir_od', 'img_ce_before.tiff'), img.transpose(1,2,0))
# import tifffile
# tifffile.imwrite(os.path.join('/home/hanoch/projects/tir_od', 'img_before_last_scaling.tiff'), img.transpose(1,2,0))
img = scaling_image(img, scaling_type=self.scaling_type,
percentile=self.percentile, beta=self.beta)
if 0:
@ -803,9 +833,9 @@ class LoadImagesAndLabels(Dataset): # for training/testing
# cv2.imwrite('test/exp40/test_batch88_labels__1.jpg', aa1*255)
# aa1 = np.repeat(img.transpose(1,2,0), 3, axis=2).astype('float32')
# print('\n 1st', img.shape)
if np.isnan(img).any():
print('img {} index : {} is nan fin'.format(self.img_files[index], index))
# if np.isnan(img).any():
# print('img {} index : {} is nan fin'.format(self.img_files[index], index))
# raise
img = np.ascontiguousarray(img)
# print('\n 2nd', img.shape)
return torch.from_numpy(img), labels_out, self.img_files[index], shapes
@ -902,7 +932,7 @@ def hist_equalize(img, clahe=True, bgr=False):
return cv2.cvtColor(yuv, cv2.COLOR_YUV2BGR if bgr else cv2.COLOR_YUV2RGB) # convert YUV image to RGB
def load_mosaic(self, index):
def load_mosaic(self, index, filling_value):
# loads images in a 4-mosaic
labels4, segments4 = [], []
@ -912,10 +942,17 @@ def load_mosaic(self, index):
for i, index in enumerate(indices):
# Load image
img, _, (h, w) = load_image(self, index)
# place img in img4
if self.scaling_before_mosaic:
img = scaling_image(img, scaling_type=self.scaling_type,
percentile=self.percentile, beta=self.beta)
# place img in img4
if i == 0: # top left
if self.is_tir_signal:
img4 = np.full((s * 2, s * 2, img.shape[2]), 0, dtype=np.uint16) # base image with 4 tiles
if self.scaling_before_mosaic:
img4 = np.full((s * 2, s * 2, img.shape[2]), 0.5, dtype=np.float32) # base image with 4 tiles
else:
img4 = np.full((s * 2, s * 2, img.shape[2]), img.mean(), dtype=np.uint16) # base image with 4 tiles
else:
img4 = np.full((s * 2, s * 2, img.shape[2]), 114, dtype=np.uint8) # base image with 4 tiles
x1a, y1a, x2a, y2a = max(xc - w, 0), max(yc - h, 0), xc, yc # xmin, ymin, xmax, ymax (large image)
@ -958,12 +995,13 @@ def load_mosaic(self, index):
scale=self.hyp['scale'],
shear=self.hyp['shear'],
perspective=self.hyp['perspective'],
border=self.mosaic_border) # border to remove
border=self.mosaic_border,
filling_value=filling_value) # border to remove
return img4, labels4
def load_mosaic9(self, index):
def load_mosaic9(self, index, filling_value):
# loads images in a 9-mosaic
labels9, segments9 = [], []
@ -972,14 +1010,17 @@ def load_mosaic9(self, index):
for i, index in enumerate(indices):
# Load image
img, _, (h, w) = load_image(self, index)
# if img.ndim <3 : #TIR =>unsqueeze ro RGB 1-channel
# tir_signal = True
# img = img[:, :, np.newaxis] #np.repeat(img[:, :, np.newaxis], 3, axis=2) #img[:, :, np.newaxis]
if self.scaling_before_mosaic:
img = scaling_image(img, scaling_type=self.scaling_type,
percentile=self.percentile, beta=self.beta)
# place img in img9
if i == 0: # center
if self.is_tir_signal:
img9 = np.full((s * 3, s * 3, img.shape[2]), 0, dtype=np.uint16) # base image with 4 tiles
if self.scaling_before_mosaic:
img9 = np.full((s * 3, s * 3, img.shape[2]), 0.5, dtype=np.float32) # base image with 4 tiles
else:
img9 = np.full((s * 3, s * 3, img.shape[2]), img.mean(), dtype=np.uint16) # base image with 4 tiles pad with : uint16 based on the DR of the image which is unknown
else:
img9 = np.full((s * 3, s * 3, img.shape[2]), 114, dtype=np.uint8) # base image with 4 tiles
@ -1035,13 +1076,17 @@ def load_mosaic9(self, index):
# Augment
#img9, labels9, segments9 = remove_background(img9, labels9, segments9)
img9, labels9, segments9 = copy_paste(img9, labels9, segments9, probability=self.hyp['copy_paste'])
# Perspective transformation can create holes in thermal better fill w/o reflection
img9, labels9 = random_perspective(img9, labels9, segments9,
degrees=self.hyp['degrees'],
translate=self.hyp['translate'],
scale=self.hyp['scale'],
shear=self.hyp['shear'],
perspective=self.hyp['perspective'],
border=self.mosaic_border) # border to remove
border=self.mosaic_border,
filling_value=filling_value) # border to remove
return img9, labels9
@ -1059,7 +1104,14 @@ def load_samples(self, index):
# place img in img4
if i == 0: # top left
img4 = np.full((s * 2, s * 2, img.shape[2]), 114, dtype=np.uint8) # base image with 4 tiles
if self.is_tir_signal:
if self.scaling_before_mosaic:
img4 = np.full((s * 2, s * 2, img.shape[2]), 0.5, dtype=np.float32) # base image with 4 tiles fill with 0.5 in [0 1] equals to 114 in [0 255]
else:
img4 = np.full((s * 2, s * 2, img.shape[2]), img.mean(), dtype=np.uint16) # base image with 4 tiles
else:
img4 = np.full((s * 2, s * 2, img.shape[2]), 114, dtype=np.uint8) # base image with 4 tiles # base image with 4 tiles
x1a, y1a, x2a, y2a = max(xc - w, 0), max(yc - h, 0), xc, yc # xmin, ymin, xmax, ymax (large image)
x1b, y1b, x2b, y2b = w - (x2a - x1a), h - (y2a - y1a), w, h # xmin, ymin, xmax, ymax (small image)
elif i == 1: # top right
@ -1127,6 +1179,7 @@ def remove_background(img, labels, segments):
h, w, c = img.shape # height, width, channels
im_new = np.zeros(img.shape, np.uint8)
img_new = np.ones(img.shape, np.uint8) * 114
raise ValueError('uint8 cast dosnot comply with TIR uint 16')
for j in range(n):
cv2.drawContours(im_new, [segments[j].astype(np.int32)], -1, (255, 255, 255), cv2.FILLED)
@ -1221,7 +1274,7 @@ def letterbox(img, new_shape=(640, 640), color=(114, 114, 114), auto=True, scale
def random_perspective(img, targets=(), segments=(), degrees=10, translate=.1, scale=.1, shear=10, perspective=0.0,
border=(0, 0)):
border=(0, 0), filling_value=114):
# torchvision.transforms.RandomAffine(degrees=(-10, 10), translate=(.1, .1), scale=(.9, 1.1), shear=(-10, 10))
# targets = [cls, xyxy]
@ -1260,9 +1313,9 @@ def random_perspective(img, targets=(), segments=(), degrees=10, translate=.1, s
M = T @ S @ R @ P @ C # order of operations (right to left) is IMPORTANT
if (border[0] != 0) or (border[1] != 0) or (M != np.eye(3)).any(): # image changed
if perspective:
img = cv2.warpPerspective(img, M, dsize=(width, height), borderValue=(114, 114, 114))
img = cv2.warpPerspective(img, M, dsize=(width, height), borderValue=(filling_value, filling_value, filling_value))
else: # affine
img = cv2.warpAffine(img, M[:2], dsize=(width, height), borderValue=(114, 114, 114))
img = cv2.warpAffine(img, M[:2], dsize=(width, height), borderValue=(filling_value, filling_value, filling_value))
# Visualize
# import matplotlib.pyplot as plt

View File

@ -195,7 +195,11 @@ def plot_pr_curve(px, py, ap, save_dir='pr_curve.png', names=(), precisions_of_i
if 0 < len(names) < 21: # display per-class legend if < 21 classes
for i, y in enumerate(py.T):
recall_of_interest_per_class = [px[int(np.where(y.reshape(-1) > x)[0][-1])] for x in precisions_of_interest]
recall_of_interest_per_class = np.zeros_like(precisions_of_interest)
if np.array(precisions_of_interest).min() > np.array(precisions_of_interest).max():
recall_of_interest_per_class = [px[int(np.where(y.reshape(-1) > x)[0][-1])] for x in precisions_of_interest]
ax.plot(px, y, linewidth=1, label=f'{names[i]} {ap[i, 0]:.3f}') # plot(recall, precision)
ax.plot(recall_of_interest_per_class, precisions_of_interest, '*', color='green')
for k in range(len(precisions_of_interest)):
@ -214,7 +218,9 @@ def plot_pr_curve(px, py, ap, save_dir='pr_curve.png', names=(), precisions_of_i
else:
ax.plot(px, py, linewidth=1, color='grey') # plot(recall, precision)
recall_of_interest = [px[int(np.where(py.mean(1).reshape(-1) > x)[0][-1])] for x in precisions_of_interest]
recall_of_interest = np.zeros_like(precisions_of_interest)
if np.array(precisions_of_interest).min() > np.array(precisions_of_interest).max():
recall_of_interest = [px[int(np.where(py.mean(1).reshape(-1) > x)[0][-1])] for x in precisions_of_interest]
ax.plot(px, py.mean(1), linewidth=3, color='blue', label='all classes %.3f mAP@0.5' % ap[:, 0].mean()) # py [ap , num_clases]
ax.plot(recall_of_interest, precisions_of_interest, '*')

View File

@ -59,9 +59,9 @@ def plot_one_box(x, img, color=None, label=None, line_thickness=3):
tl = line_thickness or round(0.002 * (img.shape[0] + img.shape[1]) / 2) + 1 # line/font thickness
color = color or [random.randint(0, 255) for _ in range(3)]
for y in x[:4]:
if np.isnan(y):
print('BP here')
# for y in x[:4]:
# if np.isnan(y):
# print('BP here')
c1, c2 = (int(x[0]), int(x[1])), (int(x[2]), int(x[3]))
cv2.rectangle(img, c1, c2, color, thickness=tl, lineType=cv2.LINE_AA)
@ -180,7 +180,7 @@ def plot_images(images, targets, paths=None, fname='images.jpg', input_channels=
cls = int(classes[j])
color = colors[cls % len(colors)]
cls = names[cls] if names else cls
if labels or conf[j] > 0.25: # 0.25 conf thresh
if labels or conf[j] > 0.1: # 0.25 conf thresh
label = '%s' % cls if labels else '%s %.1f' % (cls, conf[j])
plot_one_box(box, mosaic, label=label, color=color, line_thickness=tl)