001    /*
002     * OilFilter
003     * 
004     * Copyright (c) 2001, 2002, 2003 Marco Schmidt.
005     * All rights reserved.
006     */
007    
008    package net.sourceforge.jiu.filters;
009    
010    import net.sourceforge.jiu.data.IntegerImage;
011    import net.sourceforge.jiu.data.PixelImage;
012    import net.sourceforge.jiu.filters.AreaFilterOperation;
013    import net.sourceforge.jiu.ops.MissingParameterException;
014    import net.sourceforge.jiu.ops.WrongParameterException;
015    
016    /**
017     * Applies a filter that makes the image look like an oil painting.
018     * This is accomplished by creating a histogram of the neighboring samples
019     * for each input sample and storing the value that occurs most often
020     * in the output image.
021     * If two or more samples occur an equal number of times, the lowest
022     * sample value is picked.
023     * <h3>Supported image types</h3>
024     * Can process both {@link net.sourceforge.jiu.data.GrayIntegerImage} and
025     * {@link net.sourceforge.jiu.data.RGBIntegerImage}.
026     * Note that this operation becomes very slow with 16 bits per sample
027     * because a lot of runs over a 65536 element array are necessary.
028     * <h3>Usage example</h3>
029     * <pre>
030     * PixelImage image = ...; // some GrayIntegerImage or RGBIntegerImage
031     * OilFilter filter = new OilFilter();
032     * filter.setArea(5, 5);
033     * filter.setInputImage(image);
034     * filter.process();
035     * PixelImage filteredImage = filter.getOutputImage();
036     * </pre>
037     * <h3>Credits</h3>
038     * Idea taken from the <a target="_top" 
039     * href="http://www.acme.com/java/software/Acme.JPM.Filters.Oil.html#_top_">
040     * Oil class</a> of Jef Poskanzer's <a target="_top"
041     * href="http://www.acme.com/java/software/">ACME package</a>.
042     * @author Marco Schmidt
043     */
044    public class OilFilter extends AreaFilterOperation
045    {
046            private int[] hist;
047            private int[] zeroes;
048    
049            public final int computeSample(int[] samples, int numSamples)
050            {
051                    /* for each sample find the neighbor that occurs most often
052                       in the area surrounding that pixel specified by 
053                       getAreaWidth x getAreaHeight */
054    
055                    // clear histogram
056                    System.arraycopy(zeroes, 0, hist, 0, hist.length);
057    
058                    // initialize histogram
059                    int index = numSamples; 
060                    do
061                    {
062                            hist[samples[--index]]++;
063                    }
064                    while (index != 0);
065    
066                    // now find the value that occurs most frequently
067                    int maxIndex = 0;
068                    int maxValue = hist[0];
069                    index = 1;
070                    final int HIST_LENGTH = hist.length;
071                    while (index != HIST_LENGTH)
072                    {
073                            int value = hist[index];
074                            if (value > maxValue)
075                            {
076                                    maxIndex = index;
077                                    maxValue = value;
078                            }
079                            index++;
080                    }
081    
082                    // return value that occurs most frequently
083                    // if several samples occur most frequently, the smallest is returned
084                    return maxIndex;
085            }
086    
087            public void process() throws 
088                    MissingParameterException,
089                    WrongParameterException 
090            {
091                    ensureInputImageIsAvailable();
092                    PixelImage image = getInputImage();
093                    if (image instanceof IntegerImage)
094                    {
095                            IntegerImage ii = (IntegerImage)image;
096                            int max = ii.getMaxSample(0);
097                            int index = 1;
098                            while (index < ii.getNumChannels())
099                            {
100                                    int maxSample = ii.getMaxSample(index++);
101                                    if (maxSample > max)
102                                    {
103                                            max = maxSample;
104                                    }
105                            }
106                            hist = new int[max + 1];
107                            zeroes = new int[hist.length];
108                            for (int i = 0; i < zeroes.length; i++)
109                            {
110                                    zeroes[i] = 0;
111                            }
112                    }
113                    super.process();
114            }
115    }