Mercurial > repos > imgteam > image_registration_affine
changeset 1:fa769715b6b0 draft
"planemo upload for repository https://github.com/BMCV/galaxy-image-analysis/tools/image_registration_affine/ commit e82400162e337b36c29d6e79fb2deb9871475397"
author | imgteam |
---|---|
date | Thu, 20 Jan 2022 00:45:30 +0000 |
parents | e34222a620d4 |
children | 77dc68af2b40 |
files | image_registration_affine.py image_registration_affine.xml |
diffstat | 2 files changed, 98 insertions(+), 43 deletions(-) [+] |
line wrap: on
line diff
--- a/image_registration_affine.py Wed Dec 30 20:24:35 2020 +0000 +++ b/image_registration_affine.py Thu Jan 20 00:45:30 2022 +0000 @@ -1,12 +1,21 @@ +""" +Copyright 2021-2022 Biomedical Computer Vision Group, Heidelberg University. + +Distributed under the MIT license. +See file LICENSE for detail or copy at https://opensource.org/licenses/MIT + +""" +import argparse + +import numpy as np +import pandas as pd import skimage.io -from skimage.transform import ProjectiveTransform -from skimage.filters import gaussian from scipy.ndimage import map_coordinates from scipy.optimize import least_squares -import numpy as np -import pandas as pd -import argparse - +from scipy.signal import convolve2d +from skimage.color import rgb2gray, rgba2rgb +from skimage.filters import gaussian +from skimage.transform import ProjectiveTransform def _stackcopy(a, b): @@ -16,7 +25,6 @@ a[:] = b - def warp_coords_batch(coord_map, shape, dtype=np.float64, batch_size=1000000): rows, cols = shape[0], shape[1] coords_shape = [len(shape), rows, cols] @@ -26,8 +34,8 @@ tf_coords = np.indices((cols, rows), dtype=dtype).reshape(2, -1).T - for i in range(0, (tf_coords.shape[0]//batch_size+1)): - tf_coords[batch_size*i:batch_size*(i+1)] = coord_map(tf_coords[batch_size*i:batch_size*(i+1)]) + for i in range(0, (tf_coords.shape[0] // batch_size + 1)): + tf_coords[batch_size * i:batch_size * (i + 1)] = coord_map(tf_coords[batch_size * i:batch_size * (i + 1)]) tf_coords = tf_coords.T.reshape((-1, cols, rows)).swapaxes(1, 2) _stackcopy(coords[1, ...], tf_coords[0, ...]) @@ -38,45 +46,80 @@ return coords - -def affine_registration(params,moving,fixed): +def affine_registration(params, moving, fixed, metric='mae'): tmat = np.eye(3) - tmat[0,:] = params.take([0,1,2]) - tmat[1,:] = params.take([3,4,5]) - + tmat[0, :] = params.take([0, 1, 2]) + tmat[1, :] = params.take([3, 4, 5]) + trans = ProjectiveTransform(matrix=tmat) warped_coords = warp_coords_batch(trans, fixed.shape) - t = map_coordinates(moving, warped_coords, mode='reflect') - - eI = (t - fixed)**2 - return eI.flatten() + t = map_coordinates(moving, warped_coords, mode='nearest') + f = fixed + + if metric == 'mse': + err = (t - f) ** 2 + + elif metric == 'mae': + err = (t - f) + + elif metric == 'lcc': + sum_filt = np.ones((9, 9)) + win_size = 81 + + t_sum = convolve2d(t, sum_filt, mode='same', boundary='symm') + f_sum = convolve2d(f, sum_filt, mode='same', boundary='symm') + t2_sum = convolve2d(t * t, sum_filt, mode='same', boundary='symm') + f2_sum = convolve2d(f * f, sum_filt, mode='same', boundary='symm') + tf_sum = convolve2d(t * f, sum_filt, mode='same', boundary='symm') + + cross = tf_sum - f_sum * t_sum / win_size + t_var = t2_sum - t_sum * t_sum / win_size + f_var = f2_sum - f_sum * f_sum / win_size + cc = cross * cross / (t_var * f_var + 1e-5) + err = 1 - cc + + return err.flatten() +def read_img_as_gray(fn): + im = skimage.io.imread(fn) + nDims = len(im.shape) + assert nDims in [2, 3], 'this tool does not support multichannel images' + if nDims == 3: + assert im.shape[-1] in [3, 4], 'this tool does not support multichannel images' + if im.shape[-1] == 4: + im = rgba2rgb(im) + im = rgb2gray(im) + im = im.astype(float) + im = im / np.max(im) + return im -def image_registration(fn_moving, fn_fixed, fn_out, smooth_sigma=1): - moving = skimage.io.imread(fn_moving,as_gray=True) - fixed = skimage.io.imread(fn_fixed,as_gray=True) + +def image_registration(fn_moving, fn_fixed, fn_out, smooth_sigma=3, metric='lcc'): + moving = read_img_as_gray(fn_moving) + fixed = read_img_as_gray(fn_fixed) moving = gaussian(moving, sigma=smooth_sigma) fixed = gaussian(fixed, sigma=smooth_sigma) - - x = np.array([1, 0, 0, 0, 1, 0],dtype='float64') - result = least_squares(affine_registration, x, args=(moving,fixed)) - + + x = np.array([1, 0, 0, 0, 1, 0], dtype='float64') + result = least_squares(affine_registration, x, args=(moving, fixed, metric)) + tmat = np.eye(3) - tmat[0,:] = result.x.take([0,1,2]) - tmat[1,:] = result.x.take([3,4,5]) - + tmat[0, :] = result.x.take([0, 1, 2]) + tmat[1, :] = result.x.take([3, 4, 5]) + pd.DataFrame(tmat).to_csv(fn_out, header=None, index=False, sep="\t") - if __name__ == "__main__": parser = argparse.ArgumentParser(description="Estimate the transformation matrix") - parser.add_argument("fn_moving", help="Name of the moving image.png") - parser.add_argument("fn_fixed", help="Name of the fixed image.png") - parser.add_argument("fn_tmat", help="Name of output file to save the transformation matrix") + parser.add_argument("fn_moving", help="Path to the moving image") + parser.add_argument("fn_fixed", help="Path to the fixed (reference) image") + parser.add_argument("fn_tmat", help="Path to the output file for saving the transformation matrix") + parser.add_argument("sigma", type=float, help="Sigma of Gaussian filter for smoothing input images") + parser.add_argument("metric", help="Image similarity metric") args = parser.parse_args() - - image_registration(args.fn_moving, args.fn_fixed, args.fn_tmat) + + image_registration(args.fn_moving, args.fn_fixed, args.fn_tmat, args.sigma, args.metric)
--- a/image_registration_affine.xml Wed Dec 30 20:24:35 2020 +0000 +++ b/image_registration_affine.xml Thu Jan 20 00:45:30 2022 +0000 @@ -1,23 +1,31 @@ -<tool id="ip_image_registration" name="Image Registration" version="0.0.1"> +<tool id="ip_image_registration" name="Image Registration" version="0.0.2" profile="20.05"> <description>based on intensity information</description> <requirements> - <requirement type="package" version="0.14.2">scikit-image</requirement> - <requirement type="package" version="1.1.0">scipy</requirement> - <requirement type="package" version="0.23.4">pandas</requirement> - <requirement type="package" version="1.15.2">numpy</requirement> - <requirement type="package" version="0.15.1">tifffile</requirement> + <requirement type="package" version="0.18.1">scikit-image</requirement> + <requirement type="package" version="1.6.2">scipy</requirement> + <requirement type="package" version="1.2.4">pandas</requirement> + <requirement type="package" version="1.20.2">numpy</requirement> + <requirement type="package" version="2020.10.1">tifffile</requirement> </requirements> - <command> + <command detect_errors="aggressive"> <![CDATA[ python '$__tool_directory__/image_registration_affine.py' '$fn_moving' '$fn_fixed' '$fn_tmat' + '$sigma' + '$metric' ]]> </command> <inputs> - <param name="fn_moving" type="data" format="png" label="Moving Image" /> - <param name="fn_fixed" type="data" format="png" label="Fixed Image" /> + <param name="fn_moving" type="data" format="tiff,png,jpg" label="Moving image" /> + <param name="fn_fixed" type="data" format="tiff,png,jpg" label="Fixed (reference) image" /> + <param name="sigma" type="float" value="3" label="Sigma of Gaussian filter for smoothing input images" /> + <param name="metric" type="select" label="Image similarity metric"> + <option value="lcc" selected="True">Local cross-correlation</option> + <option value="mse">Mean squared error</option> + <option value="mae">Mean absolute error</option> + </param> </inputs> <outputs> <data format="tabular" name="fn_tmat" /> @@ -26,6 +34,8 @@ <test> <param name="fn_moving" value="moving.png"/> <param name="fn_fixed" value="fixed.png"/> + <param name="sigma" value="3"/> + <param name="metric" value="lcc"/> <output name="fn_tmat" value="tmat.tsv" ftype="tabular" compare="diff" lines_diff="6"/> </test> </tests> @@ -34,4 +44,6 @@ This tool estimates the (affine) transformation matrix for registration of two images based on intensity information. </help> + <citations> + </citations> </tool>