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.IJ; 013import ij.ImagePlus; 014import ij.ImageStack; 015import imagingbook.lib.util.LinearContainer; 016 017import java.util.Locale; 018 019/** 020 * Represents a stack of scale levels within an octave. Basically this is 021 * only an array with flexible bottom and top index. 022 */ 023public abstract class ScaleOctave { 024 025 double sigma_0 = 1.6; 026 int Q = 3; // levels per doubling scale factor 027 final int p; // octave index 028 final int width, height; 029 final int botLevelIndex, topLevelIndex; 030 031 final LinearContainer<ScaleLevel> levels; 032 033 ScaleOctave (int p, int Q, int width, int height, int botLevelIndex, int topLevelIndex) { 034 this.p = p; 035 this.Q = Q; 036 this.width = width; 037 this.height = height; 038 if (botLevelIndex > topLevelIndex) 039 throw new IllegalArgumentException("ScaleOctave (constructor): botLevelIndex > topLevelIndex"); 040 this.botLevelIndex = botLevelIndex; 041 this.topLevelIndex = topLevelIndex; 042 levels = new LinearContainer<ScaleLevel>(botLevelIndex, topLevelIndex); 043 } 044 045 /* Create a scale octave from a given bottom level level_b 046 */ 047 ScaleOctave (int p, int Q, ScaleLevel level_b, int botIndex, int topIndex) { 048 this(p, Q, level_b.getWidth(), level_b.getHeight(), botIndex, topIndex); 049 this.setLevel(botIndex, level_b); 050 } 051 052 public int getOctaveIndex() { 053 return p; 054 } 055 056 public int getWidth() { 057 return width; 058 } 059 060 public int getHeight() { 061 return height; 062 } 063 064 public ScaleLevel getLevel(int q) { // TODO: honor bottom level, check q 065 return levels.getElement(q); 066 } 067 068 void setLevel(int q, ScaleLevel level) { // TODO: check q 069 levels.setElement(q, level); 070 } 071 072 public boolean isInside(int q, int u, int v) { 073 return (botLevelIndex < q && q < topLevelIndex && 074 0 < u && u < width-1 && 075 0 < v && v < height-1); 076 } 077 078 public double getAbsoluteScale(int q) { 079 return getLevel(q).getAbsoluteScale(); 080 } 081 082 int getBottomLevelIndex() { 083 return botLevelIndex; 084 } 085 086 int getTopLevelIndex() { 087 return topLevelIndex; 088 } 089 090 091 /* 092 * Collects and returns the 3x3x3 neighborhood values from this octave 093 * at scale level q and center position u,v. The result is stored 094 * in the given 3x3x3 array nh[s][x][y], to which a reference is returned. 095 */ 096 public void getNeighborhood(int q, int u, int v, final float[][][] nh) { 097 // nh[s][x][y] 098 for (int s=0, level=q-1; s<3; s++, level++) { 099 getLevel(level).get3x3Neighborhood(u, v, nh[s]); 100 } 101 } 102 103 // for debugging only: 104 public void print() { 105 IJ.log(" Scale Octave p=" + p); 106 for (int q = botLevelIndex; q <= topLevelIndex; q++) { 107 ScaleLevel level = getLevel(q); 108 if (level != null) { 109 double scale = level.getAbsoluteScale(); 110 IJ.log(String.format(Locale.US, " level (p=%d, q=%d, \u03C3=%.4f)", p, q, scale)); 111 } 112 } 113 } 114 115 public void show(String name, int p) { 116 for (int q = botLevelIndex; q <= topLevelIndex; q++) { 117 ScaleLevel level = getLevel(q); 118 if (level != null) { 119 double scale = level.getAbsoluteScale(); 120 //String title = name + " (q=" + q + ") " + String.format(Locale.US, "\u03C3=%.3f", scale); 121 String title = String.format(Locale.US, "%s (p=%d, q=%d, \u03C3=%.4f)", name, p, q, scale); 122 // IjDisplay.showProcessor(level, title, 0, 255); 123 //IjUtils.showProcessor(level, title); 124 level.resetMinAndMax(); 125 (new ImagePlus(title, level)).show(); 126 } 127 } 128 } 129 130 public void showAsStack(String name) { 131 ImageStack stk = new ImageStack(width, height); 132 for (int q = botLevelIndex; q <= topLevelIndex; q++) { 133 ScaleLevel level = getLevel(q); 134 if (level != null) { 135 double scale = level.getAbsoluteScale(); 136 String title = String.format(Locale.US, "q=%d, \u03C3=%.4f", q, scale); 137 stk.addSlice(title, level); 138 } 139 } 140 (new ImagePlus(name,stk)).show(); 141 } 142 143 public void showAsStack(String name, int p) { 144 ImageStack stk = new ImageStack(width, height); 145 for (int q = botLevelIndex; q <= topLevelIndex; q++) { 146 ScaleLevel level = getLevel(q); 147 if (level != null) { 148 double scale = level.getAbsoluteScale(); 149 String title = String.format(Locale.US, "p=%d, q=%d, \u03C3=%.4f", p, q, scale); 150 stk.addSlice(title, level); 151 } 152 } 153 (new ImagePlus(name,stk)).show(); 154 } 155 156 157 public int getScaleIndex(int p, int q) { 158 int m = Q * p + q; 159 return m; 160 } 161 162 public double getAbsoluteScale(int p, int q) { 163 double m = getScaleIndex(p, q); 164 double sigma = sigma_0 * Math.pow(2, m/Q); 165 return sigma; 166 } 167 168// public double getRelativeScale(double scaleA, double scaleB) { // scaleA <= scaleB 169// return Math.sqrt(scaleB*scaleB - scaleA*scaleA); 170// } 171// 172// public int getOctaveIndex() { 173// return p; 174// } 175 176}