Image registration models with extendable interfaces.
Project description
imgreg
An image registration library for python including a simple interface for building new models. Currently two image registration models for linear transformations based on scikit have been implemented as part of a toolchain in the context of particle image velocimetry (PIV). Tested for Python 3.7 to Python 3.9.
Installation
pip install imgreg
or clone the repository, and run:
python setup.py install
Examples
The following example give a short introduction into the available models. For further reading the directory doc/tutorial
provides a good starting point. The full documentation can be generated using sphinx
.
Recover the rotation and translation between two images
First import the model (here based on the logpolar and fourier transformation) and load the image files into the model:
import numpy as np
import imgreg.data as data
from imgreg.models.logpolar import LogPolarSolver
ref_img = np.array(data.ref_img())
mod_img = np.array(data.mod_img())
lps = LogPolarSolver(ref_img, mod_img)
The images can be displayed with:
lps.display([lps.REF_IMG, lps.MOD_IMG])
To access the recovered rotation angle and lower error bound in degrees use:
lps.RECOVERED_ROTATION.value
# array([-13.06730769, 0.11259774])
The recovered x,y translation and lower error bound given in number of pixels is accessed with:
lps.RECOVERED_TRANSLATION.value
# array([-17.98318062, 31.037803 , 0.42407651])
The recovered scaling factor is available with:
lps.RECOVERED_SCALE.value
# array([1. , 1.00187429])
A comparision between the recovered and the reference image can be displayed with:
lps.display([lps.RECOVERED_ROT_SCALE_TR_IMG, lps.REF_IMG])
Batch image processing
First import the required modules (here we use less general domain specific RadonSolver
model, if not suitable for your application, repace with the LogPolarSolver
as in the previous example):
import os
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
from PIL import Image
from imgreg.models.radon import RadonSolver
from imgreg.util.helpers import image_save_back_tf, rot_tr_gen, solver_gen
from imgreg.util.io import DirectoryView
Define the location to your reference image according to your usecase by replacing <path/to/reference/image.jpg>
, then replace the location of your source <src>
and destination <dest>
paths. Adjust the file_pattern
and step
variables to your needs, the latter can be used to skip images for faster computation.
image_path_ref = "<path/to/reference/image.jpg>"
image_path_src = "<src>"
image_path_dest = "<dest>"
file_pattern = "*.jpg"
step = 10
Create a directory view from which the solver generates its input:
d_view = DirectoryView(image_path_src, file_pattern=file_pattern)
fnames = [file for i, file in enumerate(sorted(d_view.files)) if not i % step]
Load the reference image:
ref_img = np.array(Image.open(image_path_ref))
Initialize and configure a suitable solver:
ras = RadonSolver(ref_img=ref_img)
ras.UPSAMPLING.value = 20
Generate an array containing the recovered translation and rotation parameters for the given images:
radg = solver_gen(d_view, ras, step)
rad_rot_tr_arr = np.array(list(rot_tr_gen(radg)))
Display the relative norm NormRel_L2
over the image series as an indicator for the goodness of the recovered values:
plt.plot(rad_rot_tr_arr[:, -1])
plt.xlabel("# image")
plt.ylabel("NormRel_L2")
plt.show()
Store recovered values to .csv
df_out = pd.DataFrame(
rad_rot_tr_arr,
index=fnames,
columns=[
"tr_x",
"tr_y",
"tr_err",
"rot",
"rot_err",
"NormRel_L2",
],
)
df_out.to_csv(f"radon-{step}.csv")
df_out
tr_x | tr_y | tr_err | rot | rot_err | NormRel_L2 | |
---|---|---|---|---|---|---|
test00001.jpg | -26.4509 | 47.3258 | 0.405569 | -20.5556 | 0.282843 | 0.41641 |
test00011.jpg | -26.3339 | 47.1561 | 0.405386 | -20.5556 | 0.282843 | 0.415555 |
test00021.jpg | -26.2344 | 47.0332 | 0.405536 | -20.5556 | 0.282843 | 0.415513 |
test00031.jpg | -22.8071 | 42.6237 | 0.385188 | -18.4444 | 0.282843 | 0.396469 |
test00041.jpg | -18.4961 | 36.5684 | 0.366198 | -16 | 0.282843 | 0.379106 |
test00051.jpg | -14.7056 | 30.9144 | 0.343007 | -13.5556 | 0.282843 | 0.35666 |
test00061.jpg | -11.768 | 25.8513 | 0.316403 | -11.2469 | 0.282843 | 0.329185 |
test00071.jpg | -8.66827 | 20.3634 | 0.288842 | -8.80247 | 0.282843 | 0.300223 |
test00081.jpg | -6.02938 | 15.0685 | 0.258316 | -6.44444 | 0.282843 | 0.267387 |
test00091.jpg | -3.50923 | 9.32793 | 0.220809 | -4 | 0.282843 | 0.227255 |
test00101.jpg | -1.19596 | 3.51883 | 0.172761 | -1.55556 | 0.282843 | 0.175223 |
test00111.jpg | 0.575633 | -1.85773 | 0.129057 | 0.753086 | 0.282843 | 0.126313 |
test00121.jpg | 2.41049 | -7.94683 | 0.167134 | 3.19753 | 0.282843 | 0.16156 |
test00131.jpg | 3.81275 | -13.2214 | 0.200897 | 5.44444 | 0.282843 | 0.198397 |
test00141.jpg | 5.16611 | -19.4011 | 0.234146 | 7.95062 | 0.282843 | 0.240847 |
test00151.jpg | 6.11063 | -24.7732 | 0.264576 | 10.1975 | 0.282843 | 0.289057 |
test00161.jpg | 6.97132 | -31.2601 | 0.29121 | 12.7531 | 0.282843 | 0.335311 |
test00171.jpg | 7.47346 | -36.6325 | 0.317422 | 15 | 0.282843 | 0.387218 |
test00181.jpg | 7.68796 | -41.7207 | 0.34348 | 17 | 0.282843 | 0.426283 |
test00191.jpg | 7.70654 | -41.831 | 0.345591 | 17 | 0.282843 | 0.42826 |
test00201.jpg | 7.69192 | -41.8788 | 0.349477 | 17 | 0.282843 | 0.4287 |
test00211.jpg | 7.65427 | -39.2652 | 0.338767 | 15.9506 | 0.282843 | 0.405673 |
test00221.jpg | 7.37055 | -33.822 | 0.325869 | 13.7531 | 0.282843 | 0.370918 |
test00231.jpg | 7.39534 | -33.931 | 0.327034 | 13.7531 | 0.282843 | 0.372402 |
test00241.jpg | 7.38345 | -33.9795 | 0.33014 | 13.7531 | 0.282843 | 0.375312 |
test00251.jpg | 7.11119 | -31.5481 | 0.321188 | 12.7531 | 0.282843 | 0.357117 |
Load the recovered values from .csv
df_in = pd.read_csv(f"radon-{step}.csv", index_col=0, sep=",")
rad_rot_tr_arr = df_in.to_numpy()
fnames = df_in.index
If desired an offset can be applied to a column of the data for plotting:
rad_rot_tr_arr[:, 3] -= 15
plt.plot(rad_rot_tr_arr[:, 3])
plt.xlabel("# image")
plt.ylabel("angle")
plt.show()
Save the reconstructed images
Finally the table of reconstructed parameters can be used to save the backtransformed images.
image_save_back_tf(rad_rot_tr_arr, fnames, image_path_src, image_path_dest)
A word on the models
The implemented models differ in some of the internal parameters. As the construction of a model also defines the dependency tree of its parameters, we can display a representation of the dependency tree as follows for every model (shown for the RadonSolver):
from imgreg.models.radon import RadonSolver
from imgreg.util.graph import dot_args_box_img_func
ras=RadonSolver()
ras.dot_graph(dot_args_box_img_func)
Tutorials
Further interactive examples are available as jupyter-notebooks in doc/tutorial
.
Documentation
The API documentation can be generated using Sphinx with numpydoc formatting. To build, run:
sphinx-build -b html doc/ doc/_build/html
Testing
To run all tests and to generate a coverage report run:
pytest --cov=imgreg/ tests/
coverage html
To test the examples in the documentation run:
pytest --doctest-modules imgreg/
License
This software is published under the GPLv3 license.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.