mirror of https://github.com/WongKinYiu/yolov7.git
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
parent
c040cf8265
commit
15755c31b9
|
@ -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
|
31
detect.py
31
detect.py
|
@ -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
55
test.py
|
@ -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:
|
||||
|
|
8
train.py
8
train.py
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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, '*')
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
Loading…
Reference in New Issue