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.Comparator;
34 import java.util.HashSet;
35 import java.util.List;
36 import java.util.Set;
37
38 import org.openimaj.image.pixel.Pixel;
39 import org.openimaj.math.geometry.shape.Rectangle;
40 import org.openimaj.util.pair.Pair;
41 import org.openimaj.util.set.DisjointSetForest;
42
43
44
45
46
47
48
49
50 public class LineCandidate extends Candidate {
51 protected List<LetterCandidate> letters = new ArrayList<LetterCandidate>();
52 protected List<WordCandidate> words;
53
54 protected LineCandidate() {
55 }
56
57
58
59
60
61
62
63
64
65 protected static List<LineCandidate> extractLines(List<LetterCandidate> letters, SWTTextDetector.Options options) {
66 final List<Pair<LetterCandidate>> pairs = createLetterPairs(letters, options);
67
68 final Set<Set<Pair<LetterCandidate>>> sets = DisjointSetForest.partitionSubsets(pairs,
69 new Comparator<Pair<LetterCandidate>>() {
70 @Override
71 public int compare(Pair<LetterCandidate> pair1, Pair<LetterCandidate> pair2) {
72 final Pixel pair1d = computeDelta(pair1.firstObject(), pair1.secondObject());
73 final Pixel pair2d = computeDelta(pair2.firstObject(), pair2.secondObject());
74
75 if (pair1.firstObject() == pair2.firstObject() || pair1.secondObject() == pair2.secondObject())
76 {
77 final int tn = pair1d.y * pair2d.x - pair1d.x * pair2d.y;
78 final int td = pair1d.x * pair2d.x + pair1d.y * pair2d.y;
79
80 if (tn * 7 < -td * 4 && tn * 7 > td * 4)
81 return 0;
82 } else if (pair1.firstObject() == pair2.secondObject()
83 || pair1.secondObject() == pair2.firstObject())
84 {
85 final int tn = pair1d.y * pair2d.x - pair1d.x * pair2d.y;
86 final int td = pair1d.x * pair2d.x + pair1d.y * pair2d.y;
87
88 if (tn * 7 < td * 4 && tn * 7 > -td * 4)
89 return 0;
90 }
91
92 return 1;
93 }
94
95 private Pixel computeDelta(LetterCandidate firstObject, LetterCandidate secondObject) {
96 final Rectangle frect = firstObject.regularBoundingBox;
97 final Rectangle srect = secondObject.regularBoundingBox;
98
99 final int dx = (int) (frect.x - srect.x + (frect.width - srect.width) / 2);
100 final int dy = (int) (frect.y - srect.y + (frect.height - srect.height) / 2);
101 return new Pixel(dx, dy);
102 }
103 });
104
105 final List<LineCandidate> chains = new ArrayList<LineCandidate>();
106 for (final Set<Pair<LetterCandidate>> line : sets) {
107 final Set<LetterCandidate> lcs = new HashSet<LetterCandidate>();
108
109 for (final Pair<LetterCandidate> p : line) {
110 lcs.add(p.firstObject());
111 lcs.add(p.secondObject());
112 }
113
114 if (lcs.size() < options.minLettersPerLine)
115 continue;
116
117 final LineCandidate lc = new LineCandidate();
118 lc.letters = new ArrayList<LetterCandidate>(lcs);
119
120
121 for (final LetterCandidate letter : lc.letters)
122 letter.line = lc;
123
124 lc.regularBoundingBox = LetterCandidate.computeBounds(lc.letters);
125 lc.words = WordCandidate.extractWords(lc, options);
126
127 chains.add(lc);
128 }
129
130 return chains;
131 }
132
133
134
135
136
137
138
139
140
141
142
143 private static List<Pair<LetterCandidate>> createLetterPairs(List<LetterCandidate> letters,
144 SWTTextDetector.Options options)
145 {
146 final List<Pair<LetterCandidate>> pairs = new ArrayList<Pair<LetterCandidate>>();
147
148 final int numLetters = letters.size();
149
150 for (int j = 0; j < numLetters; j++) {
151 final LetterCandidate l1 = letters.get(j);
152
153 for (int i = j + 1; i < numLetters; i++) {
154 final LetterCandidate l2 = letters.get(i);
155
156
157 if (Math.max(l1.medianStrokeWidth, l2.medianStrokeWidth)
158 / Math.min(l1.medianStrokeWidth, l2.medianStrokeWidth) > options.medianStrokeWidthRatio)
159 continue;
160
161
162 if (Math.max(l1.regularBoundingBox.height, l2.regularBoundingBox.height)
163 / Math.min(l1.regularBoundingBox.height, l2.regularBoundingBox.height) > options.letterHeightRatio)
164 continue;
165
166
167 if (Math.abs(l1.averageBrightness - l2.averageBrightness) > options.intensityThreshold)
168 continue;
169
170
171 final double distance = l1.centroid.x - l2.centroid.x;
172 if (Math.abs(distance) > options.widthMultiplier
173 * Math.max(l1.regularBoundingBox.width, l2.regularBoundingBox.width))
174 continue;
175
176
177
178 final int oy = (int) (Math.min(l1.regularBoundingBox.y +
179 l1.regularBoundingBox.height,
180 l2.regularBoundingBox.y + l2.regularBoundingBox.height) -
181 Math.max(l1.regularBoundingBox.y,
182 l2.regularBoundingBox.y));
183 if (oy * options.intersectRatio < Math.min(l1.regularBoundingBox.height,
184 l2.regularBoundingBox.height))
185 continue;
186
187
188 pairs.add(new Pair<LetterCandidate>(l1, l2));
189 }
190 }
191
192 return pairs;
193 }
194
195
196
197
198
199
200 public List<LetterCandidate> getLetters() {
201 return this.letters;
202 }
203
204
205
206
207
208
209 public List<WordCandidate> getWords() {
210 return words;
211 }
212 }