def main(): # Use first line of file docstring as description if it exists. parser = argparse.ArgumentParser( description=__doc__.split('\n')[0] if __doc__ else '', formatter_class=argparse.ArgumentDefaultsHelpFormatter) parser.add_argument( '--dense-dir', required=True, type=Path, help='Directory containing dense segmentation .ppm files.') parser.add_argument('--images-dir', required=True, type=Path) parser.add_argument('--output-dir', required=True, type=Path) parser.add_argument('--output-fps', default=30, type=int) parser.add_argument('--output-images', action='store_true') parser.add_argument( '--background-id', required=True, help=('ID of background track in predictions. Can be an integer or ' '"infer", in which case the background id is assumed to be the ' 'id of the track with the most pixels.')) args = parser.parse_args() assert args.dense_dir.exists() assert args.images_dir.exists() args.output_dir.mkdir(exist_ok=True, parents=True) setup_logging(args.output_dir / (Path(__file__).name + '.log')) logging.info('File path: %s', Path(__file__)) logging.info('Args:\n%s', vars(args)) colors = colormap() if args.background_id != 'infer': background_prediction_id = int(args.background_id) else: background_prediction_id = None dense_segmentations = natsorted( args.dense_dir.glob('*_dense.ppm'), alg=ns.PATH) images = natsorted( [x for x in args.images_dir.iterdir() if is_image_file(x.name)], alg=ns.PATH) assert len(images) == len(dense_segmentations) segmentation_frames = np.stack( np.array(Image.open(segmentation_ppm)) for segmentation_ppm in dense_segmentations) if segmentation_frames.ndim == 4 and segmentation_frames.shape[-1] == 1: segmentation_frames = segmentation_frames[:, :, :, 0] elif segmentation_frames.ndim == 4 and segmentation_frames.shape[-1] == 3: segmentation_frames = segmentation_frames.astype(np.int32) segmentation_frames = (segmentation_frames[:, :, :, 2] + 256 * segmentation_frames[:, :, :, 1] + (256**2) * segmentation_frames[:, :, :, 0]) assert segmentation_frames.ndim == 3 all_ids, id_counts = np.unique(segmentation_frames, return_counts=True) id_counts = dict(zip(all_ids, id_counts)) sorted_ids = sorted( id_counts.keys(), key=lambda i: id_counts[i], reverse=True) if background_prediction_id is None: # Infer background id background_prediction_id = int(sorted_ids[0]) print('Inferred background prediction id as %s' % background_prediction_id) sorted_ids = sorted_ids[1:] else: sorted_ids = [ x for x in sorted_ids if x != background_prediction_id ] # Map id to size index id_rankings = { region_id: index for index, region_id in enumerate(sorted_ids) } def visualize_frame(t): frame = int(t * args.output_fps) frame_mask = segmentation_frames[frame] image_path = images[frame] ids = sorted(np.unique(frame_mask)) masks = [frame_mask == object_id for object_id in ids] # Sort masks by area ids_and_masks = sorted(zip(ids, masks), key=lambda x: x[1].sum()) vis_image = cv2.imread(str(image_path)) # vis_image = (vis_image.astype(np.float32) * 1.0).astype(np.uint8) for mask_id, mask in ids_and_masks: if isinstance(mask_id, float): assert mask_id.is_integer() mask_id = int(mask_id) if mask_id == background_prediction_id: continue color = colors[int(id_rankings[mask_id]) % len(colors)] vis_image = vis_mask( vis_image, mask.astype(np.uint8), color, alpha=0.5, border_alpha=0.5, border_color=[255, 255, 255], border_thick=1) vis_image = vis_image[:, :, ::-1] # BGR -> RGB if args.output_images: output_frame = args.output_dir / image_path.name output_frame.parent.mkdir(exist_ok=True, parents=True) Image.fromarray(vis_image).save(output_frame) return vis_image num_frames = segmentation_frames.shape[0] output_video = args.output_dir / 'video.mp4' output_video.parent.mkdir(exist_ok=True, parents=True) from moviepy.video.VideoClip import VideoClip clip = VideoClip(make_frame=visualize_frame) # Subtract a small epsilon; otherwise, moviepy can sometimes request # a frame at index num_frames. duration = num_frames / args.output_fps - 1e-10 clip = clip.set_duration(duration).set_memoize(True) clip.write_videofile( str(output_video), fps=args.output_fps, verbose=False)
def main(): # Use first line of file docstring as description if it exists. parser = argparse.ArgumentParser( description=__doc__.split('\n')[0] if __doc__ else '', formatter_class=argparse.ArgumentDefaultsHelpFormatter) parser.add_argument('--input-dir', required=True, type=Path) parser.add_argument('--output-dir', required=True, type=Path) parser.add_argument('--images-dir', required=True, type=Path) parser.add_argument('--np-extension', default='.npy') parser.add_argument('--output-fps', default=30, type=int) parser.add_argument('--output-images', action='store_true') parser.add_argument( '--background-id', required=True, help=('ID of background track in predictions. Can be an integer or ' '"infer", in which case the background id is assumed to be the ' 'id of the track with the most pixels.')) args = parser.parse_args() colors = colormap() if args.background_id != 'infer': background_prediction_id = int(args.background_id) else: background_prediction_id = None for mask_path in tqdm(list(args.input_dir.rglob('*' + args.np_extension))): relative_dir = mask_path.relative_to(args.input_dir).with_suffix('') images_subdir = args.images_dir / relative_dir assert images_subdir.exists(), ('Could not find directory %s' % images_subdir) images = natsorted( [x for x in images_subdir.iterdir() if is_image_file(x.name)], alg=ns.PATH) all_frames_mask = np.load(mask_path) if args.np_extension == '.npz': # Segmentation saved with savez_compressed; ensure there is only # one item in the dict and retrieve it. keys = all_frames_mask.keys() assert len(keys) == 1, ( 'Numpy file (%s) contained dict with multiple items, not sure ' 'which one to load.' % mask_path) all_frames_mask = all_frames_mask[keys[0]] all_ids, id_counts = np.unique(all_frames_mask, return_counts=True) id_counts = dict(zip(all_ids, id_counts)) sorted_ids = sorted(id_counts.keys(), key=lambda i: id_counts[i], reverse=True) if background_prediction_id is None: # Infer background id current_bg = int(sorted_ids[0]) print('Inferred background prediction id as %s for %s' % (current_bg, relative_dir)) sorted_ids = sorted_ids[1:] else: current_bg = background_prediction_id sorted_ids = [x for x in sorted_ids if x != current_bg] # Map id to size index id_rankings = { region_id: index for index, region_id in enumerate(sorted_ids) } def visualize_frame(t): frame = int(t * args.output_fps) frame_mask = all_frames_mask[frame] image_path = images[frame] ids = sorted(np.unique(frame_mask)) masks = [frame_mask == object_id for object_id in ids] # Sort masks by area ids_and_masks = sorted(zip(ids, masks), key=lambda x: x[1].sum()) vis_image = cv2.imread(str(image_path)) # vis_image = (vis_image.astype(np.float32) * 1.0).astype(np.uint8) for mask_id, mask in ids_and_masks: if isinstance(mask_id, float): assert mask_id.is_integer() mask_id = int(mask_id) if mask_id == current_bg: continue color = colors[int(id_rankings[mask_id]) % len(colors)] vis_image = vis_mask(vis_image, mask.astype(np.uint8), color, alpha=0.5, border_alpha=0.5, border_color=[255, 255, 255], border_thick=2) vis_image = vis_image[:, :, ::-1] # BGR -> RGB if args.output_images: output_frame = args.output_dir / image_path.relative_to( args.images_dir) output_frame.parent.mkdir(exist_ok=True, parents=True) Image.fromarray(vis_image).save(output_frame) return vis_image num_frames = all_frames_mask.shape[0] output_video = (args.output_dir / relative_dir).with_suffix('.mp4') output_video.parent.mkdir(exist_ok=True, parents=True) from moviepy.video.VideoClip import VideoClip clip = VideoClip(make_frame=visualize_frame) # Subtract a small epsilon; otherwise, moviepy can sometimes request # a frame at index num_frames. duration = num_frames / args.output_fps - 1e-10 clip = clip.set_duration(duration).set_memoize(True) clip.write_videofile(str(output_video), fps=args.output_fps, verbose=False)