forked from qayim/PutraFaceID
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathCC_Controller.java
More file actions
346 lines (318 loc) · 9.58 KB
/
CC_Controller.java
File metadata and controls
346 lines (318 loc) · 9.58 KB
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
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
package application;
import java.io.ByteArrayInputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import org.opencv.calib3d.Calib3d;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.core.MatOfByte;
import org.opencv.core.MatOfPoint2f;
import org.opencv.core.MatOfPoint3f;
import org.opencv.core.Point3;
import org.opencv.core.Size;
import org.opencv.core.TermCriteria;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;
import javafx.application.Platform;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.TextField;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import org.opencv.videoio.VideoCapture;
/**
* The controller associated to the only view of our application. The
* application logic is implemented here. It handles the button for
* starting/stopping the camera, the acquired video stream, the relative
* controls and the overall calibration process.
*
* @author <a href="mailto:luigi.derussis@polito.it">Luigi De Russis</a>
* @since 2013-11-20
*
*/
public class CC_Controller {
// FXML buttons
@FXML
private Button cameraButton;
@FXML
private Button applyButton;
@FXML
private Button snapshotButton;
// the FXML area for showing the current frame (before calibration)
@FXML
private ImageView originalFrame;
// the FXML area for showing the current frame (after calibration)
@FXML
private ImageView calibratedFrame;
// info related to the calibration process
@FXML
private TextField numBoards;
@FXML
private TextField numHorCorners;
@FXML
private TextField numVertCorners;
// a timer for acquiring the video stream
private Timer timer;
// the OpenCV object that performs the video capture
private VideoCapture capture;
// a flag to change the button behavior
private boolean cameraActive;
// the saved chessboard image
private Mat savedImage;
// the calibrated camera frame
private Image undistoredImage,CamStream;
// various variables needed for the calibration
private List<Mat> imagePoints;
private List<Mat> objectPoints;
private MatOfPoint3f obj;
private MatOfPoint2f imageCorners;
private int boardsNumber;
private int numCornersHor;
private int numCornersVer;
private int successes;
private Mat intrinsic;
private Mat distCoeffs;
private boolean isCalibrated;
/**
* Init all the (global) variables needed in the controller
*/
protected void init()
{
this.capture = new VideoCapture();
this.cameraActive = false;
this.obj = new MatOfPoint3f();
this.imageCorners = new MatOfPoint2f();
this.savedImage = new Mat();
this.undistoredImage = null;
this.imagePoints = new ArrayList<>();
this.objectPoints = new ArrayList<>();
this.intrinsic = new Mat(3, 3, CvType.CV_32FC1);
this.distCoeffs = new Mat();
this.successes = 0;
this.isCalibrated = false;
}
/**
* Store all the chessboard properties, update the UI and prepare other
* needed variables
*/
@FXML
protected void updateSettings()
{
this.boardsNumber = Integer.parseInt(this.numBoards.getText());
this.numCornersHor = Integer.parseInt(this.numHorCorners.getText());
this.numCornersVer = Integer.parseInt(this.numVertCorners.getText());
int numSquares = this.numCornersHor * this.numCornersVer;
for (int j = 0; j < numSquares; j++)
obj.push_back(new MatOfPoint3f(new Point3(j / this.numCornersHor, j % this.numCornersVer, 0.0f)));
this.cameraButton.setDisable(false);
}
/**
* The action triggered by pushing the button on the GUI
*/
@FXML
protected void startCamera()
{
if (!this.cameraActive)
{
// start the video capture
this.capture.open(0);
// is the video stream available?
if (this.capture.isOpened())
{
this.cameraActive = true;
// grab a frame every 33 ms (30 frames/sec)
TimerTask frameGrabber = new TimerTask() {
@Override
public void run()
{
CamStream=grabFrame();
// show the original frames
Platform.runLater(new Runnable() {
@Override
public void run() {
originalFrame.setImage(CamStream);
// set fixed width
originalFrame.setFitWidth(380);
// preserve image ratio
originalFrame.setPreserveRatio(true);
// show the original frames
calibratedFrame.setImage(undistoredImage);
// set fixed width
calibratedFrame.setFitWidth(380);
// preserve image ratio
calibratedFrame.setPreserveRatio(true);
}
});
}
};
this.timer = new Timer();
this.timer.schedule(frameGrabber, 0, 33);
// update the button content
this.cameraButton.setText("Stop Camera");
}
else
{
// log the error
System.err.println("Impossible to open the camera connection...");
}
}
else
{
// the camera is not active at this point
this.cameraActive = false;
// update again the button content
this.cameraButton.setText("Start Camera");
// stop the timer
if (this.timer != null)
{
this.timer.cancel();
this.timer = null;
}
// release the camera
this.capture.release();
// clean the image areas
originalFrame.setImage(null);
calibratedFrame.setImage(null);
}
}
/**
* Get a frame from the opened video stream (if any)
*
* @return the {@link Image} to show
*/
private Image grabFrame()
{
// init everything
Image imageToShow = null;
Mat frame = new Mat();
// check if the capture is open
if (this.capture.isOpened())
{
try
{
// read the current frame
this.capture.read(frame);
// if the frame is not empty, process it
if (!frame.empty())
{
// show the chessboard pattern
this.findAndDrawPoints(frame);
if (this.isCalibrated)
{
// prepare the undistored image
Mat undistored = new Mat();
Calib3d.undistort(frame, undistored, intrinsic, distCoeffs);
undistoredImage = mat2Image(undistored);
}
// convert the Mat object (OpenCV) to Image (JavaFX)
imageToShow = mat2Image(frame);
}
}
catch (Exception e)
{
// log the (full) error
System.err.print("ERROR");
e.printStackTrace();
}
}
return imageToShow;
}
/**
* Take a snapshot to be used for the calibration process
*/
@FXML
protected void takeSnapshot()
{
if (this.successes < this.boardsNumber)
{
// save all the needed values
this.imagePoints.add(imageCorners);
imageCorners = new MatOfPoint2f();
this.objectPoints.add(obj);
this.successes++;
}
// reach the correct number of images needed for the calibration
if (this.successes == this.boardsNumber)
{
this.calibrateCamera();
}
}
/**
* Find and draws the points needed for the calibration on the chessboard
*
* @param frame
* the current frame
* @return the current number of successfully identified chessboards as an
* int
*/
private void findAndDrawPoints(Mat frame)
{
// init
Mat grayImage = new Mat();
// I would perform this operation only before starting the calibration
// process
if (this.successes < this.boardsNumber)
{
// convert the frame in gray scale
Imgproc.cvtColor(frame, grayImage, Imgproc.COLOR_BGR2GRAY);
// the size of the chessboard
Size boardSize = new Size(this.numCornersVer, this.numCornersHor);
// look for the inner chessboard corners
boolean found = Calib3d.findChessboardCorners(grayImage, boardSize, imageCorners,
Calib3d.CALIB_CB_ADAPTIVE_THRESH + Calib3d.CALIB_CB_NORMALIZE_IMAGE + Calib3d.CALIB_CB_FAST_CHECK);
// all the required corners have been found...
if (found)
{
// optimization
TermCriteria term = new TermCriteria(TermCriteria.EPS | TermCriteria.MAX_ITER, 30, 0.1);
Imgproc.cornerSubPix(grayImage, imageCorners, new Size(11, 11), new Size(-1, -1), term);
// save the current frame for further elaborations
grayImage.copyTo(this.savedImage);
// show the chessboard inner corners on screen
Calib3d.drawChessboardCorners(frame, boardSize, imageCorners, found);
// enable the option for taking a snapshot
this.snapshotButton.setDisable(false);
}
else
{
this.snapshotButton.setDisable(true);
}
}
}
/**
* The effective camera calibration, to be performed once in the program
* execution
*/
private void calibrateCamera()
{
// init needed variables according to OpenCV docs
List<Mat> rvecs = new ArrayList<>();
List<Mat> tvecs = new ArrayList<>();
intrinsic.put(0, 0, 1);
intrinsic.put(1, 1, 1);
// calibrate!
Calib3d.calibrateCamera(objectPoints, imagePoints, savedImage.size(), intrinsic, distCoeffs, rvecs, tvecs);
this.isCalibrated = true;
// you cannot take other snapshot, at this point...
this.snapshotButton.setDisable(true);
}
/**
* Convert a Mat object (OpenCV) in the corresponding Image for JavaFX
*
* @param frame
* the {@link Mat} representing the current frame
* @return the {@link Image} to show
*/
private Image mat2Image(Mat frame)
{
// create a temporary buffer
MatOfByte buffer = new MatOfByte();
// encode the frame in the buffer, according to the PNG format
Imgcodecs.imencode(".png", frame, buffer);
// build and return an Image created from the image encoded in the
// buffer
return new Image(new ByteArrayInputStream(buffer.toArray()));
}
}