Commit 60385ce8 authored by Daniel Scheffler's avatar Daniel Scheffler
Browse files

Merge branch 'master' into dev

Conflicts:
	tests/test_COREG.py
parents d6c8e5bb c7fda6b8
Pipeline #2914 passed with stages
in 9 minutes and 51 seconds
......@@ -12,18 +12,16 @@ variables:
stages:
- test
- deploy_pages
- deploy_to_pypi
- deploy
- cleanup
test_arosics:
stage: test
script:
- source /root/anaconda3/bin/activate
- export GDAL_DATA=/root/anaconda3/share/gdal
- source /root/miniconda3/bin/activate
- export GDAL_DATA=/root/miniconda3/share/gdal
- export PYTHONPATH=$PYTHONPATH:/root # /root <- directory needed later
- pip install rednose # remove as soon as it is included in test runner
- make nosetests
- make docs
artifacts:
......@@ -32,35 +30,69 @@ test_arosics:
- docs/_build/html/
- nosetests.html
- nosetests.xml
when: always
test_styles:
stage: test
script:
- source /root/miniconda3/bin/activate
- export GDAL_DATA=/root/miniconda3/share/gdal
- export PYTHONPATH=$PYTHONPATH:/root # /root <- directory needed later
- make lint
artifacts:
paths:
- tests/linting/flake8.log
- tests/linting/pycodestyle.log
- tests/linting/pydocstyle.log
when: always
test_arosics_install:
stage: test
script:
- source /root/anaconda3/bin/activate
- conda create -y -q --name arosics python=3.5
- source /root/miniconda3/bin/activate
- export GDAL_DATA=/root/miniconda3/share/gdal
- conda create -y -q --name arosics python=3
- source activate arosics
- conda install --yes -q -c conda-forge numpy gdal scikit-image matplotlib # resolve these requirements with conda
# resolve some requirements with conda
- conda install --yes -q -c conda-forge numpy gdal scikit-image matplotlib pyproj rasterio shapely geopandas
# run installer
- python setup.py install
# test if its importable
- cd ..
- pwd
- ls
- python -c "import arosics; print(arosics)"
- python -c "from arosics import COREG, COREG_LOCAL"
only:
- master
pages:
stage: deploy_pages
pages: # this job must be called 'pages' to advise GitLab to upload content to GitLab Pages
stage: deploy
dependencies:
- test_arosics
script:
# Create the public directory
- rm -rf public
- mkdir public
- mkdir -p public/doc
- mkdir -p public/images/
- mkdir -p public/coverage
- cp -r htmlcov/* public/coverage/
- mkdir -p public/nosetests_reports
- cp nosetests.* public/nosetests_reports/
- mkdir -p public/doc
# Copy over the docs
- cp -r docs/_build/html/* public/doc/
- cp -r docs/images/* public/images/
# Copy over the coverage reports
- cp -r htmlcov/* public/coverage/
# Copy over the nosetests reports
- cp nosetests.* public/nosetests_reports/
# Check if everything is working great
- ls -al public
- ls -al public/doc
- ls -al public/coverage
- ls -al public/nosetests_reports
artifacts:
paths:
- public
......@@ -70,11 +102,11 @@ pages:
deploy_pypi:
stage: deploy_to_pypi
stage: deploy
dependencies:
- test_arosics
script: # Configure the PyPI credentials, then push the package, and cleanup the creds.
- source /root/anaconda3/bin/activate
- source /root/miniconda3/bin/activate
- mkdir -p public/images/
- cp -r docs/images/* public/images/
- printf "[distutils]\nindex-servers =\n pypi\n\n" >> ~/.pypirc
......
......@@ -28,11 +28,49 @@ Fixes and improvements:
* fixed warping issues in case only very few tie points could be identified
0.5.0 (coming soon)
-------------------
0.5.0 (2017-09-19)
------------------
New features:
* Added two test cases for local co-registration and the respective test data.
* Added test cases for global co-registration
* Added test of output writer and tie point grid visualization.
* Added nosetests. Resolved some setup requirements by conda during test_arosics_install.
* PEP8 code style now checked with automatic style checkers
Fixes and improvements:
* Coverage now also working in multiprocessing.
* Replaced test data of test case INTER1 with LZW compressed GeoTIFFs to speed up testing.
* Revised docker container builder.
* Bugfix for unexpected FFTW return value that caused the matching to fail
* Added some docstrings.
* Refactored command line interface 'arosics.py' to 'arosics_cli.py' to fix import issues.
* Added usage documentation for command line interface.
* Removed pykrige from automatically installed libraries during setup. It is now optional (Fixes issue #12)
* Bugfix in connection with optional library pyfftw.
* Revised installation guidelines within README.rst, README.md and installation.rst. Added link for nosetests HTML report.
* Fixed exception in case no arguments are provided to command line interface.
* Revised error handling and added additional check for projection.
* GDAL_DATA environment variable is now handled within py_tools_ds. Updated minimal version of py_tools_ds in setup.py.
* Fixed pickling error when running COREG_LOCAL in multiprocessing under a Windows environment.
* Replaced all occurrences of "quality grid" with "tie point grid". Updated version info.
......@@ -52,7 +52,9 @@ clean-test: ## remove test and coverage artifacts
rm -fr nosetests.xml
lint: ## check style with flake8
flake8 arosics tests
flake8 --max-line-length=120 arosics tests > ./tests/linting/flake8.log
pycodestyle arosics --exclude="*.ipynb,*.ipynb*" --max-line-length=120 > ./tests/linting/pycodestyle.log
-pydocstyle arosics > ./tests/linting/pydocstyle.log
test: ## run tests quickly with the default Python
python setup.py test
......
[![logo](docs/images/arosics_logo.png)](https://gitext.gfz-potsdam.de/danschef/arosics/)
### An Automated and Robust Open-Source Image Co-Registration Software for Multi-Sensor Satellite Data
* Free software: GNU General Public License v3
* Documentation: http://danschef.gitext.gfz-potsdam.de/arosics/doc/
* The (open-access) paper corresponding to this software repository can be found here:
* The (open-access) paper corresponding to this software repository can be found here:
[Scheffler D, Hollstein A, Diedrich H, Segl K, Hostert P. AROSICS: An Automated and Robust Open-Source Image Co-Registration Software for Multi-Sensor Satellite Data. Remote Sensing. 2017; 9(7):676.](http://www.mdpi.com/2072-4292/9/7/676)
......@@ -16,6 +16,9 @@
[![build status](https://gitext.gfz-potsdam.de/danschef/arosics/badges/master/build.svg)](https://gitext.gfz-potsdam.de/danschef/arosics/commits/master)
[![coverage report](https://gitext.gfz-potsdam.de/danschef/arosics/badges/master/coverage.svg)](http://danschef.gitext.gfz-potsdam.de/arosics/coverage/)
[![pypi_status](https://img.shields.io/pypi/v/arosics.svg)](https://pypi.python.org/pypi/arosics)
[![license](https://img.shields.io/pypi/l/arosics.svg)](https://gitext.gfz-potsdam.de/danschef/arosics/blob/master/LICENSE)
[![python versions](https://img.shields.io/pypi/pyversions/arosics.svg)](https://img.shields.io/pypi/pyversions/arosics.svg)
See also the latest [coverage](http://danschef.gitext.gfz-potsdam.de/arosics/coverage/) and the [nosetests](http://danschef.gitext.gfz-potsdam.de/arosics/nosetests_reports/nosetests.html) HTML report.
......@@ -28,23 +31,23 @@ Perform automatic subpixel co-registration of two satellite image datasets based
AROSICS detects and corrects local as well as global misregistrations between two input images in the subpixel scale, that are often present in satellite imagery.
Prerequisites and hints:
The input images can have any [GDAL compatible image format](http://www.gdal.org/formats_list.html). Both of them must be approximately geocoded. In case of ENVI files, this means they must have a 'map info' and a 'coordinate system string' as attributes of their header file. The input images must have a geographic overlap but clipping them to same geographical extent is NOT neccessary. Please do not perform any spatial resampling of the input images before applying this algorithm. Any needed resampling of the data is done automatically. Thus, the input images may have different spatial resolutions. The current algorithm will not perform any ortho-rectification. So please use ortho-rectified input data in order to minimize local shifts in the input images.
AROSICS supports local and global co-registration.
* Local co-registration:
A dense grid of tie points is automatically computed, whereas tie points are subsequently validated using a multistage workflow. Only those tie points not marked as false-positives are used to compute the parameters of an affine transformation. Warping of the target image is done using an appropriate resampling technique (cubic by default).
The input images can have any [GDAL compatible image format](http://www.gdal.org/formats_list.html). Both of them must be approximately geocoded. In case of ENVI files, this means they must have a 'map info' and a 'coordinate system string' as attributes of their header file. The input images must have a geographic overlap but clipping them to same geographical extent is NOT neccessary. Please do not perform any spatial resampling of the input images before applying this algorithm. Any needed resampling of the data is done automatically. Thus, the input images may have different spatial resolutions. The current algorithm will not perform any ortho-rectification. So please use ortho-rectified input data in order to minimize local shifts in the input images.
AROSICS supports local and global co-registration.
* Global co-registration:
Only a global X/Y translation is computed within a small subset of the input images (window position is adjustable). This allows very fast co-registration but only corrects for translational (global) X/Y shifts. The calculated subpixel-shifts are (by default) applied to the geocoding information of the output image. No spatial resampling is done automatically as long as both input images have the same projection. If you need the output image to be aligned to the reference image coordinate grid (by using an appropriate resampling algorithm), use the '-align_grids' option.
* Local co-registration:
A dense grid of tie points is automatically computed, whereas tie points are subsequently validated using a multistage workflow. Only those tie points not marked as false-positives are used to compute the parameters of an affine transformation. Warping of the target image is done using an appropriate resampling technique (cubic by default).
* Global co-registration:
Only a global X/Y translation is computed within a small subset of the input images (window position is adjustable). This allows very fast co-registration but only corrects for translational (global) X/Y shifts. The calculated subpixel-shifts are (by default) applied to the geocoding information of the output image. No spatial resampling is done automatically as long as both input images have the same projection. If you need the output image to be aligned to the reference image coordinate grid (by using an appropriate resampling algorithm), use the '-align_grids' option.
AROSICS is designed to robustly handle the typical difficulties of multi-sensoral/multi-temporal images. Clouds are automatically handled by the implemented outlier detection algorithms. The user may provide user-defined masks to exclude certain image areas from tie point creation. The image overlap area is automatically calculated. Thereby, no-data regions within the images are automatically respected. Providing the map coordinates of the actual data corners lets you save some calculation time, because in this case the automatic algorithm can be skipped. The no-data value of each image is automatically derived from the image corners. The verbose program mode gives some more output about the interim results, shows some figures and writes the used footprint and overlap polygons to disk. Note, that maybe the figures must be manually closed in in order to continue the processing (depending on your Python configuration).
AROSICS is designed to robustly handle the typical difficulties of multi-sensoral/multi-temporal images. Clouds are automatically handled by the implemented outlier detection algorithms. The user may provide user-defined masks to exclude certain image areas from tie point creation. The image overlap area is automatically calculated. Thereby, no-data regions within the images are automatically respected. Providing the map coordinates of the actual data corners lets you save some calculation time, because in this case the automatic algorithm can be skipped. The no-data value of each image is automatically derived from the image corners. The verbose program mode gives some more output about the interim results, shows some figures and writes the used footprint and overlap polygons to disk. Note, that maybe the figures must be manually closed in in order to continue the processing (depending on your Python configuration).
For further details regarding the implemented algorithm, example use cases, quality assessment and benchmarks refer to the above mentioned paper ([Scheffler et al. 2017](http://www.mdpi.com/2072-4292/9/7/676)).
Installation
------------
......@@ -55,11 +58,15 @@ is run. This approach avoids problems with conflicting versions of the same soft
Using [conda](https://conda.io/docs/), the recommended approach is:
```bash
# create virtual environment for arosics, this is optional
# create virtual environment for arosics, this is optional
conda create -y -q --name arosics python=3
source activate arosics
conda install -y -q -c conda-forge numpy gdal scikit-image matplotlib pyproj rasterio
conda install -y -q -c conda-forge pyfftw basemap pykrige # these libraries are optional
conda install -y -q -c conda-forge numpy gdal scikit-image matplotlib pyproj rasterio shapely geopandas
# optional libraries:
conda install -y -q -c conda-forge basemap pykrige
conda install -y -q -c conda-forge pyfftw # Linux and MacOS
conda install -y -q -c jesserobertson pyfftw # Windows
```
To install AROSICS, use the pip installer:
......@@ -81,10 +88,7 @@ PATH=$PATH:/path/to/your/installation/folder/arosics:/path/to/your/installation/
AROSICS has been tested with Python 3.4+ and Python 2.7. It should be fully compatible to all Python versions above 2.7.
# Modules
......@@ -215,7 +219,7 @@ CR.correct_shifts()
('arr_shifted', array([[ 0, 0, 0, ..., 953, 972, 1044],
[ 0, 0, 0, ..., 1001, 973, 1019],
[ 0, 0, 0, ..., 953, 985, 1020],
...,
...,
[ 0, 0, 0, ..., 755, 763, 773],
[ 0, 0, 0, ..., 760, 763, 749],
[9999, 9999, 9999, ..., 9999, 9999, 9999]], dtype=uint16)),
......@@ -265,7 +269,7 @@ DESHIFTER(im_target2, CR.coreg_info).correct_shifts()
('arr_shifted', array([[ 0, 0, 0, ..., 953, 972, 1044],
[ 0, 0, 0, ..., 1001, 973, 1019],
[ 0, 0, 0, ..., 953, 985, 1020],
...,
...,
[ 0, 0, 0, ..., 755, 763, 773],
[ 0, 0, 0, ..., 760, 763, 749],
[9999, 9999, 9999, ..., 9999, 9999, 9999]], dtype=uint16)),
......@@ -274,7 +278,7 @@ DESHIFTER(im_target2, CR.coreg_info).correct_shifts()
### Shell console interface
......@@ -285,7 +289,7 @@ The help instructions of the console interface can be accessed like this:
python arosics_cli.py -h
```
Follow these instructions to run AROSICS from a shell console. For example, the most simple call for a global
Follow these instructions to run AROSICS from a shell console. For example, the most simple call for a global
co-registration would be like this:
......@@ -293,9 +297,9 @@ co-registration would be like this:
python arosics_cli.py global /path/to/your/ref_image.bsq /path/to/your/tgt_image.bsq
```
## CoReg_local
......@@ -330,7 +334,7 @@ CRL.correct_shifts()
Corner coordinates of image to be shifted:
[[319460.0, 5790510.0], [352270.0, 5900040.0], [409790.0, 5900040.0], [409790.0, 5790250.0], [319460.0, 5790250.0]]
Matching window position (X,Y): 372220.10753674706/5841066.947109019
Calculating geometric quality grid (1977 points) in mode 'multiprocessing'...
Calculating tie point grid (1977 points) in mode 'multiprocessing'...
progress: |==================================================| 100.0% [1977/1977] Complete 9.75 sek
Found 1144 valid GCPs.
Correcting geometric shifts...
......@@ -363,7 +367,7 @@ CRL.correct_shifts()
('arr_shifted', array([[ 0, 0, 0, ..., 1034, 996, 1001],
[ 0, 0, 0, ..., 1046, 1114, 1124],
[ 0, 0, 0, ..., 1021, 1126, 1148],
...,
...,
[ 0, 0, 0, ..., 760, 769, 805],
[ 0, 0, 0, ..., 762, 755, 765],
[ 0, 0, 0, ..., 0, 0, 0]], dtype=uint16)),
......@@ -382,9 +386,9 @@ CRL = COREG_LOCAL(GeoArray(ref_ndarray, ref_gt, ref_prj),GeoArray(tgt_ndarray, t
CRL.correct_shifts()
```
#### visualize geometric quality grid with INITIAL shifts present in your input target image
#### visualize tie point grid with INITIAL shifts present in your input target image
Use the function COREG_LOCAL.view_CoRegPoints() to visualize the geometric quality grid with the calculated absolute lenghts of the shift vectors (the unit corresponds to the input projection - UTM in the shown example, thus the unit is 'meters'.).
Use the function COREG_LOCAL.view_CoRegPoints() to visualize the tie point grid with the calculated absolute lenghts of the shift vectors (the unit corresponds to the input projection - UTM in the shown example, thus the unit is 'meters'.).
NOTE: a calculation of reliable shifts above cloud covered areas is not possible. In the current version of AROSICS these areas are not masked. A proper masking is planned.
......@@ -404,7 +408,7 @@ CRL.view_CoRegPoints(figsize=(15,15),backgroundIm='ref')
The output figure shows the calculated absolute lenghts of the shift vectors - in this case with shifts up to ~25 meters.
#### visualize geometric quality grid with shifts present AFTER shift correction
#### visualize tie point grid with shifts present AFTER shift correction
The remaining shifts after local correction can be calculated and visualized by instanciating COREG_LOCAL with the output path of the above instance of COREG_LOCAL.
......@@ -422,7 +426,7 @@ CRL_after_corr.view_CoRegPoints(figsize=(15,15),backgroundIm='ref')
[[319460.0, 5790540.0], [352270.0, 5900030.0], [409780.0, 5900030.0], [409780.0, 5790260.0], [322970.0, 5790250.0], [319460.0, 5790280.0]]
Matching window position (X,Y): 372216.38593955856/5841068.390957352
Note: array has been downsampled to 1000 x 1000 for faster visualization.
Calculating geometric quality grid (1977 points) in mode 'multiprocessing'...
Calculating tie point grid (1977 points) in mode 'multiprocessing'...
progress: |==================================================| 100.0% [1977/1977] Complete 10.78 sek
......@@ -432,7 +436,7 @@ CRL_after_corr.view_CoRegPoints(figsize=(15,15),backgroundIm='ref')
The output figure shows a significant reduction of geometric shifts.
#### show the points table of the calculated geometric quality grid
#### show the points table of the calculated tie point grid
NOTE: Point records where no valid match has been found are filled with -9999.
......@@ -1510,18 +1514,18 @@ CRL.CoRegPoints_table
#### export geometric quality grid to an ESRI point shapefile
#### export tie point grid to an ESRI point shapefile
```python
CRL.quality_grid.to_PointShapefile(path_out='/path/to/your/output_shapefile.shp')
CRL.tiepoint_grid.to_PointShapefile(path_out='/path/to/your/output_shapefile.shp')
```
### Shell console interface
Follow these instructions to run AROSICS from a shell console. For example, the most simple call for a local
Follow these instructions to run AROSICS from a shell console. For example, the most simple call for a local
co-registration would be like this:
......@@ -1532,7 +1536,7 @@ python arosics_cli.py local /path/to/your/ref_image.bsq /path/to/your/tgt_image.
# Credits
This package was created with [Cookiecutter](https://github.com/audreyr/cookiecutter) and the
[audreyr/cookiecutter-pypackage](https://github.com/audreyr/cookiecutter-pypackage) project template.
This package was created with [Cookiecutter](https://github.com/audreyr/cookiecutter) and the
[audreyr/cookiecutter-pypackage](https://github.com/audreyr/cookiecutter-pypackage) project template.
The test data represent modified Copernicus Sentinel data (2016).
......@@ -29,6 +29,10 @@ Status
:target: http://danschef.gitext.gfz-potsdam.de/arosics/coverage/
.. image:: https://img.shields.io/pypi/v/arosics.svg
:target: https://pypi.python.org/pypi/arosics
.. image:: https://img.shields.io/pypi/l/arosics.svg
:target: https://gitext.gfz-potsdam.de/danschef/arosics/blob/master/LICENSE
.. image:: https://img.shields.io/pypi/pyversions/arosics.svg
:target: https://img.shields.io/pypi/pyversions/arosics.svg
See also the latest coverage_ report and the nosetests_ HTML report.
......@@ -52,8 +56,12 @@ Using conda_, the recommended approach is:
# create virtual environment for arosics, this is optional
conda create -y -q --name arosics python=3
source activate arosics
conda install -y -q -c conda-forge numpy gdal scikit-image matplotlib pyproj rasterio
conda install -y -q -c conda-forge pyfftw basemap pykrige # these libraries are optional
conda install -y -q -c conda-forge numpy gdal scikit-image matplotlib pyproj rasterio shapely geopandas
# optional libraries:
conda install -y -q -c conda-forge basemap pykrige
conda install -y -q -c conda-forge pyfftw # Linux and MacOS
conda install -y -q -c jesserobertson pyfftw # Windows
To install AROSICS, use the pip installer:
......@@ -73,7 +81,6 @@ Or clone the repository via GIT and update the PATH environment variable:
git clone https://gitext.gfz-potsdam.de/danschef/py_tools_ds.git
PATH=$PATH:/path/to/your/installation/folder/arosics:/path/to/your/installation/folder/geoarray:/path/to/your/installation/folder/py_tools_ds
Credits
-------
......
# -*- coding: utf-8 -*-
__author__='Daniel Scheffler'
import os
import re
import shutil
import subprocess
import time
import warnings
from copy import copy
from typing import Iterable
# custom
try:
......@@ -15,6 +12,7 @@ try:
except ImportError:
from osgeo import gdal
import numpy as np
try:
import pyfftw
except ImportError:
......@@ -24,24 +22,23 @@ from skimage.exposure import rescale_intensity
# internal modules
from .DeShifter import DESHIFTER, _dict_rspAlg_rsp_Int
from . import geometry as GEO
from . import io as IO
from . import plotting as PLT
from . import geometry as GEO
from . import plotting as PLT
from geoarray import GeoArray
from py_tools_ds.convenience.object_oriented import alias_property
from py_tools_ds.geo.coord_calc import corner_coord_to_minmax, get_corner_coordinates
from py_tools_ds.geo.coord_calc import get_corner_coordinates
from py_tools_ds.geo.vector.topology import get_overlap_polygon, get_smallest_boxImYX_that_contains_boxMapYX
from py_tools_ds.geo.projection import prj_equal, get_proj4info
from py_tools_ds.geo.vector.geometry import boxObj, round_shapelyPoly_coords
from py_tools_ds.geo.coord_grid import move_shapelyPoly_to_image_grid
from py_tools_ds.geo.coord_trafo import pixelToMapYX, reproject_shapelyGeometry, mapXY2imXY
from py_tools_ds.geo.coord_trafo import reproject_shapelyGeometry, mapXY2imXY, imXY2mapXY
from py_tools_ds.geo.raster.reproject import warp_ndarray
from py_tools_ds.geo.map_info import geotransform2mapinfo
from py_tools_ds.numeric.vector import find_nearest
from py_tools_ds.similarity.raster import calc_ssim
from py_tools_ds.io.vector.writer import write_shp
__author__ = 'Daniel Scheffler'
class GeoArray_CoReg(GeoArray):
......@@ -52,38 +49,39 @@ class GeoArray_CoReg(GeoArray):
# run GeoArray init
path_or_geoArr = CoReg_params['im_ref'] if imID == 'ref' else CoReg_params['im_tgt']
nodata = CoReg_params['nodata'][0 if imID == 'ref' else 1]
progress = CoReg_params['progress']
q = CoReg_params['q'] if not CoReg_params['v'] else False
nodata = CoReg_params['nodata'][0 if imID == 'ref' else 1]
progress = CoReg_params['progress']
q = CoReg_params['q'] if not CoReg_params['v'] else False
super(GeoArray_CoReg, self).__init__(path_or_geoArr, nodata=nodata, progress=progress, q=q)
self.imID = imID
self.imID = imID
self.imName = 'reference image' if imID == 'ref' else 'image to be shifted'
self.v = CoReg_params['v']
self.v = CoReg_params['v']
assert isinstance(self, GeoArray), \
'Something went wrong with the creation of GeoArray instance for the %s. The created ' \
'instance does not seem to belong to the GeoArray class. If you are working in Jupyter Notebook, reset ' \
'the kernel and try again.' %self.imName
'the kernel and try again.' % self.imName
# set title to be used in plots
self.title = os.path.basename(self.filePath) if self.filePath else self.imName
# validate params
assert self.prj, 'The %s has no projection.' % self.imName
assert not re.search('LOCAL_CS', self.prj), 'The %s is not georeferenced.' % self.imName
# assert self.prj, 'The %s has no projection.' % self.imName # TODO
# assert not re.search('LOCAL_CS', self.prj), 'The %s is not georeferenced.' % self.imName # TODO
assert self.gt, 'The %s has no map information.' % self.imName
# set band4match
self.band4match = (CoReg_params['r_b4match'] if imID == 'ref' else CoReg_params['s_b4match'])-1
assert self.bands >= self.band4match+1 >= 1, "The %s has %s %s. So its band number to match must be %s%s. " \
"Got %s." % (self.imName, self.bands, 'bands' if self.bands > 1 else 'band', 'between 1 and '
if self.bands > 1 else '', self.bands, self.band4match)
self.band4match = (CoReg_params['r_b4match'] if imID == 'ref' else CoReg_params['s_b4match']) - 1
assert self.bands >= self.band4match + 1 >= 1, \
"The %s has %s %s. So its band number to match must be %s%s. Got %s." \
% (self.imName, self.bands, 'bands' if self.bands > 1 else
'band', 'between 1 and ' if self.bands > 1 else '', self.bands, self.band4match)
# set footprint_poly
given_footprint_poly = CoReg_params['footprint_poly_%s' % ('ref' if imID == 'ref' else 'tgt')]
given_corner_coord = CoReg_params['data_corners_%s' % ('ref' if imID == 'ref' else 'tgt')]
given_corner_coord = CoReg_params['data_corners_%s' % ('ref' if imID == 'ref' else 'tgt')]
if given_footprint_poly:
self.footprint_poly = given_footprint_poly
......@@ -91,34 +89,36 @@ class GeoArray_CoReg(GeoArray):
self.footprint_poly = Polygon(given_corner_coord)
elif not CoReg_params['calc_corners']:
# use the image extent
self.footprint_poly = Polygon(get_corner_coordinates(gt=self.gt, cols=self.cols,rows=self.rows))
self.footprint_poly = Polygon(get_corner_coordinates(gt=self.gt, cols=self.cols, rows=self.rows))
else:
# footprint_poly is calculated automatically by GeoArray
if not CoReg_params['q']:
print('Calculating actual data corner coordinates for %s...' % self.imName)
self.calc_mask_nodata(fromBand=self.band4match) # this avoids that all bands have to be read
# validate footprint poly
if not self.footprint_poly.is_valid:
self.footprint_poly = self.footprint_poly.buffer(0)
if not self.q:
print('Bounding box of calculated footprint for %s:\n\t%s' % (self.imName, self.footprint_poly.bounds))
# add bad data mask
given_mask = CoReg_params['mask_baddata_%s' % ('ref' if imID == 'ref' else 'tgt')]
if given_mask:
self.mask_baddata = given_mask # runs GeoArray.mask_baddata.setter -> sets it to BadDataMask()
poly = alias_property('footprint_poly') # ensures that self.poly is updated if self.footprint_poly is updated
self.mask_baddata = given_mask # runs GeoArray.mask_baddata.setter -> sets it to BadDataMask()
poly = alias_property('footprint_poly') # ensures that self.poly is updated if self.footprint_poly is updated
class COREG(object):
"""See help(COREG) for documentation!"""
def __init__(self, im_ref, im_tgt, path_out=None, fmt_out='ENVI', out_crea_options=None, r_b4match=1, s_b4match=1,
wp=(None,None), ws=(256, 256), max_iter=5, max_shift=5, align_grids=False, match_gsd=False,
wp=(None, None), ws=(256, 256), max_iter=5, max_shift=5, align_grids=False, match_gsd=False,
out_gsd=None, target_xyGrid=None, resamp_alg_deshift='cubic', resamp_alg_calc='cubic',
footprint_poly_ref=None, footprint_poly_tgt=None, data_corners_ref=None, data_corners_tgt=None,
nodata=(None,None), calc_corners=True, binary_ws=True, mask_baddata_ref=None, mask_baddata_tgt=None,
nodata=(None, None), calc_corners=True, binary_ws=True, mask_baddata_ref=None, mask_baddata_tgt=None,
CPUs=None, force_quadratic_win=True, progress=True, v=False, path_verbose_out=None, q=False,
ignore_errors=False):
......@@ -144,25 +144,28 @@ class COREG(object):
:param ws(tuple): custom matching window size [pixels] (default: (256,256))
:param max_iter(int): maximum number of iterations for matching (default: 5)
:param max_shift(int): maximum shift distance in reference image pixel units (default: 5 px)
:param align_grids(bool): align the coordinate grids of the image to be and the reference image (default: 0)
:param align_grids(bool): align the coordinate grids of the image to be and the reference image
(default: 0)
:param match_gsd(bool): match the output pixel size to pixel size of the reference image (default: 0)
:param out_gsd(tuple): xgsd ygsd: set the output pixel size in map units
(default: original pixel size of the image to be shifted)
:param target_xyGrid(list): a list with a target x-grid and a target y-grid like [[15,45], [15,45]]
This overrides 'out_gsd', 'align_grids' and 'match_gsd'.
:param resamp_alg_deshift(str) the resampling algorithm to be used for shift correction (if neccessary)
valid algorithms: nearest, bilinear, cubic, cubic_spline, lanczos, average, mode,
max, min, med, q1, q3
valid algorithms: nearest, bilinear, cubic, cubic_spline, lanczos, average,
mode, max, min, med, q1, q3
default: cubic
:param resamp_alg_calc(str) the resampling algorithm to be used for all warping processes during calculation
of spatial shifts
(valid algorithms: nearest, bilinear, cubic, cubic_spline, lanczos, average, mode,
max, min, med, q1, q3)
(valid algorithms: nearest, bilinear, cubic, cubic_spline, lanczos, average,
mode, max, min, med, q1, q3)
default: cubic (highly recommended)
:param footprint_poly_ref(str): footprint polygon of the reference image (WKT string or shapely.geometry.Polygon),
:param footprint_poly_ref(str): footprint polygon of the reference image (WKT string or
shapely.geometry.Polygon),
e.g. 'POLYGON ((299999 6000000, 299999 5890200, 409799 5890200, 409799 6000000,
299999 6000000))'
:param footprint_poly_tgt(str): footprint polygon of the image to be shifted (WKT string or shapely.geometry.Polygon)
:param footprint_poly_tgt(str): footprint polygon of the image to be shifted (WKT string or
shapely.geometry.Polygon)
e.g. 'POLYGON ((299999 6000000, 299999 5890200, 409799 5890200, 409799 6000000,
299999 6000000))'
:param data_corners_ref(list): map coordinates of data corners within reference image.
......@@ -199,18 +202,22 @@ class COREG(object):
is None
"""
self.params = dict([x for x in locals().items() if x[0] != "self"])
self.params = dict([x for x in locals().items() if x[0] != "self"])
# assertions
assert gdal.GetDriverByName(fmt_out), "'%s' is not a supported GDAL driver." % fmt_out
if match_gsd and out_gsd: warnings.warn("'-out_gsd' is ignored because '-match_gsd' is set.\n")
if out_gsd: assert isinstance(out_gsd, list) and len(out_gsd) == 2, 'out_gsd must be a list with two values.'
if data_corners_ref and not isinstance(data_corners_ref[0], list): # group if not [[x,y],[x,y]..] but [x,y,x,y,]
if match_gsd and out_gsd:
warnings.warn("'-out_gsd' is ignored because '-match_gsd' is set.\n")
if out_gsd:
assert isinstance(out_gsd, list) and len(out_gsd) == 2, 'out_gsd must be a list with two values.'
if data_corners_ref and not isinstance(data_corners_ref[0],
list): # group if not [[x,y],[x,y]..] but [x,y,x,y,]
data_corners_ref = [data_corners_ref[i:i + 2] for i in range(0, len(data_corners_ref), 2)]
if data_corners_tgt and not isinstance(data_corners_tgt[0], list): # group if not [[x,y],[x,y]..]