def absolute_window(base_window, rel_window, strict=True): window = Window( col_off=rel_window.col_off - base_window.col_off, row_off=rel_window.row_off - base_window.row_off, width=rel_window.width, height=rel_window.height, ) return window.intersection(base_window) if strict else window
def relative_window(base_window, abs_window, strict=True): window = Window( col_off=base_window.col_off + abs_window.col_off, row_off=base_window.row_off + abs_window.row_off, width=abs_window.width, height=abs_window.height, ) return window.intersection(base_window) if strict else window
def get_chip_windows( meta_raster, chip_width: int = 256, chip_height: int = 256, skip_partial_chips: bool = False, ) -> Generator[Tuple[Window, Polygon, affine.Affine], any, None]: """Generator for rasterio windows of specified pixel size to iterate over an image in chips. Chips are created row wise, from top to bottom of the raster. Args: meta_raster: rasterio src.meta or src.profile chip_width: Desired pixel width. chip_height: Desired pixel height. skip_partial_chips: Skip image chips at the edge of the raster that do not result in a full size chip. Returns : Yields tuple of rasterio window, Polygon and transform. """ raster_width, raster_height = meta_raster["width"], meta_raster["height"] big_window = Window(col_off=0, row_off=0, width=raster_width, height=raster_height) col_row_offsets = itertools.product(range(0, raster_width, chip_width), range(0, raster_height, chip_height)) for col_off, row_off in col_row_offsets: chip_window = Window(col_off=col_off, row_off=row_off, width=chip_width, height=chip_height) if skip_partial_chips: if (row_off + chip_height > raster_height or col_off + chip_width > raster_width): continue chip_window = chip_window.intersection(big_window) chip_transform = rasterio.windows.transform(chip_window, meta_raster["transform"]) chip_bounds = rasterio.windows.bounds( chip_window, meta_raster["transform"]) # Use the transform of the full # raster here! chip_poly = shapely.geometry.box(*chip_bounds, ccw=False) yield (chip_window, chip_poly, chip_transform)
def get_chip_windows( raster_width: int, raster_height: int, raster_transform, chip_width: int = 256, chip_height: int = 256, skip_partial_chips: bool = False, ) -> Generator[Tuple[Window, affine.Affine, Polygon], any, None]: """Generator for rasterio windows of specified pixel size to iterate over an image in chips. Chips are created row wise, from top to bottom of the raster. Args: raster_width: rasterio meta['width'] raster_height: rasterio meta['height'] raster_transform: rasterio meta['transform'] chip_width: Desired pixel width. chip_height: Desired pixel height. skip_partial_chips: Skip image chips at the edge of the raster that do not result in a full size chip. Returns : Yields tuple of rasterio chip window, chip transform and chip polygon. """ col_row_offsets = itertools.product(range(0, raster_width, chip_width), range(0, raster_height, chip_height)) raster_window = Window(col_off=0, row_off=0, width=raster_width, height=raster_height) for col_off, row_off in col_row_offsets: chip_window = Window(col_off=col_off, row_off=row_off, width=chip_width, height=chip_height) if skip_partial_chips: if row_off + chip_height > raster_height or col_off + chip_width > raster_width: continue chip_window = chip_window.intersection(raster_window) chip_transform = rasterio.windows.transform(chip_window, raster_transform) chip_bounds = rasterio.windows.bounds( chip_window, raster_transform) # Uses transform of full raster. chip_poly = shapely.geometry.box(*chip_bounds, ccw=False) yield (chip_window, chip_transform, chip_poly)
def make_windows_iterator(image_size, window_size, valid_pixels, validity_threshold): """Iterates of patch windows corresponding to valid pixel positions Args: image_size (tuple[int]): (height, width) in pixels window_size (tuple[int]): (window_height, window_width) in pixels valid_pixels (np.ndarray): (height, width) boolean array validity_threshold (float): percentage of valid pixels in a window to be considered valid Yields: type: rasterio.windows.Window """ # Make full raster window object height, width = image_size full_image_window = Window(col_off=0, row_off=0, width=width, height=height) # Create offsets range to iterate on window_height, window_width = window_size col_row_offsets = product(range(0, width, window_width), range(0, height, window_height)) for window_idx, (col_offset, row_offset) in enumerate(col_row_offsets): # Create window instance window = Window(col_off=col_offset, row_off=row_offset, width=window_width, height=window_height) # Verify the window is valid window_valid_pixels = valid_pixels[window.toslices()] is_valid = window_valid_pixels.sum( ) / window_valid_pixels.size > validity_threshold if not is_valid: continue # Intersect and return window window = window.intersection(full_image_window) yield window_idx, window
def get_input_area_mask(input_area): """Get input area mask, window, and transform for a given input area. Parameters ---------- input_area : str one of the major input area codes, e.g., "gh", "sa", etc Returns ------- mask, transform, window mask is 1 INSIDE input area, 0 outside """ values = [ e["value"] for e in INPUT_AREA_VALUES if input_area in set(e["id"].split(",")) ] bnd = get_input_area_boundary(input_area) ### Get window into raster for bounds of input area with rasterio.open(data_dir / "input_areas.tif") as src: window = src.window(*pg.total_bounds(bnd)) window_floored = window.round_offsets(op="floor", pixel_precision=3) w = math.ceil(window.width + window.col_off - window_floored.col_off) h = math.ceil(window.height + window.row_off - window_floored.row_off) window = Window(window_floored.col_off, window_floored.row_off, w, h) window = window.intersection(Window(0, 0, src.width, src.height)) transform = src.window_transform(window) data = src.read(1, window=window) mask = np.zeros(shape=data.shape, dtype="uint8") for value in values: mask[data == value] = 1 return mask, transform, window
def geometry_window( dataset, shapes, pad_x=0, pad_y=0, north_up=None, rotated=None, pixel_precision=None, boundless=False, ): """Calculate the window within the raster that fits the bounds of the geometry plus optional padding. The window is the outermost pixel indices that contain the geometry (floor of offsets, ceiling of width and height). If shapes do not overlap raster, a WindowError is raised. Parameters ---------- dataset : dataset object opened in 'r' mode Raster for which the mask will be created. shapes : iterable over geometries. A geometry is a GeoJSON-like object or implements the geo interface. Must be in same coordinate system as dataset. pad_x : float Amount of padding (as fraction of raster's x pixel size) to add to left and right side of bounds. pad_y : float Amount of padding (as fraction of raster's y pixel size) to add to top and bottom of bounds. north_up : optional This parameter is ignored since version 1.2.1. A deprecation warning will be emitted in 1.3.0. rotated : optional This parameter is ignored since version 1.2.1. A deprecation warning will be emitted in 1.3.0. pixel_precision : int or float, optional Number of places of rounding precision or absolute precision for evaluating bounds of shapes. boundless : bool, optional Whether to allow a boundless window or not. Returns ------- rasterio.windows.Window """ if pad_x: pad_x = abs(pad_x * dataset.res[0]) if pad_y: pad_y = abs(pad_y * dataset.res[1]) all_bounds = [bounds(shape) for shape in shapes] xs = [ x for (left, bottom, right, top) in all_bounds for x in (left - pad_x, right + pad_x, right + pad_x, left - pad_x) ] ys = [ y for (left, bottom, right, top) in all_bounds for y in (top + pad_y, top + pad_y, bottom - pad_x, bottom - pad_x) ] rows1, cols1 = rowcol(dataset.transform, xs, ys, op=math.floor, precision=pixel_precision) if isinstance(rows1, (int, float)): rows1 = [rows1] if isinstance(cols1, (int, float)): cols1 = [cols1] rows2, cols2 = rowcol(dataset.transform, xs, ys, op=math.ceil, precision=pixel_precision) if isinstance(rows2, (int, float)): rows2 = [rows2] if isinstance(cols2, (int, float)): cols2 = [cols2] rows = rows1 + rows2 cols = cols1 + cols2 row_start, row_stop = min(rows), max(rows) col_start, col_stop = min(cols), max(cols) window = Window( col_off=col_start, row_off=row_start, width=max(col_stop - col_start, 0.0), height=max(row_stop - row_start, 0.0), ) # Make sure that window overlaps raster raster_window = Window(0, 0, dataset.width, dataset.height) if not boundless: window = window.intersection(raster_window) return window
def geometry_window(dataset, shapes, pad_x=0, pad_y=0, north_up=True, rotated=False, pixel_precision=3): """Calculate the window within the raster that fits the bounds of the geometry plus optional padding. The window is the outermost pixel indices that contain the geometry (floor of offsets, ceiling of width and height). If shapes do not overlap raster, a WindowError is raised. Parameters ---------- dataset: dataset object opened in 'r' mode Raster for which the mask will be created. shapes: iterable over geometries. A geometry is a GeoJSON-like object or implements the geo interface. Must be in same coordinate system as dataset. pad_x: float Amount of padding (as fraction of raster's x pixel size) to add to left and right side of bounds. pad_y: float Amount of padding (as fraction of raster's y pixel size) to add to top and bottom of bounds. north_up: bool If True (default), the origin point of the raster's transform is the northernmost point and y pixel values are negative. rotated: bool If true, some rotation terms exist in the dataset transform (this requires special attention.) pixel_precision: int Number of places of rounding precision for evaluating bounds of shapes. Returns ------- window: rasterio.windows.Window instance """ if pad_x: pad_x = abs(pad_x * dataset.res[0]) if pad_y: pad_y = abs(pad_y * dataset.res[1]) if not rotated: all_bounds = [bounds(shape, north_up=north_up) for shape in shapes] lefts, bottoms, rights, tops = zip(*all_bounds) left = min(lefts) - pad_x right = max(rights) + pad_x if north_up: bottom = min(bottoms) - pad_y top = max(tops) + pad_y else: bottom = max(bottoms) + pad_y top = min(tops) - pad_y else: # get the bounds in the pixel domain by specifying a transform to the bounds function all_bounds_px = [ bounds(shape, transform=~dataset.transform) for shape in shapes ] # get left, right, top, and bottom as above lefts, bottoms, rights, tops = zip(*all_bounds_px) left = min(lefts) - pad_x right = max(rights) + pad_x top = min(tops) - pad_y bottom = max(bottoms) + pad_y # do some clamping if there are any values less than zero or greater than dataset shape left = max(0, left) top = max(0, top) right = min(dataset.shape[1], right) bottom = min(dataset.shape[0], bottom) # convert the bounds back to the CRS domain left, top = (left, top) * dataset.transform right, bottom = (right, bottom) * dataset.transform window = dataset.window(left, bottom, right, top) window_floored = window.round_offsets(op='floor', pixel_precision=pixel_precision) w = math.ceil(window.width + window.col_off - window_floored.col_off) h = math.ceil(window.height + window.row_off - window_floored.row_off) window = Window(window_floored.col_off, window_floored.row_off, w, h) # Make sure that window overlaps raster raster_window = Window(0, 0, dataset.width, dataset.height) # This will raise a WindowError if windows do not overlap window = window.intersection(raster_window) return window
def geometry_window(dataset, shapes, pad_x=0, pad_y=0, north_up=True, rotated=False, pixel_precision=3): """Calculate the window within the raster that fits the bounds of the geometry plus optional padding. The window is the outermost pixel indices that contain the geometry (floor of offsets, ceiling of width and height). If shapes do not overlap raster, a WindowError is raised. Parameters ---------- dataset: dataset object opened in 'r' mode Raster for which the mask will be created. shapes: iterable over geometries. A geometry is a GeoJSON-like object or implements the geo interface. Must be in same coordinate system as dataset. pad_x: float Amount of padding (as fraction of raster's x pixel size) to add to left and right side of bounds. pad_y: float Amount of padding (as fraction of raster's y pixel size) to add to top and bottom of bounds. north_up: bool If True (default), the origin point of the raster's transform is the northernmost point and y pixel values are negative. rotated: bool If true, some rotation terms exist in the dataset transform (this requires special attention.) pixel_precision: int Number of places of rounding precision for evaluating bounds of shapes. Returns ------- window: rasterio.windows.Window instance """ if pad_x: pad_x = abs(pad_x * dataset.res[0]) if pad_y: pad_y = abs(pad_y * dataset.res[1]) if not rotated: all_bounds = [bounds(shape, north_up=north_up) for shape in shapes] lefts, bottoms, rights, tops = zip(*all_bounds) left = min(lefts) - pad_x right = max(rights) + pad_x if north_up: bottom = min(bottoms) - pad_y top = max(tops) + pad_y else: bottom = max(bottoms) + pad_y top = min(tops) - pad_y else: # get the bounds in the pixel domain by specifying a transform to the bounds function all_bounds_px = [bounds(shape, transform=~dataset.transform) for shape in shapes] # get left, right, top, and bottom as above lefts, bottoms, rights, tops = zip(*all_bounds_px) left = min(lefts) - pad_x right = max(rights) + pad_x top = min(tops) - pad_y bottom = max(bottoms) + pad_y # do some clamping if there are any values less than zero or greater than dataset shape left = max(0, left) top = max(0, top) right = min(dataset.shape[1], right) bottom = min(dataset.shape[0], bottom) # convert the bounds back to the CRS domain left, top = (left, top) * dataset.transform right, bottom = (right, bottom) * dataset.transform window = dataset.window(left, bottom, right, top) window_floored = window.round_offsets(op='floor', pixel_precision=pixel_precision) w = math.ceil(window.width + window.col_off - window_floored.col_off) h = math.ceil(window.height + window.row_off - window_floored.row_off) window = Window(window_floored.col_off, window_floored.row_off, w, h) # Make sure that window overlaps raster raster_window = Window(0, 0, dataset.width, dataset.height) # This will raise a WindowError if windows do not overlap window = window.intersection(raster_window) return window
def predict(self, path, predpath): with rasterio.open(path, "r") as src: meta = src.meta self.model.eval() # for prediction predimage = os.path.join(predpath, os.path.basename(path)) os.makedirs(predpath, exist_ok=True) meta["count"] = 1 meta["dtype"] = "uint8" # storing as uint8 saves a lot of storage space #Window(col_off, row_off, width, height) H, W = self.image_size rows = np.arange(0, meta["height"], H) cols = np.arange(0, meta["width"], W) image_window = Window(0, 0, meta["width"], meta["height"]) with rasterio.open(predimage, "w+", **meta) as dst: for r, c in tqdm(product(rows, cols), total=len(rows) * len(cols), leave=False): window = image_window.intersection( Window(c-self.offset, r-self.offset, W+self.offset, H+self.offset)) with rasterio.open(path) as src: image = src.read(window=window) # if L1C image (13 bands). read only the 12 bands compatible with L2A data if (image.shape[0] == 13): image = image[[l1cbands.index(b) for b in l2abands]] # to torch + normalize image = self.transform(torch.from_numpy(image.astype(np.float32)), [])[0].to(self.device) # predict with torch.no_grad(): x = image.unsqueeze(0) #import pdb; pdb.set_trace() y_logits = torch.sigmoid(self.model(x).squeeze(0)) if self.use_test_aug > 0: y_logits += torch.sigmoid(torch.fliplr(self.model(torch.fliplr(x)))).squeeze(0) # fliplr) y_logits += torch.sigmoid(torch.flipud(self.model(torch.flipud(x)))).squeeze(0) # flipud if self.use_test_aug > 1: for rot in [1, 2, 3]: # 90, 180, 270 degrees y_logits += torch.sigmoid(torch.rot90(self.model(torch.rot90(x, rot, [2, 3])),-rot,[2,3]).squeeze(0)) y_logits /= 6 else: y_logits /= 3 y_score = y_logits.cpu().detach().numpy()[0] #y_score = y_score[:,self.offset:-self.offset, self.offset:-self.offset] data = dst.read(window=window)[0] / 255 overlap = data > 0 if overlap.any(): # smooth transition in overlapping regions dx, dy = np.gradient(overlap.astype(float)) # get border g = np.abs(dx) + np.abs(dy) transition = gaussian_filter(g, sigma=self.offset / 2) transition /= transition.max() transition[~overlap] = 1.# normalize to 1 y_score = transition * y_score + (1-transition) * data # write writedata = (np.expand_dims(y_score, 0).astype(np.float32) * 255).astype(np.uint8) dst.write(writedata, window=window)