View Javadoc

1   /**
2    * Copyright (c) 2011, The University of Southampton and the individual contributors.
3    * All rights reserved.
4    *
5    * Redistribution and use in source and binary forms, with or without modification,
6    * are permitted provided that the following conditions are met:
7    *
8    *   * 	Redistributions of source code must retain the above copyright notice,
9    * 	this list of conditions and the following disclaimer.
10   *
11   *   *	Redistributions in binary form must reproduce the above copyright notice,
12   * 	this list of conditions and the following disclaimer in the documentation
13   * 	and/or other materials provided with the distribution.
14   *
15   *   *	Neither the name of the University of Southampton nor the names of its
16   * 	contributors may be used to endorse or promote products derived from this
17   * 	software without specific prior written permission.
18   *
19   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
20   * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21   * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22   * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
23   * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24   * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25   * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
26   * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27   * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28   * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29   */
30  /**
31   *
32   */
33  package org.openimaj.image.processing.mask;
34  
35  import org.openimaj.image.FImage;
36  import org.openimaj.image.MBFImage;
37  import org.openimaj.math.geometry.line.Line2d;
38  import org.openimaj.math.geometry.point.Point2d;
39  import org.openimaj.math.geometry.point.Point2dImpl;
40  
41  /**
42   *	Generator for grayscale mattes.
43   *
44   *	@author David Dupplaw (dpd@ecs.soton.ac.uk)
45   *  @created 31 Jan 2013
46   *	@version $Author$, $Revision$, $Date$
47   */
48  public class MatteGenerator
49  {
50  	/**
51  	 *	An enumerator for various matte algorithms.
52  	 *
53  	 *	@author David Dupplaw (dpd@ecs.soton.ac.uk)
54  	 *  @created 31 Jan 2013
55  	 *	@version $Author$, $Revision$, $Date$
56  	 */
57  	public enum MatteType
58  	{
59  		/**
60  		 *	A basic linear vertical gradient. The argument should be boolean which, if TRUE,
61  		 *	determines that the gradient will be black at the top and white at the bottom.
62  		 *	If non-existent, or FALSE, the gradient will be white at the top and black at the bottom.
63  		 */
64  		LINEAR_VERTICAL_GRADIENT
65  		{
66              @Override
67              public void generateMatte( final FImage img, final Object... args )
68              {
69              	boolean whiteAtTop = false;
70              	if( args.length == 0 || ((args[0] instanceof Boolean) && !((Boolean)args[0]).booleanValue()) )
71              			whiteAtTop = true;
72  
73              	final double g = (whiteAtTop?1:0);
74              	final double scalar = (whiteAtTop?-1d/img.getHeight():1d/img.getHeight());
75  
76              	for( int y = 0; y < img.getHeight(); y++ )
77              		for( int x = 0; x < img.getWidth(); x++ )
78              			img.pixels[y][x] = (float)(g + y*scalar);
79              }
80  		},
81  		/**
82  		 *	A basic linear horizontal gradient. The argument should be boolean which, if TRUE,
83  		 *	determines that the gradient will be black at the left and white at the right.
84  		 *	If non-existent, or FALSE, the gradient will be white at the left and black at the right.
85  		 */
86  		LINEAR_HORIZONTAL_GRADIENT
87  		{
88              @Override
89              public void generateMatte( final FImage img, final Object... args )
90              {
91              	boolean whiteAtLeft = false;
92              	if( args.length == 0 || ((args[0] instanceof Boolean) && !((Boolean)args[0]).booleanValue()) )
93              			whiteAtLeft = true;
94  
95              	final double g = (whiteAtLeft?1:0);
96              	final double scalar = (whiteAtLeft?-1d/img.getWidth():1d/img.getWidth());
97  
98              	for( int y = 0; y < img.getHeight(); y++ )
99              		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 }