/* * Resize plugin for ImageJ, * Image processing part of the plugin. * * @author Arrate Munoz * Swiss Federal Institute of Technology Lausanne * Biomedical Imaging Group * BM-Ecublens * CH-1015 Lausanne EPFL, Switzerland * URL: http:/bigwww.epfl.ch/ * email: arrate.munoz@epfl.ch * * @version July 11, 2001 */ import imageaccess.*; public class Resize { private int interpDegree; private int analyDegree; private int syntheDegree; private double zoomY; private double zoomX; private boolean inversable; private int analyEven = 0; private int corrDegree; private double halfSupport; private double[] splineArrayHeight; private double[] splineArrayWidth; private int[] indexMinHeight; private int[] indexMaxHeight; private int[] indexMinWidth; private int[] indexMaxWidth; private final double tolerance = 1e-9; // tolerance for the Bspline transform /** * Calculate the resize version of the input image. * * @param input an ImageAccess object to be resized * @param output an ImageAccess object which is the resized version of the input * @param interpDegree degree of the interpolating spline * @param analyDegree degree of the analysis spline * @param synthedegree degree of the synthesis spline * @param zoomY scaling factor that applies to the columns * @param zoomX scaling factor that applies to the rows * @param shiftY shift value that applies to the columns * @param shiftX shift value that applies to the rows * @param inversable boolean that indicates if calculate inversable image or not * */ public void computeZoom( ImageAccess input,ImageAccess output, int analyDegree, int syntheDegree, int interpDegree, double zoomY, double zoomX, double shiftY, double shiftX, boolean inversable) { this.interpDegree = interpDegree; this.analyDegree = analyDegree; this.syntheDegree = syntheDegree; this.zoomY = zoomY; this.zoomX = zoomX; this.inversable = inversable; int nx = input.getWidth(); int ny = input.getHeight(); int workingSizeX; int workingSizeY; int finalSizeY; int finalSizeX; int[] size = new int[4]; int max; int addBorderHeight; int addBorderWidth; int totalDegree = (interpDegree+analyDegree+1); size = calculatefinalsize(inversable, ny, nx, zoomY, zoomX); workingSizeX = size[1]; workingSizeY = size[0]; finalSizeX = size[3]; finalSizeY = size[2]; if ( ((analyDegree+1) / 2) * 2 == analyDegree+1) analyEven = 1; double cociente = (double)(analyDegree+1) / 2.0; double go = (double)(analyDegree+1); corrDegree = analyDegree+syntheDegree+1; halfSupport = (totalDegree+1.0) / 2.0; addBorderHeight = border(finalSizeY, corrDegree); // 1d spline values for row if (addBorderHeight < totalDegree){ addBorderHeight += totalDegree; } int finalTotalHeight = finalSizeY + addBorderHeight; int lengthTotalHeight = workingSizeY + (int)Math.ceil(addBorderHeight/zoomY); indexMinHeight = new int[finalTotalHeight]; indexMaxHeight = new int[finalTotalHeight]; int lengthArraySplnHeight = finalTotalHeight * (2+totalDegree); int i = 0; double affineIndex; double factHeight = Math.pow(zoomY, analyDegree+1); shiftY += ((analyDegree+1.0) / 2.0 - Math.floor((analyDegree+1.0)/2.0))*(1.0/zoomY-1.0); splineArrayHeight = new double[lengthArraySplnHeight]; for (int l=0; l nx) { inverImage.getColumn(nx-1, workingColumn); for (int y=nx; y ny) { inverImage.getRow(ny-1, workingRow); for (int y=ny; y= maxSymBoundary) l2 = (int)Math.abs(Math.IEEEremainder((double)l,(double)maxSymBoundary)); if (l2 >= lengthInput) l2 = maxSymBoundary - l2; addVector[l] = inputVector[l2]; } else { l2 = l; if (l >= maxAsymBoundary) l2 = (int)Math.abs(Math.IEEEremainder((double)l,(double)maxAsymBoundary)); if (l2 >= lengthInput) l2 = maxAsymBoundary - l2; addVector[l] =- inputVector[l2]; } } i = 0; for (int l=0; l= lengthtotal) index = lengthtotal-1; // Geometric transformation and resampling addOutputVector[l] += sign*addVector[index] * splineArrayWidth[i]; i++; } } // Projection Method if ( analyDegree != -1) { // Differentiation analyDegree+1 times of the signal doDiff(addOutputVector,analyDegree+1); for (i=0;i= maxSymBoundary) l2 = (int)Math.abs(Math.IEEEremainder((double)l,(double)maxSymBoundary)); if (l2 >= lengthInput) l2 = maxSymBoundary - l2; addVector[l] = inputVector[l2]; } else { l2 = l; if (l >= maxAsymBoundary) l2 = (int)Math.abs(Math.IEEEremainder((double)l,(double)maxAsymBoundary)); if (l2 >= lengthInput) l2 = maxAsymBoundary - l2; addVector[l]=-inputVector[l2]; } } i = 0; for (int l=0; l= lengthtotal) index = lengthtotal-1; // Geometric transformation and resampling addOutputVector[l] += sign*addVector[index] * splineArrayHeight[i]; i++; } } if ( analyDegree != -1) { // Projection Method // Differentiation analyDegree+1 times of the signal doDiff(addOutputVector,analyDegree+1); for (i=0;i0; i--) c[i] = c[i] - c[i-1]; c[0] = 2.0 * c[0]; } /** * Calculate the number of additional samples to add * in order not to have problems with the borders * when applying the iir filter. * * @param size the size of the array to padd * @param degree the degree corresponding to the iir filter */ private int border(int size, int degree) { double z; int horizon = size; switch (degree) { case 0: case 1: return 0; case 2: z = Math.sqrt(8.0) - 3.0; break; case 3: z = Math.sqrt(3.0) - 2.0; break; case 4: z = Math.sqrt(664.0 - Math.sqrt(438976.0)) + Math.sqrt(304.0) - 19.0; break; case 5: z = Math.sqrt(135.0 / 2.0 - Math.sqrt(17745.0 / 4.0)) + Math.sqrt(105.0 / 4.0) - 13.0 / 2.0; break; case 6: z = -0.488294589303044755130118038883789062112279161239377608394; break; case 7: z = -0.5352804307964381655424037816816460718339231523426924148812; break; default: throw new IllegalArgumentException("Invalid interpDegree degree (should be [0..7])"); } horizon = 2 + (int)(Math.log(tolerance) / Math.log(Math.abs(z))); horizon = (horizon < size) ? (horizon) : (size); return horizon; } /** * Calculate the reversable (if necessary) * and the final size. * * @param inversable boolean * @param height number of rows * @param width number of columns * @param zoomY scaling factor for the columns * @param zoomX scaling factor for the rows */ static public int[] calculatefinalsize(boolean inversable, int height, int width, double zoomY, double zoomX) { int[] size = new int[4]; int w2; int h2; size[0]=height; size[1]=width; if (inversable == true) { w2 = (int)Math.round(Math.round((size[0]-1)*zoomY)/zoomY); while (size[0]-1-w2!=0) { size[0] = size[0]+1; w2 = (int)Math.round(Math.round((size[0]-1)*zoomY)/zoomY); } h2 = (int)Math.round(Math.round((size[1]-1)*zoomX)/zoomX); while (size[1]-1-h2!=0) { size[1] = size[1]+1; h2 = (int)Math.round(Math.round((size[1]-1)*zoomX)/zoomX); } size[2] = (int)Math.round((size[0]-1)*zoomY)+1; size[3] = (int)Math.round((size[1]-1)*zoomX)+1; } else { size[2] = (int)Math.round(size[0]*zoomY); size[3] = (int)Math.round(size[1]*zoomX); } return size; } /** */ private void getInterpolationCoefficients(double[] c, int degree) { double z[] = new double[0]; double lambda = 1.0; switch (degree) { case 0: case 1: return; case 2: z = new double[1]; z[0] = Math.sqrt(8.0) - 3.0; break; case 3: z = new double[1]; z[0] = Math.sqrt(3.0) - 2.0; break; case 4: z = new double[2]; z[0] = Math.sqrt(664.0 - Math.sqrt(438976.0)) + Math.sqrt(304.0) - 19.0; z[1] = Math.sqrt(664.0 + Math.sqrt(438976.0)) - Math.sqrt(304.0) - 19.0; break; case 5: z = new double[2]; z[0] = Math.sqrt(135.0 / 2.0 - Math.sqrt(17745.0 / 4.0)) + Math.sqrt(105.0 / 4.0) - 13.0 / 2.0; z[1] = Math.sqrt(135.0 / 2.0 + Math.sqrt(17745.0 / 4.0)) - Math.sqrt(105.0 / 4.0) - 13.0 / 2.0; break; case 6: z = new double[3]; z[0] = -0.488294589303044755130118038883789062112279161239377608394; z[1] = -0.081679271076237512597937765737059080653379610398148178525368; z[2] = -0.00141415180832581775108724397655859252786416905534669851652709; break; case 7: z = new double[3]; z[0] = -0.5352804307964381655424037816816460718339231523426924148812; z[1] = -0.122554615192326690515272264359357343605486549427295558490763; z[2] = -0.0091486948096082769285930216516478534156925639545994482648003; break; default: throw new IllegalArgumentException("Invalid spline degree (should be [0..7])"); } if (c.length == 1) { return; } for (int k = 0; (k < z.length); k++) { lambda = lambda * (1.0 - z[k]) * (1.0 - 1.0 / z[k]); } for (int n = 0; (n < c.length); n++) { c[n] = c[n] * lambda; } for (int k = 0; (k < z.length); k++) { c[0] = getInitialCausalCoefficient(c, z[k], tolerance); for (int n = 1; (n < c.length); n++) { c[n] = c[n] + z[k] * c[n - 1]; } c[c.length - 1] = getInitialAntiCausalCoefficient(c, z[k], tolerance); for (int n = c.length - 2; (0 <= n); n--) { c[n] = z[k] * (c[n+1] - c[n]); } } } /** */ private void getSamples (double[] c, int degree) { double h[] = new double[0]; double s[] = new double[c.length]; switch (degree) { case 0: case 1: return; case 2: h = new double[2]; h[0] = 3.0 / 4.0; h[1] = 1.0 / 8.0; break; case 3: h = new double[2]; h[0] = 2.0 / 3.0; h[1] = 1.0 / 6.0; break; case 4: h = new double[3]; h[0] = 115.0 / 192.0; h[1] = 19.0 / 96.0; h[2] = 1.0 / 384.0; break; case 5: h = new double[3]; h[0] = 11.0 / 20.0; h[1] = 13.0 / 60.0; h[2] = 1.0 / 120.0; break; case 6: h = new double[4]; h[0] = 5887.0 / 11520.0; h[1] = 10543.0 / 46080.0; h[2] = 361.0 / 23040.0; h[3] = 1.0 / 46080.0; break; case 7: h = new double[4]; h[0] = 151.0 / 315.0; h[1] = 397.0 / 1680.0; h[2] = 1.0 / 42.0; h[3] = 1.0 / 5040.0; break; default: throw new IllegalArgumentException("Invalid spline degree (should be [0..7])"); } symmetricFir(h, c, s); System.arraycopy(s, 0, c, 0, s.length); } /** * Note: Mirror On Bounds */ private double getInitialAntiCausalCoefficient(double[] c,double z,double tolerance) { return((z * c[c.length - 2] + c[c.length - 1]) * z / (z * z - 1.0)); } /** * Note: Mirror On Bounds */ private double getInitialCausalCoefficient (double[] c, double z, double tolerance) { double z1 = z, zn = Math.pow(z, c.length - 1); double sum = c[0] + zn * c[c.length - 1]; int horizon = c.length; if (tolerance > 0.0) { horizon = 2 + (int)(Math.log(tolerance) / Math.log(Math.abs(z))); horizon = (horizon < c.length) ? (horizon) : (c.length); } zn = zn * zn; for (int n = 1; (n < (horizon - 1)); n++) { zn = zn / z; sum = sum + (z1 + zn) * c[n]; z1 = z1 * z; } return(sum / (1.0 - Math.pow(z, 2 * c.length - 2))); } /** * Note: Mirror On Bounds */ private void symmetricFir(double[] h, double[] c, double[] s) { if (c.length != s.length) { throw new IndexOutOfBoundsException("Incompatible size"); } switch (h.length) { case 2: if (2 <= c.length) { s[0] = h[0] * c[0] + 2.0 * h[1] * c[1]; for (int i = 1; (i < (c.length - 1)); i++) { s[i] = h[0] * c[i] + h[1] * (c[i - 1] + c[i + 1]); } s[s.length - 1] = h[0] * c[c.length - 1] + 2.0 * h[1] * c[c.length - 2]; } else { switch (c.length) { case 1: s[0] = (h[0] + 2.0 * h[1]) * c[0]; break; default: throw new NegativeArraySizeException("Invalid length of data"); } } break; case 3: if (4 <= c.length) { s[0] = h[0] * c[0] + 2.0 * h[1] * c[1] + 2.0 * h[2] * c[2]; s[1] = h[0] * c[1] + h[1] * (c[0] + c[2]) + h[2] * (c[1] + c[3]); for (int i = 2; (i < (c.length - 2)); i++) { s[i] = h[0] * c[i] + h[1] * (c[i - 1] + c[i + 1]) + h[2] * (c[i - 2] + c[i + 2]); } s[s.length - 2] = h[0] * c[c.length - 2] + h[1] * (c[c.length - 3] + c[c.length - 1]) + h[2] * (c[c.length - 4] + c[c.length - 2]); s[s.length - 1] = h[0] * c[c.length - 1] + 2.0 * h[1] * c[c.length - 2] + 2.0 * h[2] * c[c.length - 3]; } else { switch (c.length) { case 3: s[0] = h[0] * c[0] + 2.0 * h[1] * c[1] + 2.0 * h[2] * c[2]; s[1] = h[0] * c[1] + h[1] * (c[0] + c[2]) + 2.0 * h[2] * c[1]; s[2] = h[0] * c[2] + 2.0 * h[1] * c[1] + 2.0 * h[2] * c[0]; break; case 2: s[0] = (h[0] + 2.0 * h[2]) * c[0] + 2.0 * h[1] * c[1]; s[1] = (h[0] + 2.0 * h[2]) * c[1] + 2.0 * h[1] * c[0]; break; case 1: s[0] = (h[0] + 2.0 * (h[1] + h[2])) * c[0]; break; default: throw new NegativeArraySizeException("Invalid length of data"); } } break; case 4: if (6 <= c.length) { s[0] = h[0] * c[0] + 2.0 * h[1] * c[1] + 2.0 * h[2] * c[2] + 2.0 * h[3] * c[3]; s[1] = h[0] * c[1] + h[1] * (c[0] + c[2]) + h[2] * (c[1] + c[3]) + h[3] * (c[2] + c[4]); s[2] = h[0] * c[2] + h[1] * (c[1] + c[3]) + h[2] * (c[0] + c[4]) + h[3] * (c[1] + c[5]); for (int i = 3; (i < (c.length - 3)); i++) { s[i] = h[0] * c[i] + h[1] * (c[i - 1] + c[i + 1]) + h[2] * (c[i - 2] + c[i + 2]) + h[3] * (c[i - 3] + c[i + 3]); } s[s.length - 3] = h[0] * c[c.length - 3] + h[1] * (c[c.length - 4] + c[c.length - 2]) + h[2] * (c[c.length - 5] + c[c.length - 1]) + h[3] * (c[c.length - 6] + c[c.length - 2]); s[s.length - 2] = h[0] * c[c.length - 2] + h[1] * (c[c.length - 3] + c[c.length - 1]) + h[2] * (c[c.length - 4] + c[c.length - 2]) + h[3] * (c[c.length - 5] + c[c.length - 3]); s[s.length - 1] = h[0] * c[c.length - 1] + 2.0 * h[1] * c[c.length - 2] + 2.0 * h[2] * c[c.length - 3] + 2.0 * h[3] * c[c.length - 4]; } else { switch (c.length) { case 5: s[0] = h[0] * c[0] + 2.0 * h[1] * c[1] + 2.0 * h[2] * c[2] + 2.0 * h[3] * c[3]; s[1] = h[0] * c[1] + h[1] * (c[0] + c[2]) + h[2] * (c[1] + c[3]) + h[3] * (c[2] + c[4]); s[2] = h[0] * c[2] + (h[1] + h[3]) * (c[1] + c[3]) + h[2] * (c[0] + c[4]); s[3] = h[0] * c[3] + h[1] * (c[2] + c[4]) + h[2] * (c[1] + c[3]) + h[3] * (c[0] + c[2]); s[4] = h[0] * c[4] + 2.0 * h[1] * c[3] + 2.0 * h[2] * c[2] + 2.0 * h[3] * c[1]; break; case 4: s[0] = h[0] * c[0] + 2.0 * h[1] * c[1] + 2.0 * h[2] * c[2] + 2.0 * h[3] * c[3]; s[1] = h[0] * c[1] + h[1] * (c[0] + c[2]) + h[2] * (c[1] + c[3]) + 2.0 * h[3] * c[2]; s[2] = h[0] * c[2] + h[1] * (c[1] + c[3]) + h[2] * (c[0] + c[2]) + 2.0 * h[3] * c[1]; s[3] = h[0] * c[3] + 2.0 * h[1] * c[2] + 2.0 * h[2] * c[1] + 2.0 * h[3] * c[0]; break; case 3: s[0] = h[0] * c[0] + 2.0 * (h[1] + h[3]) * c[1] + 2.0 * h[2] * c[2]; s[1] = h[0] * c[1] + (h[1] + h[3]) * (c[0] + c[2]) + 2.0 * h[2] * c[1]; s[2] = h[0] * c[2] + 2.0 * (h[1] + h[3]) * c[1] + 2.0 * h[2] * c[0]; break; case 2: s[0] = (h[0] + 2.0 * h[2]) * c[0] + 2.0 * (h[1] + h[3]) * c[1]; s[1] = (h[0] + 2.0 * h[2]) * c[1] + 2.0 * (h[1] + h[3]) * c[0]; break; case 1: s[0] = (h[0] + 2.0 * (h[1] + h[2] + h[3])) * c[0]; break; default: throw new NegativeArraySizeException("Invalid length of data"); } } break; default: throw new IllegalArgumentException( "Invalid filter half-length (should be [2..4])"); } } } // end of class