Skip to content

Commit

Permalink
Merge pull request #16 from leftfield-geospatial/feature_image_config
Browse files Browse the repository at this point in the history
Feature image config
  • Loading branch information
dugalh authored Sep 15, 2024
2 parents 5b4bfcf + 4a2bb8b commit ec04c47
Show file tree
Hide file tree
Showing 23 changed files with 895 additions and 359 deletions.
8 changes: 4 additions & 4 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ Orthority command line functionality is accessed with the ``oty`` command, and i
- ``exif``: Orthorectify images with frame camera model(s) defined by image EXIF / XMP tags.
- ``odm``: Orthorectify images in a processed OpenDroneMap dataset that includes a DSM.
- ``rpc``: Orthorectify images with RPC camera models defined by image tags / sidecar files or parameter files.
- ``sharpen``: Pan sharpen an image using the Gram-Schmidt method.
- ``sharpen``: Pan-sharpen an image using the Gram-Schmidt method.

Get help on ``oty`` with:

Expand Down Expand Up @@ -110,7 +110,7 @@ As above, but refine the RPC camera model with GCPs in ``source.tif`` tags:
oty rpc --dem dem.tif --gcp-refine tags source.tif
Pan sharpen the multispectral image ``ms.tif`` with the panchromatic image ``pan.tif``:
Pan-sharpen the multispectral image ``ms.tif`` with the panchromatic image ``pan.tif``:

.. code-block:: bash
Expand Down Expand Up @@ -144,7 +144,7 @@ Orthorectify an image using interior and exterior parameter files to generate th
ortho.process('ortho.tif')
Pan sharpen a multispectral image with the matching panchromatic image:
Pan-sharpen a multispectral image with the matching panchromatic image:

.. below copied from docs/scripts/api_pan_sharp.py
Expand All @@ -157,7 +157,7 @@ Pan sharpen a multispectral image with the matching panchromatic image:
pan_file = url_root + 'pan_sharp/pan.tif' # panchromatic drone image
ms_file = url_root + 'pan_sharp/ms.tif' # multispectral (RGB) drone image
# create PanSharpen object and pan sharpen
# create PanSharpen object and pan-sharpen
pan_sharp = oty.PanSharpen(pan_file, ms_file)
pan_sharp.process('pan_sharp.tif')
Expand Down
2 changes: 1 addition & 1 deletion docs/api/pan_sharp.rst
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Pan sharpening
Pan-sharpening
===============

.. automodule:: orthority.pan_sharp
Expand Down
4 changes: 2 additions & 2 deletions docs/getting_started/api/pan_sharp.rst
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
Pan sharpening
Pan-sharpening
==============

The :class:`~orthority.pan_sharp.PanSharpen` class implements Gram-Schmidt pan sharpening. Panchromatic and multispectral images are required to instantiate. The :meth:`~orthority.pan_sharp.PanSharpen.process` method pan sharpens:
The :class:`~orthority.pan_sharp.PanSharpen` class implements Gram-Schmidt pan-sharpening. Panchromatic and multispectral images are required to instantiate. The :meth:`~orthority.pan_sharp.PanSharpen.process` method pan-sharpens:

.. literalinclude:: ../../scripts/api_pan_sharp.py
:language: python
Expand Down
32 changes: 27 additions & 5 deletions docs/getting_started/cli/ortho_config.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,14 @@
Ortho image configuration
=========================

Ortho images are created as GeoTIFFs. Image resolution and format (data type, compression, nodata / internal mask and overviews) can be configured. Configuration options are common to all |oty|_ orthorectification sub-commands and default to sensible values when not supplied.
Ortho image resolution and format can be configured. Configuration options are common to all |oty|_ orthorectification sub-commands and default to sensible values when not supplied.

Resolution, data type and compression
-------------------------------------

Ortho resolution defaults to an estimate of the `ground sampling distance <https://en.wikipedia.org/wiki/Ground_sample_distance>`__. This can be changed with ``--res``. The ortho data type defaults to the source image data type, and can be changed with ``--dtype``.

