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  package org.openimaj.image.text.extraction.swt;
31  
32  import java.util.ArrayList;
33  import java.util.List;
34  
35  import org.apache.commons.math.stat.descriptive.DescriptiveStatistics;
36  import org.openimaj.image.FImage;
37  import org.openimaj.image.pixel.ConnectedComponent;
38  import org.openimaj.image.pixel.Pixel;
39  import org.openimaj.image.pixel.PixelSet;
40  import org.openimaj.math.geometry.shape.Rectangle;
41  
42  /**
43   * This class models a candidate textual letter/character from the
44   * {@link SWTTextDetector}.
45   * 
46   * @author Jonathon Hare (jsh2@ecs.soton.ac.uk)
47   */
48  public class LetterCandidate extends Candidate {
49  	protected WordCandidate word;
50  	protected LineCandidate line;
51  	protected PixelSet cc;
52  	protected float averageBrightness;
53  	protected Pixel centroid;
54  	protected float medianStrokeWidth;
55  
56  	protected LetterCandidate(PixelSet cc, float medianStrokeWidth, FImage image) {
57  		this.cc = cc;
58  		this.medianStrokeWidth = medianStrokeWidth;
59  
60  		regularBoundingBox = cc.calculateRegularBoundingBox();
61  
62  		centroid = cc.calculateCentroidPixel();
63  
64  		averageBrightness = 0;
65  		for (final Pixel p : cc.pixels) {
66  			averageBrightness += image.pixels[p.y][p.x];
67  		}
68  		averageBrightness /= cc.pixels.size();
69  	}
70  
71  	/**
72  	 * Compute the regular bounding rectangle of the given list of letter
73  	 * candidates
74  	 * 
75  	 * @param letters
76  	 *            the letter candidates
77  	 * @return the bounds rectangle
78  	 */
79  	public static Rectangle computeBounds(List<LetterCandidate> letters) {
80  		float minx = Float.MAX_VALUE;
81  		float miny = Float.MAX_VALUE;
82  		float maxx = 0;
83  		float maxy = 0;
84  
85  		for (final LetterCandidate letter : letters) {
86  			final Rectangle r = letter.cc.calculateRegularBoundingBox();
87  
88  			if (r.x < minx)
89  				minx = r.x;
90  			if (r.y < miny)
91  				miny = r.y;
92  			if (r.x + r.width > maxx)
93  				maxx = r.x + r.width;
94  			if (r.y + r.height > maxy)
95  				maxy = r.y + r.height;
96  		}
97  
98  		return new Rectangle(minx, miny, maxx - minx, maxy - miny);
99  	}
100 
101 	@Override
102 	public String toString() {
103 		return regularBoundingBox.toString();
104 	}
105 
106 	/**
107 	 * Filter the components to find likely letter candidates.
108 	 * 
109 	 * @param components
110 	 *            the components to filter
111 	 * @param swt
112 	 *            the swt image
113 	 * @param image
114 	 *            the original image
115 	 * @return the potential letter candidates
116 	 */
117 	protected static List<LetterCandidate>
118 			findLetters(List<ConnectedComponent> components, FImage swt, FImage image, SWTTextDetector.Options options)
119 	{
120 		final List<LetterCandidate> output = new ArrayList<LetterCandidate>();
121 
122 		final DescriptiveStatistics stats = new DescriptiveStatistics();
123 		for (final ConnectedComponent cc : components) {
124 			// additional check for small area - speeds processing...
125 			if (cc.pixels.size() < options.minArea)
126 				continue;
127 
128 			computeStats(stats, cc, swt);
129 
130 			final double mean = stats.getMean();
131 			final double variance = stats.getVariance();
132 			final double median = stats.getPercentile(50);
133 
134 			// test variance of stroke width
135 			if (variance > options.letterVarianceMean * mean)
136 				continue;
137 
138 			final Rectangle bb = cc.calculateRegularBoundingBox();
139 
140 			// test aspect ratio
141 			final double aspect = Math.max(bb.width, bb.height) / Math.min(bb.width, bb.height);
142 			if (aspect > options.maxAspectRatio)
143 				continue;
144 
145 			// test diameter
146 			final float diameter = Math.max(bb.width, bb.height);
147 			if (diameter / median > options.maxDiameterStrokeRatio)
148 				continue;
149 
150 			// check occlusion
151 			int overlapping = 0;
152 			for (final ConnectedComponent cc2 : components) {
153 				if (cc2 == cc)
154 					continue;
155 				final Rectangle bb2 = cc2.calculateRegularBoundingBox();
156 				if (bb2.intersectionArea(bb) > 0)
157 					overlapping++;
158 			}
159 			if (overlapping > options.maxNumOverlappingBoxes)
160 				continue;
161 
162 			// check height
163 			if (bb.height < options.minHeight || bb.height > options.maxHeight)
164 				continue;
165 
166 			output.add(new LetterCandidate(cc, (float) median, image));
167 		}
168 
169 		return output;
170 	}
171 
172 	/**
173 	 * Compute the stroke statistics of a component.
174 	 * 
175 	 * @param stats
176 	 *            the stats object (will be reset)
177 	 * @param cc
178 	 *            the component
179 	 * @param swt
180 	 *            the swt image
181 	 */
182 	private static void computeStats(DescriptiveStatistics stats, PixelSet cc, FImage swt) {
183 		stats.clear();
184 		for (final Pixel p : cc.pixels) {
185 			stats.addValue(swt.pixels[p.y][p.x]);
186 		}
187 	}
188 }