Пример #1
0
def process_video(
    filepath: str,
    fps: float,
    start_time: int,
    max_frames: int,
    out_dir: str,
    quiet: bool = False,
) -> None:
    """Break one video into a folder of images."""
    if not check_video_format(filepath):
        logger.warning("Ignore invalid file %s", filepath)
        return

    if not os.path.exists(filepath):
        logger.warning("%s does not exist", filepath)
        return
    cmd = ["ffmpeg", "-i", filepath, "-qscale:v", "2"]
    if start_time > 0:
        cmd.extend(["-ss", str(start_time)])
    if max_frames > 0:
        cmd.extend(["-frames:v", str(max_frames)])
    if fps > 0:
        cmd.extend(["-r", str(fps)])
    video_name = os.path.splitext(os.path.split(filepath)[1])[0]
    out_dir = join(out_dir, video_name)
    os.makedirs(out_dir, exist_ok=True)
    cmd.append("{}/{}-%07d.jpg".format(out_dir, video_name))
    if not quiet:
        logger.info("RUNNING %s", cmd)
    pipe = DEVNULL if quiet else None
    check_call(cmd, stdout=pipe, stderr=pipe)
Пример #2
0
def main() -> None:
    """Run main function."""
    args = parse_arguments()
    if args.out_dir:
        if args.scratch and os.path.exists(args.out_dir):
            logger.info("Remove existing target directory")
            shutil.rmtree(args.out_dir)
        os.makedirs(args.out_dir, exist_ok=True)

    prepare_data(args)
Пример #3
0
def write_output(filename: str, labels: List[LabelObject]) -> None:
    """Write output file."""
    ext = splitext(filename)[1]
    logger.info("Writing %s", filename)
    with open(filename, "w") as fp:
        if ext == ".json":
            json.dump(labels, fp)
        elif ext in [".yml", ".yaml"]:
            yaml.dump(labels, fp)
        else:
            raise ValueError("Unrecognized file extension {}".format(ext))
Пример #4
0
def read_input(filename: str) -> List[LabelObject]:
    """Read one input label file."""
    labels: List[LabelObject]
    ext = splitext(filename)[1]
    logger.info("Reading %s", filename)
    with open(filename, "r") as fp:
        if ext == ".json":
            labels = json.load(fp)
        elif ext in [".yml", ".yaml"]:
            labels = yaml.load(fp)
        else:
            raise ValueError("Unrecognized file extension {}".format(ext))
    return labels
Пример #5
0
def copy_images(in_path: str, out_path: str) -> None:
    """Copy images to the output folder with proper video name."""
    file_list = glob.glob(join(in_path, "**/*.jpg"), recursive=True)
    in_path_parts = os.path.split(in_path)
    if len(in_path_parts) > 0 and len(in_path_parts[-1]) > 0:
        video_name = in_path_parts[-1]
    else:
        video_name = "default"
    out_dir = join(out_path, video_name)
    os.makedirs(out_dir)
    logger.info("Copying %s to %s", in_path, out_dir)
    for image_path in tqdm(file_list):
        image_name = os.path.split(image_path)[-1]
        shutil.copyfile(image_path, join(out_dir, image_name))
Пример #6
0
def create_image_list(out_dir: str, url_root: str) -> str:
    """Create image list from the output directory."""
    file_list = sorted(glob.glob(join(out_dir, "*/*.jpg")))

    yaml_items = [{
        "url": (os.path.abspath(img) if not url_root else join(
            url_root,
            img.split("/")[-2], os.path.basename(img))),
        "videoName":
        img.split("/")[-2],
    } for img in file_list]

    list_path = join(out_dir, "image_list.yml")
    with open(list_path, "w") as f:
        yaml.dump(yaml_items, f)
    logger.info("The configuration file saved at %s", list_path)
    return list_path
Пример #7
0
def prepare_data(args: argparse.Namespace) -> None:
    """Break one or a list of videos into frames."""
    url_root = args.url_root
    if args.s3:
        url_root = s3_setup(args.s3)

    inputs = parse_input_list(args)
    logger.info("processing %d video(s) ...", len(inputs))

    num_videos = len(inputs)
    video_range = range(len(inputs))
    quiet = num_videos > 1
    if num_videos > 1:
        video_range = tqdm(video_range)
    jobs = args.jobs
    if num_videos >= jobs > 0:
        Parallel(n_jobs=jobs,
                 backend="multiprocessing")(delayed(process_input)(
                     inputs[i],
                     args.fps,
                     args.start_time,
                     args.max_frames,
                     args.out_dir,
                     quiet,
                 ) for i in video_range)
    else:
        for i in video_range:
            process_input(
                inputs[i],
                args.fps,
                args.start_time,
                args.max_frames,
                args.out_dir,
                quiet,
            )

    # upload to s3 if needed
    if args.s3:
        upload_files_to_s3(args.s3, args.out_dir)

    # create the yaml file
    if not args.no_list:
        create_image_list(args.out_dir, url_root)
