commit 09/04/2025

This commit is contained in:
rdkartono
2025-04-09 12:14:34 +07:00
parent e72d25a213
commit 7cdefa6f1d
11 changed files with 244 additions and 188 deletions

View File

@@ -8,8 +8,12 @@ import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.stage.Modality;
import javafx.stage.Screen;
import javafx.stage.Stage;
@@ -32,6 +36,11 @@ public class AutoCloseAlert {
public static Image shownBanner = null;
public static Image banner_01;
public static Image banner_02;
private static Circle Cam1;
private static Circle Cam2;
private static Circle Cam3;
private static Circle Cam4;
private static Circle Cam5;
public static void init(){
String f_01 = ExtractResource("/IU photoboth-01.jpg");
@@ -42,6 +51,29 @@ public class AutoCloseAlert {
if (banner_01!=null) System.out.println("Banner 01 loaded"); else System.out.println("Banner 01 not loaded");
banner_02 = LoadImage(f_02);
if (banner_02!=null) System.out.println("Banner 02 loaded"); else System.out.println("Banner 02 not loaded");
Cam1 = new Circle(10, Color.RED);
Cam2 = new Circle(10, Color.RED);
Cam3 = new Circle(10, Color.RED);
Cam4 = new Circle(10, Color.RED);
Cam5 = new Circle(10, Color.RED);
}
public static void ChangeCamStatus(int id, boolean active){
Circle x = switch (id){
case 1 -> Cam1;
case 2 -> Cam2;
case 3 -> Cam3;
case 4 -> Cam4;
case 5 -> Cam5;
default -> null;
};
if (x!=null){
if (active){
x.setFill(Color.GREEN);
} else {
x.setFill(Color.RED);
}
}
}
@@ -63,10 +95,16 @@ public class AutoCloseAlert {
* @param onClose What to do after auto close
*/
public static void show(String title, String content, int seconds, Consumer<String> onClose){
Platform.runLater(()->{
if (Platform.isFxApplicationThread()){
Stage alertStage = _showtext(title, "", content);
closeAlertStage(seconds, onClose, alertStage);
});
} else {
Platform.runLater(()->{
Stage alertStage = _showtext(title, "", content);
closeAlertStage(seconds, onClose, alertStage);
});
}
}
@@ -82,17 +120,34 @@ public class AutoCloseAlert {
* @param onClose What to do after auto close
*/
public static void show(String title, String header, String content, int seconds, Consumer<String> onClose) {
Platform.runLater(()->{
if (Platform.isFxApplicationThread()){
Stage alertStage = _showtext(title, header, content);
closeAlertStage(seconds, onClose, alertStage);
});
} else {
Platform.runLater(()->{
Stage alertStage = _showtext(title, header, content);
closeAlertStage(seconds, onClose, alertStage);
});
}
}
/**
* Show a banner image
* @param b1 Image to show
* @param seconds the number of seconds before the alert is closed, or put 0 to keep it open
* @param onClose What to do after auto close
*/
public static void showbanner(Image b1, int seconds, Consumer<String> onClose){
Platform.runLater(()->{
if (Platform.isFxApplicationThread()){
Stage alertStage = _showbanner(b1);
closeAlertStage(seconds, onClose, alertStage);
});
} else {
Platform.runLater(()->{
Stage alertStage = _showbanner(b1);
closeAlertStage(seconds, onClose, alertStage);
});
}
}
private static Stage _showbanner(Image image){
@@ -103,24 +158,41 @@ public class AutoCloseAlert {
alertStage.initStyle(StageStyle.UTILITY);
alertStage.setAlwaysOnTop(true);
alertStage.setResizable(false);
//System.out.println("_showbanner creating stage");
int width = (int) Screen.getPrimary().getBounds().getWidth();
int height = (int) Screen.getPrimary().getBounds().getHeight();
BorderPane borderPane = new BorderPane();
StackPane stackPane = new StackPane();
if (image!=null){
ImageView imageView = new ImageView(image);
imageView.setPreserveRatio(true);
imageView.setFitWidth(1280);
imageView.setFitWidth(width);
imageView.setSmooth(true);
StackPane stackPane = new StackPane(imageView);
alertStage.setScene(new Scene(stackPane, 1280, 720));
//System.out.println("_showbanner setscene");
} //else System.out.println("_showbanner not setscene because image is null");
stackPane.getChildren().add(imageView);
}
HBox CamStatus = new HBox(30, Cam1, Cam2, Cam3, Cam4, Cam5);
CamStatus.setMinHeight(60);
CamStatus.setAlignment(Pos.CENTER);
VBox vBox = new VBox(CamStatus);
vBox.setAlignment(Pos.BOTTOM_CENTER);
stackPane.getChildren().add(vBox);
borderPane.setCenter(stackPane);
alertStage.setScene(new Scene(borderPane, width, height));
alertStage.centerOnScreen();
alertStage.show();
//System.out.println("_showbanner show stage");
currentAlertStage = alertStage;
shownBanner = image;
CamStatus.prefWidthProperty().bind(currentAlertStage.widthProperty());
shownTitle = "";
shownContent = "";
shownHeader = "";
@@ -147,20 +219,22 @@ public class AutoCloseAlert {
alertStage.setAlwaysOnTop(true);
alertStage.setResizable(false);
double screenwidth = Screen.getPrimary().getVisualBounds().getWidth();
double width = screenwidth/4;
double height = width * 9 / 16;
double screenwidth = Screen.getPrimary().getBounds().getWidth();
double screenheight = Screen.getPrimary().getBounds().getHeight();
double height = screenheight/4;
double width = height * 21/9;
List<Node> children = new ArrayList<>();
if (ValidString(header)){
Label headerLabel = new Label(header);
headerLabel.setStyle("-fx-font-weight: bold; -fx-font-size: 16px;");
headerLabel.setStyle("-fx-font-weight: bold; -fx-font-size: 28px;");
headerLabel.setMinHeight(height*0.25);
children.add(headerLabel);
}
if (ValidString(content)){
Label contentLabel = new Label(content);
contentLabel.setStyle("-fx-font-size: 12px;");
contentLabel.setStyle("-fx-font-size: 24px;");
contentLabel.setMinHeight(height*0.75);
children.add(contentLabel);
}
@@ -174,6 +248,11 @@ public class AutoCloseAlert {
alertStage.setTitle(title);
double x = screenwidth/2 - width/2;
double y = screenheight - height - 10;
alertStage.setX(x);
alertStage.setY(y);
alertStage.show();
currentAlertStage = alertStage;
shownHeader = ValidString(header) ? header : "";

View File

@@ -108,6 +108,9 @@ public class Cameradetail {
@FXML
private Label eye_indicator;
@FXML
private Label BlinkCounterLabel;
@FXML
private Label sharpness_indicator;
@@ -604,7 +607,7 @@ public class Cameradetail {
public boolean PutText(UMat Mat, String text, double fontScale, Scalar textColor, int thickness){
if (!Mat.empty()){
if (text!=null && !text.isEmpty()){
if (text!=null && !text.isBlank()){
//String timestamp = prefix+" "+SomeCodes.GetDateTimeString();
int fontFace = FONT_HERSHEY_SIMPLEX;
//double fontScale = 4.0;
@@ -625,7 +628,7 @@ public class Cameradetail {
public boolean PutText(Mat Mat, String text, double fontScale, Scalar textColor, int thickness){
if (!Mat.empty()){
if (text!=null && !text.isEmpty()){
if (text!=null && !text.isBlank()){
//String timestamp = prefix+" "+SomeCodes.GetDateTimeString();
int fontFace = FONT_HERSHEY_SIMPLEX;
//double fontScale = 4.0;
@@ -791,34 +794,6 @@ public class Cameradetail {
IsGrabbingLiveView.set(false);
}
// public Rect GetFace(UMat mat, boolean isfrontal){
// if (!mat.empty()){
// Mat originalmat = new Mat();
// mat.copyTo(originalmat);
// Mat graymat = new Mat();
// opencv_imgproc.cvtColor(originalmat,graymat, COLOR_BGR2GRAY); // convert to grayscale
// int size = Math.min(graymat.cols(), graymat.rows());
// int minsize = (int) (size * 0.4);
// int maxsize = (int) (size * 0.9);
// System.out.println("GetFace size = "+size+" minsize = "+minsize+" maxsize = "+maxsize);
// RectVector faces = isfrontal ? Detectors.DetectFrontalFace(graymat, minsize, maxsize) : Detectors.DetectProfileFace(graymat, minsize, maxsize);
// if (faces.size()>0){
// Rect result = null;
// for(Rect xx : faces.get()){
// if (result==null){
// result = xx;
// } else {
// if (xx.area()>result.area()){
// result = xx;
// }
// }
// }
// return result;
// }
// } else raise_log("GetFace failed, Mat is empty");
// return null;
// }
public boolean StartLiveView(LiveCamEvent event, String cameratitle, final boolean use_qr , final boolean use_face) {
this.event = event;
if (mGrabber != null) {
@@ -861,7 +836,16 @@ public class Cameradetail {
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 );
}
}
};
@@ -881,8 +865,9 @@ public class Cameradetail {
boolean have_fist = false;
int no_face_counter = 0;
int face_counter = 0;
//int open_eye_counter = 0;
//int close_eye_counter = 0;
int blink_counter = 0;
int have_eye_counter = 0;
int no_eye_counter = 0;
while (Capturing.get()) {
try {
@@ -899,7 +884,7 @@ public class Cameradetail {
} catch (Exception e){
frame = null;
if (e.getMessage()!=null && !e.getMessage().isEmpty()){
if (e.getMessage()!=null && !e.getMessage().isBlank()){
String msg = e.getMessage();
if (msg.contains("start() been called")){
if (Capturing.get()){
@@ -1008,13 +993,15 @@ public class Cameradetail {
if (theface.haveEyes()){
// ada mata (buka mata)
// close_eye_counter=0;
// if (open_eye_counter<1){
// open_eye_counter++;
// if (have_eye_counter<1){
// have_eye_counter++;
// continue;
// }
//System.out.println("Valid Open Eyes");
// no_eye_counter = 0;
//System.out.println("Valid Eye Detected from camera "+cameratitle);
if (event!=null) event.onEyeDetector(true);
LabelVisible(eye_indicator,true);
// Valid eye condition
if (eye_state!=1){
// transisi dari tutup mata ke buka mata
@@ -1025,17 +1012,20 @@ public class Cameradetail {
}
System.out.println("Transition from close to open eyes");
eye_state = 1;
if (event!=null) event.onEyeDetector(true);
LabelVisible(eye_indicator,true);
blink_counter++;
if (event!=null) event.onBlink(blink_counter);
SetText(BlinkCounterLabel, String.valueOf(blink_counter));
long now = System.currentTimeMillis();
if (waiting_for_second_blink){
long diff = now - last_blink;
// kalau beda waktu antara blink 1 dan blink 2 kurang dari 2 detik
if (diff<=2000){
// 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.onBlink((int)diff);
if (event!=null) event.onDoubleBlink((int)diff);
}
waiting_for_second_blink = false;
} else {
@@ -1047,17 +1037,20 @@ public class Cameradetail {
} else {
// ada muka, tidak ada mata
// transisi dari buka mata ke tutup mata
// open_eye_counter=0;
// if (close_eye_counter<1){
// close_eye_counter++;
// if (no_eye_counter<1){
// no_eye_counter++;
// continue;
// }
//System.out.println("Valid Closed Eyes");
// have_eye_counter = 0;
//System.out.println("Valid No Eye Detected from camera "+cameratitle);
if (event!=null) event.onEyeDetector(false);
LabelVisible(eye_indicator,false);
// Valid no eye condition
if (eye_state!=0){
System.out.println("Transition from open to close eyes");
eye_state = 0;
if (event!=null) event.onEyeDetector(false);
LabelVisible(eye_indicator,false);
}
}
} else if (have_left_45_face ){
@@ -1065,29 +1058,35 @@ public class Cameradetail {
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>90){
// kalau tidak ada face selama 30 frame, reset state
// 30 frame approximately 2 second
if (no_face_counter<60){
// toleransi no face selama 60 frame
no_face_counter++;
continue;
} else {
// beneran dianggap no face detected
eye_state = -1;
last_blink = 0;
waiting_for_second_blink = false;
face_counter = 0;
// if (close_eye_counter!=0 || open_eye_counter!=0){
// close_eye_counter=0;
// open_eye_counter=0;
// System.out.println("Reset Open and Close Eyes");
// }
blink_counter = 0;
no_eye_counter=0;
have_eye_counter=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);
SetText(BlinkCounterLabel, "");
LabelVisible(face_indicator,false);
LabelVisible(eye_indicator,false);
}
} else no_face_counter++;
}
}

View File

@@ -25,10 +25,7 @@ import javafx.stage.DirectoryChooser;
import java.util.*;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicBoolean;
import javafx.scene.control.Alert.AlertType;
@@ -91,6 +88,11 @@ public class CaptureView {
private final ErhaAPI erhaAPI = new ErhaAPI(false);
// for timeout 180 detik
private final int timeout = 180;
ScheduledExecutorService timeoutExecutor = Executors.newSingleThreadScheduledExecutor();
ScheduledFuture<?> runningTask = null;
private void TakePhotoButtonChange(){
boolean disabled = false;
String barcode = barcodeData.getText();
@@ -552,6 +554,7 @@ public class CaptureView {
@Override
public void onPlaybackFinished(String filename) {
UploadFiles(prc, prefix);
if (runningTask!=null) runningTask.cancel(false);
}
@Override
@@ -569,6 +572,8 @@ public class CaptureView {
audioPlayer.PlayFile(audio_pengambilan_gagal, null);
}
}
clear();
if (runningTask!=null) runningTask.cancel(false);
}
}
@@ -609,14 +614,8 @@ public class CaptureView {
uploadtask.setOnSucceeded(e-> {
System.out.println("UploadTask succeeded");
Platform.runLater(()->{
AutoCloseAlert.close();
barcodeData.setText("");
medicalRecordID.setText("");
PatientName.setText("");
isTakingPhoto.set(false);
});
clear();
AutoCloseAlert.close();
});
uploadtask.setOnFailed(e-> {
@@ -628,12 +627,7 @@ public class CaptureView {
}
}
AutoCloseAlert.show("Upload Failed", "Upload Failed", "Upload Failed", 5, s -> {
isTakingPhoto.set(false);
Platform.runLater(()->{
barcodeData.setText("");
medicalRecordID.setText("");
PatientName.setText("");
});
clear();
});
});
@@ -674,7 +668,7 @@ public class CaptureView {
medicalRecordID.textProperty().addListener((observable, oldValue, newValue) -> TakePhotoButtonChange());
PatientName.textProperty().addListener((observable, oldValue, newValue) -> TakePhotoButtonChange());
barcodeData.setText("");
audioPlayer = new AudioPlayer(1,48000);
Logger.info("Audio Player : "+(audioPlayer.isInited()? "Inited" : "Not Inited"));
@@ -698,6 +692,13 @@ public class CaptureView {
LoadCameraDetail(cam5, 5, CameraConfigEnum.CameraConfigRight90);
Platform.runLater(()->{
if (!Objects.equals(AutoCloseAlert.shownBanner, AutoCloseAlert.banner_01)){
if (AutoCloseAlert.banner_01!=null){
System.out.println("Showing banner 01 for first time");
AutoCloseAlert.showbanner(AutoCloseAlert.banner_01,0,null);
}
}
int indexleft90=-1;
int indexleft45=-1;
int indexcenter=-1;
@@ -770,6 +771,16 @@ public class CaptureView {
});
}
private void clear(){
isTakingPhoto.set(false);
Platform.runLater(()->{
medicalRecordID.setText("");
PatientName.setText("");
barcodeData.setText("");
});
}
public void Unload(){
@@ -879,8 +890,10 @@ public class CaptureView {
if (ValidBarCode(barCode)){
String prefix = barcodeData.getText();
if (!barCode.equals(prefix)){
String finalbarCode = barCode;
Platform.runLater(()-> barcodeData.setText(finalbarCode));
final String finalbarCode = barCode;
Platform.runLater(()->{
barcodeData.setText(finalbarCode);
});
Task<PatientRecord> checkpatientID = new Task<>() {
@Override
protected PatientRecord call() throws Exception {
@@ -889,8 +902,8 @@ public class CaptureView {
if (br.message.startsWith("Records found")){
if (br.data!=null && br.data.length>0){
PatientRecord pr = br.data[0];
if (!pr.medical_record_detail_id.isEmpty()){
if (!pr.name.isEmpty()){
if (!pr.medical_record_detail_id.isBlank()){
if (!pr.name.isBlank()){
super.succeeded();
return pr;
} else {
@@ -922,12 +935,18 @@ public class CaptureView {
checkpatientID.setOnSucceeded(event -> {
PatientRecord pr = checkpatientID.getValue();
if (pr!=null){
int medrecid = toInt(pr.medical_record_detail_id);
System.out.println("checkpatientID.setOnSucceeded medrecid : "+medrecid);
Platform.runLater(()->{
int medrecid = toInt(pr.medical_record_detail_id);
medicalRecordID.setText(medrecid+"");
medicalRecordID.setText(""+medrecid);
PatientName.setText(pr.name);
});
runningTask = timeoutExecutor.schedule(()->{
// timeout
clear();
}, timeout, TimeUnit.SECONDS);
if (audioPlayer!=null && audioPlayer.isInited()){
if (!Objects.equals(audioPlayer.getCurrentFile(),audio_posisikan_muka)) {
audioPlayer.StopCurrentPlayback();
@@ -952,12 +971,7 @@ public class CaptureView {
AutoCloseAlert.show("Data Tidak Ditemukan", message, "Pastikan data barcode anda benar", 5, s -> {
isTakingPhoto.set(false);
Platform.runLater(()->{
medicalRecordID.setText("");
PatientName.setText("");
barcodeData.setText("");
});
clear();
});
});
@@ -1002,26 +1016,6 @@ public class CaptureView {
//update_status(image);
}
@Override
public void onLeftEarDetector(boolean hasLeftEar, int width, int height) {
}
@Override
public void onRightEarDetector(boolean hasRightEar, int width, int height) {
}
@Override
public void onLeftEyeDetector(boolean hasLeftEye, int width, int height) {
}
@Override
public void onRightEyeDetector(boolean hasRightEye, int width, int height) {
}
@Override
public void onLog(String log) {
String ss = String.format("[%s] : %s", title, log);
@@ -1030,50 +1024,20 @@ public class CaptureView {
@Override
public void onBlink(int counter) {
System.out.println("Blink detected at camera "+title+" delay= "+counter);
}
@Override
public void onDoubleBlink(int counter) {
System.out.println("Double Blink detected at camera "+title+" delay= "+counter);
if (audioPlayer!=null && audioPlayer.isPlaying()) return; // let audio finish playback
if (isTakingPhoto.get()) return; // other camera is taking picture
// revisi 08/04/2025
TakePhotos();
// String directory = config.getPhotoDirectory();
// String prefix = RemoveSpaces(medicalRecordID.getText()) ;
// if (ValidDirectory(directory)){
// if (ValidMedicalRecordId(prefix)){
// isTakingPhoto.set(true);
// TakePhotos();
// } else {
// System.out.println("Prefix invalid, not taking photo");
// isTakingPhoto.set(false);
// //AutoCloseAlert.show("QR Code Not Available", "", "Please scan QR before continue", 5, s -> AutoCloseAlert.show("Scan Barcode", "Silahkan Scan Barcode Anda", "Arahkan kertas barcode ke kamera", 0, null));
// if (!Objects.equals(AutoCloseAlert.shownBanner, AutoCloseAlert.banner_01)){
// if (AutoCloseAlert.banner_01!=null){
// System.out.println("Showing banner 01 because prefix invalid");
// AutoCloseAlert.showbanner(AutoCloseAlert.banner_01,0,null);
// }
// }
// if (audioPlayer!=null && audioPlayer.isInited()){
// if (!Objects.equals(audioPlayer.getCurrentFile(), audio_scan_barcode)) {
// audioPlayer.StopCurrentPlayback();
// audioPlayer.PlayFile(audio_scan_barcode, null);
// }
// }
// }
// } else {
// System.out.println("Photo Directory invalid, not taking photo");
// isTakingPhoto.set(false);
// AutoCloseAlert.show("Invalid Photo Directory","Photo Directory not set", "Please set photo directory at Setting", 5, s -> AutoCloseAlert.show("Setting", "Setting", "Please set photo directory", 0, null));
// }
}
@Override
public void onStartCapturing() {
if (!Objects.equals(AutoCloseAlert.shownBanner, AutoCloseAlert.banner_01)){
if (AutoCloseAlert.banner_01!=null){
System.out.println("Showing banner 01 for first time");
AutoCloseAlert.showbanner(AutoCloseAlert.banner_01,0,null);
}
}
}

View File

@@ -5,6 +5,7 @@ import lombok.Setter;
import org.bytedeco.opencv.opencv_core.Rect;
import org.bytedeco.opencv.opencv_core.Scalar;
import org.bytedeco.opencv.opencv_core.UMat;
import org.opencv.imgproc.Imgproc;
import java.util.List;
@@ -14,6 +15,9 @@ import static org.bytedeco.opencv.global.opencv_imgproc.rectangle;
public class DetectorResult {
private @Setter Rect Face;
private List<Rect> Eyes;
private final int linethickness = 3;
private final int linetype = Imgproc.LINE_8;
private final int lineshift = 0;
public void AddEye(Rect eye){
if (Eyes == null) Eyes = new java.util.ArrayList<>();
@@ -22,7 +26,8 @@ public class DetectorResult {
public void FaceRectangle(UMat mat){
if (haveFace()){
rectangle(mat, Face, Scalar.GREEN);
rectangle(mat, Face, Scalar.GREEN, linethickness, linetype, lineshift);
}
}

View File

@@ -32,9 +32,9 @@ public class MainApplication extends Application {
Logger.info("Secure Dongle UserID valid");
FXMLLoader fxmlLoader = new FXMLLoader(MainApplication.class.getResource("main-view.fxml"));
Screen screen = Screen.getPrimary();
Rectangle2D screenbound = screen.getVisualBounds();
Rectangle2D screenbound = screen.getBounds();
Scene scene = new Scene(fxmlLoader.load(), screenbound.getWidth(), screenbound.getHeight());
stage.setTitle("MultiCam Capture App for ERHA 08042025-003");
stage.setTitle("MultiCam Capture App for ERHA 09042025-002");
stage.setScene(scene);
stage.setResizable(true);
stage.setMaximized(true);