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 }