From 472e55d6d1814f367b31e9f839da155f94a043a4 Mon Sep 17 00:00:00 2001 From: Joey Zhao Date: Tue, 20 Jun 2023 02:12:18 +0800 Subject: [PATCH] change absolute path to relative path for features files adjust some functions relate to SAMTool --- geo_sam_tool.py | 5 ++ icons/img_encoder.png | Bin 0 -> 16871 bytes icons/img_encoder.svg | 118 ++++++++++++++++++++++++++++++++++++++++++ tools/SAMTool.py | 11 ++-- tools/geoTool.py | 10 ++-- tools/torchgeo_sam.py | 116 +++++++++++++++++++++++++---------------- 6 files changed, 207 insertions(+), 53 deletions(-) create mode 100644 icons/img_encoder.png create mode 100644 icons/img_encoder.svg diff --git a/geo_sam_tool.py b/geo_sam_tool.py index bef102d..ead74aa 100644 --- a/geo_sam_tool.py +++ b/geo_sam_tool.py @@ -9,6 +9,7 @@ QFileDialog, QAction, QFileDialog, + QShortcut, ) from PyQt5.QtGui import QKeySequence, QIcon, QColor from PyQt5 import uic @@ -164,6 +165,10 @@ def create_widget_selector(self): self.wdg_sel.setFloating(True) self.iface.addDockWidget(Qt.TopDockWidgetArea, self.wdg_sel) + # setting shortcut + self.shortcut_save = QShortcut(Qt.Key_S, self.iface.mainWindow()) + self.shortcut_save.activated.connect(self.save_shp_file) + # default is fgpt, but do not change when reloading feature folder self.reset_prompt_type() diff --git a/icons/img_encoder.png b/icons/img_encoder.png new file mode 100644 index 0000000000000000000000000000000000000000..b8f7c4a5ba4bb0aa65b482dd38330e5928152319 GIT binary patch literal 16871 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 DM}S{H literal 0 HcmV?d00001 diff --git a/icons/img_encoder.svg b/icons/img_encoder.svg new file mode 100644 index 0000000..52cbd9f --- /dev/null +++ b/icons/img_encoder.svg @@ -0,0 +1,118 @@ + + + + + + + + + + + + diff --git a/tools/SAMTool.py b/tools/SAMTool.py index ad1f172..fd29698 100644 --- a/tools/SAMTool.py +++ b/tools/SAMTool.py @@ -4,7 +4,9 @@ import time import numpy as np -import rasterio as rio +import rasterio +from rasterio.transform import from_bounds as transform_from_bounds +from rasterio.features import shapes as get_shapes from PyQt5.QtWidgets import QMessageBox from qgis.core import QgsRectangle, QgsMessageLog, Qgis from torch.utils.data import DataLoader @@ -88,7 +90,7 @@ def sam_predict(self, canvas_points: Canvas_Points, canvas_rect: Canvas_Rectangl bbox = self.sample_bbox # batch['bbox'][0] # Change to sam.img_encoder.img_size img_width = img_height = self.predictor.model.image_encoder.img_size # 1024 - img_clip_transform = rio.transform.from_bounds( + img_clip_transform = transform_from_bounds( bbox.minx, bbox.miny, bbox.maxx, bbox.maxy, img_width, img_height) input_point, input_label = canvas_points.get_points_and_labels( @@ -123,13 +125,14 @@ def sam_predict(self, canvas_points: Canvas_Points, canvas_rect: Canvas_Rectangl # results = ({'properties': {'raster_val': v}, 'geometry': s} # for i, (s, v) in enumerate(rio.features.shapes(mask.astype(np.uint8), mask=mask, transform=img_clip_transform))) # geoms = list(results) - shape_generator = rio.features.shapes( + shape_generator = get_shapes( mask.astype(np.uint8), mask=mask, + connectivity=8, # change from default:4 to 8 transform=img_clip_transform ) geojson = [{'properties': {'raster_val': value}, 'geometry': polygon} - for polygon, value in shape_generator] + for polygon, value in shape_generator] # add to layer sam_polygon.rollback_changes() diff --git a/tools/geoTool.py b/tools/geoTool.py index 53f7d81..b3e43a5 100644 --- a/tools/geoTool.py +++ b/tools/geoTool.py @@ -1,12 +1,13 @@ import os import typing import numpy as np -from qgis.core import (QgsProject, QgsCoordinateReferenceSystem, Qgis, QgsMessageLog, +from qgis.core import (QgsProject, QgsCoordinateReferenceSystem, Qgis, QgsMessageLog, QgsCoordinateTransform, QgsPointXY, QgsRectangle, QgsVectorLayer) class ImageCRSManager: '''Manage image crs and transform point and extent between image crs and other crs''' + def __init__(self, img_crs) -> None: self.img_crs = QgsCoordinateReferenceSystem( img_crs) # from str to QgsCRS @@ -63,10 +64,10 @@ def extent_to_img_crs( dst_crs, self.img_crs, QgsProject.instance()) extent_transformed = transform.transformBoundingBox(extent) return extent_transformed - + def img_extent_to_crs(self, extent: QgsRectangle, dst_crs: QgsCoordinateReferenceSystem): '''transform extent from this image crs to destination crs - + Parameters: ---------- extent: QgsRectangle @@ -74,7 +75,8 @@ def img_extent_to_crs(self, extent: QgsRectangle, dst_crs: QgsCoordinateReferenc dst_crs: QgsCoordinateReferenceSystem destination crs for extent ''' - transform = QgsCoordinateTransform(self.img_crs, dst_crs, QgsProject.instance()) + transform = QgsCoordinateTransform( + self.img_crs, dst_crs, QgsProject.instance()) extent_transformed = transform.transformBoundingBox(extent) return extent_transformed diff --git a/tools/torchgeo_sam.py b/tools/torchgeo_sam.py index ea51ccd..fbd63e0 100644 --- a/tools/torchgeo_sam.py +++ b/tools/torchgeo_sam.py @@ -1,4 +1,4 @@ -# Extension of torchgeo library by zyzhao +# Extension of torchgeo library by zyzhao import sys import glob import os @@ -8,9 +8,9 @@ from typing import Any, Callable, Dict, List, Optional, Sequence, Tuple, cast, Union, Iterator, Iterable -import rasterio as rio +import rasterio from rasterio.vrt import WarpedVRT -from rasterio.windows import from_bounds +from rasterio.windows import from_bounds as window_from_bounds from rasterio.crs import CRS import numpy as np import pandas as pd @@ -26,8 +26,9 @@ from rtree.index import Index, Property + class TestGridGeoSampler(GridGeoSampler): - + def __iter__(self) -> Iterator[Dict[str, Any]]: """Return the index of a dataset. @@ -58,12 +59,14 @@ def __iter__(self) -> Iterator[Dict[str, Any]]: maxx = bounds.maxx minx = bounds.maxx - self.size[1] query = {"bbox": BoundingBox(minx, maxx, miny, maxy, mint, maxt), - "path": cast(str, hit.object)} + "path": cast(str, hit.object)} + + # BoundingBox(minx, maxx, miny, maxy, mint, maxt) + yield query - yield query # BoundingBox(minx, maxx, miny, maxy, mint, maxt) class SamTestFeatureDataset(RasterDataset): - filename_glob = "*.tif" + filename_glob = "*.tif" # filename_regex = r"^S2.{5}_(?P\d{8})_N\d{4}_R\d{3}_6Bands_S\d{1}" filename_regex = ".*" date_format = "" @@ -76,9 +79,10 @@ def __init__(self, root: str = "data", crs: Optional[CRS] = None, res: Optional[float] = None, bands: Optional[Sequence[str]] = None, - transforms: Optional[Callable[[Dict[str, Any]], Dict[str, Any]]] = None, + transforms: Optional[Callable[[ + Dict[str, Any]], Dict[str, Any]]] = None, cache: bool = True - ) -> None: + ) -> None: if self.separate_files: raise NotImplementedError( 'Testing for separated files are not supported yet' @@ -95,7 +99,8 @@ def __init__(self, root: str = "data", # Populate the dataset index i = 0 - pathname = os.path.join(root, "**", self.filename_glob) + # pathname = os.path.join(root, "**", self.filename_glob) + pathname = os.path.join(root, self.filename_glob) raster_list = glob.glob(pathname, recursive=True) dir_name = os.path.basename(root) csv_filepath = os.path.join(root, dir_name + '.csv') @@ -103,7 +108,8 @@ def __init__(self, root: str = "data", if os.path.exists(csv_filepath): self.index_df = pd.read_csv(csv_filepath) filepath_csv = self.index_df.loc[0, 'filepath'] - if len(self.index_df) == len(raster_list) and os.path.dirname(filepath_csv) == os.path.dirname(raster_list[0]): + # and os.path.dirname(filepath_csv) == os.path.dirname(raster_list[0]): + if len(self.index_df) == len(raster_list): for _, row_df in self.index_df.iterrows(): if crs is None: crs = row_df['crs'] @@ -113,28 +119,35 @@ def __init__(self, root: str = "data", coords = (row_df['minx'], row_df['maxx'], row_df['miny'], row_df['maxy'], row_df['mint'], row_df['maxt']) - filepath = row_df['filepath'] # TODO change to relative name + # change to relative path + filepath = os.path.join( + root, os.path.basename(row_df['filepath'])) self.index.insert(id, coords, filepath) i += 1 # print(coords[0].dtype) index_set = True - print('index loaded from: ', os.path.basename(csv_filepath)) + # print('index loaded from: ', os.path.basename(csv_filepath)) + QgsMessageLog.logMessage( + f"Index loaded from: {os.path.basename(csv_filepath)}", 'Geo SAM', level=Qgis.Info) else: - print('index file does not match the raster list, it will be recreated.') + # print('index file does not match the raster list, it will be recreated.') + QgsMessageLog.logMessage( + f"Index file does not match the raster list, will be recreated.", 'Geo SAM', level=Qgis.Info) if not index_set: - self.index_df = pd.DataFrame(columns = ['id', - 'minx', 'maxx', 'miny', 'maxy', 'mint', 'maxt', - 'filepath', - 'crs', 'res']) + self.index_df = pd.DataFrame(columns=['id', + 'minx', 'maxx', 'miny', 'maxy', 'mint', 'maxt', + 'filepath', + 'crs', 'res']) id_list = [] coords_list = [] filepath_list = [] filename_regex = re.compile(self.filename_regex, re.VERBOSE) - for filepath in raster_list: # glob.iglob(pathname, recursive=True): + # glob.iglob(pathname, recursive=True): + for filepath in raster_list: match = re.match(filename_regex, os.path.basename(filepath)) if match is not None: try: - with rio.open(filepath) as src: + with rasterio.open(filepath) as src: # See if file has a color map if len(self.cmap) == 0: try: @@ -149,7 +162,7 @@ def __init__(self, root: str = "data", with WarpedVRT(src, crs=crs) as vrt: minx, miny, maxx, maxy = vrt.bounds - except rio.errors.RasterioIOError: + except rasterio.errors.RasterioIOError: # Skip files that rasterio is unable to read continue else: @@ -157,29 +170,39 @@ def __init__(self, root: str = "data", maxt: float = sys.maxsize if "date" in match.groupdict(): date = match.group("date") - mint, maxt = disambiguate_timestamp(date, self.date_format) + mint, maxt = disambiguate_timestamp( + date, self.date_format) coords = (minx, maxx, miny, maxy, mint, maxt) self.index.insert(i, coords, filepath) id_list.append(i) coords_list.append(coords) - filepath_list.append(filepath) + # change to relative path + filepath_list.append(os.path.basename(filepath)) i += 1 self.index_df['id'] = id_list self.index_df['filepath'] = filepath_list - self.index_df['minx'] = pd.to_numeric([coord[0] for coord in coords_list], downcast='float') - self.index_df['maxx'] = pd.to_numeric([coord[1] for coord in coords_list], downcast='float') - self.index_df['miny'] = pd.to_numeric([coord[2] for coord in coords_list], downcast='float') - self.index_df['maxy'] = pd.to_numeric([coord[3] for coord in coords_list], downcast='float') - self.index_df['mint'] = pd.to_numeric([coord[4] for coord in coords_list], downcast='float') - self.index_df['maxt'] = pd.to_numeric([coord[5] for coord in coords_list], downcast='float') + self.index_df['minx'] = pd.to_numeric( + [coord[0] for coord in coords_list], downcast='float') + self.index_df['maxx'] = pd.to_numeric( + [coord[1] for coord in coords_list], downcast='float') + self.index_df['miny'] = pd.to_numeric( + [coord[2] for coord in coords_list], downcast='float') + self.index_df['maxy'] = pd.to_numeric( + [coord[3] for coord in coords_list], downcast='float') + self.index_df['mint'] = pd.to_numeric( + [coord[4] for coord in coords_list], downcast='float') + self.index_df['maxt'] = pd.to_numeric( + [coord[5] for coord in coords_list], downcast='float') # print(type(crs), res) self.index_df.loc[:, 'crs'] = str(crs) self.index_df.loc[:, 'res'] = res # print(self.index_df.dtypes) index_set = True self.index_df.to_csv(csv_filepath) - print('index file: ', os.path.basename(csv_filepath), ' saved') + # print('index file: ', os.path.basename(csv_filepath), ' saved') + QgsMessageLog.logMessage( + f"Index file: {os.path.basename(csv_filepath)} saved", 'Geo SAM', level=Qgis.Info) if i == 0: msg = f"No {self.__class__.__name__} data was found in `root='{self.root}'`" @@ -204,7 +227,6 @@ def __init__(self, root: str = "data", self._crs = cast(CRS, crs) self.res = cast(float, res) - def __getitem__(self, query: Dict[str, Any]) -> Dict[str, Any]: """Retrieve image/mask and metadata indexed by query. @@ -238,7 +260,7 @@ def __getitem__(self, query: Dict[str, Any]) -> Dict[str, Any]: # band_indexes = self.band_indexes src = vrt_fh - dest = src.read() # read all bands + dest = src.read() # read all bands # print(src.profile) # print(src.compression) @@ -248,22 +270,23 @@ def __getitem__(self, query: Dict[str, Any]) -> Dict[str, Any]: elif dest.dtype == np.uint32: dest = dest.astype(np.int64) - tensor = torch.tensor(dest) # .float() + tensor = torch.tensor(dest) # .float() # bbox may be useful to form the final mask results (geo-info) sample = {"crs": self.crs, "bbox": bbox, "path": filepath} if self.is_image: sample["image"] = tensor.float() else: - sample["mask"] = tensor # .float() #long() # modified zyzhao + sample["mask"] = tensor # .float() #long() # modified zyzhao if self.transforms is not None: sample = self.transforms(sample) return sample + class SamTestRasterDataset(RasterDataset): - filename_glob = "*.tif" + filename_glob = "*.tif" # filename_regex = r"^S2.{5}_(?P\d{8})_N\d{4}_R\d{3}_6Bands_S\d{1}" filename_regex = ".*" date_format = "" @@ -276,9 +299,10 @@ def __init__(self, root: str = "data", crs: Optional[CRS] = None, res: Optional[float] = None, bands: Optional[Sequence[str]] = None, - transforms: Optional[Callable[[Dict[str, Any]], Dict[str, Any]]] = None, + transforms: Optional[Callable[[ + Dict[str, Any]], Dict[str, Any]]] = None, cache: bool = True - ) -> None: + ) -> None: if self.separate_files: raise NotImplementedError( 'Testing for separated files are not supported yet' @@ -326,7 +350,7 @@ def __getitem__(self, query: Dict[str, Any]) -> Dict[str, Any]: dest = src.read( indexes=band_indexes, out_shape=out_shape, - window=from_bounds(*bounds, src.transform), + window=window_from_bounds(*bounds, src.transform), ) # fix numpy dtypes which are not supported by pytorch tensors @@ -335,13 +359,13 @@ def __getitem__(self, query: Dict[str, Any]) -> Dict[str, Any]: elif dest.dtype == np.uint32: dest = dest.astype(np.int64) - tensor = torch.tensor(dest) # .float() + tensor = torch.tensor(dest) # .float() sample = {"crs": self.crs, "bbox": bbox, "path": filepath} if self.is_image: sample["image"] = tensor.float() else: - sample["mask"] = tensor # .float() #long() # modified zyzhao + sample["mask"] = tensor # .float() #long() # modified zyzhao if self.transforms is not None: sample = self.transforms(sample) @@ -363,7 +387,7 @@ def plot(self, sample, bright=1): image = sample["image"][0, rgb_indices, :, :].permute(1, 2, 0) else: image = sample["image"][rgb_indices, :, :].permute(1, 2, 0) - + # if image.max() > 10: # image = self.apply_scale(image) image = torch.clamp(image*bright/255, min=0, max=1) @@ -375,6 +399,7 @@ def plot(self, sample, bright=1): return fig + class SamTestFeatureGeoSampler(GeoSampler): """Samples entire files at a time. @@ -417,7 +442,8 @@ def __init__( # roi = BoundingBox(*self.index.bounds) raise Exception('roi should be defined based on prompts!!!') else: - self.index = Index(interleaved=False, properties=Property(dimension=3)) + self.index = Index(interleaved=False, + properties=Property(dimension=3)) hits = dataset.index.intersection(tuple(roi), objects=True) # hit_nearest = list(dataset.index.nearest(tuple(roi), num_results=1, objects=True))[0] # print('nearest hit: ', hit_nearest.object) @@ -431,7 +457,8 @@ def __init__( center_x_roi = (roi.maxx + roi.minx)/2 center_y_roi = (roi.maxy + roi.miny)/2 - dist_roi_tmp = (center_x_bbox - center_x_roi)**2 + (center_y_bbox - center_y_roi)**2 + dist_roi_tmp = (center_x_bbox - center_x_roi)**2 + \ + (center_y_bbox - center_y_roi)**2 # print(dist_roi_tmp) if dist_roi_tmp < self.dist_roi: self.dist_roi = dist_roi_tmp @@ -474,5 +501,4 @@ def __len__(self) -> int: Returns: number of patches that will be sampled """ - return self.length #len(self.q_path) - + return self.length # len(self.q_path)