コード例 #1
0
def png_to_geojson(geotiff_path, label_png_path, output_path, object_half_len):
    """Convert COWC PNG labels to GeoJSON format.
    In the COWC dataset, the center position of cars is represented as
    non-zero pixels in PNG files that are aligned with the GeoTIFFs.
    This script converts the PNG file to a GeoJSON representation.
    """
    image_dataset = rasterio.open(geotiff_path)
    label_im = np.array(Image.open(label_png_path))

    point_inds = np.argwhere(label_im[:, :, 0] != 0).astype(float)

    # Normalize inds
    point_inds[:, 0] /= label_im.shape[0]
    point_inds[:, 1] /= label_im.shape[1]

    # Convert to geotiff image inds
    point_inds[:, 0] *= image_dataset.height
    point_inds[:, 1] *= image_dataset.width
    point_inds = point_inds.astype(np.int)

    # Turn points into squares and ensure edges aren't outside the array
    y_min = np.clip(point_inds[:, 0:1] - object_half_len, 0,
                    image_dataset.height)
    x_min = np.clip(point_inds[:, 1:2] - object_half_len, 0,
                    image_dataset.width)
    y_max = np.clip(point_inds[:, 0:1] + object_half_len, 0,
                    image_dataset.height)
    x_max = np.clip(point_inds[:, 1:2] + object_half_len, 0,
                    image_dataset.width)

    # Write to GeoJSON
    boxes = np.hstack([y_min, x_min, y_max, x_max]).astype(float)
    boxlist = BoxList(boxes)
    save_geojson(output_path, boxlist, image_dataset=image_dataset)
    return boxlist
コード例 #2
0
    def __init__(self, npboxes, class_ids, scores=None):
        """Construct a set of object detection labels.

        Args:
            npboxes: float numpy array of size nx4 with cols
                ymin, xmin, ymax, xmax. Should be in pixel coordinates within
                the global frame of reference.
            class_ids: int numpy array of size n with class ids starting at 1
            scores: float numpy array of size n
        """
        # Lazily load TF Object Detection
        from object_detection.utils.np_box_list import BoxList

        self.boxlist = BoxList(npboxes)
        # This field name actually needs to be 'classes' to be able to use
        # certain utility functions in the TF Object Detection API.
        self.boxlist.add_field('classes', class_ids)
        # We need to ensure that there is always a scores field so that the
        # concatenate method will work with empty labels objects.
        if scores is None:
            scores = np.ones(class_ids.shape)
        self.boxlist.add_field('scores', scores)
コード例 #3
0
    def test_from_boxlist(self):
        from object_detection.utils.np_box_list import BoxList

        boxlist = BoxList(self.npboxes)
        boxlist.add_field('classes', self.class_ids)
        boxlist.add_field('scores', self.scores)
        labels = ObjectDetectionLabels.from_boxlist(boxlist)
        labels.assert_equal(self.labels)
コード例 #4
0
    def get_overlapping(labels, window, ioa_thresh=0.000001, clip=False):
        """Return subset of labels that overlap with window.

        Args:
            labels: ObjectDetectionLabels
            window: Box
            ioa_thresh: the minimum IOA for a box to be considered as
                overlapping
            clip: if True, clip label boxes to the window
        """
        # Lazily load TF Object Detection
        from object_detection.utils.np_box_list import BoxList
        from object_detection.utils.np_box_list_ops import (
            prune_non_overlapping_boxes, clip_to_window)

        window_npbox = window.npbox_format()
        window_boxlist = BoxList(np.expand_dims(window_npbox, axis=0))
        boxlist = prune_non_overlapping_boxes(labels.boxlist,
                                              window_boxlist,
                                              minoverlap=ioa_thresh)
        if clip:
            boxlist = clip_to_window(boxlist, window_npbox)

        return ObjectDetectionLabels.from_boxlist(boxlist)
def _to_boxlist(res):
    boxlist = BoxList(res['boxes'])
    for field in ['classes', 'scores', 'proposal_inds']:
        if field in res:
            boxlist.add_field(field, res[field])
    return boxlist