Compression can be configured with ``--compress`` as either ``deflate`` (with any ortho data type) or ``jpeg`` (with the ``uint8`` or ``uint16`` ortho data types). If ``--compress`` is not specified, compression defaults to ``jpeg`` when the ortho data type is ``uint8``, otherwise it defaults to ``deflate``. When ``jpeg`` compression is used with the ``uint16`` data type, the ortho is 12 bit ``jpeg`` compressed.
Compression can be configured with ``--compress`` as either ``deflate`` or ``lzw`` (with any ortho data type), or ``jpeg`` (with the ``uint8`` or ``uint16`` ortho data types). If ``--compress`` is not specified, compression defaults to ``jpeg`` when the ortho data type is ``uint8``, and to ``deflate`` otherwise. When ``jpeg`` compression is used with the ``uint16`` data type, the ortho is 12 bit ``jpeg`` compressed.

.. note::

Expand All @@ -20,11 +23,30 @@ The next example orthorectifies using EXIF / XMP tags, and configures the ortho
oty exif --dem odm/odm_dem/dsm.tif --res 0.2 --dtype uint8 --compress deflate odm/images/100_0005_0140.tif
Valid ortho pixels are masked with either an internal mask band or a nodata value. By default, an internal mask is used when the ortho image is ``jpeg`` compressed. This avoids ``jpeg`` artefacts in invalid areas. When the ortho is ``deflate`` compressed, the default is use to a nodata value based on the data type. Masking behaviour can be changed with ``--write-mask`` to write an internal mask, or ``--no-write-mask`` to use a nodata value.
Masking and overviews
---------------------

Valid ortho pixels are masked with either an internal mask band or a nodata value. By default, an internal mask is used when the ortho image is ``jpeg`` compressed. This avoids ``jpeg`` artefacts in invalid areas. When the ortho is ``deflate`` or ``lzw`` compressed, the default is use to a nodata value based on the data type. Masking behaviour can be changed with ``--write-mask`` to write an internal mask, or ``--no-write-mask`` to use a nodata value.

Internal overviews are added by default. This can be changed with ``--no-build-ovw``. In this example, we create an ortho image with ``deflate`` compression, nodata rather than internal masking, and no internal overviews:
Internal overviews are added by default. This can be changed with ``--no-build-ovw``. In this example, we create an ortho image with ``deflate`` compression, internal masks, and no internal overviews:

.. code-block:: bash
oty exif --dem odm/odm_dem/dsm.tif --compress deflate --no-write-mask --no-build-ovw odm/images/100_0005_0140.tif
oty exif --dem odm/odm_dem/dsm.tif --compress deflate --write-mask --no-build-ovw odm/images/100_0005_0140.tif
Custom creation options and driver
----------------------------------

For ortho image configurations not possible with the above options, custom creation options can be specified with ``--creation-option``. The ``--compress`` option is ignored, and no other creation options are set by Orthority when this is supplied.

The ortho can be formatted as a ``gtiff`` (GeoTIFF - the default) or ``cog`` (Cloud Optimised GeoTIFF) with ``--driver``.

This example formats the ortho as a GeoTIFF with internal masks, and specifies custom creation options for tiled YCbCr JPEG compression with a quality of 90:

.. code-block:: bash
oty exif --dem odm/odm_dem/dsm.tif --write-mask --driver gtiff --creation-option tiled=yes --creation-option compress=jpeg --creation-option photometric=ycbcr --creation-option jpeg_quality=90 odm/images/100_0005_0140.tif
.. note::

Each driver has its own creation options. See the GDAL `GeoTIFF <https://gdal.org/en/latest/drivers/raster/gtiff.html#creation-options>`__ and `COG <https://gdal.org/en/latest/drivers/raster/cog.html#creation-options>`__ docs for details.
2 changes: 1 addition & 1 deletion docs/getting_started/cli/pan_sharpening.rst
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
.. include:: ../../shared.txt
.. include:: shared.txt

Pan sharpening
Pan-sharpening
==============

.. toctree::
Expand Down
34 changes: 29 additions & 5 deletions docs/getting_started/cli/sharp_config.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,48 @@
Output image configuration
==========================

Pan sharpened images are created as GeoTIFFs. Image format (data type, compression, nodata / internal mask and overviews) can be configured. Configuration options are shared with the |oty|_ orthorectification sub-commands, and default to sensible values when not supplied.
The pan-sharpened image format can be configured. Configuration options are shared with the |oty|_ orthorectification sub-commands, and default to sensible values when not supplied.

The image data type defaults to the multispectral image data type, and can be changed with ``--dtype``. Compression can be configured with ``--compress`` as either ``deflate`` (with any image data type) or ``jpeg`` (with the ``uint8`` or ``uint16`` image data types). If ``--compress`` is not specified, compression defaults to ``jpeg`` when the image data type is ``uint8``, otherwise it defaults to ``deflate``. When ``jpeg`` compression is used with the ``uint16`` data type, the image is 12 bit ``jpeg`` compressed.
Data type and compression
-------------------------

