Ejemplo n.º 1
0
def test_scale_bbox_dimensions(get_simple_annotations_with_img_sizes):
    df_annotations = get_simple_annotations_with_img_sizes()
    df_annotations = scale_bbox_dimensions(df_annotations, (1280, 720))
    bboxes = get_bbox_array(df_annotations, prefix="scaled")
    expected_bboxes = np.array([[0, 0, 128, 72], [256, 144, 640, 288]],
                               dtype=np.int32)
    np.testing.assert_equal(bboxes, expected_bboxes)
Ejemplo n.º 2
0
def test_get_df_from_bboxes(get_simple_annotations_with_img_sizes,
                            bbox_format):
    bboxes = np.array([[20, 20, 5, 5, 100, 100], [40, 40, 20, 20, 100, 100]])
    df_annotations = get_simple_annotations_with_img_sizes(
        bboxes, bbox_format=bbox_format)
    matrix = get_bbox_array(df_annotations,
                            input_bbox_format=bbox_format,
                            output_bbox_format=bbox_format)

    df = get_df_from_bboxes(matrix,
                            input_bbox_format=bbox_format,
                            output_bbox_format=bbox_format)
    expected_result = df_annotations[get_bbox_column_names(bbox_format)]
    pd.testing.assert_frame_equal(df, expected_result)
Ejemplo n.º 3
0
def train_config_generation(
    ground_truth_file: str,
    input_size: Tuple[int, int] = (1280, 720),
    n_ratios: int = 3,
    n_scales: int = 3,
    strides: Optional[List[int]] = None,
    base_sizes: Optional[List[int]] = None,
    show: bool = True,
    output: Optional[str] = None,
    output_size: Tuple[int, int] = (1600, 900),
    keep_ratio: bool = False,
    evaluate: bool = True,
) -> None:
    """Computes optimal anchors for a given COCO dataset based on iou clustering.

    Args:
        ground_truth_file: Path to COCO ground truth file.
        input_size: Model image input size. Defaults to (1280, 720).
        n_ratios: Number of ratios. Defaults to 3.
        n_scales: Number of scales. Defaults to 3.
        strides: List of strides. Defatults to [4, 8, 16, 32, 64].
        base_sizes: The basic sizes of anchors in multiple levels.
            If None is given, strides will be used as base_sizes.
        show: Show results or not. Defaults to True.
        output: Output directory where results going to be saved. Defaults to None.
        output_size: Size of saved images. Defaults to (1600, 900).
        keep_ratio: Whether to keep the aspect ratio or not. Defaults to False.
        evaluate: Whether to evaluate or not the anchors. Check
            [`pyodi train-config evaluation`][pyodi.apps.train_config.train_config_evaluation.train_config_evaluation]
            for more information.

    Returns:
        Anchor generator instance.
    """
    if output is not None:
        Path(output).mkdir(parents=True, exist_ok=True)

    df_annotations = coco_ground_truth_to_df(ground_truth_file)

    df_annotations = filter_zero_area_bboxes(df_annotations)

    df_annotations = scale_bbox_dimensions(df_annotations,
                                           input_size=input_size,
                                           keep_ratio=keep_ratio)

    df_annotations = get_scale_and_ratio(df_annotations, prefix="scaled")

    if strides is None:
        strides = [4, 8, 16, 32, 64]
    if base_sizes is None:
        base_sizes = strides

    # Assign fpn level
    df_annotations["fpn_level"] = find_pyramid_level(
        get_bbox_array(df_annotations, prefix="scaled")[:, 2:], base_sizes)

    df_annotations["fpn_level_scale"] = df_annotations["fpn_level"].replace(
        {i: scale
         for i, scale in enumerate(base_sizes)})

    df_annotations["level_scale"] = (df_annotations["scaled_scale"] /
                                     df_annotations["fpn_level_scale"])

    # Normalize to log scale
    df_annotations["log_ratio"] = np.log(df_annotations["scaled_ratio"])
    df_annotations["log_level_scale"] = np.log(df_annotations["level_scale"])

    # Cluster bboxes by scale and ratio independently
    clustering_results = [
        kmeans_euclidean(df_annotations[value].to_numpy(),
                         n_clusters=n_clusters)
        for i, (value, n_clusters) in enumerate(
            zip(["log_level_scale", "log_ratio"], [n_scales, n_ratios]))
    ]

    # Bring back from log scale
    scales = np.e**clustering_results[0]["centroids"]
    ratios = np.e**clustering_results[1]["centroids"]

    anchor_generator = AnchorGenerator(
        strides=strides,
        ratios=ratios,
        scales=scales,
        base_sizes=base_sizes,
    )
    logger.info(f"Anchor configuration: \n{anchor_generator.to_string()}")

    plot_clustering_results(
        df_annotations,
        anchor_generator,
        show=show,
        output=output,
        output_size=output_size,
        title="COCO_anchor_generation",
    )

    if evaluate:
        anchor_config = dict(anchor_generator=anchor_generator.to_dict())
        train_config_evaluation(
            ground_truth_file=df_annotations,
            anchor_config=anchor_config,  # type: ignore
            input_size=input_size,
            show=show,
            output=output,
            output_size=output_size,
        )

    if output:
        output_file = Path(output) / "anchor_config.py"
        with open(output_file, "w") as f:
            f.write(anchor_generator.to_string())