コード例 #6
0
class ObjectDetectionLabels(Labels):
    """A set of boxes and associated class_ids and scores.

    Implemented using the Tensorflow Object Detection API's BoxList class.
    """
    def __init__(self, npboxes, class_ids, scores=None):
        """Construct a set of object detection labels.

        Args:
            npboxes: float numpy array of size nx4 with cols
                ymin, xmin, ymax, xmax. Should be in pixel coordinates within
                the global frame of reference.
            class_ids: int numpy array of size n with class ids starting at 1
            scores: float numpy array of size n
        """
        # Lazily load TF Object Detection
        from object_detection.utils.np_box_list import BoxList

        self.boxlist = BoxList(npboxes)
        # This field name actually needs to be 'classes' to be able to use
        # certain utility functions in the TF Object Detection API.
        self.boxlist.add_field('classes', class_ids)
        # We need to ensure that there is always a scores field so that the
        # concatenate method will work with empty labels objects.
        if scores is None:
            scores = np.ones(class_ids.shape)
        self.boxlist.add_field('scores', scores)

    def __add__(self, other):
        return ObjectDetectionLabels.concatenate(self, other)

    def __eq__(self, other):
        return (isinstance(other, ObjectDetectionLabels)
                and self.to_dict() == other.to_dict())

    def assert_equal(self, expected_labels):
        np.testing.assert_array_equal(self.get_npboxes(),
                                      expected_labels.get_npboxes())
        np.testing.assert_array_equal(self.get_class_ids(),
                                      expected_labels.get_class_ids())
        np.testing.assert_array_equal(self.get_scores(),
                                      expected_labels.get_scores())

    def filter_by_aoi(self, aoi_polygons):
        boxes = self.get_boxes()
        class_ids = self.get_class_ids()
        scores = self.get_scores()

        new_boxes = []
        new_class_ids = []
        new_scores = []
        for box, class_id, score in zip(boxes, class_ids, scores):
            box_poly = box.to_shapely()
            for aoi in aoi_polygons:
                if box_poly.within(aoi):
                    new_boxes.append(box.npbox_format())
                    new_class_ids.append(class_id)
                    new_scores.append(score)
                    break

        if len(new_boxes) == 0:
            return ObjectDetectionLabels.make_empty()

        return ObjectDetectionLabels(np.array(new_boxes),
                                     np.array(new_class_ids),
                                     np.array(new_scores))

    @staticmethod
    def make_empty():
        npboxes = np.empty((0, 4))
        class_ids = np.empty((0, ))
        scores = np.empty((0, ))
        return ObjectDetectionLabels(npboxes, class_ids, scores)

    @staticmethod
    def from_boxlist(boxlist):
        """Make ObjectDetectionLabels from BoxList object."""
        scores = (boxlist.get_field('scores')
                  if boxlist.has_field('scores') else None)
        return ObjectDetectionLabels(boxlist.get(),
                                     boxlist.get_field('classes'),
                                     scores=scores)

    @staticmethod
    def from_geojson(geojson, extent=None):
        """Convert GeoJSON to ObjectDetectionLabels object.

        If extent is provided, filter out the boxes that lie "more than a little
        bit" outside the extent.

        Args:
            geojson: (dict) normalized GeoJSON (see VectorSource)
            extent: (Box) in pixel coords

        Returns:
            ObjectDetectionLabels
        """
        boxes = []
        class_ids = []
        scores = []

        for f in geojson['features']:
            geom = shape(f['geometry'])
            (xmin, ymin, xmax, ymax) = geom.bounds
            boxes.append(Box(ymin, xmin, ymax, xmax))

            props = f['properties']
            class_ids.append(props['class_id'])
            scores.append(props.get('score', 1.0))

        if len(boxes):
            boxes = np.array([box.npbox_format() for box in boxes],
                             dtype=float)
            class_ids = np.array(class_ids)
            scores = np.array(scores)
            labels = ObjectDetectionLabels(boxes, class_ids, scores=scores)
        else:
            labels = ObjectDetectionLabels.make_empty()

        if extent is not None:
            labels = ObjectDetectionLabels.get_overlapping(labels,
                                                           extent,
                                                           ioa_thresh=0.8,
                                                           clip=True)
        return labels

    def get_boxes(self):
        """Return list of Boxes."""
        return [Box.from_npbox(npbox) for npbox in self.boxlist.get()]

    def get_npboxes(self):
        return self.boxlist.get()

    def get_scores(self):
        if self.boxlist.has_field('scores'):
            return self.boxlist.get_field('scores')
        return None

    def get_class_ids(self):
        return self.boxlist.get_field('classes')

    def __len__(self):
        return self.boxlist.get().shape[0]

    def __str__(self):
        return str(self.boxlist.get())

    def to_boxlist(self):
        return self.boxlist

    def to_dict(self):
        """Returns a dict version of these labels.

        The Dict has a Box as a key, and a tuple of (class_id, score)
        as the values.
        """
        d = {}
        boxes = list(map(Box.from_npbox, self.get_npboxes()))
        classes = list(self.get_class_ids())
        scores = list(self.get_scores())
        for box, class_id, score in zip(boxes, classes, scores):
            d[box.tuple_format()] = (class_id, score)
        return d

    @staticmethod
    def local_to_global(npboxes, window):
        """Convert from local to global coordinates.

        The local coordinates are row/col within the window frame of reference.
        The global coordinates are row/col within the extent of a RasterSource.
        """
        xmin = window.xmin
        ymin = window.ymin
        return npboxes + np.array([[ymin, xmin, ymin, xmin]])

    @staticmethod
    def global_to_local(npboxes, window):
        """Convert from global to local coordinates.

        The global coordinates are row/col within the extent of a RasterSource.
        The local coordinates are row/col within the window frame of reference.
        """
        xmin = window.xmin
        ymin = window.ymin
        return npboxes - np.array([[ymin, xmin, ymin, xmin]])

    @staticmethod
    def local_to_normalized(npboxes, window):
        """Convert from local to normalized coordinates.

        The local coordinates are row/col within the window frame of reference.
        Normalized coordinates range from 0 to 1 on each (height/width) axis.
        """
        height = window.get_height()
        width = window.get_width()
        return npboxes / np.array([[height, width, height, width]])

    @staticmethod
    def normalized_to_local(npboxes, window):
        """Convert from normalized to local coordinates.

        Normalized coordinates range from 0 to 1 on each (height/width) axis.
        The local coordinates are row/col within the window frame of reference.
        """
        height = window.get_height()
        width = window.get_width()
        return npboxes * np.array([[height, width, height, width]])

    @staticmethod
    def get_overlapping(labels, window, ioa_thresh=0.000001, clip=False):
        """Return subset of labels that overlap with window.

        Args:
            labels: ObjectDetectionLabels
            window: Box
            ioa_thresh: the minimum IOA for a box to be considered as
                overlapping
            clip: if True, clip label boxes to the window
        """
        # Lazily load TF Object Detection
        from object_detection.utils.np_box_list import BoxList
        from object_detection.utils.np_box_list_ops import (
            prune_non_overlapping_boxes, clip_to_window)

        window_npbox = window.npbox_format()
        window_boxlist = BoxList(np.expand_dims(window_npbox, axis=0))
        boxlist = prune_non_overlapping_boxes(labels.boxlist,
                                              window_boxlist,
                                              minoverlap=ioa_thresh)
        if clip:
            boxlist = clip_to_window(boxlist, window_npbox)

        return ObjectDetectionLabels.from_boxlist(boxlist)

    @staticmethod
    def concatenate(labels1, labels2):
        """Return concatenation of labels.

        Args:
            labels1: ObjectDetectionLabels
            labels2: ObjectDetectionLabels
        """
        from object_detection.utils.np_box_list_ops import concatenate

        new_boxlist = concatenate([labels1.to_boxlist(), labels2.to_boxlist()])
        return ObjectDetectionLabels.from_boxlist(new_boxlist)

    @staticmethod
    def prune_duplicates(labels, score_thresh, merge_thresh):
        """Remove duplicate boxes.

        Runs non-maximum suppression to remove duplicate boxes that result from
        sliding window prediction algorithm.

        Args:
            labels: ObjectDetectionLabels
            score_thresh: the minimum allowed score of boxes
            merge_thresh: the minimum IOA allowed when merging two boxes
                together

        Returns:
            ObjectDetectionLabels
        """
        from object_detection.utils.np_box_list_ops import non_max_suppression

        max_output_size = 1000000
        pruned_boxlist = non_max_suppression(labels.boxlist,
                                             max_output_size=max_output_size,
                                             iou_threshold=merge_thresh,
                                             score_threshold=score_thresh)
        return ObjectDetectionLabels.from_boxlist(pruned_boxlist)