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 Ch08_Binary_Regions;
010
011import ij.ImagePlus;
012import ij.gui.GenericDialog;
013import ij.plugin.filter.PlugInFilter;
014import ij.process.ColorProcessor;
015import ij.process.ImageProcessor;
016import imagingbook.common.color.sets.BasicAwtColor;
017import imagingbook.common.ij.DialogUtils;
018import imagingbook.common.image.Projection;
019import imagingbook.core.plugin.IjPluginName;
020import imagingbook.core.jdoc.JavaDocHelp;
021import imagingbook.sampleimages.GeneralSampleImage;
022
023import static imagingbook.common.ij.IjUtils.noCurrentImage;
024
025/**
026 * <p>
027 * This ImageJ plugin demonstrates the calculation of horizontal and vertical
028 * projections from a grayscale image. See Sec. 8.7 of [1] for additional
029 * details. The input image is not assumed binary, i.e., gray values are simply
030 * added up without thresholding. This plugin only works for 8-bit grayscale
031 * images, the original image is not modified. 
032 * If no image is currently open, the plugin optionally loads a suitable
033 * sample image.
034 * The resulting projections are
035 * shown as separate diagrams, which either show the relative amount of white
036 * (default) or black, selectable in the user dialog:
037 * </p>
038 * <ul>
039 * <li>The <em>horizontal</em> projection diagram has the same height as the
040 * original image and user-adjustable width, with diagram bars being drawn from
041 * left to right.</li>
042 * <li>The <em>vertical</em> projection diagram has the same width as the
043 * original image and user-adjustable height, with diagram bars being drawn from
044 * top to bottom.</li>
045 * </ul>
046 * <p>
047 * [1] W. Burger, M.J. Burge, <em>Digital Image Processing &ndash; An Algorithmic
048 * Introduction</em>, 3rd ed, Springer (2022).
049 * </p>
050 * 
051 * @author WB
052 * @version 2020/12/17
053 * @version 2022/09/29 revised to match book examples
054 * 
055 * @see imagingbook.common.image.Projection
056 */
057@IjPluginName("Make Projections")
058public class Make_Projections implements PlugInFilter, JavaDocHelp {
059        
060        /** Size of horizontal/vertical projection bars. */
061        public static int ProjectionSize = 150;
062        
063        /** Background color for projection diagrams. */
064        public static BasicAwtColor BackgroundColor = BasicAwtColor.White;
065        
066        /** Foreground value for horizontal projection. */
067        public static BasicAwtColor PlotColorH = BasicAwtColor.Green;
068        
069        /** Foreground value for vertical projection. */
070        public static BasicAwtColor PlotColorV = BasicAwtColor.Magenta;
071        
072        /** Plot the relative amount of white (false) or black (true). */
073        public static boolean ShowAmountOfBlack = false;
074        
075        private ImagePlus im = null;
076        
077        
078        /**
079         * Constructor, asks to open a predefined sample image if no other image
080         * is currently open.
081         */
082        public Make_Projections() {
083                if (noCurrentImage()) {
084                        DialogUtils.askForSampleImage(GeneralSampleImage.Cat);
085                }
086        }
087        
088        @Override
089        public int setup(String arg, ImagePlus im) { 
090                this.im = im;
091                return DOES_8G + NO_CHANGES; 
092        }
093        
094        @Override
095        public void run(ImageProcessor ip) {
096                
097                if (!runDialog()) {
098                        return;
099                }
100                
101                String title = im.getShortTitle();
102                int M = ip.getWidth();
103                int N = ip.getHeight();
104                
105                Projection proj = new Projection(ip);
106                
107                // draw the horizontal projection diagram:
108                double[] pHor = proj.getHorizontal();   // pHor[v] = sum of pixel values in row v
109                ColorProcessor hP = new ColorProcessor(ProjectionSize, N);
110                hP.setColor(BackgroundColor.getColor());
111                hP.fill();
112                hP.setColor(PlotColorH.getColor());
113                double maxWhite = M * 255;
114                for (int v = 0; v < N; v++) {
115                        double amount = (ShowAmountOfBlack) ?
116                                        1 - pHor[v] / maxWhite :        // amount of black in line 'v'
117                                        pHor[v] / maxWhite;                     // amount of white in line 'v'
118                        int k = (int) Math.round(amount * ProjectionSize);
119                        if (k > 0) {
120                                hP.drawLine(0, v, k, v);
121                        }
122                }       
123                (new ImagePlus(title + "-horizontal-proj", hP)).show();
124                                
125                // draw the vertical projection diagram:
126                double[] pVer = proj.getVertical();             // pVer[u] = sum of pixel values in column u
127                ColorProcessor vP = new ColorProcessor(M, ProjectionSize);
128                vP.setColor(BackgroundColor.getColor()); 
129                vP.fill();
130                vP.setColor(PlotColorV.getColor());
131                maxWhite = N * 255;
132                for (int u = 0; u < M; u++) {
133                        double amount = (ShowAmountOfBlack) ?
134                                        1 - pVer[u] / maxWhite :        // amount of black in column 'u'
135                                        pVer[u] / maxWhite;                     // amount of white in column 'u'
136                        int k = (int) (amount * ProjectionSize);
137                        if (k > 0) {
138                                vP.drawLine(u, 0, u, k);
139                        }
140                }
141                (new ImagePlus(title + "-vertical-proj", vP)).show();
142        }
143
144        private boolean runDialog() {
145                GenericDialog gd = new GenericDialog(this.getClass().getSimpleName());
146                gd.addHelp(getJavaDocUrl());
147                gd.addNumericField("Size of projection plots", ProjectionSize, 0);
148                gd.addCheckbox("Show amount of black", ShowAmountOfBlack);
149                gd.addEnumChoice("Horizontal plot color", PlotColorH);
150                gd.addEnumChoice("Vertical plot color", PlotColorV);
151                gd.addEnumChoice("Plot background color", BackgroundColor);
152                
153                gd.showDialog();
154                if (gd.wasCanceled())
155                        return false;
156                
157                ProjectionSize = (int) gd.getNextNumber();
158                ShowAmountOfBlack = gd.getNextBoolean();
159                PlotColorH = gd.getNextEnumChoice(BasicAwtColor.class);
160                PlotColorV = gd.getNextEnumChoice(BasicAwtColor.class);
161                BackgroundColor = gd.getNextEnumChoice(BasicAwtColor.class);
162                return true;
163        }
164}