Пример #8
0
def upload_files_to_s3(s3_path: str, out_dir: str) -> None:
    """Send the files to s3."""
    s3 = boto3.resource("s3")
    s3_param = parse_s3_path(s3_path)
    file_list = glob.glob(join(out_dir, "**/*.jpg"), recursive=True)
    logger.info(
        "Uploading %d files to s3 %s:%s",
        len(file_list),
        s3_param.bucket,
        s3_param.folder,
    )
    for f in tqdm(file_list):
        try:
            # pylint is added here because it thinks boto3.resource is a string
            s3.Bucket(s3_param.bucket).upload_file(
                f,
                join(s3_param.folder, f[len(out_dir) + 1:]),
                ExtraArgs={"ACL": "public-read"},
            )
        except boto3.exceptions.S3UploadFailedError as e:
            logger.error("s3 bucket is not properly configured %s", e)
            break
Пример #9
0
def evaluate_pan_seg(
    ann_frames: List[Frame],
    pred_frames: List[Frame],
    config: Config,
    ignore_unknown_cats: bool = False,
    nproc: int = NPROC,
) -> PanSegResult:
    """Evaluate panoptic segmentation with Scalabel format.

    Args:
        ann_frames: the ground truth frames.
        pred_frames: the prediction frames.
        config: Metadata config.
        ignore_unknown_cats: ignore unknown categories.
        nproc: the number of process.

    Returns:
        PanSegResult: evaluation results.
    """
    categories = get_leaf_categories(config.categories)
    assert all(
        category.isThing is not None for category in categories
    ), "isThing should be defined for all categories for PanSeg."
    categories_stuff = [
        category for category in categories if not category.isThing
    ]
    categories_thing = [
        category for category in categories if category.isThing
    ]
    category_names = [category.name for category in categories]
    pred_frames = reorder_preds(ann_frames, pred_frames)
    label_ids_to_int(ann_frames)
    # check overlap of masks
    logger.info("checking for overlap of masks...")
    if check_overlap(pred_frames, config, nproc):
        logger.critical(
            "Found overlap in prediction bitmasks, but panoptic segmentation "
            "evaluation does not allow overlaps. Removing such predictions."
        )

    logger.info("evaluating...")
    if nproc > 1:
        with Pool(nproc) as pool:
            pq_stats = pool.starmap(
                partial(
                    pq_per_image,
                    categories=categories,
                    ignore_unknown_cats=ignore_unknown_cats,
                    image_size=config.imageSize,
                ),
                tqdm(zip(ann_frames, pred_frames), total=len(ann_frames)),
            )
    else:
        pq_stats = [
            pq_per_image(
                ann_frame,
                pred_frame,
                categories=categories,
                ignore_unknown_cats=ignore_unknown_cats,
                image_size=config.imageSize,
            )
            for ann_frame, pred_frame in tqdm(
                zip(ann_frames, pred_frames), total=len(ann_frames)
            )
        ]
    pq_stat = PQStat(categories)
    for pq_stat_ in pq_stats:
        pq_stat += pq_stat_

    logger.info("accumulating...")
    res_dict: Dict[str, ScoresList] = {}
    for category_name, category in zip(category_names, categories):
        result = pq_stat.pq_average([category])
        for metric, score in result.items():
            if metric not in res_dict:
                res_dict[metric] = [{}, {}, {}]
            res_dict[metric][0][category_name] = score

    result = pq_stat.pq_average(categories_stuff)
    for metric, score in result.items():
        res_dict[metric][1][STUFF] = score
    result = pq_stat.pq_average(categories_thing)
    for metric, score in result.items():
        res_dict[metric][1][THING] = score
    result = pq_stat.pq_average(categories)
    for metric, score in result.items():
        res_dict[metric][2][OVERALL] = score

    return PanSegResult(**res_dict)
Пример #10
0
        "--ignore-unknown-cats",
        action="store_true",
        help="ignore unknown categories for panseg evaluation",
    )
    parser.add_argument(
        "--nproc",
        "-p",
        type=int,
        default=NPROC,
        help="number of processes for panseg evaluation",
    )
    return parser.parse_args()


if __name__ == "__main__":
    args = parse_arguments()
    dataset = load(args.gt, args.nproc)
    gts, cfg = dataset.frames, dataset.config
    preds = load(args.result).frames
    if args.config is not None:
        cfg = load_label_config(args.config)
    assert cfg is not None
    eval_result = evaluate_pan_seg(
        gts, preds, cfg, args.ignore_unknown_cats, args.nproc
    )
    logger.info(eval_result)
    logger.info(eval_result.summary())
    if args.out_file:
        with open_write_text(args.out_file) as fp:
            json.dump(eval_result.dict(), fp, indent=2)
