diff --git a/detectree2/preprocessing/tiling.py b/detectree2/preprocessing/tiling.py index 3600ab21..8a520505 100644 --- a/detectree2/preprocessing/tiling.py +++ b/detectree2/preprocessing/tiling.py @@ -9,6 +9,7 @@ import random import shutil import warnings +from math import ceil from pathlib import Path import cv2 @@ -206,23 +207,31 @@ def tile_data_train( # noqa: C901 tilename = Path(data.name).stem crs = data.crs.data["init"].split(":")[1] # out_img, out_transform = mask(data, shapes=crowns.buffer(buffer), crop=True) - for minx in np.arange(data.bounds[0], data.bounds[2] - tile_width, tile_width, int): - for miny in np.arange(data.bounds[1], data.bounds[3] - tile_height, tile_height, int): + # Should start from data.bounds[0] + buffer, data.bounds[1] + buffer to avoid later complications + for minx in np.arange(ceil(data.bounds[0]) + buffer, data.bounds[2] - tile_width - buffer, tile_width, int): + for miny in np.arange(ceil(data.bounds[1]) + buffer, data.bounds[3] - tile_height - buffer, tile_height, int): out_path_root = out_path / f"{tilename}_{minx}_{miny}_{tile_width}_{buffer}_{crs}" - # new tiling bbox including the buffer - bbox = box( - minx - buffer, - miny - buffer, - minx + tile_width + buffer, - miny + tile_height + buffer, - ) - # turn the bounding boxes into geopandas DataFrames - geo = gpd.GeoDataFrame({"geometry": bbox}, - index=[0], - crs=data.crs) + # Calculate the buffered tile dimensions + #tile_width_buffered = tile_width + 2 * buffer + #tile_height_buffered = tile_height + 2 * buffer + + # Calculate the bounding box coordinates with buffer + minx_buffered = minx - buffer + miny_buffered = miny - buffer + maxx_buffered = minx + tile_width + buffer + maxy_buffered = miny + tile_height + buffer + + # Create the affine transformation matrix for the tile + # transform = from_bounds(minx_buffered, miny_buffered, maxx_buffered, + # maxy_buffered, tile_width_buffered, tile_height_buffered) + bbox = box(minx_buffered, miny_buffered, maxx_buffered, maxy_buffered) + geo = gpd.GeoDataFrame({"geometry": bbox}, index=[0], crs=data.crs) + coords = get_features(geo) + + # Skip if insufficient coverage of crowns - good to have early on to save on unnecessary processing with warnings.catch_warnings(): warnings.simplefilter("ignore") # Warning: @@ -237,9 +246,6 @@ def tile_data_train( # noqa: C901 if (overlapping_crowns.dissolve().area[0] / geo.area[0]) < threshold: continue - # here we are cropping the tiff to the bounding box of the tile we want - coords = get_features(geo) - # define the tile as a mask of the whole tiff with just the bounding box out_img, out_transform = mask(data, shapes=coords, crop=True) @@ -255,8 +261,6 @@ def tile_data_train( # noqa: C901 elif sumnan > nan_threshold * totalpix: # reject tiles with many NaN cells continue - # copy the metadata then update it, the "nodata" and "dtype" where important as made larger - # tifs have outputted tiles which were not just black out_meta = data.meta.copy() out_meta.update({ "driver": "GTiff", @@ -265,8 +269,7 @@ def tile_data_train( # noqa: C901 "transform": out_transform, "nodata": None, }) - - # dtype needs to be unchanged for some data and set to uint8 for others + # dtype needs to be unchanged for some data and set to uint8 for others to deal with black tiles if dtype_bool: out_meta.update({"dtype": "uint8"}) @@ -314,20 +317,8 @@ def tile_data_train( # noqa: C901 overlapping_crowns = overlapping_crowns.explode(index_parts=True) - # translate to 0,0 to overlay on png - # this now works as a universal approach. - if minx == data.bounds[0] and miny == data.bounds[1]: - # print("We are in the bottom left!") - moved = overlapping_crowns.translate(-minx, -miny) - elif miny == data.bounds[1]: - # print("We are on the bottom, but not bottom left") - moved = overlapping_crowns.translate(-minx + buffer, -miny) - elif minx == data.bounds[0]: - # print("We are along the left hand side, but not bottom left!") - moved = overlapping_crowns.translate(-minx, -miny + buffer) - else: - # print("We are in the middle!") - moved = overlapping_crowns.translate(-minx + buffer, -miny + buffer) + # Translate to 0,0 to overlay on png + moved = overlapping_crowns.translate(-minx + buffer, -miny + buffer) # scale to deal with the resolution scalingx = 1 / (data.transform[0]) diff --git a/docs/source/tutorial.rst b/docs/source/tutorial.rst index 3b17e5ba..c51b2294 100644 --- a/docs/source/tutorial.rst +++ b/docs/source/tutorial.rst @@ -145,6 +145,42 @@ The data has now been tiled and partitioned for model training, tuning and evalu └── test (test data folder) +It is advisable to do a visual inspection on the tiles to ensure that the tiling has worked as expected and that crowns +and images align. This can be done quickly with the inbuilt ``detectron2`` visualisation tools. + +.. code-block:: python + + from detectron2.data import DatasetCatalog, MetadataCatalog + from detectron2.utils.visualizer import Visualizer + from detectree2.models.train import combine_dicts, register_train_data + import random + import cv2 + from PIL import Image + + name = "Danum" + train_location = "/content/drive/Shareddrives/detectree2/data/" + name + "/tiles_" + appends + "/train" + dataset_dicts = combine_dicts(train_location, 1) # The number gives the fold to visualise + trees_metadata = MetadataCatalog.get(name + "_train") + + for d in dataset_dicts: + img = cv2.imread(d["file_name"]) + visualizer = Visualizer(img[:, :, ::-1], metadata=trees_metadata, scale=0.3) + out = visualizer.draw_dataset_dict(d) + image = cv2.cvtColor(out.get_image()[:, :, ::-1], cv2.COLOR_BGR2RGB) + display(Image.fromarray(image)) + + +.. image:: ../../report/figures/trees_train1.jpg + :width: 400 + :alt: Prediction + :align: left + +.. image:: ../../report/figures/trees_train2.jpg + :width: 400 + :alt: Prediction + :align: left + + Training a model ---------------- diff --git a/report/figures/trees_train1.png b/report/figures/trees_train1.png new file mode 100644 index 00000000..89d05237 Binary files /dev/null and b/report/figures/trees_train1.png differ diff --git a/report/figures/trees_train2.png b/report/figures/trees_train2.png new file mode 100644 index 00000000..27c80b94 Binary files /dev/null and b/report/figures/trees_train2.png differ