Process GeoTiff and convert predictions to geojson #870
robmarkcole
started this conversation in
Showcase
Replies: 2 comments 2 replies
-
Another way that also works with masks: import numpy as np
import affine
import geopandas as gpd
import shapely
from shapely.geometry import Polygon, mapping, shape
from pycococreatortools.pycococreatortools import binary_mask_to_polygon
def _reduce_geom_precision(geom, precision=2):
"From solaris.utils.geo"
geojson = mapping(geom)
geojson['coordinates'] = np.round(np.array(geojson['coordinates']),
precision)
return shape(geojson)
def affine_transform_gdf(gdf:gpd.GeoDataFrame, affine_obj:affine.Affine, inverse:bool=False,
geom_col:str='geometry', precision:int=None) -> gpd.GeoDataFrame:
"""Adapted from solaris, transforms all geometries in GeoDataFrame to pixel coordinates from
Georeferced coordinates and vice versa"""
if 'geometry' not in gdf.columns: gdf = gdf.rename(columns={geom_col: 'geometry'})
gdf["geometry"] = gdf["geometry"].apply(convert_poly_coords,
affine_obj=affine_obj,
inverse=inverse)
if precision is not None:
gdf['geometry'] = gdf['geometry'].apply(
_reduce_geom_precision, precision=precision)
# the CRS is no longer valid - remove it
gdf.crs = None
return gdf
def convert_poly_coords(geom:shape, raster_src:str=None, affine_obj:affine.Affine=None,
inverse:bool=False, precision=None) -> shape:
"Adapted from solaris. Converts georeferenced coordinates to pixel coordinates and vice versa"
if not raster_src and not affine_obj:
raise ValueError("Either raster_src or affine_obj must be provided.")
if raster_src is not None:
affine_xform = get_geo_transform(raster_src)
else:
if isinstance(affine_obj, affine.Affine):
affine_xform = affine_obj
else:
# assume it's a list in either gdal or "standard" order
# (list_to_affine checks which it is)
if len(affine_obj) == 9: # if it's straight from rasterio
affine_obj = affine_obj[0:6]
affine_xform = list_to_affine(affine_obj)
if inverse: # geo->px transform
affine_xform = ~affine_xform
if isinstance(geom, str):
# get the polygon out of the wkt string
g = shapely.wkt.loads(geom)
elif isinstance(geom, shapely.geometry.base.BaseGeometry):
g = geom
else:
raise TypeError('The provided geometry is not an accepted format. '
'This function can only accept WKT strings and '
'shapely geometries.')
xformed_g = shapely.affinity.affine_transform(g, [affine_xform.a,
affine_xform.b,
affine_xform.d,
affine_xform.e,
affine_xform.xoff,
affine_xform.yoff])
if isinstance(geom, str):
# restore to wkt string format
xformed_g = shapely.wkt.dumps(xformed_g)
if precision is not None:
xformed_g = _reduce_geom_precision(xformed_g, precision=precision)
return xformed_g
def georegister_px_df(df:pd.DataFrame, im_path=None, affine_obj:affine.Affine=None, crs=None,
geom_col:str='geometry', precision:int=None, output_path=None) -> gpd.GeoDataFrame:
with rio.open(im_path) as im:
affine_obj = im.transform
crs = im.crs
tmp_df = affine_transform_gdf(df, affine_obj, geom_col=geom_col,
precision=precision)
result = gpd.GeoDataFrame(tmp_df)
result.set_crs(crs='epsg:' + str(crs.to_epsg()), allow_override=True, inplace=True)
if output_path is not None:
if output_path.lower().endswith('json'):
result.to_file(output_path, driver='GeoJSON')
else:
result.to_csv(output_path, index=False)
return result
def georef_sahi_preds(preds, path_to_ref_img, result_type='bbox') -> gpd.GeoDataFrame:
"Converts a list of `ObjectPredictions` to a geodataframe, georeferenced according to reference image"
labels = [p.category.id for p in preds]
if result_type == 'bbox':
polys = [box(*p.bbox.to_xyxy()) for p in preds]
elif result_type == 'mask':
polys = []
for p in preds:
bmasks = binary_mask_to_polygon(p.mask.bool_mask)
for bmask in bmasks:
xy_coords = [(bmask[i],
bmask[i+1])
for i in range(0,len(bmask),2)]
xy_coords.append(xy_coords[-1])
polys.append(Polygon(xy_coords))
else:
print(f'Unknown result type {result_type}, defaulting to bbox')
polys = [box(*p.bbox.to_xyxy()) for p in preds]
scores = [p.score.value for p in preds]
gdf = gpd.GeoDataFrame({'label':labels, 'geometry':polys, 'score':scores})
tfmd_gdf = georegister_px_df(gdf, path_to_ref_img)
return tfmd_gdf Works like this: tile = path/to/ref/img
sliced_pred_results = get_sliced_prediction(tile,
det_model,
slice_width=slice_size,
slice_height=slice_size,
overlap_height_ratio=0.2,
overlap_width_ratio=0.2,
perform_standard_pred=False,
verbose=2)
# Convert bounding boxes
tfmd_gdf_bbox = georef_sahi_preds(preds=sliced_pred_results.object_prediction_list,
path_to_ref_img=tile, result_type='bbox')
# Convert masks
tfmd_gdf_mask = georef_sahi_preds(preds=sliced_pred_results.object_prediction_list,
path_to_ref_img=tile, result_type='mask') |
Beta Was this translation helpful? Give feedback.
0 replies
-
Any thoughts on sources to reconvert geojson back to coco or better yet, yolo darknet? The only tool online I've found is solaris but was not able to get it to work. |
Beta Was this translation helpful? Give feedback.
2 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
-
I am processing GeoTiffs and wanted to convert the coco annotations to Geojson for viewing in QGIS. The following will perform the conversion:
Beta Was this translation helpful? Give feedback.
All reactions