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-2016 Wilhelm Burger, Mark J. Burge. All rights reserved. 007 * Visit http://imagingbook.com for additional details. 008 *******************************************************************************/ 009 010package imagingbook.pub.sift.scalespace; 011 012import ij.process.FloatProcessor; 013import imagingbook.pub.sift.filters.GaussianFilter; 014 015/** 016 * Represents a single scale level. Just a special kind of FloatProcessor 017 * with some extra fields and methods. 018 */ 019public class ScaleLevel extends FloatProcessor { // TODO: make IJ independent, use only float arrays 020 021 private double absoluteScale; // really needed? 022 023 // ------------------------------ 024 025 public ScaleLevel(int width, int height, float[] data, double absoluteScale) { 026 super(width, height, data, null); // constructor of FloatProcessor 027 this.absoluteScale = absoluteScale; 028 } 029 030 public ScaleLevel(FloatProcessor fp, double absoluteScale) { 031// this(fp.getWidth(), fp.getHeight(), ((float[]) fp.getPixels()).clone(), absoluteScale); 032 this(fp.getWidth(), fp.getHeight(), getValues(fp, true), absoluteScale); 033 } 034 035 public ScaleLevel(ScaleLevel level) { 036// this(level.getWidth(), level.getHeight(), ((float[]) level.getPixels()).clone(), level.absoluteScale); 037 this(level.getWidth(), level.getHeight(), getValues(level, false), level.absoluteScale); 038 } 039 040 private static float[] getValues(FloatProcessor fp, boolean normalize) { 041 final double minVal = fp.getMin(); 042 final double maxVal = fp.getMax(); 043 final float offset = (float) -minVal; 044 final float scale = (float) (1.0 / (maxVal - minVal)); 045 final float[] values = (float[]) fp.getPixelsCopy(); 046 if (normalize) { 047 for (int i = 0; i < values.length; i++) { 048 values[i] = (values[i] + offset) * scale; 049 } 050 } 051 return values; 052 } 053 054 // ------------------------------ 055 056 public void filterGaussian(double sigma) { 057 GaussianFilter gf = new GaussianFilter(sigma); 058 gf.applyTo(this); 059 } 060 061 public ScaleLevel duplicate() { 062 return new ScaleLevel(this); 063 } 064 065 public ScaleLevel decimate() { // returns a 2:1 subsampled copy of this ScaleLevel 066 //IJ.log(" reducing ..."); 067 int width1 = this.getWidth(); 068 int height1 = this.getHeight(); 069 int width2 = width1 / 2; 070 int height2 = height1 / 2; 071 072 float[] pixels1 = (float[]) this.getPixels(); 073 float[] pixels2 = new float[width2*height2]; 074 for (int v2 = 0 ; v2 < height2; v2++) { 075 int v1 = 2 * v2; 076 for (int u2 = 0 ; u2 < width2; u2++) { 077 int u1 = 2 * u2; 078 pixels2[v2 * width2 + u2] = pixels1[v1 * width1 + u1]; 079 } 080 } 081 return new ScaleLevel(width2, height2, pixels2, absoluteScale); 082 } 083 084 public ScaleLevel subtract(FloatProcessor B) { 085 // A <-- A-B 086 ScaleLevel A = this.duplicate(); 087 float[] pixelsA = (float[]) A.getPixels(); 088 float[] pixelsB = (float[]) B.getPixels(); 089 for (int i=0; i<pixelsA.length; i++) { 090 pixelsA[i] = pixelsA[i] - pixelsB[i]; 091 } 092 A.setAbsoluteScale(0); 093 return A; 094 } 095 096 public void setAbsoluteScale(double sigma) { 097 this.absoluteScale = sigma; 098 } 099 100 public double getAbsoluteScale() { 101 return absoluteScale; 102 } 103 104 /* 105 * Collects and returns the 3x3 neighborhood values at this scale level 106 * at center position u,v. The result is stored in the given 3x3 array nh. 107 */ 108 public void get3x3Neighborhood(final int u, final int v, final float[][] nh) { 109 for (int i = 0, x = u - 1; i < 3; i++, x++) { 110 for (int j = 0, y = v - 1; j < 3; j++, y++) { 111 nh[i][j] = this.getf(x, y); 112 } 113 } 114 } 115 116 public void getGradientPolar(int u, int v, double[] grad) { 117 final double grad_x = this.getf(u+1, v) - this.getf(u-1, v); // x-component of local gradient 118 final double grad_y = this.getf(u, v+1) - this.getf(u, v-1); // y-component of local gradient 119 grad[0] = Math.sqrt(grad_x * grad_x + grad_y * grad_y); // local gradient magnitude (E) 120 grad[1] = Math.atan2(grad_y, grad_x); // local gradient direction (phi) 121 } 122}