001/******************************************************************************* 002 * This software is provided as a supplement to the authors' textbooks on digital 003 * image processing published by Springer-Verlag in various languages and editions. 004 * Permission to use and distribute this software is granted under the BSD 2-Clause 005 * "Simplified" License (see http://opensource.org/licenses/BSD-2-Clause). 006 * Copyright (c) 2006-2023 Wilhelm Burger, Mark J. Burge. All rights reserved. 007 * Visit https://imagingbook.com for additional details. 008 ******************************************************************************/ 009package imagingbook.common.geometry.mappings.nonlinear; 010 011 012import imagingbook.common.geometry.basic.Pnt2d; 013import imagingbook.common.geometry.mappings.Inversion; 014import imagingbook.common.geometry.mappings.Mapping2D; 015 016import static imagingbook.common.math.Arithmetic.mod; 017import static java.lang.Math.atan2; 018import static java.lang.Math.cos; 019import static java.lang.Math.exp; 020import static java.lang.Math.hypot; 021import static java.lang.Math.log1p; 022import static java.lang.Math.sin; 023 024/** 025 * <p> 026 * This class implements a 2D log-polar mapping transformation. Simple version (Version 1), maps radius [0,rmax] to 027 * [0,nr]). See Sec. 21.1.6 (Eq. 21.65 - 21.70) of [1] for additional details and examples. 028 * </p> 029 * <p> 030 * [1] W. Burger, M.J. Burge, <em>Digital Image Processing – An Algorithmic Introduction</em>, 3rd ed, Springer 031 * (2022). 032 * </p> 033 * 034 * @author WB 035 * @version 2022/11/16 036 */ 037public class LogPolarMapping1 implements Mapping2D, Inversion { 038 039 private static final double PI2 = 2 * Math.PI; 040 041 private final double xc, yc; // center point in source image 042 private final int P; // # of radial steps 043 private final double c1, c2, c3, c4; // pre-calculated constants 044 045 /** 046 * Constructor. 047 * @param xc x-coordinate of center point in source image 048 * @param yc y-coordinate of center point in source image 049 * @param P number of radial steps 050 * @param Q number of angular steps 051 * @param rmax maximum radius 052 */ 053 public LogPolarMapping1(double xc, double yc, int P, int Q, double rmax) { 054 this.P = P; 055 this.xc = xc; 056 this.yc = yc; 057 this.c1 = this.P / log1p(rmax); 058 this.c2 = Q / PI2; 059 this.c3 = PI2 / Q; 060 this.c4 = log1p(rmax) / P; 061 } 062 063 // -------------------------------------------------------------------- 064 065 @Override 066 public Pnt2d applyTo(Pnt2d xy) { // image -> log-polar 067 double dx = xy.getX() - xc; 068 double dy = xy.getY() - yc; 069 double r = hypot(dx, dy); 070 double rho = c1 * log1p(r); // = Math.log(r + 1) 071 double theta = mod(atan2(dy, dx), PI2); // theta in [0, 2 PI) 072 double omega = c2 * theta; 073 return Pnt2d.from(rho, omega); 074 } 075 076 @Override 077 public Mapping2D getInverse() { // log-polar -> image 078 return new Mapping2D() { 079 @Override 080 public Pnt2d applyTo(Pnt2d ra) { 081 double rho = ra.getX(); 082 double omega = ra.getY(); 083 double r = exp(rho * c4) - 1; 084 double theta = c3 * omega; 085 double x = r * cos(theta); 086 double y = r * sin(theta); 087 return Pnt2d.from(xc + x, yc + y); 088 } 089 }; 090 } 091 092}