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}