-
Notifications
You must be signed in to change notification settings - Fork 5
/
MeshGrid.java
302 lines (257 loc) · 10.5 KB
/
MeshGrid.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
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
package org.genericsystem.cv.application;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.opencv.core.Core;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.core.MatOfPoint2f;
import org.opencv.core.Point;
import org.opencv.core.Rect;
import org.opencv.core.Scalar;
import org.opencv.core.Size;
import org.opencv.imgproc.Imgproc;
public class MeshGrid {
public Mat image;
public int size; // nombre de polygones par côté
public List<Point> points = new ArrayList<>();
public List<Integer[]> polygons = new ArrayList<>(); // les polygones dans une liste
private Map<Key, Integer[]> mesh = new HashMap<>(); // les mêmes polygones mais en i, j
SuperContourInterpolator interpolator;
public double deltaX, deltaY; // déplacement d'un polygone
private int nbIter; // nombre d'itérations à chaque déplacement
private int iP = 0, jP = 0; // polygone courant
private int minIndex, maxIndex; // indices min et max des polygones
private double rectWidth, rectHeight; // taille d'un polygone après redressement
public MeshGrid(int size, Mat image, SuperContourInterpolator interpolator, double deltaX, double deltaY) {
this.size = size;
this.image = image;
this.interpolator = interpolator;
this.deltaX = deltaX;
this.deltaY = deltaY;
}
public void build() {
nbIter = (int) Math.round(deltaY); // avance d'un pixel à chaque itération
addPolygon(iP, jP);
for (int k = 1; k < size; k++) {
int dir = k % 2 == 0 ? -1 : 1;
horizontalSpiral(k, dir);
verticalSpiral(k, dir);
}
horizontalSpiral(size - 1, size % 2 == 0 ? -1 : 1);
}
public void draw(Scalar color) {
polygons.forEach(p -> drawPolygon(p, color));
}
public Mat dewarp(Size dewarpedSize) {
minIndex = -size / 2 + 1;
maxIndex = size / 2;
rectHeight = dewarpedSize.height / size;
// rectWidth = dewarpedSize.width / size;
Mat dewarpedImage = new Mat(dewarpedSize, CvType.CV_8UC3, new Scalar(255, 255, 255));
for (int iP = minIndex; iP <= maxIndex; iP++) {
for (int jP = minIndex; jP <= maxIndex; jP++) {
Rect subImageRect = subImageRect(iP, jP);
double xInf = subImageRect.x, yInf = subImageRect.y;
if (xInf > 0 && yInf > 0 && xInf + subImageRect.width < image.width() && yInf + subImageRect.height < image.height()) {
Mat homography = dewarpPolygon(mesh.get(new Key(iP, jP)), subImageRect);
double x = (int) Math.floor(dewarpedSize.width / 2) + (jP - 1) * rectHeight;
double y = (int) Math.floor(dewarpedSize.height / 2) + (iP - 1) * rectHeight;
Mat subDewarpedImage = new Mat(dewarpedImage, new Rect(new Point(x, y), new Point(x + rectHeight, y + rectHeight)));
Mat subImage = new Mat(image, subImageRect);
Imgproc.warpPerspective(subImage, subDewarpedImage, homography, new Size(rectHeight, rectHeight), Imgproc.INTER_LINEAR, Core.BORDER_REPLICATE, Scalar.all(0));
}
}
}
return dewarpedImage;
}
private Rect subImageRect(int iP, int jP) {
Integer[] polygon = mesh.get(new Key(iP, jP));
Point warpedTopLeft = points.get(polygon[0]);
Point warpedTopRight = points.get(polygon[1]);
Point warpedBottomRight = points.get(polygon[2]);
Point warpedBottomLeft = points.get(polygon[3]);
double xMin, xMax, yMin, yMax;
xMin = Math.min(warpedTopLeft.x, warpedBottomLeft.x);
xMax = Math.max(warpedBottomRight.x, warpedTopRight.x);
yMin = Math.min(warpedTopRight.y, warpedTopLeft.y);
yMax = Math.max(warpedBottomLeft.y, warpedBottomRight.y);
return new Rect(new Point(xMin, yMin), new Point(xMax, yMax));
}
private Mat dewarpPolygon(Integer[] polygon, Rect subImageRect) {
Point warpedTopLeft = changeOrigin(subImageRect, points.get(polygon[0]));
Point warpedTopRight = changeOrigin(subImageRect, points.get(polygon[1]));
Point warpedBottomRight = changeOrigin(subImageRect, points.get(polygon[2]));
Point warpedBottomLeft = changeOrigin(subImageRect, points.get(polygon[3]));
Point dewarpedTopLeft = new Point(0, 0);
Point dewarpedTopRight = new Point(rectHeight, 0);
Point dewarpedBottomRight = new Point(rectHeight, rectHeight);
Point dewarpedBottomLeft = new Point(0, rectHeight);
return Imgproc.getPerspectiveTransform(new MatOfPoint2f(warpedTopLeft, warpedTopRight, warpedBottomRight, warpedBottomLeft), new MatOfPoint2f(dewarpedTopLeft, dewarpedTopRight, dewarpedBottomRight, dewarpedBottomLeft));
}
private Point changeOrigin(Rect subImageRect, Point point) {
return new Point(point.x - subImageRect.x, point.y - subImageRect.y);
}
private void drawPolygon(Integer[] polygon, Scalar color) {
Point topLeft = points.get(polygon[0]);
Point topRight = points.get(polygon[1]);
Point bottomRight = points.get(polygon[2]);
Point bottomLeft = points.get(polygon[3]);
Imgproc.line(image, topLeft, topRight, color);
Imgproc.line(image, topRight, bottomRight, color);
Imgproc.line(image, bottomRight, bottomLeft, color);
Imgproc.line(image, bottomLeft, topLeft, color);
}
private Integer[] getPolygon(int i, int j) {
return mesh.get(new Key(i, j));
}
private void horizontalSpiral(int n, int dir) { // avance horizontale de n polygones dans la spirale, vers la droite (dir = +1) ou la gauche (dir = -1)
for (int k = 1; k <= n; k++) {
jP += dir;
addPolygon(iP, jP);
}
}
private void verticalSpiral(int n, int dir) { // avance verticale de n polygones dans la spirale, vers le haut (dir = -1) ou le bas (dir = +1);
for (int k = 1; k <= n; k++) {
iP += dir;
addPolygon(iP, jP);
}
}
private void addPolygon(int i, int j) { // ajoute un polygone compte tenu des polygones voisins
// les polygones au dessus, à droite, en dessous et à gauche
Integer[] abovePolygon = getPolygon(i - 1, j);
Integer[] rightPolygon = getPolygon(i, j + 1);
Integer[] belowPolygon = getPolygon(i + 1, j);
Integer[] leftPolygon = getPolygon(i, j - 1);
// les points du polygone à terminer
int topLeft, topRight, bottomRight, bottomLeft;
if (abovePolygon != null) { // si le polygone du dessus existe
topLeft = abovePolygon[3];
topRight = abovePolygon[2];
if (leftPolygon != null) { // si le polygone de gauche existe aussi
bottomLeft = leftPolygon[2];
bottomRight = intersect(bottomLeft, topRight);
} else if (rightPolygon != null) { // sinon si le polygone de droite existe aussi
bottomRight = rightPolygon[3];
bottomLeft = intersect(bottomRight, topLeft);
} else { // s'il n'y a que le polygone du dessus
bottomLeft = verticalNext(topLeft);
bottomRight = verticalNext(topRight);
}
} else if (rightPolygon != null) { // si le polygone de droite existe mais pas celui du dessus
topRight = rightPolygon[0];
bottomRight = rightPolygon[3];
if (belowPolygon != null) { // si le polygone du dessous existe aussi
bottomLeft = belowPolygon[0];
topLeft = intersect(topRight, bottomLeft);
} else { // s'il n'y a que le polygone de droite
topLeft = horizontalPrevious(topRight);
bottomLeft = horizontalPrevious(bottomRight);
}
} else if (belowPolygon != null) { // si le polygone du dessous existe
bottomLeft = belowPolygon[0];
bottomRight = belowPolygon[1];
if (leftPolygon != null) { // si le polygone de gauche existe aussi
topLeft = leftPolygon[1];
topRight = intersect(topLeft, bottomRight);
} else { // s'il n'y a que le polygone du dessous
topLeft = verticalPrevious(bottomLeft);
topRight = verticalPrevious(bottomRight);
}
} else if (leftPolygon != null) { // s'il n'y a que le polygone de gauche
topLeft = leftPolygon[1];
bottomLeft = leftPolygon[2];
topRight = horizontalNext(topLeft);
bottomRight = horizontalNext(bottomLeft);
} else { // s'il n'y a aucun autre polygone, c'est le premier
bottomRight = addPoint(new Point(image.size().width / 2, image.size().height / 2));
topRight = verticalPrevious(bottomRight);
bottomLeft = horizontalPrevious(bottomRight);
topLeft = intersect(topRight, bottomLeft);
}
Integer[] polygon = new Integer[] { topLeft, topRight, bottomRight, bottomLeft };
mesh.put(new Key(i, j), polygon);
polygons.add(polygon);
}
private int intersect(int hPt, int vPt) { // intersection de la ligne horizontale partant de hPoint avec la ligne verticale partant de vPoint
Point hPoint = points.get(hPt);
Point vPoint = points.get(vPt);
Point intersection = null;
double xDiff = xDiff(hPoint, vPoint);
double yDiff = yDiff(vPoint, hPoint);
for (int i = 1; i < 1000; i++) {
xDiff = xDiff(hPoint, vPoint);
yDiff = yDiff(vPoint, hPoint);
hPoint = horizontalMove(hPoint, xDiff);
vPoint = verticalMove(vPoint, yDiff);
if (Math.abs(xDiff) < 0.5 && Math.abs(yDiff) < 0.5) {
intersection = new Point(0.5 * (hPoint.x + vPoint.x), 0.5 * (hPoint.y + vPoint.y));
break;
}
}
return addPoint(intersection);
}
private double xDiff(Point pt1, Point pt2) {
return pt2.x - pt1.x;
}
private double yDiff(Point pt1, Point pt2) {
return pt2.y - pt1.y;
}
private int verticalNext(int point) {
return addPoint(verticalMove(points.get(point), deltaY));
}
private int verticalPrevious(int point) {
return addPoint(verticalMove(points.get(point), -deltaY));
}
private int horizontalNext(int point) {
return addPoint(horizontalMove(points.get(point), deltaX));
}
private int horizontalPrevious(int point) {
return addPoint(horizontalMove(points.get(point), -deltaX));
}
private int addPoint(Point point) { // ajoute un point et renvoie son index
points.add(point);
return points.size() - 1;
}
private Point verticalMove(Point startingPoint, double deltaY) {
double dY = deltaY / nbIter; // proche de 1 pixel
double x = startingPoint.x, y = startingPoint.y;
for (int i = 0; i < nbIter; i++) {
double[] angles = interpolator.interpolate(x, y);
double dX = dY / Math.tan(angles[1]);
x += dX;
y += dY;
}
return new Point(x, y);
}
private Point horizontalMove(Point startingPoint, double deltaX) {
double dX = deltaX / nbIter; // proche de 1 pixel
double x = startingPoint.x, y = startingPoint.y;
for (int i = 0; i < nbIter; i++) {
double[] angles = interpolator.interpolate(x, y);
double dY = Math.tan(angles[0]) * dX;
x += dX;
y += dY;
}
return new Point(x, y);
}
private class Key { // tableau multidimensionnel
public int i, j; // i = numero de ligne, j = numero de colonne
public Key(int i, int j) {
this.i = i;
this.j = j;
}
@Override
public int hashCode() {
return 31 * i + 7 * j;
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof Key))
return false;
Key other = (Key) obj;
return i == other.i && j == other.j;
}
}
}