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 */ 030/** 031 * 032 */ 033package org.openimaj.image.processing.mask; 034 035import org.openimaj.image.FImage; 036import org.openimaj.image.MBFImage; 037import org.openimaj.math.geometry.line.Line2d; 038import org.openimaj.math.geometry.point.Point2d; 039import org.openimaj.math.geometry.point.Point2dImpl; 040 041/** 042 * Generator for grayscale mattes. 043 * 044 * @author David Dupplaw (dpd@ecs.soton.ac.uk) 045 * @created 31 Jan 2013 046 * @version $Author$, $Revision$, $Date$ 047 */ 048public class MatteGenerator 049{ 050 /** 051 * An enumerator for various matte algorithms. 052 * 053 * @author David Dupplaw (dpd@ecs.soton.ac.uk) 054 * @created 31 Jan 2013 055 * @version $Author$, $Revision$, $Date$ 056 */ 057 public enum MatteType 058 { 059 /** 060 * A basic linear vertical gradient. The argument should be boolean which, if TRUE, 061 * determines that the gradient will be black at the top and white at the bottom. 062 * If non-existent, or FALSE, the gradient will be white at the top and black at the bottom. 063 */ 064 LINEAR_VERTICAL_GRADIENT 065 { 066 @Override 067 public void generateMatte( final FImage img, final Object... args ) 068 { 069 boolean whiteAtTop = false; 070 if( args.length == 0 || ((args[0] instanceof Boolean) && !((Boolean)args[0]).booleanValue()) ) 071 whiteAtTop = true; 072 073 final double g = (whiteAtTop?1:0); 074 final double scalar = (whiteAtTop?-1d/img.getHeight():1d/img.getHeight()); 075 076 for( int y = 0; y < img.getHeight(); y++ ) 077 for( int x = 0; x < img.getWidth(); x++ ) 078 img.pixels[y][x] = (float)(g + y*scalar); 079 } 080 }, 081 /** 082 * A basic linear horizontal gradient. The argument should be boolean which, if TRUE, 083 * determines that the gradient will be black at the left and white at the right. 084 * If non-existent, or FALSE, the gradient will be white at the left and black at the right. 085 */ 086 LINEAR_HORIZONTAL_GRADIENT 087 { 088 @Override 089 public void generateMatte( final FImage img, final Object... args ) 090 { 091 boolean whiteAtLeft = false; 092 if( args.length == 0 || ((args[0] instanceof Boolean) && !((Boolean)args[0]).booleanValue()) ) 093 whiteAtLeft = true; 094 095 final double g = (whiteAtLeft?1:0); 096 final double scalar = (whiteAtLeft?-1d/img.getWidth():1d/img.getWidth()); 097 098 for( int y = 0; y < img.getHeight(); y++ ) 099 for( int x = 0; x < img.getWidth(); x++ ) 100 img.pixels[y][x] = (float)(g + x*scalar); 101 } 102 }, 103 /** 104 * Basic radial gradient centred on the middle of the matte. The argument should be 105 * boolean which, if TRUE, determines whether the gradient will be black or white in 106 * the middle (TRUE for white). 107 */ 108 RADIAL_GRADIENT 109 { 110 @Override 111 public void generateMatte( final FImage img, final Object... args ) 112 { 113 boolean whiteInCentre = false; 114 if( args.length > 0 && args[0] instanceof Boolean && ((Boolean)args[0]).booleanValue()) 115 whiteInCentre = true; 116 117 // Centre coordinates 118 final int cx = img.getWidth() /2; 119 final int cy = img.getHeight() /2; 120 121 // Outside edge of radial 122 final int maxDist = Math.max( 123 Math.max( img.getWidth() - cx, cx ), 124 Math.max( img.getHeight() - cy, cy ) 125 ); 126 final double scale = maxDist; 127 128 for( int y = 0; y < img.getHeight(); y++ ) 129 for( int x = 0; x < img.getWidth(); x++ ) 130 img.pixels[y][x] = whiteInCentre? 131 1f-(float)this.distanceFromCentre( cx, cy, x, y, scale ): 132 (float)this.distanceFromCentre( cx, cy, x, y, scale ); 133 } 134 135 /** 136 * Calculates the distance from the centre, scaled to the maximum distance 137 * with clipping bounds 0 <= d <= 1. 138 * 139 * @param cx Centre x-coordinate 140 * @param cy Centre y-coordinate 141 * @param x X position of point to find distance 142 * @param y Y position of point to find distance 143 * @param scale maximum distance 144 * @return Scaled, clipped distance measure 145 */ 146 private double distanceFromCentre( final int cx, final int cy, final int x, final int y, 147 final double scale ) 148 { 149 final double b = cx - x; 150 final double c = cy - y; 151 double v = Math.abs( Math.sqrt( b*b + c*c ) )/scale; 152 if( v > 1 ) v = 1; 153 if( v < 0 ) v = 0; 154 return v; 155 } 156 }, 157 /** 158 * Generates linear gradients that can be angled to any angle. The angle of the 159 * gradient is given in the first argument as a double. The position of the point 160 * at which the gradient is rotated, is given in the next two arguments, also as 161 * doubles. If any are missing, they are considered 0. To invert the gradient, 162 * invert the resulting FImage. 163 */ 164 ANGLED_LINEAR_GRADIENT 165 { 166 167 @Override 168 public void generateMatte( final FImage img, final Object... args ) 169 { 170 // Angle and position of gradient axis 171 double angle = 0; 172 double lx = 0; 173 double ly = 0; 174 175 // Get any arguments 176 if( args.length > 0 && args[0] instanceof Double ) 177 angle = ((Double)args[0]).doubleValue(); 178 if( args.length > 1 && args[1] instanceof Double ) 179 lx = ((Double)args[1]).doubleValue(); 180 if( args.length > 2 && args[2] instanceof Double ) 181 ly = ((Double)args[2]).doubleValue(); 182 183 // Outside edge of radial 184 final double scalar = Math.max( 185 Math.max( img.getWidth() - lx, lx ), 186 Math.max( img.getHeight() - ly, ly ) 187 ); 188 189 for( int y = 0; y < img.getHeight(); y++ ) 190 for( int x = 0; x < img.getWidth(); x++ ) 191 img.pixels[y][x] = (float)this.distanceFromAxis( lx, ly, angle, x, y, scalar ); 192 } 193 194 /** 195 * calculate the distance from the gradient axis. 196 * @param lx A point on the gradient axis - x coordinate 197 * @param ly A point on the gradient axis - y coordinate 198 * @param angle The angle of the axis 199 * @param x The x position to find the distance 200 * @param y The y position to find the distance 201 * @param scalar The scalar for the final vector 202 * @return 203 */ 204 private double distanceFromAxis( final double lx, final double ly, final double angle, 205 final double x, final double y, final double scalar ) 206 { 207 // See http://en.wikipedia.org/wiki/Distance_from_a_point_to_a_line 208 final Line2d line = Line2d.lineFromRotation( (int)lx, (int)ly, angle, 1 ); 209 final Point2d A = line.begin; 210 final Point2d B = line.end; 211 final Point2dImpl P = new Point2dImpl( (float)x, (float)y ); 212 final double normalLength = Math.hypot(B.getX() - A.getX(), B.getY() - A.getY()); 213 double grad = Math.abs((P.x - A.getX()) * (B.getY() - A.getY()) - (P.y - A.getY()) * 214 (B.getX() - A.getX())) / normalLength / scalar; 215 if( grad < 0 ) grad = 0; 216 if( grad > 1 ) grad = 1; 217 return grad; 218 } 219 } 220 ; 221 222 /** 223 * Generate the matte into the given image. 224 * @param img The image to generate the matte into 225 * @param args The arguments for the matte generator 226 */ 227 public abstract void generateMatte( FImage img, Object... args ); 228 } 229 230 /** 231 * Generate a matte into the given image on the given band. 232 * 233 * @param image The image to write the matte into 234 * @param band The band of the image to write the matte into 235 * @param type The type of the matte to draw 236 * @param args The arguments for the matte generator 237 * @return The input image (for chaining) 238 */ 239 public static FImage generateMatte( final MBFImage image, final int band, 240 final MatteType type, final Object... args ) 241 { 242 return MatteGenerator.generateMatte( image.getBand( band ), type, args ); 243 } 244 245 /** 246 * Generate a matte into the given {@link FImage}. 247 * 248 * @param image The image to write the matte into 249 * @param type The type of the matte to draw 250 * @param args The arguments for the matte generator 251 * @return The input image (for chaining) 252 */ 253 public static FImage generateMatte( final FImage image, final MatteType type, final Object... args ) 254 { 255 type.generateMatte( image, args ); 256 return image; 257 } 258}