001/**
002 * Copyright (c) 2011, The University of Southampton and the individual contributors.
003 * All rights reserved.
004 *
005 * Redistribution and use in source and binary forms, with or without modification,
006 * are permitted provided that the following conditions are met:
007 *
008 *   *  Redistributions of source code must retain the above copyright notice,
009 *      this list of conditions and the following disclaimer.
010 *
011 *   *  Redistributions in binary form must reproduce the above copyright notice,
012 *      this list of conditions and the following disclaimer in the documentation
013 *      and/or other materials provided with the distribution.
014 *
015 *   *  Neither the name of the University of Southampton nor the names of its
016 *      contributors may be used to endorse or promote products derived from this
017 *      software without specific prior written permission.
018 *
019 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
020 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
021 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
022 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
023 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
024 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
025 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
026 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
027 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
028 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
029 */
030package org.openimaj.image.processing.algorithm;
031
032import org.openimaj.citation.annotation.Reference;
033import org.openimaj.citation.annotation.ReferenceType;
034import org.openimaj.image.FImage;
035import org.openimaj.image.mask.AbstractMaskedObject;
036import org.openimaj.image.processor.ImageProcessor;
037
038/**
039 * An image processor that performs contrast equalisation
040 * in a robust manner with a mask.
041 * 
042 * @author Jonathon Hare (jsh2@ecs.soton.ac.uk)
043 */
044@Reference(
045                type = ReferenceType.Article,
046                author = { "Tan, Xiaoyang", "Triggs, Bill" },
047                title = "Enhanced local texture feature sets for face recognition under difficult lighting conditions",
048                year = "2010",
049                journal = "Trans. Img. Proc.",
050                pages = { "1635", "", "1650" },
051                url = "http://dx.doi.org/10.1109/TIP.2010.2042645",
052                month = "June",
053                number = "6",
054                publisher = "IEEE Press",
055                volume = "19"
056        )
057public class MaskedRobustContrastEqualisation extends AbstractMaskedObject<FImage> implements ImageProcessor<FImage> {
058        double alpha = 0.1;
059        double tau = 10;
060
061        /**
062         * Construct with no mask set
063         */
064        public MaskedRobustContrastEqualisation() {
065                super();
066        }
067
068        /**
069         * Construct with a mask.
070         * @param mask the mask.
071         */
072        public MaskedRobustContrastEqualisation(FImage mask) {
073                super(mask);
074        }
075        
076        @Override
077        public void processImage(FImage image) {
078                //1st pass
079                image.divideInplace(firstPassDivisor(image, mask));
080                
081                //2nd pass
082                image.divideInplace(secondPassDivisor(image, mask));
083                
084                //3rd pass
085                for (int y=0; y<image.height; y++) {
086                        for (int x=0; x<image.width; x++) {
087                                if (mask.pixels[y][x] == 1) {
088                                        image.pixels[y][x] = (float) (tau * Math.tanh(image.pixels[y][x] / tau));
089                                } else {
090                                        image.pixels[y][x] = 0;
091                                }
092                        }
093                }
094        }
095        
096        float firstPassDivisor(FImage image, FImage mask) {
097                double accum = 0;
098                int count = 0;
099                
100                for (int y=0; y<image.height; y++) {
101                        for (int x=0; x<image.width; x++) {
102                                if (mask.pixels[y][x] == 1) {
103                                        double ixy = image.pixels[y][x];
104                                        
105                                        accum += Math.pow(Math.abs(ixy), alpha);
106                                        count++;
107                                }
108                        }
109                }
110                
111                return (float) Math.pow(accum / count, 1.0 / alpha);
112        }
113        
114        float secondPassDivisor(FImage image, FImage mask) {
115                double accum = 0;
116                int count = 0;
117                
118                for (int y=0; y<image.height; y++) {
119                        for (int x=0; x<image.width; x++) {
120                                if (mask.pixels[y][x] == 1) {
121                                        double ixy = image.pixels[y][x];
122                                        
123                                        accum += Math.pow(Math.min(tau, Math.abs(ixy)), alpha);
124                                        count++;
125                                }
126                        }
127                }
128                
129                return (float) Math.pow(accum / count, 1.0 / alpha);
130        }
131}