-
Notifications
You must be signed in to change notification settings - Fork 56
fixed issue of disgarding edges when tiling #214
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
|
Thanks for this @TeddyLiang01 - are you able to check why it is failing to build? |
|
The Cocoapi fork can't see cython that was installed. Is there any particular reason why we are using that fork? Also I didn't change dependencies so I'm not sure why it's failing to build when it worked before. |
|
@ChristopherKotthoff was this something you set? |
|
See #211 @TeddyLiang01 can you give a bit more detail about how you solved this issue? Was there any need to handle training tiling and prediction tiling differently given the different requirements? |
|
@PatBall1 The revised code uses essentially the same logic as the original, same grid, same tile size. However, after generating the base grid using range and stopping at the same point the original code did, the code now checks whether the tiles reach the top/right boundary. If not, then it will append x_last and/or y_last, which are the starting positions (bottom-left point of a tile) that place a full tile flush with the boundaries while remaining within them. In terms of the changes to training and prediction, for training, all tiles are still full size and within bounds, so training will continue exactly as before but with fuller coverage near the boundaries, adding at most one extra column and/or row of tiles in the x and y directions. For prediction, this is what this change was mainly trying to fix. The change keeps everything the same but adds at most one extra tile column and/or row that overlaps slightly with the previous tiles while still being full size, ensuring full coverage at the boundaries. Detectree2’s stitching and post-processing already handle overlapping tiles, so this change does not require any modifications to later processing. |
|
Possibly some issues -- the changes make the grid generation more explicit and often more correct, but they also change behavior in corner cases (small images, negative coordinates, odd tile sizes) and could introduce tiles that extend past image bounds or duplicate starts. I recommend testing the cases listed below and (depending on desired behavior) tightening a couple of checks. Detailed points and recommendations
Actionable suggestions
|
|
@TeddyLiang01 let me know what you think. We could have a Me: Would it be possible say, to keep the original discarding of incomplete tiles to avoid any issues BUT then to cover the final incomplete row/column start a tiling that is from the opposite corner of the raster from the initial starting point to cover just that last column row (accepting some overlap with the original tiles)? Copilot: Below is a ready-to-drop-in implementation you can paste in place of the current "grid" block. It:
Paste this inside the tile_placement == "grid" block (replacing the current grid-generation logic): # Ensure integer pixel/metre steps for grid generation
buffer = int(round(buffer))
tile_width = max(1, int(round(tile_width)))
tile_height = max(1, int(round(tile_height)))
coordinates: List[Tuple[int, int]] = []
if tile_placement == "grid":
with rasterio.open(img_path) as data:
left, bottom, right, top = data.bounds
# Inclusive start positions (rounded to integer pixel coordinates)
x0 = int(math.ceil(left)) + buffer
y0 = int(math.ceil(bottom)) + buffer
# Last valid start that keeps a whole tile inside the bounds
x_last = int(math.floor(right)) - tile_width - buffer
y_last = int(math.floor(top)) - tile_height - buffer
def make_regular_starts(start: int, last: int, step: int) -> List[int]:
"""Return a regular stepping list of starts that keep whole tiles inside bounds.
DO NOT add a final 'last' start that isn't on the regular step (preserves
the original 'discard incomplete' behaviour)."""
if last >= start:
return list(range(start, last + 1, step))
return []
# Base (left-to-right / bottom-to-top) starts - only full tiles on the regular grid
x_starts = make_regular_starts(x0, x_last, tile_width)
y_starts = make_regular_starts(y0, y_last, tile_height)
grid_coords: List[Tuple[int, int]] = []
# base grid
grid_coords.extend([(int(minx), int(miny)) for minx in x_starts for miny in y_starts])
# Complementary tiling from the opposite edge to cover the *final* incomplete column/row:
# If the regular left-start grid didn't include the rightmost valid start (x_last),
# add that one start (cover last column). Same for y.
x_opposite = []
if x_last >= x0 and (x_last not in x_starts):
x_opposite = [x_last]
y_opposite = []
if y_last >= y0 and (y_last not in y_starts):
y_opposite = [y_last]
# Add the final column (opposite x) across the base rows (if any)
if x_opposite and y_starts:
grid_coords.extend([(int(x_opposite[0]), int(y)) for y in y_starts])
# Add the final row (opposite y) across the base columns (if any)
if y_opposite and x_starts:
grid_coords.extend([(int(x), int(y_opposite[0])) for x in x_starts])
# Add the corner tile (opposite x and opposite y) if both are needed
if x_opposite and y_opposite:
grid_coords.append((int(x_opposite[0]), int(y_opposite[0])))
# Overlapping (half-shifted) grid: same approach but with half-shifted starts
if overlapping_tiles:
x0_h = x0 + (tile_width // 2)
y0_h = y0 + (tile_height // 2)
x_starts_h = make_regular_starts(x0_h, x_last, tile_width)
y_starts_h = make_regular_starts(y0_h, y_last, tile_height)
grid_coords.extend([(int(minx), int(miny)) for minx in x_starts_h for miny in y_starts_h])
# Opposite for half-shift: same rule - add right/topmost start if it wasn't on the half-shift stepping
if x_last >= x0_h and (x_last not in x_starts_h) and y_starts_h:
grid_coords.extend([(int(x_last), int(y)) for y in y_starts_h])
if y_last >= y0_h and (y_last not in y_starts_h) and x_starts_h:
grid_coords.extend([(int(x), int(y_last)) for x in x_starts_h])
if (x_last >= x0_h and (x_last not in x_starts_h)) and (y_last >= y0_h and (y_last not in y_starts_h)):
grid_coords.append((int(x_last), int(y_last)))
# Deduplicate while preserving order
seen = set()
unique_coords = []
for c in grid_coords:
if c not in seen:
seen.add(c)
unique_coords.append(c)
coordinates = unique_coordsWhy this approach
Tests to run
|
full scene coverage by including partial tiles and the right and top edges during tiling