Mercurial > repos > imgteam > landmark_registration
changeset 2:4e089a0983b1 draft
"planemo upload for repository https://github.com/BMCV/galaxy-image-analysis/tools/landmark_registration/ commit 927b78d47c31714776ccdf3d16f26c3779298abb"
author | imgteam |
---|---|
date | Sun, 20 Feb 2022 15:46:58 +0000 |
parents | b0503eec7bd6 |
children | 9ccd642e7ae2 |
files | landmark_registration.py landmark_registration.xml test-data/points_fixed.tsv test-data/points_moving.tsv test-data/tmat.tsv |
diffstat | 5 files changed, 117 insertions(+), 42 deletions(-) [+] |
line wrap: on
line diff
--- a/landmark_registration.py Fri Feb 22 19:04:47 2019 -0500 +++ b/landmark_registration.py Sun Feb 20 15:46:58 2022 +0000 @@ -1,27 +1,65 @@ -from skimage.measure import ransac -from skimage.transform import AffineTransform -import pandas as pd -import numpy as np +""" +Copyright 2017-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 -def landmark_registration(points_file1, points_file2, out_file, residual_threshold=2, max_trials=100, delimiter="\t"): - points1 = pd.read_csv(points_file1, delimiter=delimiter) - points2 = pd.read_csv(points_file2, delimiter=delimiter) +import numpy as np +import pandas as pd +from scipy.linalg import lstsq +from skimage.measure import ransac +from skimage.transform import AffineTransform + + +def landmark_registration(pts_f1, pts_f2, out_f, res_th=None, max_ite=None, delimiter="\t"): + + points1 = pd.read_csv(pts_f1, delimiter=delimiter) + points2 = pd.read_csv(pts_f2, delimiter=delimiter) + + src = np.concatenate([np.array(points1['x']).reshape([-1, 1]), + np.array(points1['y']).reshape([-1, 1])], + axis=-1) + dst = np.concatenate([np.array(points2['x']).reshape([-1, 1]), + np.array(points2['y']).reshape([-1, 1])], + axis=-1) - src = np.concatenate([np.array(points1['x']).reshape([-1,1]), np.array(points1['y']).reshape([-1,1])], axis=-1) - dst = np.concatenate([np.array(points2['x']).reshape([-1,1]), np.array(points2['y']).reshape([-1,1])], axis=-1) + if res_th is not None and max_ite is not None: + model_robust, inliers = ransac((src, dst), AffineTransform, min_samples=3, residual_threshold=res_th, max_trials=max_ite) + pd.DataFrame(model_robust.params).to_csv(out_f, header=None, index=False, sep="\t") - model = AffineTransform() - model_robust, inliers = ransac((src, dst), AffineTransform, min_samples=3, - residual_threshold=residual_threshold, max_trials=max_trials) - pd.DataFrame(model_robust.params).to_csv(out_file, header=None, index=False, sep="\t") + else: + A = np.zeros((src.size, 6)) + A[0:src.shape[0], [0, 1]] = src + A[0:src.shape[0], 2] = 1 + A[src.shape[0]:, [3, 4]] = src + A[src.shape[0]:, 5] = 1 + b = dst.T.flatten().astype('float64') + x = lstsq(A, b) + + tmat = np.eye(3) + tmat[0, :] = x[0].take([0, 1, 2]) + tmat[1, :] = x[0].take([3, 4, 5]) + pd.DataFrame(tmat).to_csv(out_f, header=None, index=False, sep="\t") + if __name__ == "__main__": - parser = argparse.ArgumentParser(description="Estimate transformation from points") - parser.add_argument("points_file1", help="Paste path to src points") - parser.add_argument("points_file2", help="Paste path to dst points") - parser.add_argument("warp_matrix", help="Paste path to warp_matrix.csv that should be used for transformation") - parser.add_argument("--residual_threshold", dest="residual_threshold", help="Maximum distance for a data point to be classified as an inlier.", type=float, default=2) - parser.add_argument("--max_trials", dest="max_trials", help="Maximum number of iterations for random sample selection.", type=int, default=100) + parser = argparse.ArgumentParser(description="Estimate affine transformation matrix based on landmark coordinates") + parser.add_argument("fn_pts1", help="Coordinates of SRC landmarks (tsv file)") + parser.add_argument("fn_pts2", help="Coordinates of DST landmarks (tsv file)") + parser.add_argument("fn_tmat", help="Path the output (transformation matrix)") + parser.add_argument("--res_th", dest="res_th", type=float, help="Maximum distance for a data point to be classified as an inlier") + parser.add_argument("--max_ite", dest="max_ite", type=int, help="Maximum number of iterations for random sample selection") args = parser.parse_args() - landmark_registration(args.points_file1, args.points_file2, args.warp_matrix, residual_threshold=args.residual_threshold, max_trials=args.max_trials) + + res_th = None + if args.res_th: + res_th = args.res_th + max_ite = None + if args.max_ite: + max_ite = args.max_ite + + landmark_registration(args.fn_pts1, args.fn_pts2, args.fn_tmat, res_th=res_th, max_ite=max_ite)
--- a/landmark_registration.xml Fri Feb 22 19:04:47 2019 -0500 +++ b/landmark_registration.xml Sun Feb 20 15:46:58 2022 +0000 @@ -1,40 +1,62 @@ -<tool id="ip_landmark_registration" name="Landmark Registration" version="0.0.2"> - <description>Landmark Registration</description> +<tool id="ip_landmark_registration" name="Landmark Registration" version="0.0.3" profile="20.05"> + <description>estimates the affine transformation matrix</description> <requirements> - <requirement type="package" version="0.14.2">scikit-image</requirement> - <requirement type="package" version="0.23.4">pandas</requirement> - <requirement type="package" version="1.15.4">numpy</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> </requirements> - <command><![CDATA[ - python '$__tool_directory__/landmark_registration.py' - --residual_threshold $residual_threshold - --max_trials $max_trials - '$points_file1' - '$points_file2' - '$warp_matrix' + <command detect_errors="aggressive"> + <![CDATA[ + python '$__tool_directory__/landmark_registration.py' + '$fn_pts1' + '$fn_pts2' + '$fn_tmat' + #if $algo_option.algo == 'ransac' + --res_th $algo_option.res_thr + --max_ite $algo_option.max_iter + #end if ]]></command> <inputs> - <param name="points_file1" type="data" format="tabular" label="Path to tab-separated file with src points" /> - <param name="points_file2" type="data" format="tabular" label="Path to tab-separated file with dst points" /> - <param name="residual_threshold" type="float" value="2" label="Maximum distance for a data point to be classified as an inlier." /> - <param name="max_trials" type="integer" value="100" label="Maximum number of iterations for random sample selection." /> + <param name="fn_pts1" type="data" format="tabular" label="Coordinates of SRC landmarks (tsv file)" /> + <param name="fn_pts2" type="data" format="tabular" label="Coordinates of DST landmarks (tsv file)" /> + <conditional name="algo_option"> + <param name="algo" type="select" label="Select the algorithm"> + <option value="ransac" selected="True">RANSAC</option> + <option value="ls">Least Squares</option> + </param> + <when value="ransac"> + <param name="res_thr" type="float" value="2.0" label="Maximum distance for a data point to be classified as an inlier." /> + <param name="max_iter" type="integer" value="100" label="Maximum number of iterations for random sample selection." /> + </when> + <when value="ls"></when> + </conditional> </inputs> <outputs> - <data format="tabular" name="warp_matrix" /> + <data format="tabular" name="fn_tmat" /> </outputs> <tests> <test> - <param name="points_file1" value="points1.tsv"/> - <param name="points_file2" value="points2.tsv"/> - <param name="residual_threshold" value="2"/> - <param name="max_trials" value="100"/> - <output name="warp_matrix" value="warp.tsv" ftype="tabular" compare="diff" lines_diff="6"/> + <param name="fn_pts1" value="points_moving.tsv"/> + <param name="fn_pts2" value="points_fixed.tsv"/> + <param name="algo_option.algo" value="ls"/> + <output name="fn_tmat" value="tmat.tsv" ftype="tabular" compare="diff" lines_diff="6"/> + </test> + <test> + <param name="fn_pts1" value="points_moving.tsv"/> + <param name="fn_pts2" value="points_fixed.tsv"/> + <param name="algo_option.algo" value="ransac"/> + <param name="algo_option.res_thr" value="2"/> + <param name="algo_option.max_iter" value="100"/> + <output name="fn_tmat" value="tmat.tsv" ftype="tabular" compare="diff" lines_diff="6"/> </test> </tests> <help> **What it does** This tool estimates the transformation matrix between two sets of 2d points. + + About the format of landmark coordinates in the input TSV table: Columns with header "x" and "y" are for x- and y-coordinate, respectively. </help> <citations> <citation type="doi">10.1016/j.jbiotec.2017.07.019</citation>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test-data/points_fixed.tsv Sun Feb 20 15:46:58 2022 +0000 @@ -0,0 +1,6 @@ + x y +0 33 107 +1 169 74 +2 178 207 +3 230 136 +4 114 131 \ No newline at end of file