1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
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
44
45
46
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
73
74
75
76
77
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
108
109
110
111
112
113
114
115
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
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
135 if (variance > options.letterVarianceMean * mean)
136 continue;
137
138 final Rectangle bb = cc.calculateRegularBoundingBox();
139
140
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
146 final float diameter = Math.max(bb.width, bb.height);
147 if (diameter / median > options.maxDiameterStrokeRatio)
148 continue;
149
150
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
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
174
175
176
177
178
179
180
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 }