The image data type defaults to the multispectral image data type, and can be changed with ``--dtype``. Compression can be configured with ``--compress`` as either ``deflate`` or ``lzw`` (with any image data type), or ``jpeg`` (with the ``uint8`` or ``uint16`` image data types). If ``--compress`` is not specified, compression defaults to ``jpeg`` when the image data type is ``uint8``, and to ``deflate`` otherwise. When ``jpeg`` compression is used with the ``uint16`` data type, the image is 12 bit ``jpeg`` compressed.

.. note::

Support for 12 bit JPEG compression is Rasterio_ build / package dependent.

The next example creates a pan sharpened image with the ``int16`` data type, and ``deflate`` compression:
The next example creates a pan-sharpened image with the ``int16`` data type, and ``deflate`` compression:

.. code-block:: bash
oty sharpen --pan pan_sharp/pan.tif --multispectral pan_sharp/ms.tif --out-file pan_sharp.tif --dtype int16 --compress deflate
Valid image pixels are masked with either an internal mask band or a nodata value. By default, an internal mask is used when the image image is ``jpeg`` compressed. This avoids ``jpeg`` artefacts in invalid areas. When the image is ``deflate`` compressed, the default is use to a nodata value based on the data type. Masking behaviour can be changed with ``--write-mask`` to write an internal mask, or ``--no-write-mask`` to use a nodata value.
Masking and overviews
---------------------

Valid image pixels are masked with either an internal mask band or a nodata value. By default, an internal mask is used when the image is ``jpeg`` compressed. This avoids ``jpeg`` artefacts in invalid areas. When the image is ``deflate`` or ``lzw`` compressed, the default is use to a nodata value based on the data type. Masking behaviour can be changed with ``--write-mask`` to write an internal mask, or ``--no-write-mask`` to use a nodata value.

Internal overviews are added by default. This can be changed with ``--no-build-ovw``. In this example, we create an pan sharpened image with ``deflate`` compression, internal masking rather than nodata, and no internal overviews:
Internal overviews are added by default. This can be changed with ``--no-build-ovw``. In this example, we create an pan-sharpened image with ``deflate`` compression, internal masking rather than nodata, and no internal overviews:

.. code-block:: bash
oty sharpen --pan pan_sharp/pan.tif --multispectral pan_sharp/ms.tif --out-file pan_sharp.tif --compress deflate --write-mask --no-build-ovw
Custom creation options and driver
----------------------------------

For pan-sharpened image configurations not possible with the above options, custom creation options can be specified with ``--creation-option``. The ``--compress`` option is ignored, and no other creation options are set by Orthority when this is supplied.

The image can be formatted as a ``gtiff`` (GeoTIFF - the default) or ``cog`` (Cloud Optimised GeoTIFF) with ``--driver``.

This example formats the pan-sharpened image as a COG with internal masks, and specifies custom creation options for JPEG compression with a quality of 90:

.. code-block:: bash
oty sharpen --pan pan_sharp/pan.tif --multispectral pan_sharp/ms.tif --out-file pan_sharp.tif --write-mask --driver cog --creation-option compress=jpeg --creation-option quality=90
.. note::

Each driver has its own creation options. See the GDAL `GeoTIFF <https://gdal.org/en/latest/drivers/raster/gtiff.html#creation-options>`__ and `COG <https://gdal.org/en/latest/drivers/raster/cog.html#creation-options>`__ docs for details.

2 changes: 1 addition & 1 deletion docs/getting_started/cli/sharpen.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
``oty sharpen``
===============

|oty sharpen|_ implements the Gram-Schmidt pan sharpening method. This example pan sharpens a multispectral (RGB) drone image with its matching panchromatic image:
|oty sharpen|_ implements the Gram-Schmidt pan-sharpening method. This example pan-sharpens a multispectral (RGB) drone image with its matching panchromatic image:

.. code-block:: bash
Expand Down
1 change: 0 additions & 1 deletion docs/scripts/api_frame.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
# TODO: change URL to main branch
# code for getting started->api->camera models->frame cameras
# [create camera]
import orthority as oty
Expand Down
2 changes: 1 addition & 1 deletion docs/scripts/api_pan_sharp.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@
pan_file = url_root + 'pan_sharp/pan.tif' # panchromatic drone image
ms_file = url_root + 'pan_sharp/ms.tif' # multispectral (RGB) drone image

