commit 11/04/2025
This commit is contained in:
@@ -12,7 +12,6 @@ import com.google.zxing.client.j2se.BufferedImageLuminanceSource;
|
||||
import com.google.zxing.common.HybridBinarizer;
|
||||
import javafx.application.Platform;
|
||||
|
||||
import javafx.concurrent.Task;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.control.Slider;
|
||||
import javafx.scene.image.Image;
|
||||
@@ -25,6 +24,7 @@ import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import org.bytedeco.javacv.Frame;
|
||||
import org.bytedeco.javacv.FrameGrabber;
|
||||
import org.bytedeco.javacv.OpenCVFrameGrabber;
|
||||
import org.bytedeco.opencv.global.opencv_core;
|
||||
import org.bytedeco.opencv.global.opencv_imgcodecs;
|
||||
@@ -42,13 +42,15 @@ import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.Semaphore;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
import static Config.SomeCodes.*;
|
||||
import static id.co.gtc.erhacam.Detectors.*;
|
||||
import static org.bytedeco.opencv.global.opencv_core.CV_8UC3;
|
||||
import static org.bytedeco.opencv.global.opencv_core.mean;
|
||||
import static org.bytedeco.opencv.global.opencv_imgproc.*;
|
||||
|
||||
@SuppressWarnings({"unused"})
|
||||
@@ -65,17 +67,14 @@ public class Cameradetail {
|
||||
|
||||
}
|
||||
private final AtomicBoolean Capturing = new AtomicBoolean(false);
|
||||
private final AtomicBoolean TakingPhoto = new AtomicBoolean(false);
|
||||
private final AtomicBoolean IsGrabbingLiveView = new AtomicBoolean(false);
|
||||
private CountDownLatch TakingPhoto = null;
|
||||
private final Semaphore IsGrabbingLiveView = new Semaphore(0);
|
||||
private OpenCVFrameGrabber mGrabber = null;
|
||||
private LiveCamEvent event = null;
|
||||
private @Getter @Setter CameraConfigEnum cameraConfigEnum = CameraConfigEnum.CameraConfigCenter;
|
||||
private @Getter int LiveFPS = 0;
|
||||
|
||||
/**
|
||||
* Get detected QR text from Live View
|
||||
*/
|
||||
private @Getter String qrtext = null;
|
||||
|
||||
|
||||
@FXML
|
||||
private Label cameratitle;
|
||||
@@ -145,12 +144,27 @@ public class Cameradetail {
|
||||
int[] paramjpeg = {opencv_imgcodecs.IMWRITE_JPEG_QUALITY, 100};
|
||||
int[] parampng = {opencv_imgcodecs.IMWRITE_PNG_COMPRESSION, 0};
|
||||
|
||||
private boolean use_qr = false;
|
||||
private boolean use_face = false;
|
||||
|
||||
|
||||
private void setSliderValue(Slider sld, CameraProperty prop, double value){
|
||||
|
||||
if (sld!=null){
|
||||
sld.setMin(prop.Min);
|
||||
sld.setMax(prop.Max);
|
||||
sld.setValue(value);
|
||||
if (prop!=null){
|
||||
if (Platform.isFxApplicationThread()){
|
||||
sld.setMin(prop.Min);
|
||||
sld.setMax(prop.Max);
|
||||
sld.setValue(value);
|
||||
} else {
|
||||
Platform.runLater(()->{
|
||||
sld.setMin(prop.Min);
|
||||
sld.setMax(prop.Max);
|
||||
sld.setValue(value);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -664,42 +678,49 @@ public class Cameradetail {
|
||||
if (!ValidDirectory(directory)) directory = currentDirectory;
|
||||
|
||||
if (mGrabber!=null){
|
||||
while(IsGrabbingLiveView.get()){
|
||||
Wait(10);
|
||||
try{
|
||||
// wait if the camera is still capturing
|
||||
IsGrabbingLiveView.acquire();
|
||||
TakingPhoto = new CountDownLatch(1);
|
||||
|
||||
|
||||
if (!BestMat.empty()){
|
||||
|
||||
// save BestMat at quality 9 PNG
|
||||
String filename = GetFullQualityPhotoPath(directory, prefix);
|
||||
|
||||
if (opencv_imgcodecs.imwrite(filename, BestMat, parampng)){
|
||||
result.setFullres(filename);
|
||||
} else System.out.println("TakePhoto failed, Unable to Save FullQUality Photo for camera "+cameratitle.getText());
|
||||
|
||||
String xx = CropBestMat(directory, prefix, BestMatROI);
|
||||
if (ValidFile(xx)) {
|
||||
result.setFullcrop(xx);
|
||||
result.setBestROI(new Rect(BestMatROI.x(), BestMatROI.y(), BestMatROI.width(), BestMatROI.height()));
|
||||
}
|
||||
|
||||
|
||||
// save ReducedMat at 100% JPEG
|
||||
String reducedfilename = GetReducedPhotoPath(directory, prefix);
|
||||
opencv_imgproc.resize(BestMat, ReducedMat, ReducedSize);
|
||||
if (!opencv_imgcodecs.imwrite(reducedfilename, ReducedMat, paramjpeg)){
|
||||
System.out.println("TakePhoto failed, Unable to Save Reduced Photo for camera "+cameratitle.getText());
|
||||
} else result.setCompressedfile(reducedfilename);
|
||||
|
||||
String xy = CropReducedMat(directory, prefix, ReducedMatROI);
|
||||
if (ValidFile(xy)){
|
||||
result.setCompressedcrop(xy);
|
||||
result.setReducedROI(new Rect(ReducedMatROI.x(), ReducedMatROI.y(), ReducedMatROI.width(), ReducedMatROI.height()));
|
||||
}
|
||||
|
||||
} else raise_log("TakePhoto failed, Live View is Empty");
|
||||
} catch (InterruptedException e){
|
||||
System.out.println("TakePhoto IsGrabbingLiveView interrupted");
|
||||
}
|
||||
TakingPhoto.set(true);
|
||||
if (!BestMat.empty()){
|
||||
|
||||
// save BestMat at quality 9 PNG
|
||||
String filename = GetFullQualityPhotoPath(directory, prefix);
|
||||
TakingPhoto.countDown();
|
||||
|
||||
if (opencv_imgcodecs.imwrite(filename, BestMat, parampng)){
|
||||
result.setFullres(filename);
|
||||
} else System.out.println("TakePhoto failed, Unable to Save FullQUality Photo for camera "+cameratitle.getText());
|
||||
|
||||
String xx = CropBestMat(directory, prefix, BestMatROI);
|
||||
if (ValidFile(xx)) {
|
||||
result.setFullcrop(xx);
|
||||
result.setBestROI(new Rect(BestMatROI.x(), BestMatROI.y(), BestMatROI.width(), BestMatROI.height()));
|
||||
}
|
||||
|
||||
|
||||
// save ReducedMat at 100% JPEG
|
||||
String reducedfilename = GetReducedPhotoPath(directory, prefix);
|
||||
opencv_imgproc.resize(BestMat, ReducedMat, ReducedSize);
|
||||
if (!opencv_imgcodecs.imwrite(reducedfilename, ReducedMat, paramjpeg)){
|
||||
System.out.println("TakePhoto failed, Unable to Save Reduced Photo for camera "+cameratitle.getText());
|
||||
} else result.setCompressedfile(reducedfilename);
|
||||
|
||||
String xy = CropReducedMat(directory, prefix, ReducedMatROI);
|
||||
if (ValidFile(xy)){
|
||||
result.setCompressedcrop(xy);
|
||||
result.setReducedROI(new Rect(ReducedMatROI.x(), ReducedMatROI.y(), ReducedMatROI.width(), ReducedMatROI.height()));
|
||||
}
|
||||
|
||||
} else raise_log("TakePhoto failed, Live View is Empty");
|
||||
} else raise_log("TakePhoto failed, Grabber is null");
|
||||
TakingPhoto.set(false);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -766,17 +787,320 @@ public class Cameradetail {
|
||||
try{
|
||||
mGrabber.close();
|
||||
System.out.println("Camera "+cameratitle.getText()+" stopped");
|
||||
Platform.runLater(()->setCameraStatus("Camera Stopped"));
|
||||
setCameraStatus("Camera Stopped");
|
||||
} catch (Exception e){
|
||||
raise_log("StopLiveView failed, Unable to Stop Camera, Error: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
TakingPhoto.set(false);
|
||||
IsGrabbingLiveView.set(false);
|
||||
TakingPhoto = null;
|
||||
IsGrabbingLiveView.drainPermits();
|
||||
|
||||
// stop FPS calculation
|
||||
timer.cancel();
|
||||
// stop camera capture thread
|
||||
cam_capture.interrupt();
|
||||
// stop qr detection thread
|
||||
qr_detect.interrupt();
|
||||
// stop face detection thread
|
||||
face_detect.interrupt();
|
||||
}
|
||||
|
||||
public boolean StartLiveView(LiveCamEvent event, String cameratitle, final boolean use_qr , final boolean use_face) {
|
||||
Timer timer = new java.util.Timer();
|
||||
// FPS Calculator
|
||||
AtomicInteger fps = new AtomicInteger(0);
|
||||
|
||||
// use for locking
|
||||
final Object lockObject = new Object();
|
||||
|
||||
|
||||
|
||||
// QR Detection Thread
|
||||
Semaphore qr_semaphore = new Semaphore(0);
|
||||
Thread qr_detect = new Thread(()->{
|
||||
while(Capturing.get()){
|
||||
try {
|
||||
qr_semaphore.acquire();
|
||||
UMat gray;
|
||||
synchronized (lockObject){
|
||||
gray = GrayMat;
|
||||
}
|
||||
String qr = DetectQRFromMat(gray);
|
||||
if (ValidBarCode(qr)){
|
||||
if (event!=null) event.onDetectedQRCode(qr);
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
System.out.println(Thread.currentThread().getName()+" interrupted");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Face Detection Thread
|
||||
Semaphore face_semaphore = new Semaphore(0);
|
||||
Thread face_detect = new Thread(()->{
|
||||
// eye state = -1 means unknown, 0 means closed, 1 means open
|
||||
final AtomicInteger eye_state = new AtomicInteger(-1);
|
||||
final AtomicBoolean waiting_for_second_blink = new AtomicBoolean(false);
|
||||
final AtomicLong last_blink = new AtomicLong(0);
|
||||
final AtomicInteger no_face_counter = new AtomicInteger(0);
|
||||
final AtomicInteger face_counter = new AtomicInteger(0);
|
||||
final AtomicInteger blink_counter = new AtomicInteger(0);
|
||||
final AtomicInteger no_eye_counter = new AtomicInteger(0);
|
||||
final AtomicInteger have_eye_counter = new AtomicInteger(0);
|
||||
while(Capturing.get()){
|
||||
try {
|
||||
face_semaphore.acquire();
|
||||
UMat gray;
|
||||
synchronized (lockObject){
|
||||
gray = GrayMat;
|
||||
}
|
||||
|
||||
DetectorResult theface = null;
|
||||
boolean have_frontal_face = false;
|
||||
boolean have_left_45_face = false;
|
||||
int _face_width = 0;
|
||||
int _face_height = 0;
|
||||
|
||||
List<DetectorResult> frontalfaces = HaveFrontalFace(gray);
|
||||
if (!frontalfaces.isEmpty()){
|
||||
for(DetectorResult rect : frontalfaces){
|
||||
if (rect.haveFace() ){
|
||||
rect.FaceRectangle(LiveMat);
|
||||
if (rect.getFaceWidth()>_face_width) _face_width = rect.getFaceWidth();
|
||||
if (rect.getFaceHeight()>_face_height) _face_height = rect.getFaceHeight();
|
||||
theface = rect;
|
||||
have_frontal_face = true;
|
||||
if (rect.haveEyes()){
|
||||
rect.EyesRectangle(LiveMat);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// gak punya frontal face
|
||||
// coba cek punya profile left face 45 gak
|
||||
List<DetectorResult> Left45Faces = HaveLeft45Face(gray);
|
||||
if (!Left45Faces.isEmpty()){
|
||||
for(DetectorResult rect : Left45Faces){
|
||||
if (rect.haveFace()){
|
||||
rect.FaceRectangle(LiveMat);
|
||||
if (rect.getFaceWidth()>_face_width) _face_width = rect.getFaceWidth();
|
||||
if (rect.getFaceHeight()>_face_height) _face_height = rect.getFaceHeight();
|
||||
have_left_45_face = true;
|
||||
if (rect.haveEyes()){
|
||||
rect.EyesRectangle(LiveMat);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (have_frontal_face){
|
||||
if (face_counter.incrementAndGet()<5) continue;
|
||||
no_face_counter.set(0);
|
||||
if (event!=null) event.onFrontalFaceDetector(true, _face_width, _face_height);
|
||||
LabelVisible(face_indicator,true);
|
||||
|
||||
if (theface.getFace()!=null){
|
||||
LiveMatROI = new Rect(theface.getFace().x(), theface.getFace().y(), theface.getFace().width(), theface.getFace().height());
|
||||
//System.out.println("Frontal Face Detected from camera "+cameratitle+" "+RectToString(LiveMatROI));
|
||||
}
|
||||
|
||||
if (theface.getEyesCount()>=2){
|
||||
// ada mata (buka mata)
|
||||
if (have_eye_counter.incrementAndGet()<5) continue;
|
||||
no_eye_counter.set(0);
|
||||
|
||||
if (event!=null) event.onEyeDetector(true);
|
||||
LabelVisible(eye_indicator,true);
|
||||
|
||||
//System.out.println("Valid Eye Detected from camera "+cameratitle);
|
||||
// Valid eye condition
|
||||
|
||||
if (eye_state.get()!=1){
|
||||
// transisi dari tutup mata ke buka mata
|
||||
if (eye_state.get()==-1) {
|
||||
System.out.println("First Eye Detected from camera "+cameratitle.getText());
|
||||
eye_state.set(1);
|
||||
} else {
|
||||
System.out.println("Transition from close to open eyes");
|
||||
eye_state.set(1);
|
||||
|
||||
blink_counter.incrementAndGet();
|
||||
if (event!=null) event.onBlink(blink_counter.get());
|
||||
LabelSetText(BlinkCounterLabel, String.valueOf(blink_counter.get()),null);
|
||||
|
||||
long now = System.currentTimeMillis();
|
||||
if (waiting_for_second_blink.get()){
|
||||
long diff = now - last_blink.get();
|
||||
// kalau beda waktu antara blink 1 dan blink 2 kurang dari 3 detik
|
||||
if (diff<=3000){
|
||||
System.out.println("Double Blink Detected from camera "+cameratitle.getText());
|
||||
if (event!=null) event.onDoubleBlink((int)diff);
|
||||
}
|
||||
waiting_for_second_blink.set(false);
|
||||
} else {
|
||||
waiting_for_second_blink.set(true);
|
||||
System.out.println("First Blink Detected from camera "+cameratitle.getText());
|
||||
}
|
||||
last_blink.set(now);
|
||||
}
|
||||
|
||||
}
|
||||
} else {
|
||||
// ada muka, tidak ada mata
|
||||
// transisi dari buka mata ke tutup mata
|
||||
if (no_eye_counter.incrementAndGet()<5) continue;
|
||||
have_eye_counter.set(0);
|
||||
|
||||
if (event!=null) event.onEyeDetector(false);
|
||||
LabelVisible(eye_indicator,false);
|
||||
// Valid no eye condition
|
||||
|
||||
|
||||
if (eye_state.get()!=0){
|
||||
System.out.println("Transition from open to close eyes");
|
||||
eye_state.set(0);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
} else if (have_left_45_face ){
|
||||
if (event!=null) event.onProfileFaceDetector(true, _face_width, _face_height);
|
||||
LabelVisible(face_indicator,true);
|
||||
|
||||
|
||||
} else {
|
||||
// no face detected, but let's not cancel the previous state immediately
|
||||
if (no_face_counter.incrementAndGet()<30) continue;
|
||||
// beneran dianggap no face detected
|
||||
eye_state.set(-1);
|
||||
last_blink.set(0);
|
||||
waiting_for_second_blink.set(false);
|
||||
face_counter.set(0);
|
||||
blink_counter.set(0);
|
||||
no_eye_counter.set(0);
|
||||
have_eye_counter.set(0);
|
||||
|
||||
if (event!=null) {
|
||||
event.onFrontalFaceDetector(false, _face_width, _face_height);
|
||||
event.onProfileFaceDetector(false, _face_width, _face_height);
|
||||
event.onEyeDetector(false);
|
||||
event.onBlink(blink_counter.get());
|
||||
|
||||
LabelSetText(BlinkCounterLabel, "",null);
|
||||
LabelVisible(face_indicator,false);
|
||||
LabelVisible(eye_indicator,false);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
UMat rgbmat = new UMat(LiveMat.size(), CV_8UC3);
|
||||
cvtColor(LiveMat, rgbmat, COLOR_BGR2RGB);
|
||||
|
||||
Mat imgmat = new Mat();
|
||||
rgbmat.copyTo(imgmat); // copy back to CPU
|
||||
// Update Task Value usign matToWritableImage
|
||||
setCameraStream(matToWritableImage(imgmat));
|
||||
//updateValue(matToWritableImage(imgmat));
|
||||
} catch (InterruptedException e) {
|
||||
System.out.println(Thread.currentThread().getName()+" interrupted");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Camera Capture Thread
|
||||
Thread cam_capture = new Thread(()->{
|
||||
while (Capturing.get()) {
|
||||
try {
|
||||
// selama proses pengambilan foto, jangan ambil frame
|
||||
if (TakingPhoto!=null) TakingPhoto.await();
|
||||
|
||||
IsGrabbingLiveView.drainPermits();
|
||||
IsGrabbingLiveView.release();
|
||||
//IsGrabbingLiveView.set(true);
|
||||
Frame frame = null;
|
||||
if (Capturing.get()) {
|
||||
try{
|
||||
frame = mGrabber.grab(); // grab frame
|
||||
} catch (FrameGrabber.Exception e){
|
||||
if (Capturing.get()){
|
||||
// kalau ada exception padahal masih capturing. Kalau sudah tidak capturing, tidak peduli
|
||||
if (ValidString(e.getMessage())){
|
||||
String msg = e.getMessage();
|
||||
System.out.println("Exception on "+Thread.currentThread().getName()+" :"+msg);
|
||||
if (msg.contains("start() been called")){
|
||||
if (Capturing.get()){
|
||||
System.out.println("Camera "+Thread.currentThread().getName()+" has been stopped, restarting");
|
||||
mGrabber.close();
|
||||
//Wait(100);
|
||||
mGrabber.start();
|
||||
mGrabber.flush();
|
||||
} else {
|
||||
System.out.println("Camera "+Thread.currentThread().getName()+" has been stopped, not restarting");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
//IsGrabbingLiveView.set(false);
|
||||
if (frame==null) continue;
|
||||
Mat mat = matconverter.convert(frame); // convert to Mat
|
||||
fps.incrementAndGet();
|
||||
|
||||
UMat originalmat = new UMat();
|
||||
mat.copyTo(originalmat); // copy to originalmat for using OpenCL
|
||||
if (config.isMirrorCamera()){
|
||||
// revisi 18/03/2025
|
||||
UMat flippedmat = new UMat();
|
||||
opencv_core.flip(originalmat, flippedmat, 0); // flip vertical
|
||||
flippedmat.copyTo(originalmat);
|
||||
flippedmat.close();
|
||||
}
|
||||
if (config.isFlipCamera()){
|
||||
// revisi 18/03/2025
|
||||
UMat flippedmat = new UMat();
|
||||
opencv_core.flip(originalmat, flippedmat, 1); // flip horizontal
|
||||
flippedmat.copyTo(originalmat);
|
||||
flippedmat.close();
|
||||
}
|
||||
|
||||
// rotate 90 degree counter clockwise karena kamera potrait
|
||||
opencv_core.rotate(originalmat, BestMat, opencv_core.ROTATE_90_COUNTERCLOCKWISE);
|
||||
|
||||
|
||||
|
||||
if (!BestMat.empty()) {
|
||||
|
||||
// LiveMat and GrayMat are synchronized
|
||||
synchronized (lockObject){
|
||||
opencv_imgproc.resize(BestMat, LiveMat, LiveSize); // resize to LiveSize
|
||||
opencv_imgproc.cvtColor(LiveMat,GrayMat, COLOR_BGR2GRAY); // convert to grayscale
|
||||
}
|
||||
|
||||
if (use_qr){
|
||||
qr_semaphore.release();
|
||||
}
|
||||
if (use_face){
|
||||
face_semaphore.release();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
} catch ( FrameGrabber.Exception fe){
|
||||
System.out.println("FrameGrabber Exception in" + Thread.currentThread().getName() + " : " + fe.getMessage());
|
||||
} catch (InterruptedException e) {
|
||||
System.out.println(Thread.currentThread().getName()+" interrupted");
|
||||
} catch (Exception e){
|
||||
System.out.println(Thread.currentThread().getName()+" exception : "+e.getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
public boolean StartLiveView(LiveCamEvent event, String cameratitle, boolean use_qr , boolean use_face) {
|
||||
this.event = event;
|
||||
if (mGrabber != null) {
|
||||
try {
|
||||
@@ -800,310 +1124,51 @@ public class Cameradetail {
|
||||
Capturing.set(true);
|
||||
if (event!=null) event.onStartCapturing();
|
||||
|
||||
Task<Image> task = new Task<>() {
|
||||
|
||||
|
||||
TimerTask fpsTask = new TimerTask() {
|
||||
@Override
|
||||
protected Image call() {
|
||||
// repeat until capturing is false
|
||||
AtomicInteger fps = new AtomicInteger(0);
|
||||
// eye state = -1 means unknown, 0 means closed, 1 means open
|
||||
final int[] eye_state = {-1};
|
||||
final boolean[] waiting_for_second_blink = {false};
|
||||
final long[] last_blink = {0};
|
||||
final int[] no_face_counter = {0};
|
||||
final int[] face_counter = {0};
|
||||
final int[] blink_counter = {0};
|
||||
|
||||
|
||||
TimerTask fpsTask = new TimerTask() {
|
||||
@Override
|
||||
public void run() {
|
||||
int fpsval = fps.getAndSet(0);
|
||||
if (fpsval!=LiveFPS){
|
||||
LiveFPS = fpsval;
|
||||
if (event!=null) event.onIntervalUpdate();
|
||||
AutoCloseAlert.ChangeCamStatus(switch (cameratitle){
|
||||
case "01" -> 1;
|
||||
case "02" -> 2;
|
||||
case "03" -> 3;
|
||||
case "04" -> 4;
|
||||
case "05" -> 5;
|
||||
default -> 0;
|
||||
}, LiveFPS>0 );
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Timer timer = new java.util.Timer();
|
||||
timer.scheduleAtFixedRate(fpsTask, 1000, 1000);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
while (Capturing.get()) {
|
||||
try {
|
||||
// selama proses pengambilan foto, jangan ambil frame
|
||||
while(TakingPhoto.get() && Capturing.get()){
|
||||
Wait(10);
|
||||
}
|
||||
|
||||
if (!Capturing.get()) return null;
|
||||
IsGrabbingLiveView.set(true);
|
||||
Frame frame;
|
||||
try{
|
||||
frame = mGrabber.grab(); // grab frame
|
||||
} catch (Exception e){
|
||||
frame = null;
|
||||
|
||||
if (e.getMessage()!=null && !e.getMessage().isBlank()){
|
||||
String msg = e.getMessage();
|
||||
if (msg.contains("start() been called")){
|
||||
if (Capturing.get()){
|
||||
System.out.println("Camera "+cameratitle+" has been stopped, restarting");
|
||||
mGrabber.close();
|
||||
Wait(100);
|
||||
mGrabber.start();
|
||||
mGrabber.flush();
|
||||
} else {
|
||||
System.out.println("Camera "+cameratitle+" has been stopped, not restarting");
|
||||
}
|
||||
} else System.out.println("Exception on grab frame from camera "+cameratitle+", Message : "+e.getMessage());
|
||||
}
|
||||
}
|
||||
if (frame==null) continue;
|
||||
Mat mat = matconverter.convert(frame); // convert to Mat
|
||||
fps.incrementAndGet();
|
||||
|
||||
UMat originalmat = new UMat();
|
||||
mat.copyTo(originalmat); // copy to originalmat for using OpenCL
|
||||
if (config.isMirrorCamera()){
|
||||
// revisi 18/03/2025
|
||||
UMat flippedmat = new UMat();
|
||||
opencv_core.flip(originalmat, flippedmat, 0); // flip vertical
|
||||
flippedmat.copyTo(originalmat);
|
||||
flippedmat.close();
|
||||
}
|
||||
if (config.isFlipCamera()){
|
||||
// revisi 18/03/2025
|
||||
UMat flippedmat = new UMat();
|
||||
opencv_core.flip(originalmat, flippedmat, 1); // flip horizontal
|
||||
flippedmat.copyTo(originalmat);
|
||||
flippedmat.close();
|
||||
}
|
||||
|
||||
// rotate 90 degree counter clockwise karena kamera potrait
|
||||
opencv_core.rotate(originalmat, BestMat, opencv_core.ROTATE_90_COUNTERCLOCKWISE);
|
||||
|
||||
IsGrabbingLiveView.set(false);
|
||||
|
||||
if (!BestMat.empty()) {
|
||||
opencv_imgproc.resize(BestMat, LiveMat, LiveSize); // resize to LiveSize
|
||||
opencv_imgproc.cvtColor(LiveMat,GrayMat, COLOR_BGR2GRAY); // convert to grayscale
|
||||
|
||||
if (use_qr){
|
||||
String qr = DetectQRFromMat(GrayMat);
|
||||
if (ValidBarCode(qr)){
|
||||
qrtext = qr;
|
||||
if (event!=null) event.onDetectedQRCode(qrtext);
|
||||
}
|
||||
}
|
||||
if (use_face){
|
||||
|
||||
DetectorResult theface = null;
|
||||
boolean have_frontal_face = false;
|
||||
boolean have_left_45_face = false;
|
||||
int _face_width = 0;
|
||||
int _face_height = 0;
|
||||
|
||||
List<DetectorResult> frontalfaces = HaveFrontalFace(GrayMat);
|
||||
if (!frontalfaces.isEmpty()){
|
||||
for(DetectorResult rect : frontalfaces){
|
||||
if (rect.haveFace() ){
|
||||
rect.FaceRectangle(LiveMat);
|
||||
if (rect.getFaceWidth()>_face_width) _face_width = rect.getFaceWidth();
|
||||
if (rect.getFaceHeight()>_face_height) _face_height = rect.getFaceHeight();
|
||||
theface = rect;
|
||||
have_frontal_face = true;
|
||||
if (rect.haveEyes()){
|
||||
rect.EyesRectangle(LiveMat);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// gak punya frontal face
|
||||
// coba cek punya profile left face 45 gak
|
||||
List<DetectorResult> Left45Faces = HaveLeft45Face(GrayMat);
|
||||
if (!Left45Faces.isEmpty()){
|
||||
for(DetectorResult rect : Left45Faces){
|
||||
if (rect.haveFace()){
|
||||
if (rect.haveEyes()){
|
||||
rect.FaceRectangle(LiveMat);
|
||||
rect.EyesRectangle(LiveMat);
|
||||
if (rect.getFaceWidth()>_face_width) _face_width = rect.getFaceWidth();
|
||||
if (rect.getFaceHeight()>_face_height) _face_height = rect.getFaceHeight();
|
||||
have_left_45_face = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (have_frontal_face){
|
||||
|
||||
if (face_counter[0]<5){
|
||||
face_counter[0]++;
|
||||
//System.out.println("Frontal Face Counter = "+face_counter+ " from camera "+cameratitle+" eye count = "+theface.getEyesCount());
|
||||
//continue;
|
||||
} else {
|
||||
no_face_counter[0] = 0;
|
||||
if (event!=null) event.onFrontalFaceDetector(true, _face_width, _face_height);
|
||||
//LabelVisible(face_indicator,true);
|
||||
Platform.runLater(()-> face_indicator.setVisible(true));
|
||||
|
||||
if (theface.getFace()!=null){
|
||||
LiveMatROI = new Rect(theface.getFace().x(), theface.getFace().y(), theface.getFace().width(), theface.getFace().height());
|
||||
//System.out.println("Frontal Face Detected from camera "+cameratitle+" "+RectToString(LiveMatROI));
|
||||
}
|
||||
|
||||
if (theface.haveEyes()){
|
||||
// ada mata (buka mata)
|
||||
|
||||
if (event!=null) event.onEyeDetector(true);
|
||||
//LabelVisible(eye_indicator,true);
|
||||
Platform.runLater(()-> eye_indicator.setVisible(true));
|
||||
|
||||
//System.out.println("Valid Eye Detected from camera "+cameratitle);
|
||||
// Valid eye condition
|
||||
|
||||
if (eye_state[0]!=1){
|
||||
// transisi dari tutup mata ke buka mata
|
||||
if (eye_state[0]==-1) {
|
||||
System.out.println("First Eye Detected from camera "+cameratitle);
|
||||
eye_state[0]=1;
|
||||
continue;
|
||||
} else {
|
||||
System.out.println("Transition from close to open eyes");
|
||||
eye_state[0] = 1;
|
||||
|
||||
blink_counter[0]++;
|
||||
if (event!=null) event.onBlink(blink_counter[0]);
|
||||
Platform.runLater(()-> BlinkCounterLabel.setText(blink_counter[0]+""));
|
||||
//LabelSetText(BlinkCounterLabel, String.valueOf(blink_counter[0]),null);
|
||||
|
||||
long now = System.currentTimeMillis();
|
||||
if (waiting_for_second_blink[0]){
|
||||
long diff = now - last_blink[0];
|
||||
// kalau beda waktu antara blink 1 dan blink 2 kurang dari 3 detik
|
||||
if (diff<=3000){
|
||||
System.out.println("Double Blink Detected from camera "+cameratitle);
|
||||
if (event!=null) event.onDoubleBlink((int)diff);
|
||||
}
|
||||
waiting_for_second_blink[0] = false;
|
||||
} else {
|
||||
waiting_for_second_blink[0] = true;
|
||||
System.out.println("First Blink Detected from camera "+cameratitle);
|
||||
}
|
||||
last_blink[0] = now;
|
||||
}
|
||||
|
||||
}
|
||||
} else {
|
||||
// ada muka, tidak ada mata
|
||||
// transisi dari buka mata ke tutup mata
|
||||
|
||||
if (event!=null) event.onEyeDetector(false);
|
||||
//LabelVisible(eye_indicator,false);
|
||||
Platform.runLater(()-> eye_indicator.setVisible(false));
|
||||
// Valid no eye condition
|
||||
|
||||
|
||||
if (eye_state[0]!=0){
|
||||
System.out.println("Transition from open to close eyes");
|
||||
eye_state[0] = 0;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} else if (have_left_45_face ){
|
||||
if (event!=null) event.onProfileFaceDetector(true, _face_width, _face_height);
|
||||
//LabelVisible(face_indicator,true);
|
||||
Platform.runLater(()-> face_indicator.setVisible(true));
|
||||
|
||||
|
||||
} else {
|
||||
// no face detected, but let's not cancel the previous state immediately
|
||||
|
||||
if (no_face_counter[0]<60){
|
||||
// toleransi no face selama 60 frame
|
||||
no_face_counter[0]++;
|
||||
continue;
|
||||
} else {
|
||||
// beneran dianggap no face detected
|
||||
eye_state[0] = -1;
|
||||
last_blink[0] = 0;
|
||||
waiting_for_second_blink[0] = false;
|
||||
face_counter[0] = 0;
|
||||
blink_counter[0] = 0;
|
||||
|
||||
if (event!=null) {
|
||||
event.onFrontalFaceDetector(false, _face_width, _face_height);
|
||||
event.onProfileFaceDetector(false, _face_width, _face_height);
|
||||
event.onEyeDetector(false);
|
||||
event.onBlink(blink_counter[0]);
|
||||
Platform.runLater(()->{
|
||||
face_indicator.setVisible(false);
|
||||
eye_indicator.setVisible(false);
|
||||
BlinkCounterLabel.setText("");
|
||||
});
|
||||
//LabelSetText(BlinkCounterLabel, "",null);
|
||||
//LabelVisible(face_indicator,false);
|
||||
//LabelVisible(eye_indicator,false);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
UMat rgbmat = new UMat(LiveMat.size(), CV_8UC3);
|
||||
cvtColor(LiveMat, rgbmat, COLOR_BGR2RGB);
|
||||
|
||||
Mat imgmat = new Mat();
|
||||
rgbmat.copyTo(imgmat); // copy back to CPU
|
||||
// Update Task Value usign matToWritableImage
|
||||
updateValue(matToWritableImage(imgmat));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
if (ValidString(e.getMessage())){
|
||||
raise_log("Unable to Grab Frame, Error: " + e.getMessage());
|
||||
}
|
||||
//if (!Capturing.get()) Platform.runLater(this::StopLiveView);
|
||||
public void run() {
|
||||
if (Capturing.get()){
|
||||
int fpsval = fps.getAndSet(0);
|
||||
if (fpsval!=LiveFPS){
|
||||
LiveFPS = fpsval;
|
||||
if (event!=null) event.onIntervalUpdate();
|
||||
AutoCloseAlert.ChangeCamStatus(switch (cameratitle){
|
||||
case "01" -> 1;
|
||||
case "02" -> 2;
|
||||
case "03" -> 3;
|
||||
case "04" -> 4;
|
||||
case "05" -> 5;
|
||||
default -> 0;
|
||||
}, LiveFPS>0 );
|
||||
}
|
||||
} else {
|
||||
fps.set(0);
|
||||
this.cancel();
|
||||
}
|
||||
timer.cancel();
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
// value dari task, yaitu image, akan diupdate ke camerastream
|
||||
task.valueProperty().addListener((obs, oldVal, newVal) -> {
|
||||
if (newVal != null) {
|
||||
setCameraStream(newVal);
|
||||
}
|
||||
});
|
||||
timer.scheduleAtFixedRate(fpsTask, 1000, 1000);
|
||||
|
||||
// start task
|
||||
Thread thread = new Thread(task);
|
||||
thread.setDaemon(true);
|
||||
thread.start();
|
||||
|
||||
this.use_qr = use_qr;
|
||||
this.use_face = use_face;
|
||||
cam_capture.setName("cam_capture "+cameratitle);
|
||||
cam_capture.setDaemon(true);
|
||||
cam_capture.start();
|
||||
System.out.println("Starting cam_capture thread");
|
||||
|
||||
qr_detect.setName("qr_detect "+cameratitle);
|
||||
qr_detect.setDaemon(true);
|
||||
qr_detect.start();
|
||||
System.out.println("Starting qr_detect thread");
|
||||
|
||||
face_detect.setName("face_detect "+cameratitle);
|
||||
face_detect.setDaemon(true);
|
||||
face_detect.start();
|
||||
System.out.println("Starting face_detect thread");
|
||||
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
@@ -1204,14 +1269,6 @@ public class Cameradetail {
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
private double getBrightnessFromGrayMat(Mat graymat){
|
||||
Scalar mean = mean(graymat);
|
||||
return mean.get(0);
|
||||
}
|
||||
|
||||
private WritableImage matToWritableImage(Mat mat){
|
||||
int cols = mat.cols();
|
||||
int rows = mat.rows();
|
||||
|
||||
Reference in New Issue
Block a user