Ejemplo n.º 4
0
def train_config_evaluation(
    ground_truth_file: Union[str, pd.DataFrame],
    anchor_config: str,
    input_size: Tuple[int, int] = (1280, 720),
    show: bool = True,
    output: Optional[str] = None,
    output_size: Tuple[int, int] = (1600, 900),
    keep_ratio: bool = False,
) -> None:
    """Evaluates the fitness between `ground_truth_file` and `anchor_config_file`.

    Args:
        ground_truth_file: Path to COCO ground truth file or coco df_annotations DataFrame
            to be used from
            [`pyodi train-config generation`][pyodi.apps.train_config.train_config_generation.train_config_generation]
        anchor_config: Path to MMDetection-like `anchor_generator` section. It can also be a
            dictionary with the required data.
        input_size: Model image input size. Defaults to (1333, 800).
        show: Show results or not. Defaults to True.
        output: Output directory where results going to be saved. Defaults to None.
        output_size: Size of saved images. Defaults to (1600, 900).
        keep_ratio: Whether to keep the aspect ratio or not. Defaults to False.

    Examples:
        ```python
        # faster_rcnn_r50_fpn.py:
        anchor_generator=dict(
            type='AnchorGenerator',
            scales=[8],
            ratios=[0.5, 1.0, 2.0],
            strides=[4, 8, 16, 32, 64]
        )
        ```
    """
    if output is not None:
        Path(output).mkdir(parents=True, exist_ok=True)

    if isinstance(ground_truth_file, str):
        df_annotations = coco_ground_truth_to_df(ground_truth_file)

        df_annotations = filter_zero_area_bboxes(df_annotations)

        df_annotations = scale_bbox_dimensions(df_annotations,
                                               input_size=input_size,
                                               keep_ratio=keep_ratio)

        df_annotations = get_scale_and_ratio(df_annotations, prefix="scaled")

    else:
        df_annotations = ground_truth_file

    df_annotations["log_scaled_ratio"] = np.log(df_annotations["scaled_ratio"])

    if isinstance(anchor_config, str):
        anchor_config_data = load_anchor_config_file(anchor_config)
    elif isinstance(anchor_config, dict):
        anchor_config_data = anchor_config
    else:
        raise ValueError("anchor_config must be string or dictionary.")

    anchor_config_data["anchor_generator"].pop("type", None)
    anchor_generator = AnchorGenerator(
        **anchor_config_data["anchor_generator"])

    if isinstance(anchor_config, str):
        logger.info(anchor_generator.to_string())

    width, height = input_size
    featmap_sizes = [(width // stride, height // stride)
                     for stride in anchor_generator.strides]
    anchors_per_level = anchor_generator.grid_anchors(
        featmap_sizes=featmap_sizes)

    bboxes = get_bbox_array(df_annotations,
                            prefix="scaled",
                            output_bbox_format="corners")

    overlaps = np.zeros(bboxes.shape[0])
    max_overlap_level = np.zeros(bboxes.shape[0])

    logger.info("Computing overlaps between anchors and ground truth ...")
    for i, anchor_level in enumerate(anchors_per_level):
        level_overlaps = get_max_overlap(bboxes.astype(np.float32),
                                         anchor_level.astype(np.float32))
        max_overlap_level[level_overlaps > overlaps] = i
        overlaps = np.maximum(overlaps, level_overlaps)

    df_annotations["overlaps"] = overlaps
    df_annotations["max_overlap_level"] = max_overlap_level

    logger.info("Plotting results ...")
    plot_overlap_result(df_annotations,
                        show=show,
                        output=output,
                        output_size=output_size)
Ejemplo n.º 5
0
def test_get_bbox_matrix_corners(get_simple_annotations_with_img_sizes):
    df_annotations = get_simple_annotations_with_img_sizes()
    matrix = get_bbox_array(df_annotations, output_bbox_format="corners")
    expected_result = np.array([[0, 0, 10, 10], [20, 20, 70, 60]])
    np.testing.assert_equal(matrix, expected_result)