001/**
002 *
003 * Copyright (c) 2014, the Railo Company Ltd. All rights reserved.
004 *
005 * This library is free software; you can redistribute it and/or
006 * modify it under the terms of the GNU Lesser General Public
007 * License as published by the Free Software Foundation; either 
008 * version 2.1 of the License, or (at your option) any later version.
009 * 
010 * This library is distributed in the hope that it will be useful,
011 * but WITHOUT ANY WARRANTY; without even the implied warranty of
012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
013 * Lesser General Public License for more details.
014 * 
015 * You should have received a copy of the GNU Lesser General Public 
016 * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
017 * 
018 **/
019/*
020*
021
022Licensed under the Apache License, Version 2.0 (the "License");
023you may not use this file except in compliance with the License.
024You may obtain a copy of the License at
025
026   http://www.apache.org/licenses/LICENSE-2.0
027
028Unless required by applicable law or agreed to in writing, software
029distributed under the License is distributed on an "AS IS" BASIS,
030WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
031See the License for the specific language governing permissions and
032limitations under the License.
033*/
034
035package lucee.runtime.img.filter;import java.awt.Rectangle;
036import java.awt.image.BufferedImage;
037
038import lucee.runtime.engine.ThreadLocalPageContext;
039import lucee.runtime.exp.FunctionException;
040import lucee.runtime.exp.PageException;
041import lucee.runtime.img.ImageUtil;
042import lucee.runtime.type.Struct;
043import lucee.runtime.type.util.CollectionUtil;
044
045/**
046 * A filter which performs a 3x3 median operation. Useful for removing dust and noise.
047 */
048public class MedianFilter extends WholeImageFilter  implements DynFiltering {
049
050        public MedianFilter() {
051        }
052
053        private int median(int[] array) {
054                int max, maxIndex;
055                
056                for (int i = 0; i < 4; i++) {
057                        max = 0;
058                        maxIndex = 0;
059                        for (int j = 0; j < 9; j++) {
060                                if (array[j] > max) {
061                                        max = array[j];
062                                        maxIndex = j;
063                                }
064                        }
065                        array[maxIndex] = 0;
066                }
067                max = 0;
068                for (int i = 0; i < 9; i++) {
069                        if (array[i] > max)
070                                max = array[i];
071                }
072                return max;
073        }
074
075        private int rgbMedian(int[] r, int[] g, int[] b) {
076                int sum, index = 0, min = Integer.MAX_VALUE;
077                
078                for (int i = 0; i < 9; i++) {
079                        sum = 0;
080                        for (int j = 0; j < 9; j++) {
081                                sum += Math.abs(r[i]-r[j]);
082                                sum += Math.abs(g[i]-g[j]);
083                                sum += Math.abs(b[i]-b[j]);
084                        }
085                        if (sum < min) {
086                                min = sum;
087                                index = i;
088                        }
089                }
090                return index;
091        }
092
093        protected int[] filterPixels( int width, int height, int[] inPixels, Rectangle transformedSpace ) {
094                int index = 0;
095                int[] argb = new int[9];
096                int[] r = new int[9];
097                int[] g = new int[9];
098                int[] b = new int[9];
099                int[] outPixels = new int[width * height];
100
101                for (int y = 0; y < height; y++) {
102                        for (int x = 0; x < width; x++) {
103                                int k = 0;
104                                for (int dy = -1; dy <= 1; dy++) {
105                                        int iy = y+dy;
106                                        if (0 <= iy && iy < height) {
107                                                int ioffset = iy*width;
108                                                for (int dx = -1; dx <= 1; dx++) {
109                                                        int ix = x+dx;
110                                                        if (0 <= ix && ix < width) {
111                                                                int rgb = inPixels[ioffset+ix];
112                                                                argb[k] = rgb;
113                                                                r[k] = (rgb >> 16) & 0xff;
114                                                                g[k] = (rgb >> 8) & 0xff;
115                                                                b[k] = rgb & 0xff;
116                                                                k++;
117                                                        }
118                                                }
119                                        }
120                                }
121                                while (k < 9) {
122                                        argb[k] = 0xff000000;
123                                        r[k] = g[k] = b[k] = 0;
124                                        k++;
125                                }
126                                outPixels[index++] = argb[rgbMedian(r, g, b)];
127                        }
128                }
129                return outPixels;
130        }
131
132        public String toString() {
133                return "Blur/Median";
134        }
135
136        public BufferedImage filter(BufferedImage src, Struct parameters) throws PageException {BufferedImage dst=ImageUtil.createBufferedImage(src);
137                //Object o;
138
139                // check for arguments not supported
140                if(parameters.size()>0) {
141                        throw new FunctionException(ThreadLocalPageContext.get(), "ImageFilter", 3, "parameters", "the parameter"+(parameters.size()>1?"s":"")+" ["+CollectionUtil.getKeyList(parameters,", ")+"] "+(parameters.size()>1?"are":"is")+" not allowed, only the following parameters are supported []");
142                }
143
144                return filter(src, dst);
145        }
146}
147