Пример #11
0
def evaluate_sem_seg(
    ann_frames: List[Frame],
    pred_frames: List[Frame],
    config: Config,
    nproc: int = NPROC,
) -> SegResult:
    """Evaluate segmentation with Scalabel format.

    Args:
        ann_frames: the ground truth frames.
        pred_frames: the prediction frames.
        config: Metadata config.
        nproc: the number of process.

    Returns:
        SegResult: evaluation results.
    """
    categories = {
        cat.name: id
        for id, cat in enumerate(get_leaf_categories(config.categories))
    }
    ignore_label = 255
    pred_frames = reorder_preds(ann_frames, pred_frames)

    logger.info("evaluating...")
    if nproc > 1:
        with Pool(nproc) as pool:
            hist_and_gt_id_sets = pool.starmap(
                partial(
                    per_image_hist,
                    categories=categories,
                    image_size=config.imageSize,
                    ignore_label=ignore_label,
                ),
                tqdm(zip(ann_frames, pred_frames), total=len(ann_frames)),
            )
    else:
        hist_and_gt_id_sets = [
            per_image_hist(
                ann_frame,
                pred_frame,
                categories=categories,
                image_size=config.imageSize,
                ignore_label=ignore_label,
            )
            for ann_frame, pred_frame in tqdm(
                zip(ann_frames, pred_frames), total=len(ann_frames)
            )
        ]

    logger.info("accumulating...")
    num_classes = len(categories) + 1
    hist: NDArrayI32 = np.zeros((num_classes, num_classes), dtype=np.int32)
    gt_id_set = set()
    for (hist_, gt_id_set_) in hist_and_gt_id_sets:
        hist += hist_
        gt_id_set.update(gt_id_set_)

    ious = per_class_iou(hist)
    accs = per_class_acc(hist)
    IoUs = [  # pylint: disable=invalid-name
        {cat_name: 100 * score for cat_name, score in zip(categories, ious)},
        {AVERAGE: np.multiply(np.mean(ious[list(gt_id_set)]), 100)},
    ]
    Accs = [  # pylint: disable=invalid-name
        {cat_name: 100 * score for cat_name, score in zip(categories, accs)},
        {AVERAGE: np.multiply(np.mean(accs[list(gt_id_set)]), 100)},
    ]
    res_dict: Dict[str, Union[float, ScoresList]] = dict(
        IoU=IoUs,
        Acc=Accs,
        fIoU=np.multiply(freq_iou(hist), 100),  # pylint: disable=invalid-name
        pAcc=np.multiply(whole_acc(hist), 100),  # pylint: disable=invalid-name
    )

    logger.info("GT id set [%s]", ",".join(str(s) for s in gt_id_set))
    return SegResult(**res_dict)
Пример #12
0
def evaluate_seg_track(
    acc_single_video: VidFunc,
    gts: List[List[Frame]],
    results: List[List[Frame]],
    config: Config,
    iou_thr: float = 0.5,
    ignore_iof_thr: float = 0.5,
    ignore_unknown_cats: bool = False,
    nproc: int = NPROC,
) -> TrackResult:
    """Evaluate CLEAR MOT metrics for a Scalabel format dataset.

    Args:
        acc_single_video: Function for calculating metrics over a single video.
        gts: the ground truth annotations in Scalabel format
        results: the prediction results in Scalabel format.
        config: Config object
        iou_thr: Minimum IoU for a mask to be considered a positive.
        ignore_iof_thr: Min. Intersection over foreground with ignore regions.
        ignore_unknown_cats: if False, raise KeyError when trying to evaluate
            unknown categories.
        nproc: processes number for loading files

    Returns:
        TrackResult: evaluation results.
    """
    logger.info("Tracking evaluation with CLEAR MOT metrics.")
    t = time.time()
    assert len(gts) == len(results)
    # check overlap of masks
    logger.info("checking for overlap of masks...")
    if check_overlap([frame for res in results for frame in res], config,
                     nproc):
        logger.critical(
            "Found overlap in prediction bitmasks, but segmentation tracking "
            "evaluation does not allow overlaps. Removing such predictions.")

    classes = get_leaf_categories(config.categories)
    super_classes = get_parent_categories(config.categories)

    logger.info("evaluating...")
    image_size = config.imageSize
    if nproc > 1:
        with Pool(nproc) as pool:
            video_accs = pool.starmap(
                partial(
                    acc_single_video,
                    classes=classes,
                    ignore_iof_thr=ignore_iof_thr,
                    ignore_unknown_cats=ignore_unknown_cats,
                    image_size=image_size,
                ),
                zip(gts, results),
            )
    else:
        video_accs = [
            acc_single_video(
                gt,
                result,
                classes,
                iou_thr,
                ignore_iof_thr,
                ignore_unknown_cats,
                image_size,
            ) for gt, result in zip(gts, results)
        ]

    class_names, metric_names, class_accs = aggregate_accs(
        video_accs, classes, super_classes)

    logger.info("accumulating...")
    if nproc > 1:
        with Pool(nproc) as pool:
            flat_dicts = pool.starmap(evaluate_single_class,
                                      zip(metric_names, class_accs))
    else:
        flat_dicts = [
            evaluate_single_class(names, accs)
            for names, accs in zip(metric_names, class_accs)
        ]

    metrics = list(METRIC_MAPS.values())
    result = generate_results(flat_dicts, class_names, metrics, classes,
                              super_classes)
    t = time.time() - t
    logger.info("evaluation finishes with %.1f s.", t)
    return result