From 0c858cbdf183ecb9a0dc806639684a4e9e0a5b88 Mon Sep 17 00:00:00 2001 From: Qiusheng Wu Date: Sat, 5 Aug 2023 23:42:18 -0400 Subject: [PATCH 1/8] Add support for multiple input boxes --- samgeo/common.py | 9 +++- samgeo/samgeo.py | 125 +++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 118 insertions(+), 16 deletions(-) diff --git a/samgeo/common.py b/samgeo/common.py index 12eae9f5..f1ece6c8 100644 --- a/samgeo/common.py +++ b/samgeo/common.py @@ -755,6 +755,8 @@ def bbox_to_xy( src_fp: str, coords: list, coord_crs: str = "epsg:4326", **kwargs ) -> list: """Converts a list of coordinates to pixel coordinates, i.e., (col, row) coordinates. + Note that map bbox coords is [minx, miny, maxx, maxy] from bottomleft to topright + While rasterio bbox coords is [minx, max, maxx, min] from topleft to bottomright Args: src_fp (str): The source raster file path. @@ -762,7 +764,7 @@ def bbox_to_xy( coord_crs (str, optional): The coordinate CRS of the input coordinates. Defaults to "epsg:4326". Returns: - list: A list of pixel coordinates in the format of [[minx, miny, maxx, maxy], ...] + list: A list of pixel coordinates in the format of [[minx, maxy, maxx, miny], ...] from top left to bottom right. """ if isinstance(coords, str): @@ -825,7 +827,9 @@ def bbox_to_xy( and maxx < width and maxy < height ): - result.append(coord) + # Note that map bbox coords is [minx, miny, maxx, maxy] from bottomleft to topright + # While rasterio bbox coords is [minx, max, maxx, min] from topleft to bottomright + result.append([minx, maxy, maxx, miny]) if len(result) == 0: print("No valid pixel coordinates found.") @@ -1245,6 +1249,7 @@ def array_to_image( compress = src.compression # Determine the minimum and maximum values in the array + min_value = np.min(array) max_value = np.max(array) diff --git a/samgeo/samgeo.py b/samgeo/samgeo.py index 062255e6..604810f0 100644 --- a/samgeo/samgeo.py +++ b/samgeo/samgeo.py @@ -486,7 +486,7 @@ def predict( self, point_coords=None, point_labels=None, - box=None, + boxes=None, point_crs=None, mask_input=None, multimask_output=True, @@ -507,7 +507,7 @@ def predict( point_labels (list | int | np.ndarray, optional): A length N array of labels for the point prompts. 1 indicates a foreground point and 0 indicates a background point. point_crs (str, optional): The coordinate reference system (CRS) of the point prompts. - box (list | np.ndarray, optional): A length 4 array given a box prompt to the + boxes (list | np.ndarray, optional): A length 4 array given a box prompt to the model, in XYXY format. mask_input (np.ndarray, optional): A low resolution mask input to the model, typically coming from a previous prediction iteration. Has form 1xHxW, where for SAM, H=W=256. @@ -540,16 +540,17 @@ def predict( if hasattr(self, "point_labels"): point_labels = self.point_labels - if point_crs is not None: + if (point_crs is not None) and (point_coords is not None): point_coords = coords_to_xy(self.source, point_coords, point_crs) if isinstance(point_coords, list): point_coords = np.array(point_coords) - if point_labels is None: - point_labels = [1] * len(point_coords) - elif isinstance(point_labels, int): - point_labels = [point_labels] * len(point_coords) + if point_coords is not None: + if point_labels is None: + point_labels = [1] * len(point_coords) + elif isinstance(point_labels, int): + point_labels = [point_labels] * len(point_coords) if isinstance(point_labels, list): if len(point_labels) != len(point_coords): @@ -561,23 +562,119 @@ def predict( ) point_labels = np.array(point_labels) - if isinstance(box, list) and point_crs is not None: - box = np.array(bbox_to_xy(self.source, box, point_crs)) - predictor = self.predictor - masks, scores, logits = predictor.predict( - point_coords, point_labels, box, mask_input, multimask_output, return_logits - ) + + input_boxes = None + if isinstance(boxes, list) and (point_crs is not None): + coords = bbox_to_xy(self.source, boxes, point_crs) + input_boxes = np.array(coords) + if isinstance(coords[0], int): + input_boxes = input_boxes[None, :] + else: + input_boxes = torch.tensor(input_boxes, device=self.device) + input_boxes = predictor.transform.apply_boxes_torch( + input_boxes, self.image.shape[:2] + ) + elif isinstance(boxes, list) and (point_crs is None): + input_boxes = np.array(boxes) + if isinstance(boxes[0], int): + input_boxes = input_boxes[None, :] + + self.boxes = input_boxes + + if boxes is None or (not isinstance(boxes[0], list)): + masks, scores, logits = predictor.predict( + point_coords, + point_labels, + input_boxes, + mask_input, + multimask_output, + return_logits, + ) + else: + masks, scores, logits = predictor.predict_torch( + point_coords=point_coords, + point_labels=point_coords, + boxes=input_boxes, + multimask_output=True, + ) + self.masks = masks self.scores = scores self.logits = logits if output is not None: - self.save_prediction(output, index, mask_multiplier, dtype, **kwargs) + if boxes is None or (not isinstance(boxes[0], list)): + self.save_prediction(output, index, mask_multiplier, dtype, **kwargs) + else: + self.tensor_to_numpy( + index, output, mask_multiplier, dtype, save_args=kwargs + ) if return_results: return masks, scores, logits + def tensor_to_numpy( + self, index=None, output=None, mask_multiplier=255, dtype=np.uint8, save_args={} + ): + """Convert the predicted masks from tensors to numpy arrays. + + Args: + index (index, optional): The index of the mask to save. Defaults to None, + which will save the mask with the highest score. + output (str, optional): The path to the output image. Defaults to None. + mask_multiplier (int, optional): The mask multiplier for the output mask, which is usually a binary mask [0, 1]. + dtype (np.dtype, optional): The data type of the output image. Defaults to np.uint8. + save_args (dict, optional): Optional arguments for saving the output image. Defaults to {}. + + Returns: + np.ndarray: The predicted mask as a numpy array. + """ + + boxes = self.boxes + masks = self.masks + + image_pil = self.image + image_np = np.array(image_pil) + + if index is None: + index = 1 + + masks = masks[:, index, :, :] + masks = masks.squeeze(1) + # masks = torch.tensor([]) + # if len(boxes) > 0: + # masks = self.predict_sam(image_pil, boxes) + + if boxes is None or (len(boxes) == 0): # No "object" instances found + print("No objects found in the image.") + return + else: + # Create an empty image to store the mask overlays + mask_overlay = np.zeros_like( + image_np[..., 0], dtype=dtype + ) # Adjusted for single channel + + for i, (box, mask) in enumerate(zip(boxes, masks)): + # Convert tensor to numpy array if necessary and ensure it contains integers + if isinstance(mask, torch.Tensor): + mask = ( + mask.cpu().numpy().astype(dtype) + ) # If mask is on GPU, use .cpu() before .numpy() + mask_overlay += ((mask > 0) * (i + 1)).astype( + dtype + ) # Assign a unique value for each mask + + # Normalize mask_overlay to be in [0, 255] + mask_overlay = ( + mask_overlay > 0 + ) * mask_multiplier # Binary mask in [0, 255] + + if output is not None: + array_to_image(mask_overlay, output, self.source, dtype=dtype, **save_args) + else: + return mask_overlay + def show_map(self, basemap="SATELLITE", repeat_mode=True, out_dir=None, **kwargs): """Show the interactive map. From 127360a71db60638c5b79aa8f5f752ad42730e7f Mon Sep 17 00:00:00 2001 From: Qiusheng Wu Date: Sat, 5 Aug 2023 23:52:09 -0400 Subject: [PATCH 2/8] Clean up code --- samgeo/samgeo.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/samgeo/samgeo.py b/samgeo/samgeo.py index 604810f0..f9299eb1 100644 --- a/samgeo/samgeo.py +++ b/samgeo/samgeo.py @@ -494,7 +494,7 @@ def predict( output=None, index=None, mask_multiplier=255, - dtype=np.float32, + dtype="float32", return_results=False, **kwargs, ): @@ -615,7 +615,7 @@ def predict( return masks, scores, logits def tensor_to_numpy( - self, index=None, output=None, mask_multiplier=255, dtype=np.uint8, save_args={} + self, index=None, output=None, mask_multiplier=255, dtype="uint8", save_args={} ): """Convert the predicted masks from tensors to numpy arrays. @@ -642,9 +642,6 @@ def tensor_to_numpy( masks = masks[:, index, :, :] masks = masks.squeeze(1) - # masks = torch.tensor([]) - # if len(boxes) > 0: - # masks = self.predict_sam(image_pil, boxes) if boxes is None or (len(boxes) == 0): # No "object" instances found print("No objects found in the image.") From 3753c2b6bf19de2d6a6f9e8557dee13793598ff5 Mon Sep 17 00:00:00 2001 From: Qiusheng Wu Date: Sun, 6 Aug 2023 01:04:08 -0400 Subject: [PATCH 3/8] Add functions for saving bbox coords to vector --- samgeo/common.py | 89 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) diff --git a/samgeo/common.py b/samgeo/common.py index f1ece6c8..4e117586 100644 --- a/samgeo/common.py +++ b/samgeo/common.py @@ -751,6 +751,95 @@ def coords_to_xy( return result +def boxes_to_vector(coords, src_crs, dst_crs="EPSG:4326", output=None, **kwargs): + """ + Convert a list of bounding box coordinates to vector data. + + Args: + coords (list): A list of bounding box coordinates in the format [[left, top, right, bottom], [left, top, right, bottom], ...]. + src_crs (int or str): The EPSG code or proj4 string representing the source coordinate reference system (CRS) of the input coordinates. + dst_crs (int or str, optional): The EPSG code or proj4 string representing the destination CRS to reproject the data (default is "EPSG:4326"). + output (str or None, optional): The full file path (including the directory and filename without the extension) where the vector data should be saved. + If None (default), the function returns the GeoDataFrame without saving it to a file. + **kwargs: Additional keyword arguments to pass to geopandas.GeoDataFrame.to_file() when saving the vector data. + + Returns: + geopandas.GeoDataFrame or None: The GeoDataFrame with the converted vector data if output is None, otherwise None if the data is saved to a file. + """ + + from shapely.geometry import box + + # Create a list of Shapely Polygon objects based on the provided coordinates + polygons = [box(*coord) for coord in coords] + + # Create a GeoDataFrame with the Shapely Polygon objects + gdf = gpd.GeoDataFrame({"geometry": polygons}, crs=src_crs) + + # Reproject the GeoDataFrame to the specified EPSG code + gdf_reprojected = gdf.to_crs(dst_crs) + + if output is not None: + gdf_reprojected.to_file(output, **kwargs) + else: + return gdf_reprojected + + +def rowcol_to_xy( + src_fp, + rows=None, + cols=None, + boxes=None, + zs=None, + offset="center", + output=None, + dst_crs="EPSG:4326", + **kwargs, +): + """Converts a list of (row, col) coordinates to (x, y) coordinates. + + Args: + src_fp (str): The source raster file path. + rows (list, optional): A list of row coordinates. Defaults to None. + cols (list, optional): A list of col coordinates. Defaults to None. + boxes (list, optional): A list of (row, col) coordinates in the format of [[left, top, right, bottom], [left, top, right, bottom], ...] + zs: zs (list or float, optional): Height associated with coordinates. Primarily used for RPC based coordinate transformations. + offset (str, optional): Determines if the returned coordinates are for the center of the pixel or for a corner. + output (str, optional): The output vector file path. Defaults to None. + dst_crs (str, optional): The destination CRS. Defaults to "EPSG:4326". + **kwargs: Additional keyword arguments to pass to rasterio.transform.xy. + + Returns: + A list of (x, y) coordinates. + """ + + if boxes is not None: + rows = [] + cols = [] + + for box in boxes: + rows.append(box[1]) + rows.append(box[3]) + cols.append(box[0]) + cols.append(box[2]) + + if rows is None or cols is None: + raise ValueError("rows and cols must be provided.") + + with rasterio.open(src_fp) as src: + xs, ys = rasterio.transform.xy(src.transform, rows, cols, zs, offset, **kwargs) + src_crs = src.crs + + if boxes is None: + return [[x, y] for x, y in zip(xs, ys)] + else: + result = [[xs[i], ys[i + 1], xs[i + 1], ys[i]] for i in range(0, len(xs), 2)] + + if output is not None: + boxes_to_vector(result, src_crs, dst_crs, output) + else: + return result + + def bbox_to_xy( src_fp: str, coords: list, coord_crs: str = "epsg:4326", **kwargs ) -> list: From 7ef4680c2eb00edf632fd79a40742cf016d5e8cc Mon Sep 17 00:00:00 2001 From: Qiusheng Wu Date: Sun, 6 Aug 2023 09:34:07 -0400 Subject: [PATCH 4/8] Add save_boxes method --- docs/examples/data/tree_boxes.geojson | 91 +++++++++++++++++++++++++++ samgeo/samgeo.py | 6 ++ samgeo/text_sam.py | 24 ++++++- 3 files changed, 119 insertions(+), 2 deletions(-) create mode 100644 docs/examples/data/tree_boxes.geojson diff --git a/docs/examples/data/tree_boxes.geojson b/docs/examples/data/tree_boxes.geojson new file mode 100644 index 00000000..5bcee1bc --- /dev/null +++ b/docs/examples/data/tree_boxes.geojson @@ -0,0 +1,91 @@ +{ +"type": "FeatureCollection", +"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:OGC:1.3:CRS84" } }, +"features": [ +{ "type": "Feature", "properties": { }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -51.252247217575253, -22.17537397143715 ], [ -51.252247217575253, -22.175151299350627 ], [ -51.252569487228079, -22.175151299350627 ], [ -51.252569487228079, -22.17537397143715 ], [ -51.252247217575253, -22.17537397143715 ] ] ] } }, +{ "type": "Feature", "properties": { }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -51.256113235301434, -22.176086807142351 ], [ -51.256113235301434, -22.175921089370259 ], [ -51.256278216562002, -22.175921089370259 ], [ -51.256278216562002, -22.176086807142351 ], [ -51.256113235301434, -22.176086807142351 ] ] ] } }, +{ "type": "Feature", "properties": { }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -51.256023013188283, -22.176328719131231 ], [ -51.256023013188283, -22.17611542526388 ], [ -51.256166909782699, -22.17611542526388 ], [ -51.256166909782699, -22.176328719131231 ], [ -51.256023013188283, -22.176328719131231 ] ] ] } }, +{ "type": "Feature", "properties": { }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -51.25174848482078, -22.175220129487528 ], [ -51.25174848482078, -22.175001478354293 ], [ -51.252101193620319, -22.175001478354293 ], [ -51.252101193620319, -22.175220129487528 ], [ -51.25174848482078, -22.175220129487528 ] ] ] } }, +{ "type": "Feature", "properties": { }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -51.251831483292669, -22.17723784867631 ], [ -51.251831483292669, -22.177045411589194 ], [ -51.25211439467828, -22.177045411589194 ], [ -51.25211439467828, -22.17723784867631 ], [ -51.251831483292669, -22.17723784867631 ] ] ] } }, +{ "type": "Feature", "properties": { }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -51.255398734669548, -22.175359341616346 ], [ -51.255398734669548, -22.175218453724209 ], [ -51.255541934012591, -22.175218453724209 ], [ -51.255541934012591, -22.175359341616346 ], [ -51.255398734669548, -22.175359341616346 ] ] ] } }, +{ "type": "Feature", "properties": { }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -51.254476405081192, -22.176538853756128 ], [ -51.254476405081192, -22.176368306904592 ], [ -51.254665437329251, -22.176368306904592 ], [ -51.254665437329251, -22.176538853756128 ], [ -51.254476405081192, -22.176538853756128 ] ] ] } }, +{ "type": "Feature", "properties": { }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -51.254146500921827, -22.175946187999259 ], [ -51.254146500921827, -22.175777254045851 ], [ -51.254317658258351, -22.175777254045851 ], [ -51.254317658258351, -22.175946187999259 ], [ -51.254146500921827, -22.175946187999259 ] ] ] } }, +{ "type": "Feature", "properties": { }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -51.252496069255628, -22.176046610896478 ], [ -51.252496069255628, -22.175890064998367 ], [ -51.252631021334381, -22.175890064998367 ], [ -51.252631021334381, -22.176046610896478 ], [ -51.252496069255628, -22.176046610896478 ] ] ] } }, +{ "type": "Feature", "properties": { }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -51.251199510186694, -22.177298785407608 ], [ -51.251199510186694, -22.176997022906932 ], [ -51.251334587992929, -22.176997022906932 ], [ -51.251334587992929, -22.177298785407608 ], [ -51.251199510186694, -22.177298785407608 ] ] ] } }, +{ "type": "Feature", "properties": { }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -51.25527598642357, -22.176425044852568 ], [ -51.25527598642357, -22.176237089396594 ], [ -51.255437684240611, -22.176237089396594 ], [ -51.255437684240611, -22.176425044852568 ], [ -51.25527598642357, -22.176425044852568 ] ] ] } }, +{ "type": "Feature", "properties": { }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -51.254020654426611, -22.176101069433539 ], [ -51.254020654426611, -22.175976755798299 ], [ -51.25414647423748, -22.175976755798299 ], [ -51.25414647423748, -22.176101069433539 ], [ -51.254020654426611, -22.176101069433539 ] ] ] } }, +{ "type": "Feature", "properties": { }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -51.252982088473544, -22.175510993538328 ], [ -51.252982088473544, -22.175002664478747 ], [ -51.253642012042455, -22.175002664478747 ], [ -51.253642012042455, -22.175510993538328 ], [ -51.252982088473544, -22.175510993538328 ] ] ] } }, +{ "type": "Feature", "properties": { }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -51.254181103154231, -22.177191606323184 ], [ -51.254181103154231, -22.177037274823089 ], [ -51.254398749699767, -22.177037274823089 ], [ -51.254398749699767, -22.177191606323184 ], [ -51.254181103154231, -22.177191606323184 ] ] ] } }, +{ "type": "Feature", "properties": { }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -51.252553118099563, -22.175275243720392 ], [ -51.252553118099563, -22.175103044529301 ], [ -51.252776739810834, -22.175103044529301 ], [ -51.252776739810834, -22.175275243720392 ], [ -51.252553118099563, -22.175275243720392 ] ] ] } }, +{ "type": "Feature", "properties": { }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -51.254886362775423, -22.177370311057302 ], [ -51.254886362775423, -22.17683294664706 ], [ -51.255158841235826, -22.17683294664706 ], [ -51.255158841235826, -22.177370311057302 ], [ -51.254886362775423, -22.177370311057302 ] ] ] } }, +{ "type": "Feature", "properties": { }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -51.255842224848507, -22.177657241655524 ], [ -51.255842224848507, -22.176788715198295 ], [ -51.256496406960991, -22.176788715198295 ], [ -51.256496406960991, -22.177657241655524 ], [ -51.255842224848507, -22.177657241655524 ] ] ] } }, +{ "type": "Feature", "properties": { }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -51.253533705676617, -22.176424120970584 ], [ -51.253533705676617, -22.176125130063305 ], [ -51.253771488427532, -22.176125130063305 ], [ -51.253771488427532, -22.176424120970584 ], [ -51.253533705676617, -22.176424120970584 ] ] ] } }, +{ "type": "Feature", "properties": { }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -51.254900776743405, -22.175319919719421 ], [ -51.254900776743405, -22.175190588146418 ], [ -51.255048534853543, -22.175190588146418 ], [ -51.255048534853543, -22.175319919719421 ], [ -51.254900776743405, -22.175319919719421 ] ] ] } }, +{ "type": "Feature", "properties": { }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -51.254279533692028, -22.177698366317355 ], [ -51.254279533692028, -22.177590263056885 ], [ -51.254393467181124, -22.177590263056885 ], [ -51.254393467181124, -22.177698366317355 ], [ -51.254279533692028, -22.177698366317355 ] ] ] } }, +{ "type": "Feature", "properties": { }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -51.255279989485025, -22.175226169698121 ], [ -51.255279989485025, -22.175062744829052 ], [ -51.255393664316031, -22.175062744829052 ], [ -51.255393664316031, -22.175226169698121 ], [ -51.255279989485025, -22.175226169698121 ] ] ] } }, +{ "type": "Feature", "properties": { }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -51.255333579759174, -22.177231123963931 ], [ -51.255333579759174, -22.177074239336154 ], [ -51.255428836414787, -22.177074239336154 ], [ -51.255428836414787, -22.177231123963931 ], [ -51.255333579759174, -22.177231123963931 ] ] ] } }, +{ "type": "Feature", "properties": { }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -51.255308403813601, -22.176880507927397 ], [ -51.255308403813601, -22.176747809794122 ], [ -51.255450543558837, -22.176747809794122 ], [ -51.255450543558837, -22.176880507927397 ], [ -51.255308403813601, -22.176880507927397 ] ] ] } }, +{ "type": "Feature", "properties": { }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -51.253843165696487, -22.177660626363128 ], [ -51.253843165696487, -22.177512703224846 ], [ -51.254059945901112, -22.177512703224846 ], [ -51.254059945901112, -22.177660626363128 ], [ -51.253843165696487, -22.177660626363128 ] ] ] } }, +{ "type": "Feature", "properties": { }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -51.255203064730871, -22.175576395400999 ], [ -51.255203064730871, -22.175452516487447 ], [ -51.255300678776727, -22.175452516487447 ], [ -51.255300678776727, -22.175576395400999 ], [ -51.255203064730871, -22.175576395400999 ] ] ] } }, +{ "type": "Feature", "properties": { }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -51.255459793205105, -22.175143751428489 ], [ -51.255459793205105, -22.175022129424732 ], [ -51.255589073550262, -22.175022129424732 ], [ -51.255589073550262, -22.175143751428489 ], [ -51.255459793205105, -22.175143751428489 ] ] ] } }, +{ "type": "Feature", "properties": { }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -51.25376973020731, -22.176569471455952 ], [ -51.25376973020731, -22.176256373221324 ], [ -51.254311054782633, -22.176256373221324 ], [ -51.254311054782633, -22.176569471455952 ], [ -51.25376973020731, -22.176569471455952 ] ] ] } }, +{ "type": "Feature", "properties": { }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -51.255254036173639, -22.177629387779884 ], [ -51.255254036173639, -22.177522699538091 ], [ -51.255373575422155, -22.177522699538091 ], [ -51.255373575422155, -22.177629387779884 ], [ -51.255254036173639, -22.177629387779884 ] ] ] } }, +{ "type": "Feature", "properties": { }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -51.255022837499084, -22.175462728531254 ], [ -51.255022837499084, -22.17527484711799 ], [ -51.255209919661546, -22.17527484711799 ], [ -51.255209919661546, -22.175462728531254 ], [ -51.255022837499084, -22.175462728531254 ] ] ] } }, +{ "type": "Feature", "properties": { }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -51.253937030755175, -22.17524145092889 ], [ -51.253937030755175, -22.175092561907878 ], [ -51.254052215789294, -22.175092561907878 ], [ -51.254052215789294, -22.17524145092889 ], [ -51.253937030755175, -22.17524145092889 ] ] ] } }, +{ "type": "Feature", "properties": { }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -51.253226189685158, -22.17548953849699 ], [ -51.253226189685158, -22.175002175218996 ], [ -51.253628061530854, -22.175002175218996 ], [ -51.253628061530854, -22.17548953849699 ], [ -51.253226189685158, -22.17548953849699 ] ] ] } }, +{ "type": "Feature", "properties": { }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -51.256201379596078, -22.176660946867511 ], [ -51.256201379596078, -22.176420117077612 ], [ -51.25640672169002, -22.176420117077612 ], [ -51.25640672169002, -22.176660946867511 ], [ -51.256201379596078, -22.176660946867511 ] ] ] } }, +{ "type": "Feature", "properties": { }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -51.254006250444789, -22.175344454366439 ], [ -51.254006250444789, -22.175210150066263 ], [ -51.25413220318628, -22.175210150066263 ], [ -51.25413220318628, -22.175344454366439 ], [ -51.254006250444789, -22.175344454366439 ] ] ] } }, +{ "type": "Feature", "properties": { }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -51.253855978112469, -22.176154259163283 ], [ -51.253855978112469, -22.176036320135985 ], [ -51.253971638389928, -22.176036320135985 ], [ -51.253971638389928, -22.176154259163283 ], [ -51.253855978112469, -22.176154259163283 ] ] ] } }, +{ "type": "Feature", "properties": { }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -51.255625451519613, -22.177320134315153 ], [ -51.255625451519613, -22.177189905322297 ], [ -51.255739237999236, -22.177189905322297 ], [ -51.255739237999236, -22.177320134315153 ], [ -51.255625451519613, -22.177320134315153 ] ] ] } }, +{ "type": "Feature", "properties": { }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -51.254473750397835, -22.175799664273885 ], [ -51.254473750397835, -22.175344044271263 ], [ -51.254806095929467, -22.175344044271263 ], [ -51.254806095929467, -22.175799664273885 ], [ -51.254473750397835, -22.175799664273885 ] ] ] } }, +{ "type": "Feature", "properties": { }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -51.252270473884856, -22.175158916187012 ], [ -51.252270473884856, -22.175001670439169 ], [ -51.252632109662876, -22.175001670439169 ], [ -51.252632109662876, -22.175158916187012 ], [ -51.252270473884856, -22.175158916187012 ] ] ] } }, +{ "type": "Feature", "properties": { }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -51.254103794014497, -22.176866406719498 ], [ -51.254103794014497, -22.176662926827866 ], [ -51.254258087170555, -22.176662926827866 ], [ -51.254258087170555, -22.176866406719498 ], [ -51.254103794014497, -22.176866406719498 ] ] ] } }, +{ "type": "Feature", "properties": { }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -51.254124338834202, -22.175529770908618 ], [ -51.254124338834202, -22.175399242691789 ], [ -51.254229337650557, -22.175399242691789 ], [ -51.254229337650557, -22.175529770908618 ], [ -51.254124338834202, -22.175529770908618 ] ] ] } }, +{ "type": "Feature", "properties": { }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -51.252768725011414, -22.176344486383396 ], [ -51.252768725011414, -22.175825795057055 ], [ -51.253249611666909, -22.175825795057055 ], [ -51.253249611666909, -22.176344486383396 ], [ -51.252768725011414, -22.176344486383396 ] ] ] } }, +{ "type": "Feature", "properties": { }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -51.251970008782145, -22.175829915703432 ], [ -51.251970008782145, -22.175676730896242 ], [ -51.252105986325653, -22.175676730896242 ], [ -51.252105986325653, -22.175829915703432 ], [ -51.251970008782145, -22.175829915703432 ] ] ] } }, +{ "type": "Feature", "properties": { }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -51.255715506200751, -22.177084629549 ], [ -51.255715506200751, -22.176704622421386 ], [ -51.255933348786201, -22.176704622421386 ], [ -51.255933348786201, -22.177084629549 ], [ -51.255715506200751, -22.177084629549 ] ] ] } }, +{ "type": "Feature", "properties": { }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -51.255258298548029, -22.175386056658354 ], [ -51.255258298548029, -22.175277758757222 ], [ -51.255361981400398, -22.175277758757222 ], [ -51.255361981400398, -22.175386056658354 ], [ -51.255258298548029, -22.175386056658354 ] ] ] } }, +{ "type": "Feature", "properties": { }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -51.252212664455158, -22.176971389149749 ], [ -51.252212664455158, -22.176768382568707 ], [ -51.252472844705309, -22.176768382568707 ], [ -51.252472844705309, -22.176971389149749 ], [ -51.252212664455158, -22.176971389149749 ] ] ] } }, +{ "type": "Feature", "properties": { }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -51.253971019738692, -22.175432297407173 ], [ -51.253971019738692, -22.175334799240005 ], [ -51.25405783063448, -22.175334799240005 ], [ -51.25405783063448, -22.175432297407173 ], [ -51.253971019738692, -22.175432297407173 ] ] ] } }, +{ "type": "Feature", "properties": { }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -51.251546303246378, -22.176345078856787 ], [ -51.251546303246378, -22.176035512836151 ], [ -51.251868256616014, -22.176035512836151 ], [ -51.251868256616014, -22.176345078856787 ], [ -51.251546303246378, -22.176345078856787 ] ] ] } }, +{ "type": "Feature", "properties": { }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -51.252792288436581, -22.17729482535319 ], [ -51.252792288436581, -22.177074032699245 ], [ -51.253015219628963, -22.177074032699245 ], [ -51.253015219628963, -22.17729482535319 ], [ -51.252792288436581, -22.17729482535319 ] ] ] } }, +{ "type": "Feature", "properties": { }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -51.256233176431778, -22.177643677961267 ], [ -51.256233176431778, -22.177461638891735 ], [ -51.256424720425933, -22.177461638891735 ], [ -51.256424720425933, -22.177643677961267 ], [ -51.256233176431778, -22.177643677961267 ] ] ] } }, +{ "type": "Feature", "properties": { }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -51.253196892890472, -22.175423715318004 ], [ -51.253196892890472, -22.175308523733065 ], [ -51.253327275233708, -22.175308523733065 ], [ -51.253327275233708, -22.175423715318004 ], [ -51.253196892890472, -22.175423715318004 ] ] ] } }, +{ "type": "Feature", "properties": { }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -51.254447984940995, -22.177471817739892 ], [ -51.254447984940995, -22.177330993724841 ], [ -51.25461119099667, -22.177330993724841 ], [ -51.25461119099667, -22.177471817739892 ], [ -51.254447984940995, -22.177471817739892 ] ] ] } }, +{ "type": "Feature", "properties": { }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -51.254751341448333, -22.176215338494423 ], [ -51.254751341448333, -22.176050508751977 ], [ -51.254897536150359, -22.176050508751977 ], [ -51.254897536150359, -22.176215338494423 ], [ -51.254751341448333, -22.176215338494423 ] ] ] } }, +{ "type": "Feature", "properties": { }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -51.253888934264396, -22.175382692324931 ], [ -51.253888934264396, -22.175272264199329 ], [ -51.25398330943613, -22.175272264199329 ], [ -51.25398330943613, -22.175382692324931 ], [ -51.253888934264396, -22.175382692324931 ] ] ] } }, +{ "type": "Feature", "properties": { }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -51.253882993640978, -22.175095598379986 ], [ -51.253882993640978, -22.175001570519239 ], [ -51.254067751645849, -22.175001570519239 ], [ -51.254067751645849, -22.175095598379986 ], [ -51.253882993640978, -22.175095598379986 ] ] ] } }, +{ "type": "Feature", "properties": { }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -51.254326468022143, -22.177572807986152 ], [ -51.254326468022143, -22.177475464120555 ], [ -51.254422521852199, -22.177475464120555 ], [ -51.254422521852199, -22.177572807986152 ], [ -51.254326468022143, -22.177572807986152 ] ] ] } }, +{ "type": "Feature", "properties": { }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -51.253497950942311, -22.176019515639407 ], [ -51.253497950942311, -22.175813486103429 ], [ -51.253746588165647, -22.175813486103429 ], [ -51.253746588165647, -22.176019515639407 ], [ -51.253497950942311, -22.176019515639407 ] ] ] } }, +{ "type": "Feature", "properties": { }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -51.253233157410413, -22.175280671760856 ], [ -51.253233157410413, -22.175002300908517 ], [ -51.253587377231611, -22.175002300908517 ], [ -51.253587377231611, -22.175280671760856 ], [ -51.253233157410413, -22.175280671760856 ] ] ] } }, +{ "type": "Feature", "properties": { }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -51.255407239036714, -22.176643356752969 ], [ -51.255407239036714, -22.176510401273571 ], [ -51.255564419259137, -22.176510401273571 ], [ -51.255564419259137, -22.176643356752969 ], [ -51.255407239036714, -22.176643356752969 ] ] ] } }, +{ "type": "Feature", "properties": { }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -51.254103111353579, -22.177050904675433 ], [ -51.254103111353579, -22.176662003857071 ], [ -51.254551297895922, -22.176662003857071 ], [ -51.254551297895922, -22.177050904675433 ], [ -51.254103111353579, -22.177050904675433 ] ] ] } }, +{ "type": "Feature", "properties": { }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -51.253987731671039, -22.177225710022181 ], [ -51.253987731671039, -22.177101834836218 ], [ -51.254203570556641, -22.177101834836218 ], [ -51.254203570556641, -22.177225710022181 ], [ -51.253987731671039, -22.177225710022181 ] ] ] } }, +{ "type": "Feature", "properties": { }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -51.25483374729852, -22.177504099242217 ], [ -51.25483374729852, -22.177390487599148 ], [ -51.254948223151082, -22.177390487599148 ], [ -51.254948223151082, -22.177504099242217 ], [ -51.25483374729852, -22.177504099242217 ] ] ] } }, +{ "type": "Feature", "properties": { }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -51.252064133153752, -22.177332972159711 ], [ -51.252064133153752, -22.177218357970524 ], [ -51.252215846277153, -22.177218357970524 ], [ -51.252215846277153, -22.177332972159711 ], [ -51.252064133153752, -22.177332972159711 ] ] ] } }, +{ "type": "Feature", "properties": { }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -51.254311988898522, -22.175540929954547 ], [ -51.254311988898522, -22.175309064152984 ], [ -51.254464009137486, -22.175309064152984 ], [ -51.254464009137486, -22.175540929954547 ], [ -51.254311988898522, -22.175540929954547 ] ] ] } }, +{ "type": "Feature", "properties": { }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -51.251667505191691, -22.177153368818864 ], [ -51.251667505191691, -22.176976849643896 ], [ -51.251819380058251, -22.176976849643896 ], [ -51.251819380058251, -22.177153368818864 ], [ -51.251667505191691, -22.177153368818864 ] ] ] } }, +{ "type": "Feature", "properties": { }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -51.253382906041942, -22.175488224109206 ], [ -51.253382906041942, -22.175234184582699 ], [ -51.25362559118232, -22.175234184582699 ], [ -51.25362559118232, -22.175488224109206 ], [ -51.253382906041942, -22.175488224109206 ] ] ] } }, +{ "type": "Feature", "properties": { }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -51.253892933151313, -22.177324146065899 ], [ -51.253892933151313, -22.177236445123633 ], [ -51.253979801508478, -22.177236445123633 ], [ -51.253979801508478, -22.177324146065899 ], [ -51.253892933151313, -22.177324146065899 ] ] ] } }, +{ "type": "Feature", "properties": { }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -51.25451426116696, -22.175112565026041 ], [ -51.25451426116696, -22.175001144989398 ], [ -51.254808799397701, -22.175001144989398 ], [ -51.254808799397701, -22.175112565026041 ], [ -51.25451426116696, -22.175112565026041 ] ] ] } }, +{ "type": "Feature", "properties": { }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -51.254272725581735, -22.175810847706931 ], [ -51.254272725581735, -22.17515142011473 ], [ -51.254814776855341, -22.17515142011473 ], [ -51.254814776855341, -22.175810847706931 ], [ -51.254272725581735, -22.175810847706931 ] ] ] } }, +{ "type": "Feature", "properties": { }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -51.252018107728532, -22.17561766136205 ], [ -51.252018107728532, -22.175450838774978 ], [ -51.252175606280495, -22.175450838774978 ], [ -51.252175606280495, -22.17561766136205 ], [ -51.252018107728532, -22.17561766136205 ] ] ] } }, +{ "type": "Feature", "properties": { }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -51.25403770081369, -22.177537217287895 ], [ -51.25403770081369, -22.177411341656921 ], [ -51.254260360251358, -22.177411341656921 ], [ -51.254260360251358, -22.177537217287895 ], [ -51.25403770081369, -22.177537217287895 ] ] ] } }, +{ "type": "Feature", "properties": { }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -51.251957547682792, -22.177051415279415 ], [ -51.251957547682792, -22.176887120923897 ], [ -51.252095574191323, -22.176887120923897 ], [ -51.252095574191323, -22.177051415279415 ], [ -51.251957547682792, -22.177051415279415 ] ] ] } }, +{ "type": "Feature", "properties": { }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -51.255813759606866, -22.176389867897559 ], [ -51.255813759606866, -22.1762011424664 ], [ -51.255958632124006, -22.1762011424664 ], [ -51.255958632124006, -22.176389867897559 ], [ -51.255813759606866, -22.176389867897559 ] ] ] } }, +{ "type": "Feature", "properties": { }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -51.252015761142992, -22.175619866813197 ], [ -51.252015761142992, -22.17538634687072 ], [ -51.25217956571386, -22.17538634687072 ], [ -51.25217956571386, -22.175619866813197 ], [ -51.252015761142992, -22.175619866813197 ] ] ] } }, +{ "type": "Feature", "properties": { }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -51.254304556898362, -22.177049198669895 ], [ -51.254304556898362, -22.176827106377427 ], [ -51.254540840087145, -22.176827106377427 ], [ -51.254540840087145, -22.177049198669895 ], [ -51.254304556898362, -22.177049198669895 ] ] ] } }, +{ "type": "Feature", "properties": { }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -51.254073291349222, -22.175716534588481 ], [ -51.254073291349222, -22.175592275267164 ], [ -51.254161158977929, -22.175592275267164 ], [ -51.254161158977929, -22.175716534588481 ], [ -51.254073291349222, -22.175716534588481 ] ] ] } }, +{ "type": "Feature", "properties": { }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -51.252761306435282, -22.176742416755225 ], [ -51.252761306435282, -22.175820901144991 ], [ -51.253267291438917, -22.175820901144991 ], [ -51.253267291438917, -22.176742416755225 ], [ -51.252761306435282, -22.176742416755225 ] ] ] } }, +{ "type": "Feature", "properties": { }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -51.251494495011919, -22.175566188178951 ], [ -51.251494495011919, -22.175396429788673 ], [ -51.251661809474932, -22.175396429788673 ], [ -51.251661809474932, -22.175566188178951 ], [ -51.251494495011919, -22.175566188178951 ] ] ] } }, +{ "type": "Feature", "properties": { }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -51.253001071685915, -22.176744130951146 ], [ -51.253001071685915, -22.176337969630719 ], [ -51.253239857637347, -22.176337969630719 ], [ -51.253239857637347, -22.176744130951146 ], [ -51.253001071685915, -22.176744130951146 ] ] ] } }, +{ "type": "Feature", "properties": { }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -51.252639682451623, -22.176651947143846 ], [ -51.252639682451623, -22.17652772427661 ], [ -51.252804244661519, -22.17652772427661 ], [ -51.252804244661519, -22.176651947143846 ], [ -51.252639682451623, -22.176651947143846 ] ] ] } }, +{ "type": "Feature", "properties": { }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -51.252505027011146, -22.177570920517017 ], [ -51.252505027011146, -22.176790122545562 ], [ -51.253346493530266, -22.176790122545562 ], [ -51.253346493530266, -22.177570920517017 ], [ -51.252505027011146, -22.177570920517017 ] ] ] } }, +{ "type": "Feature", "properties": { }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -51.251644665026951, -22.1760408714877 ], [ -51.251644665026951, -22.175714619426348 ], [ -51.251969370649697, -22.175714619426348 ], [ -51.251969370649697, -22.1760408714877 ], [ -51.251644665026951, -22.1760408714877 ] ] ] } }, +{ "type": "Feature", "properties": { }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -51.255138364109236, -22.176202171867232 ], [ -51.255138364109236, -22.175675016459898 ], [ -51.255831098130251, -22.175675016459898 ], [ -51.255831098130251, -22.176202171867232 ], [ -51.255138364109236, -22.176202171867232 ] ] ] } }, +{ "type": "Feature", "properties": { }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -51.255211596682777, -22.176196650928219 ], [ -51.255211596682777, -22.175763804481022 ], [ -51.25582800004473, -22.175763804481022 ], [ -51.25582800004473, -22.176196650928219 ], [ -51.255211596682777, -22.176196650928219 ] ] ] } }, +{ "type": "Feature", "properties": { }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -51.251756229502568, -22.17588919705539 ], [ -51.251756229502568, -22.175715027322148 ], [ -51.251969752743364, -22.175715027322148 ], [ -51.251969752743364, -22.17588919705539 ], [ -51.251756229502568, -22.17588919705539 ] ] ] } }, +{ "type": "Feature", "properties": { }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -51.255872095970588, -22.175423367571135 ], [ -51.255872095970588, -22.175002079543685 ], [ -51.25626271964741, -22.175002079543685 ], [ -51.25626271964741, -22.175423367571135 ], [ -51.255872095970588, -22.175423367571135 ] ] ] } }, +{ "type": "Feature", "properties": { }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -51.255281003817664, -22.175172186260202 ], [ -51.255281003817664, -22.175064612722487 ], [ -51.255391404495555, -22.175064612722487 ], [ -51.255391404495555, -22.175172186260202 ], [ -51.255281003817664, -22.175172186260202 ] ] ] } } +] +} diff --git a/samgeo/samgeo.py b/samgeo/samgeo.py index f9299eb1..0aaa1056 100644 --- a/samgeo/samgeo.py +++ b/samgeo/samgeo.py @@ -528,6 +528,12 @@ def predict( """ + if isinstance(boxes, str): + gdf = gpd.read_file(boxes) + if gdf.crs is not None: + gdf = gdf.to_crs("epsg:4326") + boxes = gdf.geometry.bounds.values.tolist() + if isinstance(point_coords, str): point_coords = vector_to_geojson(point_coords) diff --git a/samgeo/text_sam.py b/samgeo/text_sam.py index 7f2b34e7..e5ff055a 100644 --- a/samgeo/text_sam.py +++ b/samgeo/text_sam.py @@ -268,8 +268,8 @@ def predict( image_np = src.read().transpose( (1, 2, 0) ) # Convert rasterio image to numpy array - transform = src.transform # Save georeferencing information - crs = src.crs # Save the Coordinate Reference System + self.transform = src.transform # Save georeferencing information + self.crs = src.crs # Save the Coordinate Reference System image_pil = Image.fromarray( image_np[:, :, :3] ) # Convert numpy array to PIL image, excluding the alpha channel @@ -396,6 +396,26 @@ def predict_batch( if verbose: print(f"Saved the merged prediction to {output}.") + def save_boxes(self, output=None, dst_crs="EPSG:4326", **kwargs): + """Save the bounding boxes to a vector file. + + Args: + output (str): The path to the output vector file. + dst_crs (str, optional): The destination CRS. Defaults to "EPSG:4326". + **kwargs: Additional arguments for boxes_to_vector(). + """ + + if self.boxes is None: + print("Please run predict() first.") + return + else: + boxes = self.boxes.tolist() + coords = rowcol_to_xy(self.source, boxes=boxes, dst_crs=dst_crs, **kwargs) + if output is None: + return boxes_to_vector(coords, self.crs, dst_crs, output) + else: + boxes_to_vector(coords, self.crs, dst_crs, output) + def show_anns( self, figsize=(12, 10), From a16753f35e594fe07ffddc9417557090b26852af Mon Sep 17 00:00:00 2001 From: Qiusheng Wu Date: Sun, 6 Aug 2023 09:47:38 -0400 Subject: [PATCH 5/8] Add geojson support for boxes --- samgeo/samgeo.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/samgeo/samgeo.py b/samgeo/samgeo.py index 0aaa1056..fcce9308 100644 --- a/samgeo/samgeo.py +++ b/samgeo/samgeo.py @@ -533,6 +533,12 @@ def predict( if gdf.crs is not None: gdf = gdf.to_crs("epsg:4326") boxes = gdf.geometry.bounds.values.tolist() + elif isinstance(boxes, dict): + import json + + geojson = json.dumps(boxes) + gdf = gpd.read_file(geojson, driver="GeoJSON") + boxes = gdf.geometry.bounds.values.tolist() if isinstance(point_coords, str): point_coords = vector_to_geojson(point_coords) From 70b21823d5b215c830563185d1de37ffa531cff3 Mon Sep 17 00:00:00 2001 From: Qiusheng Wu Date: Sun, 6 Aug 2023 14:48:39 -0400 Subject: [PATCH 6/8] Add notebook example --- .gitignore | 1 + docs/examples/automatic_mask_generator.ipynb | 2 +- docs/examples/box_prompts.ipynb | 349 +++++++++++++++++++ docs/examples/input_prompts.ipynb | 9 +- docs/examples/satellite-predictor.ipynb | 2 +- docs/examples/satellite.ipynb | 2 +- mkdocs.yml | 1 + setup.py | 4 +- 8 files changed, 358 insertions(+), 12 deletions(-) create mode 100644 docs/examples/box_prompts.ipynb diff --git a/.gitignore b/.gitignore index ae21f962..1e89bb94 100644 --- a/.gitignore +++ b/.gitignore @@ -13,6 +13,7 @@ private/ **/*.jpg **/*.png **/*.csv +docs/examples/*.geojson # C extensions *.so diff --git a/docs/examples/automatic_mask_generator.ipynb b/docs/examples/automatic_mask_generator.ipynb index 5982b202..a38b64d3 100644 --- a/docs/examples/automatic_mask_generator.ipynb +++ b/docs/examples/automatic_mask_generator.ipynb @@ -32,7 +32,7 @@ "metadata": {}, "outputs": [], "source": [ - "# %pip install segment-geospatial leafmap localtileserver" + "# %pip install segment-geospatial" ] }, { diff --git a/docs/examples/box_prompts.ipynb b/docs/examples/box_prompts.ipynb new file mode 100644 index 00000000..3da736bd --- /dev/null +++ b/docs/examples/box_prompts.ipynb @@ -0,0 +1,349 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Segmenting remote sensing imagery with box prompts\n", + "\n", + "[![image](https://studiolab.sagemaker.aws/studiolab.svg)](https://studiolab.sagemaker.aws/import/github/opengeos/segment-geospatial/blob/main/docs/examples/box_prompts.ipynb)\n", + "[![image](https://img.shields.io/badge/Open-Planetary%20Computer-black?style=flat&logo=microsoft)](https://pccompute.westeurope.cloudapp.azure.com/compute/hub/user-redirect/git-pull?repo=https://github.com/opengeos/segment-geospatial&urlpath=lab/tree/segment-geospatial/docs/examples/box_prompts.ipynb&branch=main)\n", + "[![image](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/opengeos/segment-geospatial/blob/main/docs/examples/box_prompts.ipynb)\n", + "\n", + "This notebook shows how to generate object masks from text prompts with the Segment Anything Model (SAM). \n", + "\n", + "Make sure you use GPU runtime for this notebook. For Google Colab, go to `Runtime` -> `Change runtime type` and select `GPU` as the hardware accelerator. " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Install dependencies\n", + "\n", + "Uncomment and run the following cell to install the required dependencies." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# %pip install segment-geospatial" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import leafmap\n", + "from samgeo import tms_to_geotiff\n", + "from samgeo import SamGeo" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Create an interactive map" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "m = leafmap.Map(center=[-22.17615, -51.253043], zoom=18, height=\"800px\")\n", + "m.add_basemap(\"SATELLITE\")\n", + "m" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Download a sample image\n", + "\n", + "Pan and zoom the map to select the area of interest. Use the draw tools to draw a polygon or rectangle on the map" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "bbox = m.user_roi_bounds()\n", + "if bbox is None:\n", + " bbox = [-51.2565, -22.1777, -51.2512, -22.175]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "image = \"Image.tif\"\n", + "tms_to_geotiff(output=image, bbox=bbox, zoom=19, source=\"Satellite\", overwrite=True)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "You can also use your own image. Uncomment and run the following cell to use your own image." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# image = '/path/to/your/own/image.tif'" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Display the downloaded image on the map." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "m.layers[-1].visible = False\n", + "m.add_raster(image, layer_name=\"Image\")\n", + "m" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Initialize SAM class\n", + "\n", + "The initialization of the LangSAM class might take a few minutes. The initialization downloads the model weights and sets up the model for inference.\n", + "\n", + "Specify the file path to the model checkpoint. If it is not specified, the model will to downloaded to the working directory.\n", + "\n", + "Set `automatic=False` to disable the `SamAutomaticMaskGenerator` and enable the `SamPredictor`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "sam = SamGeo(\n", + " model_type=\"vit_h\",\n", + " checkpoint=\"sam_vit_h_4b8939.pth\",\n", + " automatic=False,\n", + " sam_kwargs=None,\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Specify the image to segment. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "sam.set_image(image)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Display the map. Use the drawing tools to draw some rectangles around the features you want to extract, such as trees, buildings." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "m" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The no rectangles are drawn, the default bounding boxes will be used as follows:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "if m.user_rois is not None:\n", + " boxes = m.user_rois\n", + "else:\n", + " boxes = [\n", + " [-51.2546, -22.1771, -51.2541, -22.1767],\n", + " [-51.2538, -22.1764, -51.2535, -22.1761],\n", + " ]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Use the `predict()` method to segment the image with specified bounding boxes. The `boxes` parameter accepts a list of bounding box coordinates in the format of [[left, bottom, right, top], [left, bottom, right, top], ...], a GeoJSON dictionary, or a file path to a GeoJSON file." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "sam.predict(boxes=boxes, point_crs=\"EPSG:4326\", output=\"mask.tif\", dtype=\"uint8\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Add the segmented image to the map." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "m.add_raster('mask.tif', cmap='viridis', nodata=0, layer_name='Mask')\n", + "m" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Alternatively, you can specify a file path to a vector file. Let's download a sample vector file from GitHub." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "url = 'https://opengeos.github.io/data/sam/tree_boxes.geojson'\n", + "geojson = \"tree_boxes.geojson\"\n", + "leafmap.download_file(url, geojson)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Display the vector data on the map." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "m = leafmap.Map()\n", + "m.add_raster(\"Image.tif\", layer_name=\"image\")\n", + "style = {\n", + " \"color\": \"#ffff00\",\n", + " \"weight\": 2,\n", + " \"fillColor\": \"#7c4185\",\n", + " \"fillOpacity\": 0,\n", + "}\n", + "m.add_vector(geojson, style=style, layer_name=\"Bounding boxes\")\n", + "m" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Segment the image using the specified file path to the vector mask." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "sam.predict(boxes=geojson, point_crs=\"EPSG:4326\", output=\"mask2.tif\", dtype=\"uint8\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Display the segmented masks on the map." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "m.add_raster(\"mask2.tif\", cmap=\"Greens\", nodata=0, opacity=0.5, layer_name=\"Tree masks\")\n", + "m" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "![](https://i.imgur.com/SpA2NV9.gif)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "sam", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.8" + }, + "orig_nbformat": 4 + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/docs/examples/input_prompts.ipynb b/docs/examples/input_prompts.ipynb index 8d41df7c..f92c9ee4 100644 --- a/docs/examples/input_prompts.ipynb +++ b/docs/examples/input_prompts.ipynb @@ -32,7 +32,7 @@ "metadata": {}, "outputs": [], "source": [ - "# %pip install segment-geospatial leafmap localtileserver" + "# %pip install segment-geospatial" ] }, { @@ -242,11 +242,6 @@ "source": [ "![](https://i.imgur.com/2Nyg9uW.gif)" ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [] } ], "metadata": { @@ -265,7 +260,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.9.16" + "version": "3.10.8" }, "orig_nbformat": 4 }, diff --git a/docs/examples/satellite-predictor.ipynb b/docs/examples/satellite-predictor.ipynb index 750c2cc3..591b9ef4 100644 --- a/docs/examples/satellite-predictor.ipynb +++ b/docs/examples/satellite-predictor.ipynb @@ -35,7 +35,7 @@ }, "outputs": [], "source": [ - "# %pip install segment-geospatial leafmap localtileserver" + "# %pip install segment-geospatial" ] }, { diff --git a/docs/examples/satellite.ipynb b/docs/examples/satellite.ipynb index c60f17a7..616173fa 100644 --- a/docs/examples/satellite.ipynb +++ b/docs/examples/satellite.ipynb @@ -35,7 +35,7 @@ }, "outputs": [], "source": [ - "# %pip install segment-geospatial leafmap localtileserver" + "# %pip install segment-geospatial" ] }, { diff --git a/mkdocs.yml b/mkdocs.yml index 647360f3..dadc29af 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -52,6 +52,7 @@ nav: - examples/satellite.ipynb - examples/automatic_mask_generator.ipynb - examples/input_prompts.ipynb + - examples/box_prompts.ipynb - examples/text_prompts.ipynb - examples/text_prompts_batch.ipynb - examples/swimming_pools.ipynb diff --git a/setup.py b/setup.py index 48543f48..1e3f0083 100644 --- a/setup.py +++ b/setup.py @@ -19,7 +19,7 @@ dependency_links = [x.strip().replace("git+", "") for x in all_reqs if "git+" not in x] extras_requires = { - "all": ["leafmap", "localtileserver"], + "all": ["groundingdino-py"], } requirements = [] @@ -57,6 +57,6 @@ test_suite="tests", tests_require=test_requirements, url="https://github.com/opengeos/segment-geospatial", - version='0.8.5', + version="0.8.5", zip_safe=False, ) From 7d5df22030af5c7b813d031d5a1c1ded61945441 Mon Sep 17 00:00:00 2001 From: Qiusheng Wu Date: Sun, 6 Aug 2023 14:52:46 -0400 Subject: [PATCH 7/8] Update README --- README.md | 7 ++++--- docs/index.md | 7 ++++--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 36aa1fbd..7e1768fb 100644 --- a/README.md +++ b/README.md @@ -36,10 +36,11 @@ The **segment-geospatial** package draws its inspiration from [segment-anything- ## Examples -- [Segmenting satellite imagery](https://samgeo.gishub.org/examples/satellite) +- [Segmenting remote sensing imagery](https://samgeo.gishub.org/examples/satellite) - [Automatically generating object masks](https://samgeo.gishub.org/examples/automatic_mask_generator) -- [Segmenting satellite imagery with input prompts](https://samgeo.gishub.org/examples/input_prompts) -- [Segmenting imagery with text prompts](https://samgeo.gishub.org/examples/text_prompts) +- [Segmenting remote sensing imagery with input prompts](https://samgeo.gishub.org/examples/input_prompts) +- [Segmenting remote sensing imagery with box prompts](https://samgeo.gishub.org/examples/box_prompts) +- [Segmenting remote sensing imagery with text prompts](https://samgeo.gishub.org/examples/text_prompts) - [Batch segmentation with text prompts](https://samgeo.gishub.org/examples/text_prompts_batch) - [Using segment-geospatial with ArcGIS Pro](https://samgeo.gishub.org/examples/arcgis) - [Segmenting swimming pools with text prompts](https://samgeo.gishub.org/examples/swimming_pools) diff --git a/docs/index.md b/docs/index.md index cda38179..4dbea994 100644 --- a/docs/index.md +++ b/docs/index.md @@ -36,10 +36,11 @@ The **segment-geospatial** package draws its inspiration from [segment-anything- ## Examples -- [Segmenting satellite imagery](https://samgeo.gishub.org/examples/satellite) +- [Segmenting remote sensing imagery](https://samgeo.gishub.org/examples/satellite) - [Automatically generating object masks](https://samgeo.gishub.org/examples/automatic_mask_generator) -- [Segmenting satellite imagery with input prompts](https://samgeo.gishub.org/examples/input_prompts) -- [Segmenting imagery with text prompts](https://samgeo.gishub.org/examples/text_prompts) +- [Segmenting remote sensing imagery with input prompts](https://samgeo.gishub.org/examples/input_prompts) +- [Segmenting remote sensing imagery with box prompts](https://samgeo.gishub.org/examples/box_prompts) +- [Segmenting remote sensing imagery with text prompts](https://samgeo.gishub.org/examples/text_prompts) - [Batch segmentation with text prompts](https://samgeo.gishub.org/examples/text_prompts_batch) - [Using segment-geospatial with ArcGIS Pro](https://samgeo.gishub.org/examples/arcgis) - [Segmenting swimming pools with text prompts](https://samgeo.gishub.org/examples/swimming_pools) From e5b8d8054dccbbbf86705790da244961725d7a4d Mon Sep 17 00:00:00 2001 From: Qiusheng Wu Date: Sun, 6 Aug 2023 15:06:12 -0400 Subject: [PATCH 8/8] Prep for v0.9.0 --- docs/changelog.md | 11 +++++++++++ docs/examples/box_prompts.ipynb | 12 +++++++++++- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/docs/changelog.md b/docs/changelog.md index 9163529f..587eb6d2 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,5 +1,16 @@ # Changelog +## v0.9.0 - Aug 6, 2023 + +**New Features** + +- Added support for multiple input boxes (#159) + +**Improvements** + +- UpdateD groundingdino installation (#147) +- Updated README (#152) + ## v0.8.5 - Jul 19, 2023 **Improvements** diff --git a/docs/examples/box_prompts.ipynb b/docs/examples/box_prompts.ipynb index 3da736bd..edbaf953 100644 --- a/docs/examples/box_prompts.ipynb +++ b/docs/examples/box_prompts.ipynb @@ -189,7 +189,9 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "The no rectangles are drawn, the default bounding boxes will be used as follows:" + "## Create bounding boxes\n", + "\n", + "If no rectangles are drawn, the default bounding boxes will be used as follows:" ] }, { @@ -211,6 +213,8 @@ "cell_type": "markdown", "metadata": {}, "source": [ + "## Segment the image\n", + "\n", "Use the `predict()` method to segment the image with specified bounding boxes. The `boxes` parameter accepts a list of bounding box coordinates in the format of [[left, bottom, right, top], [left, bottom, right, top], ...], a GeoJSON dictionary, or a file path to a GeoJSON file." ] }, @@ -227,6 +231,8 @@ "cell_type": "markdown", "metadata": {}, "source": [ + "## Display the result\n", + "\n", "Add the segmented image to the map." ] }, @@ -244,6 +250,8 @@ "cell_type": "markdown", "metadata": {}, "source": [ + "## Use an existing vector file as box prompts\n", + "\n", "Alternatively, you can specify a file path to a vector file. Let's download a sample vector file from GitHub." ] }, @@ -287,6 +295,8 @@ "cell_type": "markdown", "metadata": {}, "source": [ + "## Segment image with box prompts\n", + "\n", "Segment the image using the specified file path to the vector mask." ] },