def export_croplets(self, anno_index, dir_out, width=256): """Save croplets to disk""" fiox.ensure_dir(dir_out) anno_index_croplets = self.append_croplets(anno_index, width=width) for class_idx, anno_obj in anno_index_croplets.items(): class_label_index = anno_obj['class_index'] dir_class_idx = os.path.join(dir_out, str(class_label_index)) fiox.ensure_dir(dir_class_idx) for region in anno_obj['regions']: try: im = region['croplet'] anno_id = region['id'] fp_croplet = os.path.join(dir_class_idx, '{}.jpg'.format(anno_id)) cv.imwrite(fp_croplet, im) except: print('[-] Error. No croplet.') return False return True
def main(args): if os.path.isdir(args.input): from glob import glob files = glob(join(args.input, '*.mp4')) else: files = [args.input] if len(files) == 0: print('No files in "{}"'.format(args.input)) sys.exit() # create directories where needed if args.save_debug: dir_debug = join(args.output, 'debug') fiox.ensure_dir(dir_debug) if not args.no_save: fiox.ensure_dir(args.output) if not args.no_save_log: # write to log file dir_log = join(args.output, 'log') fiox.ensure_dir(dir_log) f_logfile = join(dir_log, 'log.csv') # start file with open(f_logfile, 'w') as fp: fp.write('# filename, fps, frames, duplicates, logos_found\n') for f in files: print('Process {}'.format(os.path.basename(f))) if os.path.isfile(f): extract_logo(args, f)
def convert_vid2sceneframes(args, fpath): bname = os.path.basename(fpath) name, ext = os.path.splitext(bname) if args.littlefork: save_name = os.path.basename(os.path.dirname(fpath)) else: save_name = bname # reset frames, max read is about 200FPS fps, nframes, frameset = imx.vid2frames(fpath, width=args.width) if nframes == 0: print('fps: {}, nframes: {}, len frames: {}'.format( fps, nframes, len(frameset))) print('[-] ERROR could not read movie {}'.format(bname)) return print('[+] fps: {}, frames: {}'.format(fps, nframes)) # Create Focal Mask # add active zones: horizontal and vertical try: h, w = frameset[0].shape[:2] except: print('[-] Error. No frames available for {}. Skipping video.'.format( fpath)) focal_zone_dim = (0.33, 0.33) x1 = int(((.5 - (focal_zone_dim[0] / 2)) * w)) x2 = int(((.5 + (focal_zone_dim[0] / 2)) * w)) y1 = int(((.5 - (focal_zone_dim[1] / 2)) * h)) y2 = int(((.5 + (focal_zone_dim[1] / 2)) * h)) focal_zone_col = (x1, 0, x2, h) focal_zone_row = (0, y1, w, y2) # add active zone: center rect focal_zone_center_dim = (0.5, 0.5) x1 = int(((.5 - (focal_zone_center_dim[0] / 2)) * w)) x2 = int(((.5 + (focal_zone_center_dim[0] / 2)) * w)) y1 = int(((.5 - (focal_zone_center_dim[1] / 2)) * h)) y2 = int(((.5 + (focal_zone_center_dim[1] / 2)) * h)) focal_zone_center = (x1, y1, x2, y2) focal_regions = [focal_zone_col, focal_zone_row, focal_zone_center] im_focal_mask = np.zeros_like(imx.bgr2gray(frameset[0])).astype(np.bool) for x1, y1, x2, y2 in focal_regions: im_focal_mask[y1:y2, x1:x2] = 1 num_total_pixels = w * h num_focal_pixels = len(np.where(im_focal_mask > 0)[0]) focal_mask_per = num_focal_pixels / (w * h) # # Use Canny Edges to find empty frames # mask focal region of canny images ims_canny_orig = [ cv.Canny(f, args.canny_min, args.canny_max) for f in frameset ] ims_canny = [ np.logical_and(im.astype(np.bool), im_focal_mask) for im in ims_canny_orig ] # focal mask # Compute sum of edge information in focal zone in canny images sum_thresh_per = 1. # max percentage of non-normalized summed edge pixels sum_thresh_pixels = sum_thresh_per / 100. * num_focal_pixels # num pixels ims_canny_sums = np.array([(len(np.where(im > 0)[0])) for im in ims_canny]) canny_max = np.max(ims_canny_sums) ims_canny_sums_norm = [v / canny_max for v in ims_canny_sums] ims_canny_flags = np.array([v > sum_thresh_pixels for v in ims_canny_sums]) # Visualize the frames that will be ignored ims_canny_keep_idxs = np.where(ims_canny_flags == 1)[0] ims_canny_ignore_idxs = np.where(ims_canny_flags == 0)[0] print('[+] Keeping frames more than {} edge pixels ({}%)'.format( int(sum_thresh_per / 100. * num_focal_pixels), sum_thresh_per)) print("[+] Keep: {}, Ignore: {}".format(len(ims_canny_keep_idxs), len(ims_canny_ignore_idxs))) # Use Pixel Mean to find empty frames # use focal mask to average only focal pixels im_focal_mask_uint8 = im_focal_mask.astype(np.uint8) mean_adj = num_total_pixels / num_focal_pixels ims_mean = np.array([ mean_adj * np.mean(imx.bgr2gray(cv.bitwise_and(im, im, mask=im_focal_mask_uint8))) for im in frameset ]) #ims_mean = np.array([mean_adj*imx.bgr2gray(cv.bitwise_and(im,im,mask=im_focal_mask_uint8)) for im in frameset]) max_mean = np.max(ims_mean) ims_mean_norm = ims_mean / max_mean ims_mean_flags = np.array([v > args.mean_thresh for v in ims_mean]) # Keep 1-flag frames # # Combine Edge/Mean Vals to Filter Empty Frames # Mark frames as 1 to keep, 0 to ignore # use logical OR on inverted logic to keep, then de-invert to keep 1(keep), 0(skip) structure ims_flags_comb = np.invert( np.logical_or(np.invert(ims_canny_flags), np.invert(ims_mean_flags))) print('[+] Combined mean + edge frames to skip: {}'.format( len(np.where(ims_flags_comb == 0)[0]))) # gather the frames # Visualize the frames that will be ignored ims_keep_idxs = np.where(ims_flags_comb == 1)[0] ims_ignore_idxs = np.where(ims_flags_comb == 0)[0] # Load frames frames_ignore = [frameset[i] for i in ims_ignore_idxs] frames_keep = [frameset[i] for i in ims_keep_idxs] # TODO, inspect src --> dst error print('[+] Computing feature vectors...(ignoring warnings)') vals_phash = [imx.compute_phash(f) for f in frameset] #vals_feats = imx.compute_img2vec_feats(frameset,vals_phash,phash_thresh=1) # Paramerterize fe = feature_extractor_pytorch.FeatureExtractor(cuda=True, net='alexnet') vals_feats = imx.compute_features(fe, frameset, vals_phash, phash_thresh=2) scenes = [] scene_idxs = [] start_at = 0 end_at = len(frameset) last_scene_idx = 0 for idx in range(start_at, end_at): if ims_flags_comb[idx] == False: continue feat_delta = imx.cosine_delta(vals_feats[idx], vals_feats[last_scene_idx]) phash_delta = (vals_phash[idx] - vals_phash[last_scene_idx]) if feat_delta > args.thresh_feat_a or phash_delta > args.thresh_phash_a: last_scene_idx = idx scenes.append(scene_idxs) scene_idxs = [] scene_idxs.append(idx) scenes.append(scene_idxs) # reduce scenes by removing transition frames min_scene_frames = int(fps * args.min_scene_duration) scenes_tmp = scenes.copy() scenes = [s for s in scenes_tmp if len(s) > min_scene_frames] scene_rep_idxs = [] for scene in scenes: scene_edge_sum = ims_canny_sums[scene[0]:] # ensure there are best_idx = np.argmax(scene_edge_sum) + scene[0] scene_rep_idxs.append(best_idx) print("[+] Feature based scenes: {}, best: {}".format( len(scenes), len(scene_rep_idxs))) # Get final scene indic scene_deduped_idxs = imx.dedupe_idxs(scene_rep_idxs, phashes=vals_phash, feats=vals_feats, feat_thresh=args.thresh_feat_b, phash_thresh=args.thresh_phash_b) # if too few, decrease thresholding nscene_frames = len(scene_deduped_idxs) print('[+] {} scenes with default thresh'.format(nscene_frames)) if nscene_frames > 0 and nscene_frames < args.min_frames: print('[+] dec thresh') scene_deduped_idxs = imx.dedupe_idxs( scene_rep_idxs, phashes=vals_phash, feats=vals_feats, feat_thresh=args.thresh_feat_b / 2, phash_thresh=args.thresh_phash_b / 2) # if too few, increase thresholding elif nscene_frames > args.max_frames: print('[+] too many frames, increasing threshold') scene_deduped_idxs = imx.dedupe_idxs( scene_rep_idxs, phashes=vals_phash, feats=vals_feats, feat_thresh=args.thresh_feat_b * 1.25, phash_thresh=args.thresh_phash_b * 1.25) # Create dense keyframes representation with KMeans clusters if len(scene_deduped_idxs) < args.max_dense_frames: scene_frames_dense_idxs = scene_deduped_idxs.copy() else: dense_max_frames = min(args.max_dense_frames, len(scene_deduped_idxs)) n_pca_components = min(12, len(scene_deduped_idxs)) vec_length = len(vals_feats[0]) vec_mat = np.zeros((len(scene_deduped_idxs), vec_length)) for i in range(len(scene_deduped_idxs)): idx = scene_deduped_idxs[i] vec = vals_feats[idx] vec_mat[i, :] = vec print('[+] using {} PCA components'.format(n_pca_components)) reduced_data = PCA( n_components=n_pca_components).fit_transform(vec_mat) kmeans = KMeans(init='k-means++', n_clusters=dense_max_frames, n_init=10) kmeans.fit(reduced_data) preds = kmeans.predict(reduced_data) scene_frames_dense_idxs = [] clusters_used = [] for i in range(len(scene_deduped_idxs)): idx = scene_deduped_idxs[i] if not preds[i] in clusters_used: clusters_used.append(preds[i]) scene_frames_dense_idxs.append(idx) # reload video cap = cv.VideoCapture(fpath) print('[+] {}'.format(fpath)) # Create list of poster frames (resized) # set capture object to frame index - 1 (read next frame) scene_frames_poster = [] for idx in scene_deduped_idxs: cap.set(cv.CAP_PROP_POS_FRAMES, idx - 1) res, frame = cap.read() scene_frames_poster.append( imutils.resize(frame, width=args.poster_im_width)) # Create list of full resolution frames scene_frames_full = [] for idx in scene_deduped_idxs: cap.set(cv.CAP_PROP_POS_FRAMES, idx - 1) res, frame = cap.read() scene_frames_full.append(frame) scene_frames_poster_dense = [] for idx in scene_frames_dense_idxs: cap.set(cv.CAP_PROP_POS_FRAMES, idx - 1) res, frame = cap.read() scene_frames_poster_dense.append( imutils.resize(frame, width=args.poster_im_width)) scene_frames_dense = [] for idx in scene_frames_dense_idxs: cap.set(cv.CAP_PROP_POS_FRAMES, idx - 1) res, frame = cap.read() scene_frames_dense.append(frame) # pad with empty frames if len(scene_frames_poster_dense) > 0: while len(scene_frames_poster_dense) < args.max_dense_frames: scene_frames_poster_dense.append( np.zeros_like(scene_frames_poster_dense[0])) #scene_frames_all = [frameset[idx] for idx in scene_rep_idxs] n = len(scene_rep_idxs) - len(scene_deduped_idxs) print('[+] {} scene keyframes removed from de-duplication'.format(n)) print('[+] {} final scene frames'.format(len(scene_deduped_idxs))) if len(scene_deduped_idxs) > 0: # save the montage of all keyframes im_summary = imx.ims2montage(scene_frames_poster, ncols=args.num_cols) #fname = os.path.splitext(os.path.basename(f))[0] dir_poster = join(args.output, 'posters') fiox.ensure_dir(dir_poster) fp_im = join(dir_poster, '{}.jpg'.format(save_name)) cv.imwrite(fp_im, im_summary) # save the montage of dense keyframes im_summary = imx.ims2montage(scene_frames_poster_dense, ncols=args.num_cols_dense) #fname = os.path.splitext(os.path.basename(f))[0] dir_poster_dense = join(args.output, 'posters_dense') fiox.ensure_dir(dir_poster_dense) fp_im = join(dir_poster_dense, '{}.jpg'.format(save_name)) cv.imwrite(fp_im, im_summary) # output all video scene keyframes dir_frames_png = join(args.output, 'frames_png', save_name) dir_frames_jpg = join(args.output, 'frames_jpg', save_name) fiox.ensure_dir(dir_frames_png) fiox.ensure_dir(dir_frames_jpg) for idx, im in zip(scene_deduped_idxs, scene_frames_full): fp_im_png = join(dir_frames_png, '{:06d}.png'.format(idx)) fp_im_jpg = join(dir_frames_jpg, '{:06d}.jpg'.format(idx)) cv.imwrite(fp_im_png, im) cv.imwrite(fp_im_jpg, im) # write all dense keyframes for web dir_dense_frames_jpg = join(args.output, 'frames_dense_jpg', save_name) fiox.ensure_dir(dir_dense_frames_jpg) for idx, im in zip(scene_frames_dense_idxs, scene_frames_dense): fp_im_jpg = join(dir_dense_frames_jpg, '{:06d}.jpg'.format(idx)) cv.imwrite(fp_im_jpg, im) else: print('[+] no scenes found')
def create_project(self, kwargs): vcat_utils = VcatUtils() # load vcat JSON vcat_data = json.load(kwargs['input']) # hierarchy is hierarchy = vcat_data['hierarchy'] # if exclusions, remove classes if kwargs['exclude'] is not None: exclusions = [int(i) for i in kwargs['exclude'].split(',')] print('[+] Excluding: {}'.format(exclusions)) # remove from hierarchy hierarchy_tmp = hierarchy.copy() for obj_class_id, obj_class_meta in hierarchy_tmp.items(): if int(obj_class_id) in exclusions: print('[+] Removing ID: {} ({})'.format( obj_class_id, obj_class_meta['slug'])) del hierarchy[obj_class_id] # class label index lookup (YOLO ID --> VCAT ID) class_label_index_lkup = vcat_utils.create_class_label_index( vcat_data, kwargs) print('----Classes----') for k, v in class_label_index_lkup.items(): print('VCAT: {} --> YOLO: {}, Label: {}'.format( k, v, hierarchy[str(k)]['slug'])) # create object class lookup anno_index = vcat_utils.create_annotation_index( vcat_data, class_label_index_lkup, kwargs) for a in anno_index: print('Annotation index: {}'.format(a)) sys.exit() # inflate annotation index to include parent class (ghost) annotations # TODO # create image index lookup image_anno_index = vcat_utils.create_image_anno_index(anno_index) # Create randomize, class-based splits for train and valid anno_splits = vcat_utils.create_splits(anno_index, split_val=kwargs['split_val']) annos_train = anno_splits['train'] annos_valid = anno_splits['valid'] # Statistics n_regions_train = np.sum( [len(v['regions']) for k, v in annos_train.items()]) n_regions_test = np.sum( [len(v['regions']) for k, v in annos_valid.items()]) print('[+] Regions train: {}, Test: {}'.format(n_regions_train, n_regions_test)) print('----Classes----') for k, v in class_label_index_lkup.items(): print('VCAT: {} --> YOLO: {}, Label: {}'.format( k, v, hierarchy[str(k)]['slug'])) print('----Train----') for k, v in annos_train.items(): print('{}\t{}'.format(len(v['regions']), v['slug_vcat'])) print('----Validate----') for k, v in annos_valid.items(): print('{}\t{}'.format(len(v['regions']), v['slug_vcat'])) print('----Labels----') for k, v in class_label_index_lkup.items(): print('{}\t{}'.format(k, hierarchy[str(k)]['slug'])) sys.exit() # --------------------------------------------------- # Filenames and paths # --------------------------------------------------- # convenience vars n_classes = len(anno_index) yolov = str(kwargs['yolo_version']) project_name = kwargs['name'] dir_output = kwargs['dir_output'] dir_project = os.path.join(dir_output, project_name) # create project_name directory fiox.ensure_dir(dir_project) # create images directory dir_project_images = os.path.join(dir_project, 'images') fiox.ensure_dir(dir_project_images) # Create dirs if not exist dir_labels = os.path.join(dir_project, 'labels') fiox.ensure_dir(dir_labels) # create dir for weights/backup dir_weights = os.path.join(dir_project, 'weights') fiox.ensure_dir(dir_weights) # --------------------------------------------------- # Create "classes.txt" # --------------------------------------------------- fp_classes = os.path.join(dir_project, 'classes.txt') class_labels = [] for k, v in class_label_index_lkup.items(): # maps vcat id to yolo id slug = hierarchy[str(k)]['slug'] slug_sp = slug.split(':') if len(slug_sp) > 1: display_name = '{} ({})'.format(slug_sp[0], slug_sp[1]) else: display_name = slug_sp[0] class_labels.append(display_name) with open(fp_classes, 'w') as fp: fp.write('\n'.join(class_labels)) # --------------------------------------------------- # Create "train.txt", "valid.txt", "classes.txt" # --------------------------------------------------- fp_train = os.path.join(dir_project, 'train.txt') im_list = self.generate_image_list(annos_train, dir_project_images) with open(fp_train, 'w') as fp: fp.write('\n'.join(im_list)) fp_valid = os.path.join(dir_project, 'valid.txt') im_list = self.generate_image_list(annos_valid, dir_project_images) with open(fp_valid, 'w') as fp: fp.write('\n'.join(im_list)) # --------------------------------------------------- # Crate sym-linked image files # --------------------------------------------------- # TODO remove folder if exists self.create_image_symlinks(image_anno_index, dir_project_images) # --------------------------------------------------- # Create "labels.txt" # --------------------------------------------------- # labels.txt contains one line per annotation # "1 0.434 0.605 0.2980 0.37222" # class_idx, cx, cy, w, h # for YoloV3 add parent class hierarchy to the label label_anno_index = self.create_label_anno_index(image_anno_index) for fname, anno_obj in label_anno_index.items(): fp_txt = os.path.join(dir_labels, '{}.txt'.format(fname)) with open(fp_txt, 'w') as fp: fp.write('\n'.join(anno_obj)) # --------------------------------------------------- # Create "meta.data" # --------------------------------------------------- txts = [] txts.append('classes = {}'.format(n_classes)) txts.append('train = {}'.format(fp_train)) txts.append('valid = {}'.format(fp_valid)) txts.append('names = {}'.format(fp_classes)) txts.append('backup = {}'.format(dir_weights)) fp_meta = os.path.join(dir_project, 'meta.data') with open(fp_meta, 'w') as fp: fp.write('\n'.join(txts)) # --------------------------------------------------- # create train and test .cfg files # --------------------------------------------------- cfg_train_data = self.generate_yolo_config( yolov, n_classes, 'train', n_subdiv=kwargs['subdivisions'], n_batch=kwargs['batch_size']) cfg_test_data = self.generate_yolo_config(yolov, n_classes, 'test') # write data to new .cfg files fp_cfg_train = os.path.join(dir_project, 'yolov{}.cfg'.format(yolov)) fp_cfg_test = os.path.join(dir_project, 'yolov{}_test.cfg'.format(yolov)) with open(fp_cfg_train, 'w') as fp: fp.write('\n'.join(cfg_train_data)) with open(fp_cfg_test, 'w') as fp: fp.write('\n'.join(cfg_test_data)) # --------------------------------------------------- # Create shell scripts to start and resume training # --------------------------------------------------- txts_base = [] txts_base.append('#!/bin/bash') txts_base.append('DARKNET=/opt/darknet_pjreddie/darknet') txts_base.append('DIR_PROJECT={}'.format(dir_project)) txts_base.append('FP_CFG={}'.format(fp_cfg_train)) txts_base.append('FP_META={}'.format(fp_meta)) txts_base.append('CMD="detector train"') txts_base.append('LOGFILE="2>&1 | tee train_log.txt"') # run_train_init.sh fp_sh_train_init = os.path.join(dir_project, 'run_train_init.sh') txts_init = txts_base.copy() txts_init.append('GPUS="-gpus {}"'.format(kwargs['gpu_init'])) txts_init.append('FP_WEIGHTS={}'.format(kwargs['init_weights'])) txts_init.append('$DARKNET $CMD $FP_META $FP_CFG $FP_WEIGHTS $GPUS') with open(fp_sh_train_init, 'w') as fp: fp.write('\n'.join(txts_init)) # run_train_resume.sh fp_sh_train_resume = os.path.join(dir_project, 'run_train_resume.sh') txts_resume = txts_base.copy() txts_resume.append('GPUS="-gpus {}"'.format(kwargs['gpu_full'])) txts_resume.append('FP_WEIGHTS={}/{}.backup'.format( dir_weights, project_name)) txts_resume.append('$DARKNET $CMD $FP_META $FP_CFG $FP_WEIGHTS $GPUS') with open(fp_sh_train_resume, 'w') as fp: fp.write('\n'.join(txts_resume)) # run_test.sh fp_sh_test_image = os.path.join(dir_project, 'run_test_image.sh') txts = [] txts.append('#!/bin/bash') txts.append('DIR_DARKNET=/opt/darknet_pjreddie/') txts.append('DIR_PROJECT={}'.format(dir_project)) txts.append('FP_CFG={}'.format(fp_cfg_test)) txts.append('FP_META={}'.format(fp_meta)) rn_filepath = self.get_random_image(annos_valid) txts.append('FP_IMG={}'.format(rn_filepath)) txts.append('FP_WEIGHTS={}/{}_900.weights'.format( dir_weights, project_name)) txts.append('#FP_WEIGHTS={}/{}_10000.weights'.format( dir_weights, project_name)) txts.append('#FP_WEIGHTS={}/{}_30000.weights'.format( dir_weights, project_name)) txts.append('CMD="detector test"') txts.append('cd $DIR_DARKNET') txts.append('./darknet $CMD $FP_META $FP_CFG $FP_WEIGHTS $FP_IMG') with open(fp_sh_test_image, 'w') as fp: fp.write('\n'.join(txts)) print('[+] Wrote Darknet files for {}'.format(project_name)) print('[+] Project path: {}'.format(dir_project))
def extract_keyframes(self, fp_src_video, scenes, sha256, kwargs): """Write scene indices to disk""" # this should be multithreaded cap = cv.VideoCapture(fp_src_video) sha256_path = fiox.sha256_tree(sha256) fp_dst_keyframes = join(kwargs['output'], sha256_path, sha256) fiox.ensure_dir(fp_dst_keyframes) # load video cap = cv.VideoCapture(fp_src_video) # this should be multithreaded sha256_tree = fiox.sha256_tree(sha256) # provision vars montage_im_width = kwargs['montage_image_width'] num_zeros = cfg.FRAME_NAME_ZERO_PADDING im_sizes = cfg.WEB_IMAGE_SIZES # --------------------------------------------- # Copy keyframes from video # --------------------------------------------- # expanded contains all frames frameset = {} for idx in scenes['expanded']: cap.set(cv.CAP_PROP_POS_FRAMES, idx - 1) res, frame = cap.read() frames_expanded.append(frame) # pad dense summary to maintain consistent row x col layout if len(frames_poster_dense) > 0: while len(frames_poster_dense) < kwargs['max_dense_frames']: frames_poster_dense.append( np.zeros_like(frames_poster_dense[0])) #output_keyframes = join(kwargs['output'],'keyframes',save_name) #output_keyframes_training = join(kwargs['output'],'keyframes_training',save_name) dir_frames = join(kwargs['output'], 'frames', sha256_path, sha256) dir_posters_default = join(kwargs['output'], 'posters/default', sha256_path, sha256) dir_posters_dense = join(kwargs['output'], 'posters/dense', sha256_path, sha256) dir_posters_expanded = join(kwargs['output'], 'posters/expanded', sha256_path, sha256) # save all exapnded frames to frames at raw size for idx, frame in zip(scenes['expanded'], frames_expanded): fp_im = join(dir_frames, '{:07d}'.format(idx), 'index.png') fiox.ensure_dir(fp_im, parent=True) cv.imwrite(fp_im, frame) if len(scenes['basic']) > 0: # save the montage of all keyframes im_summary = imx.montage(frames_poster, ncols=kwargs['num_cols']) dir_poster = join(kwargs['output'], 'posters') fiox.ensure_dir(dir_poster) fp_im = join(dir_poster, 'index.jpg') cv.imwrite(fp_im, im_summary) # save the montage of dense keyframes im_summary = imx.montage(frames_poster_dense, ncols=kwargs['num_cols_dense']) dir_poster_dense = join(kwargs['output'], 'posters_dense') fiox.ensure_dir(dir_poster_dense) fp_im = join(dir_poster_dense, 'index.jpg') cv.imwrite(fp_im, im_summary) # write full size local files for training fiox.ensure_dir(dir_frames) frameset[idx] = frame if kwargs['scene_type']: scene_types = [kwargs['scene_type']] else: scene_types = ['basic', 'expanded', 'dense'] for scene_type in scene_types: frames = [] # choose scenes to write scene_type = 'expanded' if kwargs['verified'] else 'basic' for idx, im in zip(scenes[scene_type], frames_full): fp_im = join(dir_frames, '{:06d}.png'.format(idx)) cv.imwrite(fp_im, im) for size_dir, width in jpg_sizes.items(): d = join(output_keyframes, size_dir) im_sized = imutils.resize(im, width=width) fp_im = join(output_keyframes, size_dir, '{:06d}.jpg'.format(idx)) cv.imwrite(fp_im, im_sized) # load frames for idx in scenes[scene_type]: frames.append(frameset[idx]) # create montages if kwargs['output_montages'] is not None: dp_montage = join(kwargs['output_montages'], scene_type, sha256_tree, sha256) Path(dp_montage).mkdir(parents=True, exist_ok=True) ncols, nrows = kwargs['montage_size'] im_montage = imx.montage(frames, nrows=nrows, ncols=ncols, width=montage_im_width) fp_im = join(dp_montage, 'index.jpg') cv.imwrite(fp_im, im_montage) # path to web images JPG dp_web = join(kwargs['output_web'], sha256_tree, sha256) Path(dp_web).mkdir(parents=True, exist_ok=True) # path to training PNG frames if kwargs['output_training'] is not None: dp_train = join(kwargs['output_training'], sha256_tree, sha256) Path(dp_train).mkdir(parents=True, exist_ok=True) for idx, im in zip(scenes[scene_type], frames): idx_zpad = str(idx).zfill(num_zeros) # save training images (PNG) if kwargs['output_training'] is not None: dp = join(dp_train, idx_zpad) Path(dp).mkdir(parents=True, exist_ok=True) fp = join(dp_train, idx_zpad, 'index.png') cv.imwrite(fp, im) # generate directories for label, width in im_sizes.items(): dp = join(dp_web, idx_zpad, label) Path(dp).mkdir(parents=True, exist_ok=True) # generate images for label, width in im_sizes.items(): fp = join(dp_web, idx_zpad, label, 'index.jpg') im_resized = imutils.resize(im, width=width) cv.imwrite(fp, im_resized) return True