# create PanSharpen object and pan sharpen
# create PanSharpen object and pan-sharpen
pan_sharp = oty.PanSharpen(pan_file, ms_file)
pan_sharp.process('pan_sharp.tif')
2 changes: 1 addition & 1 deletion orthority/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
import logging
import pathlib

from orthority.enums import Compress, Interp, RpcRefine
from orthority.enums import Compress, Interp, RpcRefine, Driver
from orthority.factory import FrameCameras, RpcCameras
from orthority.ortho import Ortho
from orthority.pan_sharp import PanSharpen
Expand Down
12 changes: 6 additions & 6 deletions orthority/camera.py
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,7 @@ def rect_boundary(im_size: np.ndarray, num_pts: int) -> np.ndarray:
perim = 2 * br.sum()
cnr_ji = np.array([[0, 0], [br[0], 0], br, [0, br[1]], [0, 0]])
dist = np.sum(np.abs(np.diff(cnr_ji, axis=0)), axis=1)
return np.row_stack(
return np.vstack(
[
np.linspace(
cnr_ji[i],
Expand Down Expand Up @@ -754,7 +754,7 @@ def _get_rectangles(
n = 9
scale_j, scale_i = np.meshgrid(range(0, n), range(0, n))
scale_j, scale_i = scale_j.ravel(), scale_i.ravel()
ji = np.row_stack([scale_j * w / (n - 1), scale_i * h / (n - 1)])
ji = np.vstack([scale_j * w / (n - 1), scale_i * h / (n - 1)])
xy = self._pixel_to_camera(ji)[:2]
outer = xy.min(axis=1), xy.max(axis=1) - xy.min(axis=1)
inner_ul = np.array((xy[0][scale_j == 0].max(), xy[1][scale_i == 0].max()))
Expand Down Expand Up @@ -791,7 +791,7 @@ def _camera_to_pixel(self, xyz_: np.ndarray) -> np.ndarray:

def _pixel_to_camera(self, ji: np.ndarray) -> np.ndarray:
"""Transform 2D pixel to homogenous 3D camera coordinates."""
ji_ = np.row_stack([ji.astype('float64', copy=False), np.ones((1, ji.shape[1]))])
ji_ = np.vstack([ji.astype('float64', copy=False), np.ones((1, ji.shape[1]))])
xyz_ = self._K_undistort_inv.dot(ji_)
return xyz_

Expand Down Expand Up @@ -1237,7 +1237,7 @@ def _get_undistort_maps(self) -> tuple[np.ndarray, np.ndarray]:
# equivalent to the above, but using Camera methods (works out slower):
# j = np.arange(0, self.im_size[0], dtype='int32')
# i = np.zeros(self.im_size[0], dtype='int32')
# ji = np.row_stack((j, i))
# ji = np.vstack((j, i))
# undistort_maps = (
# np.zeros(self.im_size[::-1], dtype='float32'),
# np.zeros(self.im_size[::-1], dtype='float32'),
Expand All @@ -1258,7 +1258,7 @@ def _camera_to_pixel(self, xyz_: np.ndarray) -> np.ndarray:
def _pixel_to_camera(self, ji: np.ndarray) -> np.ndarray:
ji_cv = ji.T.astype('float64', copy=False)
xyz_ = cv2.undistortPoints(ji_cv, self._K, self._dist_param)
xyz_ = np.row_stack([xyz_[:, 0, :].T, np.ones((1, ji.shape[1]))])
xyz_ = np.vstack([xyz_[:, 0, :].T, np.ones((1, ji.shape[1]))])
return xyz_


Expand Down Expand Up @@ -1480,7 +1480,7 @@ def _camera_to_pixel(self, xyz_: np.ndarray) -> np.ndarray:
def _pixel_to_camera(self, ji: np.ndarray) -> np.ndarray:
ji_cv = ji.T[None, :].astype('float64', copy=False)
xyz_ = cv2.fisheye.undistortPoints(ji_cv, self._K, self._dist_param, None, None)
xyz_ = np.row_stack([xyz_[0].T, np.ones((1, ji.shape[1]))])
xyz_ = np.vstack([xyz_[0].T, np.ones((1, ji.shape[1]))])
return xyz_


Expand Down
Loading

0 comments on commit ec04c47

Please sign in to comment.