001    /*
002     * ReduceToBilevelThreshold
003     * 
004     * Copyright (c) 2001, 2002, 2003 Marco Schmidt.
005     * All rights reserved.
006     */
007    
008    package net.sourceforge.jiu.color.reduction;
009    
010    import net.sourceforge.jiu.data.BilevelImage;
011    import net.sourceforge.jiu.data.GrayIntegerImage;
012    import net.sourceforge.jiu.data.MemoryBilevelImage;
013    import net.sourceforge.jiu.data.PixelImage;
014    import net.sourceforge.jiu.ops.ImageToImageOperation;
015    import net.sourceforge.jiu.ops.MissingParameterException;
016    import net.sourceforge.jiu.ops.WrongParameterException;
017    
018    /**
019     * Reduces a {@link net.sourceforge.jiu.data.GrayIntegerImage} to a 
020     * {@link net.sourceforge.jiu.data.BilevelImage} by setting all values below
021     * a certain threshold value to black and everything else to white.
022     * <h3>Default value</h3>
023     * If no threshold is specified via {@link #setThreshold(int)}, this operation
024     * uses a default value of ({@link net.sourceforge.jiu.data.IntegerImage#getMaxSample(int)} + 1) / 2.
025     * <h3>Usage example</h3>
026     * This example sets all values below 33 percent luminance to black,
027     * everything else to white.
028     * <pre>
029     * GrayIntegerImage image = ...;
030     * ReduceToBilevelThreshold red = new ReduceToBilevelThreshold();
031     * red.setInputImage(image);
032     * red.setThreshold(image.getMaxSample(0) / 3);
033     * red.process();
034     * BilevelImage reducedImage= (BilevelImage)red.getOutputImage();
035     * </pre>
036     * @author Marco Schmidt
037     */
038    public class ReduceToBilevelThreshold extends ImageToImageOperation
039    {
040            private Integer threshold;
041    
042            /**
043             * Returns the current threshold value, or <code>null</code> if
044             * none was specified and the operation's process method was not
045             * run yet.
046             * @return threshold value
047             */
048            public Integer getThreshold()
049            {
050                    return threshold;
051            }
052    
053            private void process(GrayIntegerImage in, BilevelImage out) throws WrongParameterException
054            {
055                    final int MAX_SAMPLE = in.getMaxSample(0);
056                    if (threshold == null)
057                    {
058                            threshold = new Integer((MAX_SAMPLE + 1) / 2);
059                    }
060                    final int THRESHOLD = threshold.intValue();
061                    if (THRESHOLD > MAX_SAMPLE)
062                    {
063                            throw new WrongParameterException("Threshold must be smaller than or equal to the maximum sample of the input image.");
064                    }
065                    final int WIDTH = in.getWidth();
066                    final int HEIGHT = in.getHeight();
067                    out.clear(BilevelImage.BLACK);
068                    for (int y = 0; y < HEIGHT; y++)
069                    {
070                            for (int x = 0; x < WIDTH; x++)
071                            {
072                                    if (in.getSample(0, x, y) >= THRESHOLD)
073                                    {
074                                            out.putWhite(x, y);
075                                    }
076                            }
077                            setProgress(y, HEIGHT);
078                    }
079            }
080    
081            public void process() throws
082                    MissingParameterException,
083                    WrongParameterException
084            {
085                    PixelImage in = getInputImage();
086                    if (in == null)
087                    {
088                            throw new MissingParameterException("Input image missing.");
089                    }
090                    if (!(in instanceof GrayIntegerImage))
091                    {
092                            throw new WrongParameterException("Input image must implement GrayIntegerImage.");
093                    }
094                    PixelImage out = getOutputImage();
095                    if (out == null)
096                    {
097                            out = new MemoryBilevelImage(in.getWidth(), in.getHeight());
098                            setOutputImage(out);
099                    }
100                    if (out != null && !(out instanceof BilevelImage))
101                    {
102                            throw new WrongParameterException("Output image must implement BilevelImage.");
103                    }
104                    if (out != null && (in.getWidth() != out.getWidth() || in.getHeight() != out.getHeight()))
105                    {
106                            throw new WrongParameterException("Input and output images must have the same resolution.");
107                    }
108                    process((GrayIntegerImage)in, (BilevelImage)out);
109            }
110    
111            /**
112             * Sets a new threshold value.
113             * @param newThreshold the new threshold value to be used for this operation
114             * @throws IllegalArgumentException if the threshold value is negative
115             */
116            public void setThreshold(int newThreshold)
117            {
118                    if (newThreshold < 0)
119                    {
120                            throw new IllegalArgumentException("New threshold value must be 0 or larger.");
121                    }
122                    threshold = new Integer(newThreshold);
123            }
124    }