-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathSearchCharacter.java
198 lines (168 loc) · 6.68 KB
/
SearchCharacter.java
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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
package com.uddernetworks.newocr.character;
import com.uddernetworks.newocr.utils.IntPair;
import com.uddernetworks.newocr.utils.SegmentationUtils;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
/**
* An object meant to store characters directly scanned from an image and that is being searched for/mutated.
*
* @author Adam Yarris
* @version 2.0.0
* @since April 25, 2019
*/
public class SearchCharacter extends CoordinateCharacter {
private List<IntPair> segments = new LinkedList<>();
private double[] segmentPercentages = new double[8 + 9]; // Percentage <= 1 // First 8 are the normal ones, last 9 are for the grid created
private Map<String, Double> trainingMeta = new HashMap<>();
private double centerOffset = 0;
/**
* Creates a SearchCharacter from a list of coordinates used by the character.
*
* @param coordinates Coordinates used by the character
*/
public SearchCharacter(List<IntPair> coordinates) {
this(coordinates, 0, 0);
}
/**
* Creates a SearchCharacter from a list of coordinates used by the character.
*
* @param coordinates Coordinates used by the character
* @param xOffset The X offset of the coordinates
* @param yOffset The Y offset of the coordinates
*/
public SearchCharacter(List<IntPair> coordinates, int xOffset, int yOffset) {
this.coordinates = coordinates;
int maxX = Integer.MIN_VALUE, minX = Integer.MAX_VALUE;
int maxY = Integer.MIN_VALUE, minY = Integer.MAX_VALUE;
for (var pair : coordinates) {
int key = pair.getKey(), value = pair.getValue();
if (key > maxX) {
maxX = key;
}
if (key < minX) {
minX = key;
}
if (value > maxY) {
maxY = value;
}
if (value < minY) {
minY = value;
}
}
this.x = minX + xOffset;
this.y = minY + yOffset;
this.width = maxX - minX + 1;
this.height = maxY - minY + 1;
values = new boolean[this.height][];
for (int i = 0; i < values.length; i++) {
values[i] = new boolean[width];
}
coordinates.forEach(pair -> values[pair.getValue() - this.y + yOffset][pair.getKey() - this.x + xOffset] = true);
}
/**
* Creates sections and invokes {@link #addSegment(IntPair)} for each one. This is vital for the use of this object.
*/
public void applySections() {
AtomicInteger index = new AtomicInteger();
SegmentationUtils.getHorizontalHalf(this.values)
.flatMap(SegmentationUtils::getVerticalHalf)
.forEach(section -> SegmentationUtils.getDiagonal(section, index.get() == 1 || index.getAndIncrement() == 2).forEach(this::addSegment));
SegmentationUtils.getHorizontalThird(this.values).forEach(values ->
SegmentationUtils.getVerticalThird(values).forEach(this::addSegment));
}
/**
* Performs calculations for the sections added by {@link #addSegment(IntPair)}, getting their <= 1 percentages
* accessible from {@link #getSegmentPercentages()}. This must be invoked after {@link #applySections()}.
*/
public void analyzeSlices() {
AtomicInteger temp = new AtomicInteger();
this.segmentPercentages = new double[segments.size()];
this.segments.forEach((entry) -> {
double amountTrue = entry.getKey();
double total = entry.getValue();
double val = total == 0 ? 1 : amountTrue / total;
this.segmentPercentages[temp.getAndIncrement()] = val;
});
}
/**
* Adds a data segment to be calculated in the future. The segments may be fetched via {@link #getSegments()}.
*
* @param entry The data segment in the format of [total black, size of segment]
*/
public void addSegment(IntPair entry) {
this.segments.add(entry);
}
/**
* Gets the raw segments added via {@link #addSegment(IntPair)} where the Entry format is
* [total black, size of segment].
*
* @return The raw segments
*/
public List<IntPair> getSegments() {
return segments;
}
/**
* Gets the raw segment percentages all <= 1. This will return an empty array until {@link #applySections()} and
* {@link #analyzeSlices()} have been invoked.
*
* @return The raw array of segment percentages with a length of 17
*/
public double[] getSegmentPercentages() {
return this.segmentPercentages;
}
/**
* Gets the training meta with the given name. This contains data such as separation of the dots of an i, data on
* the holes of a %, etc.
*
* @param name The name of the training data
* @return The value of the training data
*/
public OptionalDouble getTrainingMeta(String name) {
return this.trainingMeta.containsKey(name) ? OptionalDouble.of(this.trainingMeta.get(name)) : OptionalDouble.empty();
}
/**
* Sets the training data with a given name.
*
* @param name The name of the data
* @param data The data to set
*/
public void setTrainingMeta(String name, double data) {
this.trainingMeta.put(name, data);
}
/**
* Gets the amount away a character is from the center of the line. This isn't useful for detecting single
* characters.
*
* @return The offset of the character
*/
public double getCenterOffset() {
return centerOffset;
}
/**
* Sets the amount away a character is from the center of the line. This isn't useful for detecting single
* characters.
*
* @param centerOffset The offset of the character to set
*/
public void setCenterOffset(double centerOffset) {
this.centerOffset = centerOffset;
}
@Override
public int hashCode() {
return Objects.hash(this.letter, this.x, this.y, this.width, this.height, this.segments, this.segmentPercentages, this.trainingMeta, this.centerOffset);
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof SearchCharacter)) return false;
var character = (SearchCharacter) obj;
return character.letter == this.letter
&& character.x == this.x
&& character.y == this.y
&& character.width == this.width
&& character.height == this.height
&& character.segments == this.segments
&& character.segmentPercentages == this.segmentPercentages
&& character.trainingMeta == this.trainingMeta
&& character.centerOffset == this.centerOffset;
}
}