Compare commits

20 Commits

Author SHA1 Message Date
668a477289 commit 27/08/2025 2025-08-27 13:02:06 +07:00
8f85e3fccd commit 22/08/2025 2025-08-22 08:11:36 +07:00
f7c74304f5 commit 18/08/2025 2025-08-18 16:27:27 +07:00
d566e4bc4f commit 25/07/2025 2025-07-25 09:32:00 +07:00
49ac4353b3 commit 22/07/2025 2025-07-22 11:33:13 +07:00
3539fb7d65 commit 10/07/2025 2025-07-10 08:48:12 +07:00
e4d2bfa00e commit 27/2025
Tambah Crop Setting
2025-06-30 16:31:57 +07:00
0728ae3ca2 commit 27/2025
Tambah Crop Setting
2025-05-27 14:47:52 +07:00
957d642f5d commit 27/2025
Tambah Crop Setting
2025-05-27 12:38:38 +07:00
3d02641479 commit 27/2025
Tambah Crop Setting
2025-05-27 11:28:56 +07:00
d0fe8123e9 commit 27/2025
Tambah Crop Setting
2025-05-27 11:26:29 +07:00
9e69714ae1 commit 26/05/2025
Tambah Crop Setting
2025-05-26 15:05:02 +07:00
9effe8a7a9 commit 26/05/2025
Tambah Crop Setting
2025-05-26 14:21:22 +07:00
rdkartono
1fcd905b07 commit 22/05/2025 2025-05-22 11:25:54 +07:00
rdkartono
ee0a82aa32 commit 21/05/2025 2025-05-21 15:42:09 +07:00
rdkartono
d1a6dd9f98 commit 21/05/2025 2025-05-21 15:35:18 +07:00
rdkartono
e47e1252fb Merge branch 'master' of https://gitea.rdkartono.my.id/rdkartono/ErhaCam 2025-05-20 10:32:55 +07:00
rdkartono
ef94f87d8a revisi 09/05/2025
Detectors.java :
* scaleFactor 1.05 become 1.2
* minNeighbors 3 become 5
* haarcascade_frontalface_default.xml become haarcascade_frontalface_alt.xml
CaptureView.java :
OpenCVFrameGrabber grabber = new OpenCVFrameGrabber(devicenumber) become OpenCVFrameGrabber grabber = new OpenCVFrameGrabber(CAP_DSHOW+devicenumber);

MainApplication.java :
stage.setTitle("MultiCam Capture App for ERHA 09052025-001")
2025-05-20 10:32:38 +07:00
rdkartono
4e3381edc4 Merge branch 'master' of https://gitea.rdkartono.my.id/rdkartono/ErhaCam
# Conflicts:
#	out/artifacts/ErhaCam_jar/ErhaCam.jar
2025-05-13 15:19:48 +07:00
rdkartono
1d617a0559 revisi 09/05/2025
Detectors.java :
* scaleFactor 1.05 become 1.2
* minNeighbors 3 become 5
* haarcascade_frontalface_default.xml become haarcascade_frontalface_alt.xml
CaptureView.java :
OpenCVFrameGrabber grabber = new OpenCVFrameGrabber(devicenumber) become OpenCVFrameGrabber grabber = new OpenCVFrameGrabber(CAP_DSHOW+devicenumber);

MainApplication.java :
stage.setTitle("MultiCam Capture App for ERHA 09052025-001")
2025-05-13 15:19:29 +07:00
30 changed files with 68023 additions and 734 deletions

3
.gitignore vendored
View File

@@ -2,6 +2,9 @@ target/
!.mvn/wrapper/maven-wrapper.jar !.mvn/wrapper/maven-wrapper.jar
!**/src/main/**/target/ !**/src/main/**/target/
!**/src/test/**/target/ !**/src/test/**/target/
logs/
out/
hs_err*.log
### IntelliJ IDEA ### ### IntelliJ IDEA ###
.idea/modules.xml .idea/modules.xml

View File

@@ -1,9 +1,10 @@
<component name="InspectionProjectProfileManager"> <component name="InspectionProjectProfileManager">
<profile version="1.0"> <profile version="1.0">
<option name="myName" value="Project Default" /> <option name="myName" value="Project Default" />
<inspection_tool class="AutoCloseableResource" enabled="true" level="WARNING" enabled_by_default="true"> <inspection_tool class="AutoCloseableResource" enabled="false" level="WARNING" enabled_by_default="false">
<option name="METHOD_MATCHER_CONFIG" value="java.util.Formatter,format,java.io.Writer,append,com.google.common.base.Preconditions,checkNotNull,org.hibernate.Session,close,java.io.PrintWriter,printf,java.io.PrintStream,printf,java.lang.foreign.Arena,ofAuto,java.lang.foreign.Arena,global,java.util.concurrent.Executors,newFixedThreadPool" /> <option name="METHOD_MATCHER_CONFIG" value="java.util.Formatter,format,java.io.Writer,append,com.google.common.base.Preconditions,checkNotNull,org.hibernate.Session,close,java.io.PrintWriter,printf,java.io.PrintStream,printf,java.lang.foreign.Arena,ofAuto,java.lang.foreign.Arena,global,java.util.concurrent.Executors,newFixedThreadPool" />
</inspection_tool> </inspection_tool>
<inspection_tool class="ClassEscapesItsScope" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="CommentedOutCode" enabled="true" level="WEAK WARNING" enabled_by_default="true"> <inspection_tool class="CommentedOutCode" enabled="true" level="WEAK WARNING" enabled_by_default="true">
<option name="minLines" value="3" /> <option name="minLines" value="3" />
</inspection_tool> </inspection_tool>
@@ -15,5 +16,6 @@
<option name="processComments" value="true" /> <option name="processComments" value="true" />
</inspection_tool> </inspection_tool>
<inspection_tool class="SqlNoDataSourceInspection" enabled="false" level="WARNING" enabled_by_default="false" /> <inspection_tool class="SqlNoDataSourceInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="UnusedReturnValue" enabled="false" level="WARNING" enabled_by_default="false" />
</profile> </profile>
</component> </component>

BIN
IU photoboth-01.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

BIN
IU photoboth-02.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 253 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 452 KiB

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 452 KiB

View File

@@ -1,9 +1,29 @@
#Tue May 13 14:43:52 WIB 2025 #Wed Aug 27 11:07:31 WIB 2025
AudioPhase1=C\:\\Users\\Erha\\IdeaProjects\\ErhaCam\\audio\\phase1.mp3 AudioPhase1=C\:\\Users\\rdkar\\OneDrive\\Documents\\IntelliJ Project\\ErhaCam\\audio\\phase1.mp3
AudioPhase2=C\:\\Users\\Erha\\IdeaProjects\\ErhaCam\\audio\\phase2.mp3 AudioPhase2=C\:\\Users\\rdkar\\OneDrive\\Documents\\IntelliJ Project\\ErhaCam\\audio\\phase2.mp3
AudioPhase3=C\:\\Users\\Erha\\IdeaProjects\\ErhaCam\\audio\\phase3.mp3 AudioPhase3=C\:\\Users\\rdkar\\OneDrive\\Documents\\IntelliJ Project\\ErhaCam\\audio\\phase3.mp3
AudioPhase4=C\:\\Users\\Erha\\IdeaProjects\\ErhaCam\\audio\\phase4.mp3 AudioPhase4=C\:\\Users\\rdkar\\OneDrive\\Documents\\IntelliJ Project\\ErhaCam\\audio\\phase4.mp3
AudioPhase5=C\:\\Users\\Erha\\IdeaProjects\\ErhaCam\\audio\\phase5.mp3 AudioPhase5=C\:\\Users\\rdkar\\OneDrive\\Documents\\IntelliJ Project\\ErhaCam\\audio\\phase5.mp3
Cam1BottomCrop=20.0
Cam1LeftCrop=8.0
Cam1RightCrop=8.0
Cam1TopCrop=10.0
Cam2BottomCrop=20.0
Cam2LeftCrop=8.0
Cam2RightCrop=8.0
Cam2TopCrop=10.0
Cam3BottomCrop=20.0
Cam3LeftCrop=8.0
Cam3RightCrop=8.0
Cam3TopCrop=10.0
Cam4BottomCrop=20.0
Cam4LeftCrop=8.0
Cam4RightCrop=8.0
Cam4TopCrop=10.0
Cam5BottomCrop=20.0
Cam5LeftCrop=8.0
Cam5RightCrop=8.0
Cam5TopCrop=10.0
CameraCenter= CameraCenter=
CameraConfigCenter={"Brightness"\:0.0,"Contrast"\:0.0,"Saturation"\:0.0,"Hue"\:0.0,"Gain"\:1.0,"Exposure"\:1.0,"Sharpness"\:0.0,"Gamma"\:0.0,"AutoExposure"\:true,"AutoFocus"\:true,"AutoWhiteBalance"\:true} CameraConfigCenter={"Brightness"\:0.0,"Contrast"\:0.0,"Saturation"\:0.0,"Hue"\:0.0,"Gain"\:1.0,"Exposure"\:1.0,"Sharpness"\:0.0,"Gamma"\:0.0,"AutoExposure"\:true,"AutoFocus"\:true,"AutoWhiteBalance"\:true}
CameraConfigLeft45={"Brightness"\:0.0,"Contrast"\:0.0,"Saturation"\:0.0,"Hue"\:0.0,"Gain"\:1.0,"Exposure"\:1.0,"Sharpness"\:0.0,"Gamma"\:0.0,"AutoExposure"\:true,"AutoFocus"\:true,"AutoWhiteBalance"\:true} CameraConfigLeft45={"Brightness"\:0.0,"Contrast"\:0.0,"Saturation"\:0.0,"Hue"\:0.0,"Gain"\:1.0,"Exposure"\:1.0,"Sharpness"\:0.0,"Gamma"\:0.0,"AutoExposure"\:true,"AutoFocus"\:true,"AutoWhiteBalance"\:true}
@@ -19,11 +39,12 @@ FTPPass=password
FTPPath=/ FTPPath=/
FTPPort=21 FTPPort=21
FTPUser=user FTPUser=user
FlipCamera=true FlipCamera=false
MirrorCamera=true MirrorCamera=false
PhotoDirectory=C\:\\Users\\rdkar\\OneDrive\\Desktop\\Erha Capture PhotoDirectory=D\:\\Capture
SharpnessThreshold=1000.0 Production=false
cascadeMaxSize=500 SharpnessThreshold=850.0
cascadeMaxSize=360
cascadeMinNeighbors=3 cascadeMinNeighbors=3
cascadeMinSize=250 cascadeMinSize=250
cascadeScaleFactor=1.1 cascadeScaleFactor=1.05

Binary file not shown.

12213
haarcascade_eye.xml Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

29690
haarcascade_profileface.xml Normal file

File diff suppressed because it is too large Load Diff

BIN
hubungistafkami.wav Normal file

Binary file not shown.

Binary file not shown.

View File

@@ -1,6 +1,6 @@
package Config; package Config;
import id.co.gtc.erhacam.Detectors; import id.co.gtc.erhacam.MainApplication;
import lombok.Getter; import lombok.Getter;
import org.tinylog.Logger; import org.tinylog.Logger;
@@ -47,9 +47,35 @@ public class ConfigFile {
private @Getter boolean MirrorCamera = false; private @Getter boolean MirrorCamera = false;
private @Getter boolean FlipCamera = false; private @Getter boolean FlipCamera = false;
private @Getter boolean isProduction = true;
private @Getter double SharpnessThreshold; private @Getter double SharpnessThreshold;
private @Getter final double topcropmax = 30.0;
private @Getter final double bottomcropmax = 30.0;
private @Getter final double leftcropmax = 30.0;
private @Getter final double rightcropmax = 30.0;
private @Getter double Cam1TopCrop = 10.0;
private @Getter double Cam1BottomCrop = 20.0;
private @Getter double Cam1LeftCrop = 8.0;
private @Getter double Cam1RightCrop = 8.0;
private @Getter double Cam2TopCrop = 10.0;
private @Getter double Cam2BottomCrop = 20.0;
private @Getter double Cam2LeftCrop = 8.0;
private @Getter double Cam2RightCrop = 8.0;
private @Getter double Cam3TopCrop = 10.0;
private @Getter double Cam3BottomCrop = 20.0;
private @Getter double Cam3LeftCrop = 8.0;
private @Getter double Cam3RightCrop = 8.0;
private @Getter double Cam4TopCrop = 10.0;
private @Getter double Cam4BottomCrop = 20.0;
private @Getter double Cam4LeftCrop = 8.0;
private @Getter double Cam4RightCrop = 8.0;
private @Getter double Cam5TopCrop = 10.0;
private @Getter double Cam5BottomCrop = 20.0;
private @Getter double Cam5LeftCrop = 10.0;
private @Getter double Cam5RightCrop = 10.0;
private boolean needsave = false; private boolean needsave = false;
public ConfigFile(){ public ConfigFile(){
@@ -58,6 +84,195 @@ public class ConfigFile {
Load(); Load();
} }
public void setAPI(boolean isProduction){
if (this.isProduction != isProduction){
this.isProduction = isProduction;
needsave = true;
}
}
public void setCam1TopCrop(String value){
if (ValidDouble(value)){
double v = toDouble(value);
if (v != Cam1TopCrop && v <= topcropmax && v >= 0.0){
Cam1TopCrop = v;
needsave = true;
}
}
}
public void setCam1BottomCrop(String value){
if (ValidDouble(value)){
double v = toDouble(value);
if (v != Cam1BottomCrop && v <= bottomcropmax && v >= 0.0){
Cam1BottomCrop = v;
needsave = true;
}
}
}
public void setCam1LeftCrop(String value){
if (ValidDouble(value)){
double v = toDouble(value);
if (v != Cam1LeftCrop && v <= leftcropmax && v >= 0.0){
Cam1LeftCrop = v;
needsave = true;
}
}
}
public void setCam1RightCrop(String value){
if (ValidDouble(value)){
double v = toDouble(value);
if (v != Cam1RightCrop && v <= rightcropmax && v >= 0.0){
Cam1RightCrop = v;
needsave = true;
}
}
}
public void setCam2TopCrop(String value){
if (ValidDouble(value)){
double v = toDouble(value);
if (v != Cam2TopCrop && v <= topcropmax && v >= 0.0){
Cam2TopCrop = v;
needsave = true;
}
}
}
public void setCam2BottomCrop(String value){
if (ValidDouble(value)){
double v = toDouble(value);
if (v != Cam2BottomCrop && v <= bottomcropmax && v >= 0.0){
Cam2BottomCrop = v;
needsave = true;
}
}
}
public void setCam2LeftCrop(String value){
if (ValidDouble(value)){
double v = toDouble(value);
if (v != Cam2LeftCrop && v <= leftcropmax && v >= 0.0){
Cam2LeftCrop = v;
needsave = true;
}
}
}
public void setCam2RightCrop(String value){
if (ValidDouble(value)){
double v = toDouble(value);
if (v != Cam2RightCrop && v <= rightcropmax && v >= 0.0){
Cam2RightCrop = v;
needsave = true;
}
}
}
public void setCam3TopCrop(String value){
if (ValidDouble(value)){
double v = toDouble(value);
if (v != Cam3TopCrop && v <= topcropmax && v >= 0.0){
Cam3TopCrop = v;
needsave = true;
}
}
}
public void setCam3BottomCrop(String value){
if (ValidDouble(value)){
double v = toDouble(value);
if (v != Cam3BottomCrop && v <= bottomcropmax && v >= 0.0){
Cam3BottomCrop = v;
needsave = true;
}
}
}
public void setCam3LeftCrop(String value){
if (ValidDouble(value)){
double v = toDouble(value);
if (v != Cam3LeftCrop && v <= leftcropmax && v >= 0.0){
Cam3LeftCrop = v;
needsave = true;
}
}
}
public void setCam3RightCrop(String value){
if (ValidDouble(value)){
double v = toDouble(value);
if (v != Cam3RightCrop && v <= rightcropmax && v >= 0.0){
Cam3RightCrop = v;
needsave = true;
}
}
}
public void setCam4TopCrop(String value){
if (ValidDouble(value)){
double v = toDouble(value);
if (v != Cam4TopCrop && v <= topcropmax && v >= 0.0){
Cam4TopCrop = v;
needsave = true;
}
}
}
public void setCam4BottomCrop(String value){
if (ValidDouble(value)){
double v = toDouble(value);
if (v != Cam4BottomCrop && v <= bottomcropmax && v >= 0.0){
Cam4BottomCrop = v;
needsave = true;
}
}
}
public void setCam4LeftCrop(String value){
if (ValidDouble(value)){
double v = toDouble(value);
if (v != Cam4LeftCrop && v <= leftcropmax && v >= 0.0){
Cam4LeftCrop = v;
needsave = true;
}
}
}
public void setCam4RightCrop(String value){
if (ValidDouble(value)){
double v = toDouble(value);
if (v != Cam4RightCrop && v <= rightcropmax && v >= 0.0){
Cam4RightCrop = v;
needsave = true;
}
}
}
public void setCam5TopCrop(String value){
if (ValidDouble(value)){
double v = toDouble(value);
if (v != Cam5TopCrop && v <= topcropmax && v >= 0.0){
Cam5TopCrop = v;
needsave = true;
}
}
}
public void setCam5BottomCrop(String value){
if (ValidDouble(value)){
double v = toDouble(value);
if (v != Cam5BottomCrop && v <= bottomcropmax && v >= 0.0){
Cam5BottomCrop = v;
needsave = true;
}
}
}
public void setCam5LeftCrop(String value){
if (ValidDouble(value)){
double v = toDouble(value);
if (v != Cam5LeftCrop && v <= leftcropmax && v >= 0.0){
Cam5LeftCrop = v;
needsave = true;
}
}
}
public void setCam5RightCrop(String value){
if (ValidDouble(value)){
double v = toDouble(value);
if (v != Cam5RightCrop && v <= rightcropmax && v >= 0.0){
Cam5RightCrop = v;
needsave = true;
}
}
}
public void setMirrorCamera(boolean value){ public void setMirrorCamera(boolean value){
if (MirrorCamera != value){ if (MirrorCamera != value){
MirrorCamera = value; MirrorCamera = value;
@@ -583,6 +798,7 @@ public class ConfigFile {
if (prop.getProperty("MirrorCamera") == null) allcorrect = false; if (prop.getProperty("MirrorCamera") == null) allcorrect = false;
if (prop.getProperty("FlipCamera") == null) allcorrect = false; if (prop.getProperty("FlipCamera") == null) allcorrect = false;
if (prop.getProperty("SharpnessThreshold") == null) allcorrect = false; if (prop.getProperty("SharpnessThreshold") == null) allcorrect = false;
if (prop.getProperty("Production") == null) allcorrect = false;
if (prop.getProperty(CameraConfigEnum.CameraConfigLeft90.toString()) == null) allcorrect = false; if (prop.getProperty(CameraConfigEnum.CameraConfigLeft90.toString()) == null) allcorrect = false;
@@ -596,6 +812,28 @@ public class ConfigFile {
if (prop.getProperty("cascadeMinSize") == null) allcorrect = false; if (prop.getProperty("cascadeMinSize") == null) allcorrect = false;
if (prop.getProperty("cascadeMaxSize") == null) allcorrect = false; if (prop.getProperty("cascadeMaxSize") == null) allcorrect = false;
if (prop.getProperty("Cam1TopCrop") == null) allcorrect = false;
if (prop.getProperty("Cam1BottomCrop") == null) allcorrect = false;
if (prop.getProperty("Cam1LeftCrop") == null) allcorrect = false;
if (prop.getProperty("Cam1RightCrop") == null) allcorrect = false;
if (prop.getProperty("Cam2TopCrop") == null) allcorrect = false;
if (prop.getProperty("Cam2BottomCrop") == null) allcorrect = false;
if (prop.getProperty("Cam2LeftCrop") == null) allcorrect = false;
if (prop.getProperty("Cam2RightCrop") == null) allcorrect = false;
if (prop.getProperty("Cam3TopCrop") == null) allcorrect = false;
if (prop.getProperty("Cam3BottomCrop") == null) allcorrect = false;
if (prop.getProperty("Cam3LeftCrop") == null) allcorrect = false;
if (prop.getProperty("Cam3RightCrop") == null) allcorrect = false;
if (prop.getProperty("Cam4TopCrop") == null) allcorrect = false;
if (prop.getProperty("Cam4BottomCrop") == null) allcorrect = false;
if (prop.getProperty("Cam4LeftCrop") == null) allcorrect = false;
if (prop.getProperty("Cam4RightCrop") == null) allcorrect = false;
if (prop.getProperty("Cam5TopCrop") == null) allcorrect = false;
if (prop.getProperty("Cam5BottomCrop") == null) allcorrect = false;
if (prop.getProperty("Cam5LeftCrop") == null) allcorrect = false;
if (prop.getProperty("Cam5RightCrop") == null) allcorrect = false;
if (allcorrect){ if (allcorrect){
AudioPhase1 = prop.getProperty("AudioPhase1"); AudioPhase1 = prop.getProperty("AudioPhase1");
AudioPhase2 = prop.getProperty("AudioPhase2"); AudioPhase2 = prop.getProperty("AudioPhase2");
@@ -622,20 +860,55 @@ public class ConfigFile {
ConfigRight45 = gson.fromJson(prop.getProperty(CameraConfigEnum.CameraConfigRight45.toString()), CameraConfig.class); ConfigRight45 = gson.fromJson(prop.getProperty(CameraConfigEnum.CameraConfigRight45.toString()), CameraConfig.class);
ConfigRight90 = gson.fromJson(prop.getProperty(CameraConfigEnum.CameraConfigRight90.toString()), CameraConfig.class); ConfigRight90 = gson.fromJson(prop.getProperty(CameraConfigEnum.CameraConfigRight90.toString()), CameraConfig.class);
cascadeScaleFactor = toDouble(prop.getProperty("cascadeScaleFactor")); cascadeScaleFactor = cascadetoDouble(prop.getProperty("cascadeScaleFactor"));
cascadeMinNeighbors = toInt(prop.getProperty("cascadeMinNeighbors")); cascadeMinNeighbors = toInt(prop.getProperty("cascadeMinNeighbors"));
cascadeMinSize = toInt(prop.getProperty("cascadeMinSize")); cascadeMinSize = toInt(prop.getProperty("cascadeMinSize"));
cascadeMaxSize = toInt(prop.getProperty("cascadeMaxSize")); cascadeMaxSize = toInt(prop.getProperty("cascadeMaxSize"));
Detectors.setFaceMaxSize(cascadeMaxSize); if (MainApplication.detectorsList!=null){
Detectors.setFaceMinSize(cascadeMinSize); MainApplication.detectorsList.forEach((i, d) -> {
Detectors.setScaleFactor(cascadeScaleFactor); if (d != null) {
System.out.println("Setting face detection parameters for detector: " + i);
d.setFaceMaxSize(cascadeMaxSize);
d.setFaceMinSize(cascadeMinSize);
d.setScaleFactor(cascadeScaleFactor);
}
});
}
// Detectors.setFaceMaxSize(cascadeMaxSize);
// Detectors.setFaceMinSize(cascadeMinSize);
// Detectors.setScaleFactor(cascadeScaleFactor);
MirrorCamera = toBoolean(prop.getProperty("MirrorCamera")); MirrorCamera = toBoolean(prop.getProperty("MirrorCamera"));
FlipCamera = toBoolean(prop.getProperty("FlipCamera")); FlipCamera = toBoolean(prop.getProperty("FlipCamera"));
isProduction = toBoolean(prop.getProperty("Production"));
SharpnessThreshold = toDouble(prop.getProperty("SharpnessThreshold")); SharpnessThreshold = toDouble(prop.getProperty("SharpnessThreshold"));
Cam1TopCrop = toDouble(prop.getProperty("Cam1TopCrop"));
Cam1BottomCrop = toDouble(prop.getProperty("Cam1BottomCrop"));
Cam1LeftCrop = toDouble(prop.getProperty("Cam1LeftCrop"));
Cam1RightCrop = toDouble(prop.getProperty("Cam1RightCrop"));
Cam2TopCrop = toDouble(prop.getProperty("Cam2TopCrop"));
Cam2BottomCrop = toDouble(prop.getProperty("Cam2BottomCrop"));
Cam2LeftCrop = toDouble(prop.getProperty("Cam2LeftCrop"));
Cam2RightCrop = toDouble(prop.getProperty("Cam2RightCrop"));
Cam3TopCrop = toDouble(prop.getProperty("Cam3TopCrop"));
Cam3BottomCrop = toDouble(prop.getProperty("Cam3BottomCrop"));
Cam3LeftCrop = toDouble(prop.getProperty("Cam3LeftCrop"));
Cam3RightCrop = toDouble(prop.getProperty("Cam3RightCrop"));
Cam4TopCrop = toDouble(prop.getProperty("Cam4TopCrop"));
Cam4BottomCrop = toDouble(prop.getProperty("Cam4BottomCrop"));
Cam4LeftCrop = toDouble(prop.getProperty("Cam4LeftCrop"));
Cam4RightCrop = toDouble(prop.getProperty("Cam4RightCrop"));
Cam5TopCrop = toDouble(prop.getProperty("Cam5TopCrop"));
Cam5BottomCrop = toDouble(prop.getProperty("Cam5BottomCrop"));
Cam5LeftCrop = toDouble(prop.getProperty("Cam5LeftCrop"));
Cam5RightCrop = toDouble(prop.getProperty("Cam5RightCrop"));
Logger.info("Config Loaded"); Logger.info("Config Loaded");
MakeDirectories(); MakeDirectories();
return; return;
@@ -651,7 +924,7 @@ public class ConfigFile {
CreateDefault(); CreateDefault();
} }
private double toDouble(String cascadeScaleFactor) { private double cascadetoDouble(String cascadeScaleFactor) {
try{ try{
return Double.parseDouble(cascadeScaleFactor); return Double.parseDouble(cascadeScaleFactor);
} catch (Exception e){ } catch (Exception e){
@@ -659,6 +932,14 @@ public class ConfigFile {
} }
} }
private double toDouble(String value) {
try{
return Double.parseDouble(value);
} catch (Exception e){
return 0.0;
}
}
private void CreateDefault(){ private void CreateDefault(){
AudioPhase1 = Path.of(currentDirectory, "audio", "phase1.mp3").toString(); AudioPhase1 = Path.of(currentDirectory, "audio", "phase1.mp3").toString();
AudioPhase2 = Path.of(currentDirectory, "audio", "phase2.mp3").toString(); AudioPhase2 = Path.of(currentDirectory, "audio", "phase2.mp3").toString();
@@ -675,22 +956,56 @@ public class ConfigFile {
FTPUser = "user"; FTPUser = "user";
FTPPass = "password"; FTPPass = "password";
FTPPath = "/"; FTPPath = "/";
PhotoDirectory = currentDirectory; PhotoDirectory = "D:\\Capture";
SetDefaultCameraConfig(ConfigLeft90); SetDefaultCameraConfig(ConfigLeft90);
SetDefaultCameraConfig(ConfigLeft45); SetDefaultCameraConfig(ConfigLeft45);
SetDefaultCameraConfig(ConfigCenter); SetDefaultCameraConfig(ConfigCenter);
SetDefaultCameraConfig(ConfigRight45); SetDefaultCameraConfig(ConfigRight45);
SetDefaultCameraConfig(ConfigRight90); SetDefaultCameraConfig(ConfigRight90);
cascadeScaleFactor = 1.1; cascadeScaleFactor = 1.05;
cascadeMinNeighbors = 3; cascadeMinNeighbors = 3;
cascadeMinSize = 250; cascadeMinSize = 250;
cascadeMaxSize = 500; cascadeMaxSize = 360;
Detectors.setFaceMaxSize(cascadeMaxSize); if (MainApplication.detectorsList!= null) MainApplication.detectorsList.forEach((i, d) -> {
Detectors.setFaceMinSize(cascadeMinSize); if (d != null) {
Detectors.setScaleFactor(cascadeScaleFactor); d.setFaceMaxSize(cascadeMaxSize);
d.setFaceMinSize(cascadeMinSize);
d.setScaleFactor(cascadeScaleFactor);
}
});
// Detectors.setFaceMaxSize(cascadeMaxSize);
// Detectors.setFaceMinSize(cascadeMinSize);
// Detectors.setScaleFactor(cascadeScaleFactor);
MirrorCamera = false; MirrorCamera = false;
FlipCamera = false; FlipCamera = false;
SharpnessThreshold = 300.0; isProduction = true;
SharpnessThreshold = 850.0;
Cam1TopCrop = 10.0;
Cam1BottomCrop = 20.0;
Cam1LeftCrop = 8.0;
Cam1RightCrop = 8.0;
Cam2TopCrop = 10.0;
Cam2BottomCrop = 20.0;
Cam2LeftCrop = 8.0;
Cam2RightCrop = 8.0;
Cam3TopCrop = 10.0;
Cam3BottomCrop = 20.0;
Cam3LeftCrop = 8.0;
Cam3RightCrop = 8.0;
Cam4TopCrop = 10.0;
Cam4BottomCrop = 20.0;
Cam4LeftCrop = 8.0;
Cam4RightCrop = 8.0;
Cam5TopCrop = 10.0;
Cam5BottomCrop = 20.0;
Cam5LeftCrop = 8.0;
Cam5RightCrop = 8.0;
Logger.info("Default Config Created"); Logger.info("Default Config Created");
needsave = true; needsave = true;
Save(); Save();
@@ -749,8 +1064,31 @@ public class ConfigFile {
prop.setProperty("MirrorCamera", String.valueOf(MirrorCamera)); prop.setProperty("MirrorCamera", String.valueOf(MirrorCamera));
prop.setProperty("FlipCamera", String.valueOf(FlipCamera)); prop.setProperty("FlipCamera", String.valueOf(FlipCamera));
prop.setProperty("Production", String.valueOf(isProduction));
prop.setProperty("SharpnessThreshold", String.valueOf(SharpnessThreshold)); prop.setProperty("SharpnessThreshold", String.valueOf(SharpnessThreshold));
prop.setProperty("Cam1TopCrop", String.valueOf(Cam1TopCrop));
prop.setProperty("Cam1BottomCrop", String.valueOf(Cam1BottomCrop));
prop.setProperty("Cam1LeftCrop", String.valueOf(Cam1LeftCrop));
prop.setProperty("Cam1RightCrop", String.valueOf(Cam1RightCrop));
prop.setProperty("Cam2TopCrop", String.valueOf(Cam2TopCrop));
prop.setProperty("Cam2BottomCrop", String.valueOf(Cam2BottomCrop));
prop.setProperty("Cam2LeftCrop", String.valueOf(Cam2LeftCrop));
prop.setProperty("Cam2RightCrop", String.valueOf(Cam2RightCrop));
prop.setProperty("Cam3TopCrop", String.valueOf(Cam3TopCrop));
prop.setProperty("Cam3BottomCrop", String.valueOf(Cam3BottomCrop));
prop.setProperty("Cam3LeftCrop", String.valueOf(Cam3LeftCrop));
prop.setProperty("Cam3RightCrop", String.valueOf(Cam3RightCrop));
prop.setProperty("Cam4TopCrop", String.valueOf(Cam4TopCrop));
prop.setProperty("Cam4BottomCrop", String.valueOf(Cam4BottomCrop));
prop.setProperty("Cam4LeftCrop", String.valueOf(Cam4LeftCrop));
prop.setProperty("Cam4RightCrop", String.valueOf(Cam4RightCrop));
prop.setProperty("Cam5TopCrop", String.valueOf(Cam5TopCrop));
prop.setProperty("Cam5BottomCrop", String.valueOf(Cam5BottomCrop));
prop.setProperty("Cam5LeftCrop", String.valueOf(Cam5LeftCrop));
prop.setProperty("Cam5RightCrop", String.valueOf(Cam5RightCrop));
try{ try{
prop.store(new FileOutputStream(Path.of(currentDirectory, "config.properties").toString()), null); prop.store(new FileOutputStream(Path.of(currentDirectory, "config.properties").toString()), null);
Logger.info("Config Saved"); Logger.info("Config Saved");

View File

@@ -12,6 +12,7 @@ import javafx.scene.control.TextField;
import javafx.scene.image.Image; import javafx.scene.image.Image;
import javafx.scene.image.PixelFormat; import javafx.scene.image.PixelFormat;
import javafx.scene.image.WritableImage; import javafx.scene.image.WritableImage;
import javafx.stage.Stage;
import lombok.NonNull; import lombok.NonNull;
import org.bytedeco.javacv.Frame; import org.bytedeco.javacv.Frame;
import org.bytedeco.javacv.Java2DFrameConverter; import org.bytedeco.javacv.Java2DFrameConverter;
@@ -42,6 +43,8 @@ import java.time.format.DateTimeFormatter;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.function.Consumer;
import static org.bytedeco.opencv.global.opencv_core.CV_64F; import static org.bytedeco.opencv.global.opencv_core.CV_64F;
@@ -147,6 +150,19 @@ public class SomeCodes {
} }
} }
public static void WaitAsync(long millis, String threadname, Consumer<Void> success){
Thread thread = new Thread(()->{
try {
Thread.sleep(millis);
} catch (Exception ignored){ }
if (success != null) success.accept(null);
});
thread.setName(threadname);
thread.setDaemon(true);
thread.start();
}
/** /**
* Make thumbfile from source jpg file * Make thumbfile from source jpg file
* @param sourcejpg source jpg file * @param sourcejpg source jpg file
@@ -629,7 +645,7 @@ public class SomeCodes {
* Check if Region of Interest 1 is same with Region of Interest 2 * Check if Region of Interest 1 is same with Region of Interest 2
* @param ROI1 Region of Interest 1 * @param ROI1 Region of Interest 1
* @param ROI2 Region of Interest 2 * @param ROI2 Region of Interest 2
* @return true if same, false if not same * @return true if same, false if different
*/ */
public static boolean IsSameROI(Rect ROI1, Rect ROI2){ public static boolean IsSameROI(Rect ROI1, Rect ROI2){
if (ValidROI(ROI1) && ValidROI(ROI2)){ if (ValidROI(ROI1) && ValidROI(ROI2)){
@@ -764,7 +780,7 @@ public class SomeCodes {
public static double CalculateSharpness(String filename){ public static double CalculateSharpness(String filename){
if (ValidFile(filename)){ if (ValidFile(filename)){
try(Mat mat = opencv_imgcodecs.imread(filename)){ try(Mat mat = opencv_imgcodecs.imread(filename)){
return CalculateSharpness(new UMat(mat)); return CalculateSharpness(mat);
} catch (Exception e){ } catch (Exception e){
Logger.error("Error calculating sharpness: "+filename+", Msg : "+e.getMessage()); Logger.error("Error calculating sharpness: "+filename+", Msg : "+e.getMessage());
} }
@@ -802,23 +818,23 @@ public class SomeCodes {
* @param mat image in UMat format, expected in gray scale * @param mat image in UMat format, expected in gray scale
* @return sharpness value * @return sharpness value
*/ */
public static double CalculateSharpness(UMat mat){ public static double CalculateSharpness(Mat mat){
if (mat!=null && !mat.empty()){ if (mat!=null && !mat.empty()){
if (mat.channels()!=1){ if (mat.channels()!=1){
UMat grey = new UMat(); Mat grey = new Mat();
opencv_imgproc.cvtColor(mat, grey, opencv_imgproc.COLOR_BGR2GRAY); opencv_imgproc.cvtColor(mat, grey, opencv_imgproc.COLOR_BGR2GRAY);
mat = grey; mat = grey;
} }
opencv_imgproc.equalizeHist(mat, mat); opencv_imgproc.equalizeHist(mat, mat);
UMat laplacian = new UMat(); Mat laplacian = new Mat();
opencv_imgproc.Laplacian(mat, laplacian, CV_64F); opencv_imgproc.Laplacian(mat, laplacian, CV_64F);
UMat mean = new UMat(1,1, CV_64F); Mat mean = new Mat(1,1, CV_64F);
UMat stddev = new UMat(1,1, CV_64F); Mat stddev = new Mat(1,1, CV_64F);
opencv_core.meanStdDev(laplacian, mean, stddev); opencv_core.meanStdDev(laplacian, mean, stddev);
@@ -833,7 +849,7 @@ public class SomeCodes {
return 0; return 0;
} }
public static boolean IsBlurred(UMat mat, double threshold){ public static boolean IsBlurred(Mat mat, double threshold){
return CalculateSharpness(mat)<threshold; return CalculateSharpness(mat)<threshold;
} }
@@ -865,4 +881,30 @@ public class SomeCodes {
mat.data().get(data); mat.data().get(data);
return image; return image;
} }
/**
* Close Stage if not null
* and wait until closed
* @param obj Stage object to close
*/
public static void closeStage(Stage obj) throws InterruptedException {
if (obj != null) {
if (Platform.isFxApplicationThread()) {
// Already on FX thread, just close directly
obj.close();
} else {
CountDownLatch latch = new CountDownLatch(1);
Platform.runLater(() -> {
try {
obj.close();
} finally {
latch.countDown();
}
});
// Wait for runLater task to finish
latch.await();
}
}
}
} }

View File

@@ -0,0 +1,10 @@
package ErhaAPI;
import lombok.Data;
@Data
public class BarcodeHttpResult {
private int statusCode;
private String body;
private BarcodeResult result;
}

View File

@@ -1,7 +1,7 @@
package ErhaAPI; package ErhaAPI;
public class BarcodeResullt { public class BarcodeResult {
public int currentPage; public int currentPage;
public int limit; public int limit;
public int totalPages; public int totalPages;

View File

@@ -3,15 +3,19 @@ package ErhaAPI;
import Config.SomeCodes; import Config.SomeCodes;
import com.google.gson.Gson; import com.google.gson.Gson;
import lombok.Getter; import lombok.Getter;
import lombok.NonNull;
import org.tinylog.Logger; import org.tinylog.Logger;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.net.ConnectException;
import java.net.URI; import java.net.URI;
import java.net.UnknownHostException;
import java.net.http.HttpClient; import java.net.http.HttpClient;
import java.net.http.HttpRequest; import java.net.http.HttpRequest;
import java.net.http.HttpResponse; import java.net.http.HttpResponse;
import java.net.http.HttpTimeoutException;
import java.nio.file.Files; import java.nio.file.Files;
import java.util.Base64; import java.util.Base64;
import java.util.UUID; import java.util.UUID;
@@ -26,7 +30,7 @@ public class ErhaAPI {
private String auth; private String auth;
private final String API_URL; private String API_URL="";
private final Gson gson = new Gson(); private final Gson gson = new Gson();
@@ -35,11 +39,25 @@ public class ErhaAPI {
* @param isProduction if true will use Production URL, if false will use Staging URL * @param isProduction if true will use Production URL, if false will use Staging URL
*/ */
public ErhaAPI(boolean isProduction){ public ErhaAPI(boolean isProduction){
setProduction(isProduction);
}
public void setProduction(boolean isProduction){
final String API_URL_PROD = "https://connect-api.aryanoble.co.id/api"; final String API_URL_PROD = "https://connect-api.aryanoble.co.id/api";
final String API_URL_STAGING = "https://connect-api-staging.aryanoble.web.id/api"; final String API_URL_STAGING = "https://connect-api-staging.aryanoble.web.id/api";
API_URL = isProduction ? API_URL_PROD : API_URL_STAGING; if (isProduction){
if (!API_URL.equals(API_URL_PROD)){
API_URL = API_URL_PROD;
update_auth(); update_auth();
} }
} else {
if (!API_URL.equals(API_URL_STAGING)){
API_URL = API_URL_STAGING;
update_auth();
}
}
}
/** /**
* Set API Username * Set API Username
* @param API_USERNAME API Username * @param API_USERNAME API Username
@@ -68,14 +86,16 @@ public class ErhaAPI {
} }
} }
/** /**
* Validate Barcode data * Validate Barcode data
* @param Barcode Barcode to verify * @param Barcode Barcode to verify
* @return BarcodeResullt object if success, or null if failed * @return BarcodeHttpResult object
*/ */
public BarcodeResullt Validate_Barcode(String Barcode, boolean printdebug){ public @NonNull BarcodeHttpResult Validate_Barcode(String Barcode, boolean printdebug){
BarcodeHttpResult bhr = new BarcodeHttpResult();
if (ValidBarCode(Barcode)){ if (ValidBarCode(Barcode)){
System.out.println("Checking Barcode : " + Barcode +" on "+(config.isProduction()?"Production":"Staging")+" API " );
try (HttpClient client = HttpClient.newHttpClient()) { try (HttpClient client = HttpClient.newHttpClient()) {
int medical_record_detail_id = toInt(Barcode); int medical_record_detail_id = toInt(Barcode);
HttpRequest request = HttpRequest.newBuilder() HttpRequest request = HttpRequest.newBuilder()
@@ -84,6 +104,11 @@ public class ErhaAPI {
.GET() .GET()
.build(); .build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString()); HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
bhr.setStatusCode(response.statusCode());
bhr.setBody(response.body());
if (response.statusCode()==200){ if (response.statusCode()==200){
String body = response.body(); String body = response.body();
if (printdebug){ if (printdebug){
@@ -91,19 +116,36 @@ public class ErhaAPI {
System.out.println("Validate_Barcode HTTP body : "); System.out.println("Validate_Barcode HTTP body : ");
System.out.println(body); System.out.println(body);
} }
return gson.fromJson(body, BarcodeResullt.class); BarcodeResult brr = gson.fromJson(body, BarcodeResult.class);
bhr.setResult(brr);
} else { } else {
Logger.error("Validate_Barcode failed, status code : " , response.statusCode()); Logger.error("Validate_Barcode failed, status code : " , response.statusCode());
} }
} catch (IOException e) { } catch (UnknownHostException e) {
Logger.error("Validate_Barcode IO Exception, Msg : " , e.getMessage()); // no internet or no DNS
} catch (InterruptedException e) { Logger.error("Validate_Barcode UnknownHostException, Msg : " , e.getMessage());
Logger.error("Validate_Barcode Interrupted Exception, Msg : " , e.getMessage()); bhr.setStatusCode(0);
bhr.setBody("No internet connection or DNS error");
} catch (ConnectException e) {
// connection refused
Logger.error("Validate_Barcode ConnectException , Msg : " , e.getMessage());
bhr.setStatusCode(-1);
bhr.setBody("Connection refused");
} catch( HttpTimeoutException e){
// timeout
Logger.error("Validate_Barcode HttpTimeoutException, Msg : " , e.getMessage());
bhr.setStatusCode(408);
bhr.setBody("Http Request timeout");
} catch (IOException | InterruptedException e) {
// generic network error
Logger.error("Validate_Barcode IOException/InterruptedException, Msg : " , e.getMessage());
bhr.setStatusCode(-2);
bhr.setBody("Network error: " + e.getMessage());
} }
} }
return null; return bhr;
} }
@@ -113,9 +155,10 @@ public class ErhaAPI {
* Upload File * Upload File
* @param patientID Patient ID * @param patientID Patient ID
* @param filename File to upload * @param filename File to upload
* @return null if failed, or response body if success * @return UploadHttpResult object
*/ */
public UploadResult Upload_File(String patientID, String filename, boolean printdebug) { public @NonNull UploadHttpResult Upload_File(String patientID, String filename, boolean printdebug) {
UploadHttpResult uhr = new UploadHttpResult();
if (ValidMedicalRecordId(patientID)){ if (ValidMedicalRecordId(patientID)){
int medical_record_detail_id = toInt(patientID); int medical_record_detail_id = toInt(patientID);
if (ValidFile(filename)){ if (ValidFile(filename)){
@@ -148,7 +191,7 @@ public class ErhaAPI {
endBoundary.getBytes() endBoundary.getBytes()
); );
System.out.println("Uploading file : " + filename + " to " + (config.isProduction()?"Production":"Staging")+" API ");
HttpRequest request = HttpRequest.newBuilder() HttpRequest request = HttpRequest.newBuilder()
.uri(new URI(API_URL + "/photobooth/photobooth")) .uri(new URI(API_URL + "/photobooth/photobooth"))
.header("Authorization", "Basic " + auth) .header("Authorization", "Basic " + auth)
@@ -159,6 +202,8 @@ public class ErhaAPI {
// Send request // Send request
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString()); HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
uhr.setStatusCode(response.statusCode());
uhr.setBody(response.body());
if (printdebug){ if (printdebug){
System.out.println("Upload_File status code : " + response.statusCode()); System.out.println("Upload_File status code : " + response.statusCode());
System.out.println("Upload_File HTTP body : "); System.out.println("Upload_File HTTP body : ");
@@ -167,7 +212,8 @@ public class ErhaAPI {
if (response.statusCode()==200){ if (response.statusCode()==200){
return gson.fromJson(response.body(), UploadResult.class); UploadResult ur = gson.fromJson(response.body(), UploadResult.class);
uhr.setResult(ur);
} else { } else {
Logger.error("Upload_File file ",filename," failed, status code : " , response.statusCode()); Logger.error("Upload_File file ",filename," failed, status code : " , response.statusCode());
@@ -179,7 +225,7 @@ public class ErhaAPI {
} }
} }
} }
return null; return uhr;
} }

View File

@@ -0,0 +1,10 @@
package ErhaAPI;
import lombok.Data;
@Data
public class UploadHttpResult {
int statusCode;
String body;
UploadResult result;
}

View File

@@ -5,16 +5,14 @@ import javafx.animation.PauseTransition;
import javafx.animation.Timeline; import javafx.animation.Timeline;
import javafx.application.Platform; import javafx.application.Platform;
import javafx.geometry.Insets;
import javafx.geometry.Pos; import javafx.geometry.Pos;
import javafx.scene.Node; import javafx.scene.Node;
import javafx.scene.Scene; import javafx.scene.Scene;
import javafx.scene.control.Label; import javafx.scene.control.Label;
import javafx.scene.image.Image; import javafx.scene.image.Image;
import javafx.scene.image.ImageView; import javafx.scene.image.ImageView;
import javafx.scene.layout.BorderPane; import javafx.scene.layout.*;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color; import javafx.scene.paint.Color;
import javafx.scene.shape.Circle; import javafx.scene.shape.Circle;
import javafx.stage.*; import javafx.stage.*;
@@ -24,7 +22,6 @@ import org.tinylog.Logger;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Optional;
import java.util.function.Consumer; import java.util.function.Consumer;
import static Config.SomeCodes.*; import static Config.SomeCodes.*;
@@ -46,9 +43,9 @@ public class AutoCloseAlert {
public static void init(){ public static void init(){
String f_01 = ExtractResource("/IU photoboth-01.jpg"); String f_01 = ExtractResource("/IU photoboth-01.jpg");
System.out.println("Banner 01 extracted as file: "+f_01); //System.out.println("Banner 01 extracted as file: "+f_01);
String f_02 = ExtractResource("/IU photoboth-02.jpg"); String f_02 = ExtractResource("/IU photoboth-02.jpg");
System.out.println("Banner 02 extracted as file: "+f_02); //System.out.println("Banner 02 extracted as file: "+f_02);
banner_01 = LoadImage(f_01); banner_01 = LoadImage(f_01);
if (banner_01!=null) System.out.println("Banner 01 loaded"); else System.out.println("Banner 01 not loaded"); if (banner_01!=null) System.out.println("Banner 01 loaded"); else System.out.println("Banner 01 not loaded");
banner_02 = LoadImage(f_02); banner_02 = LoadImage(f_02);
@@ -84,8 +81,16 @@ public class AutoCloseAlert {
* Close the current alert if it is shown * Close the current alert if it is shown
*/ */
public static void close(){ public static void close(){
Optional.ofNullable(currentAlertStage).ifPresent(Stage::close); if (currentAlertStage!=null){
try {
closeStage(currentAlertStage);
} catch (InterruptedException e) {
Logger.error("Error closing alert stage: " + e.getMessage());
}
currentAlertStage = null; currentAlertStage = null;
}
clear(); clear();
} }
@@ -188,7 +193,12 @@ public class AutoCloseAlert {
timeline.getKeyFrames().add(new KeyFrame(Duration.seconds(0), event -> alertStage.show())); timeline.getKeyFrames().add(new KeyFrame(Duration.seconds(0), event -> alertStage.show()));
timeline.getKeyFrames().add(new KeyFrame(Duration.seconds(seconds* (pictures.length)), event -> { timeline.getKeyFrames().add(new KeyFrame(Duration.seconds(seconds* (pictures.length)), event -> {
alertStage.close(); try {
closeStage(alertStage);
} catch (InterruptedException e) {
Logger.error("Error closing alert stage: " + e.getMessage());
}
//alertStage.close();
if (currentAlertStage == alertStage) { if (currentAlertStage == alertStage) {
currentAlertStage = null; currentAlertStage = null;
} }
@@ -277,26 +287,37 @@ public class AutoCloseAlert {
double screenwidth = Screen.getPrimary().getBounds().getWidth(); double screenwidth = Screen.getPrimary().getBounds().getWidth();
double screenheight = Screen.getPrimary().getBounds().getHeight(); double screenheight = Screen.getPrimary().getBounds().getHeight();
double height = screenheight/4; double height = screenheight/4.0;
double width = height * 21/9; double width = height * 21.0/9.0;
VBox root = new VBox(10);
root.setPadding(new Insets(12));
root.setPrefSize(width, height);
root.setAlignment(Pos.CENTER);
List<Node> children = new ArrayList<>();
if (ValidString(header)){ if (ValidString(header)){
Label headerLabel = new Label(header); Label headerLabel = new Label(header);
headerLabel.setStyle("-fx-font-weight: bold; -fx-font-size: 28px;"); headerLabel.setStyle("-fx-font-weight: bold; -fx-font-size: 28px;");
headerLabel.setWrapText(true);
headerLabel.setMinHeight(height*0.25); headerLabel.setMinHeight(height*0.25);
children.add(headerLabel); headerLabel.setMaxWidth(Double.MAX_VALUE);
headerLabel.prefWidthProperty().bind(root.widthProperty());
root.getChildren().add(headerLabel);
} }
if (ValidString(content)){ if (ValidString(content)){
Label contentLabel = new Label(content); Label contentLabel = new Label(content);
contentLabel.setWrapText(true);
contentLabel.setStyle("-fx-font-size: 24px;"); contentLabel.setStyle("-fx-font-size: 24px;");
contentLabel.setMinHeight(height*0.75); contentLabel.setMinHeight(height*0.75);
children.add(contentLabel); contentLabel.setMaxWidth(Double.MAX_VALUE);
contentLabel.prefWidthProperty().bind(root.widthProperty());
VBox.setVgrow(contentLabel, Priority.ALWAYS);
contentLabel.setAlignment(Pos.TOP_CENTER);
root.getChildren().add(contentLabel);
} }
VBox root = new VBox(10, children.toArray(new Node[0]));
root.setPrefSize(width, height);
root.setAlignment(Pos.CENTER);
Scene scene = new Scene(root); Scene scene = new Scene(root);
alertStage.setScene(scene); alertStage.setScene(scene);
@@ -325,10 +346,16 @@ public class AutoCloseAlert {
* @param alertStage the alert stage to be closed * @param alertStage the alert stage to be closed
*/ */
private static void closeAlertStage(int seconds, Consumer<String> onClose, Stage alertStage) { private static void closeAlertStage(int seconds, Consumer<String> onClose, Stage alertStage) {
if (seconds>0){ if (seconds>0){
PauseTransition delay = new PauseTransition(Duration.seconds(seconds)); PauseTransition delay = new PauseTransition(Duration.seconds(seconds));
delay.setOnFinished(e -> { delay.setOnFinished(e -> {
alertStage.close(); try {
closeStage(alertStage);
} catch (InterruptedException err) {
Logger.error("Error closing alert stage: " + err.getMessage());
}
//alertStage.close();
if (currentAlertStage == alertStage) { if (currentAlertStage == alertStage) {
currentAlertStage = null; currentAlertStage = null;
} }
@@ -342,8 +369,7 @@ public class AutoCloseAlert {
private static Image LoadImage(String filename){ private static Image LoadImage(String filename){
if (ValidFile(filename)){ if (ValidFile(filename)){
try{ try{
Image mm = new Image(Paths.get(filename).toUri().toString()); return new Image(Paths.get(filename).toUri().toString());
return mm;
} catch (Exception e){ } catch (Exception e){
Logger.error("Error loading image: " + filename+", Message: "+e.getMessage()); Logger.error("Error loading image: " + filename+", Message: "+e.getMessage());
} }

View File

@@ -44,7 +44,6 @@ import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicLong;
import static Config.SomeCodes.*; import static Config.SomeCodes.*;
import static id.co.gtc.erhacam.Detectors.*;
import static org.bytedeco.opencv.global.opencv_imgproc.*; import static org.bytedeco.opencv.global.opencv_imgproc.*;
@SuppressWarnings({"unused"}) @SuppressWarnings({"unused"})
@@ -67,6 +66,11 @@ public class Cameradetail {
private LiveCamEvent event = null; private LiveCamEvent event = null;
private @Getter @Setter CameraConfigEnum cameraConfigEnum = CameraConfigEnum.CameraConfigCenter; private @Getter @Setter CameraConfigEnum cameraConfigEnum = CameraConfigEnum.CameraConfigCenter;
private @Getter int LiveFPS = 0; private @Getter int LiveFPS = 0;
private double topcrop = 0.0;
private double bottomcrop = 0.0;
private double leftcrop = 0.0;
private double rightcrop = 0.0;
private String title = "";
@@ -111,11 +115,11 @@ public class Cameradetail {
private @Getter final UMat BestMat = new UMat(); private @Getter final UMat BestMat = new UMat();
private @Getter final UMat LiveMat = new UMat(); private @Getter final UMat LiveMat = new UMat();
private @Getter final UMat ReducedMat = new UMat(); private @Getter final UMat ReducedMat = new UMat();
private @Getter final UMat GrayMat = new UMat(); private @Getter final Mat GrayMat = new Mat();
private @Getter Rect BestMatROI; // private @Getter Rect BestMatROI;
private @Getter Rect ReducedMatROI; // private @Getter Rect ReducedMatROI;
private @Getter Rect LiveMatROI; // private @Getter Rect LiveMatROI;
private boolean IsPortrait = false; private boolean IsPortrait = false;
@@ -151,7 +155,8 @@ public class Cameradetail {
private boolean use_qr = false; private boolean use_qr = false;
private boolean use_face = false; private boolean use_face = false;
private int _hardwareID = -1;
private @Getter Detectors detector;
private void setSliderValue(Slider sld, CameraProperty prop, double value){ private void setSliderValue(Slider sld, CameraProperty prop, double value){
@@ -235,6 +240,8 @@ public class Cameradetail {
raise_log("Exposure for "+getCameraTitle()+" changed to "+newVal); raise_log("Exposure for "+getCameraTitle()+" changed to "+newVal);
}); });
detector = new Detectors();
} }
@FXML @FXML
@@ -261,14 +268,20 @@ public class Cameradetail {
public void setCameraTitle(String title){ public void setCameraTitle(String title){
if (ValidString(title)){ if (ValidString(title)){
this.title = title;
LabelSetText(cameratitle, title,null); LabelSetText(cameratitle, title,null);
} }
} }
public void setSharpness_indicator(double value){ public void setSharpness_indicator(double value){
if (value >= config.getSharpnessThreshold()){ if (value < 0){
// not defined
LabelSetText(sharpness_indicator, "","");
} else if (value >= config.getSharpnessThreshold()){
// sharpness is good
LabelSetText(sharpness_indicator, "OK","-fx-text-fill: green; -fx-border-color: black"); LabelSetText(sharpness_indicator, "OK","-fx-text-fill: green; -fx-border-color: black");
} else { } else {
// sharpness is bad
LabelSetText(sharpness_indicator,"BAD","-fx-text-fill: red; -fx-border-color: black"); LabelSetText(sharpness_indicator,"BAD","-fx-text-fill: red; -fx-border-color: black");
} }
} }
@@ -413,7 +426,6 @@ public class Cameradetail {
} }
mGrabber = grabber; mGrabber = grabber;
_hardwareID = hardwareID;
} }
//Exposure and Focus Tricks : //Exposure and Focus Tricks :
@@ -678,7 +690,7 @@ public class Cameradetail {
* @return filename path of the saved photo, or null if failed * @return filename path of the saved photo, or null if failed
*/ */
public PhotoResult TakePhoto(String directory, String prefix) { public PhotoResult TakePhoto(String directory, String prefix) {
PhotoResult result = new PhotoResult(cameratitle.getText()); PhotoResult result = new PhotoResult(title);
if (!ValidDirectory(directory)) directory = currentDirectory; if (!ValidDirectory(directory)) directory = currentDirectory;
if (mGrabber!=null){ if (mGrabber!=null){
@@ -690,37 +702,48 @@ public class Cameradetail {
if (!BestMat.empty()){ if (!BestMat.empty()){
UMat cloned; UMat cloned = new UMat();
synchronized (BestMat){ synchronized (BestMat){
cloned = BestMat.clone(); BestMat.copyTo(cloned);
} }
// save BestMat at quality 9 PNG // save BestMat at quality 9 PNG
String filename = GetFullQualityPhotoPath(directory, prefix); String fullresfile = GetFullQualityPhotoPath(directory, prefix);
if (opencv_imgcodecs.imwrite(filename, cloned, parampng)){ if (opencv_imgcodecs.imwrite(fullresfile, cloned, parampng)){
result.setFullres(filename); result.setFullres(fullresfile);
} else System.out.println("TakePhoto failed, Unable to Save FullQUality Photo for camera "+cameratitle.getText()); } else System.out.println("TakePhoto failed, Unable to Save FullQUality Photo for camera "+title);
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 // save ReducedMat at 100% JPEG
String reducedfilename = GetReducedPhotoPath(directory, prefix); String reducedfile = GetReducedPhotoPath(directory, prefix);
opencv_imgproc.resize(cloned, ReducedMat, ReducedSize); opencv_imgproc.resize(cloned, ReducedMat, ReducedSize);
if (!opencv_imgcodecs.imwrite(reducedfilename, ReducedMat, paramjpeg)){ if (opencv_imgcodecs.imwrite(reducedfile, ReducedMat, paramjpeg)){
System.out.println("TakePhoto failed, Unable to Save Reduced Photo for camera "+cameratitle.getText()); result.setCompressedfile(reducedfile);
} else result.setCompressedfile(reducedfilename); } else {
System.out.println("TakePhoto failed, Unable to Save Reduced Photo for camera "+title);
String xy = CropReducedMat(directory, prefix, ReducedMatROI);
if (ValidFile(xy)){
result.setCompressedcrop(xy);
result.setReducedROI(new Rect(ReducedMatROI.x(), ReducedMatROI.y(), ReducedMatROI.width(), ReducedMatROI.height()));
} }
String fullcropfile = GetFullQualityCropPhotoPath(directory, prefix);
UMat croppedfull = CropMat(cloned);
if (croppedfull!=null && !croppedfull.empty()){
if (opencv_imgcodecs.imwrite(fullcropfile, croppedfull, parampng)){
result.setFullcrop(fullcropfile);
} else {
System.out.println("TakePhoto failed, Unable to Save FullCrop Photo for camera "+title);
}
} else System.out.println("TakePhoto failed, Unable to Save FullCrop Photo for camera "+title);
String reducedcropfile = GetReducedCropPhotoPath(directory, prefix);
UMat reducedcrop = CropMat(ReducedMat);
if (reducedcrop!=null && !reducedcrop.empty()){
if (opencv_imgcodecs.imwrite(reducedcropfile, reducedcrop, paramjpeg)){
result.setCompressedcrop(reducedcropfile);
} else {
System.out.println("TakePhoto failed, Unable to Save ReducedCrop Photo for camera "+title);
}
} else System.out.println("TakePhoto failed, Unable to Save ReducedCrop Photo for camera "+title);
cloned.release(); cloned.release();
} else raise_log("TakePhoto failed, Live View is Empty"); } else raise_log("TakePhoto failed, Live View is Empty");
@@ -740,9 +763,9 @@ public class Cameradetail {
public String CropBestMat(String directory, String prefix, Rect ROI){ public String CropBestMat(String directory, String prefix, Rect ROI){
UMat cloned; UMat cloned = new UMat();
synchronized (BestMat){ synchronized (BestMat){
cloned = BestMat.clone(); BestMat.copyTo(cloned);
} }
if (!cloned.empty()) { if (!cloned.empty()) {
if (ValidROI(ROI)){ if (ValidROI(ROI)){
@@ -787,22 +810,34 @@ public class Cameradetail {
private String makeFileName(String prefix, String extension){ private String makeFileName(String prefix, String extension){
LocalDateTime ldt = LocalDateTime.now(); LocalDateTime ldt = LocalDateTime.now();
String timetag = ldt.getYear() + "-" + ldt.getMonthValue() + "-" + ldt.getDayOfMonth() + "_" + ldt.getHour() + "-" + ldt.getMinute() + "-" + ldt.getSecond(); String timetag = ldt.getYear() + "-" + ldt.getMonthValue() + "-" + ldt.getDayOfMonth() + "_" + ldt.getHour() + "-" + ldt.getMinute() + "-" + ldt.getSecond();
return prefix+" "+timetag+" "+cameratitle.getText() + extension; return prefix+" "+timetag+" "+title + extension;
} }
@SuppressWarnings("SameParameterValue") @SuppressWarnings("SameParameterValue")
private String makeReducedFileName(String prefix, String extension){ private String makeReducedFileName(String prefix, String extension){
LocalDateTime ldt = LocalDateTime.now(); LocalDateTime ldt = LocalDateTime.now();
String timetag = ldt.getYear() + "-" + ldt.getMonthValue() + "-" + ldt.getDayOfMonth() + "_" + ldt.getHour() + "-" + ldt.getMinute() + "-" + ldt.getSecond(); String timetag = ldt.getYear() + "-" + ldt.getMonthValue() + "-" + ldt.getDayOfMonth() + "_" + ldt.getHour() + "-" + ldt.getMinute() + "-" + ldt.getSecond();
return prefix+" "+timetag+" "+cameratitle.getText() + "_reduced" + extension; return prefix+" "+timetag+" "+title + "_reduced" + extension;
} }
// public void Release(){
// if (mGrabber!=null){
// try{
// StopLiveView();
// mGrabber.release();
// mGrabber = null;
// } catch (Exception e){
// System.out.println("Release failed, Unable to Release Camera, Error: " + e.getMessage());
// }
// }
// }
public void StopLiveView(){ public void StopLiveView(){
Capturing.set(false); Capturing.set(false);
if (mGrabber!=null){ if (mGrabber!=null){
try{ try{
mGrabber.close(); mGrabber.close();
System.out.println("Camera "+cameratitle.getText()+" stopped"); System.out.println("Camera "+title+" stopped");
setCameraStatus("Camera Stopped"); setCameraStatus("Camera Stopped");
} catch (Exception e){ } catch (Exception e){
raise_log("StopLiveView failed, Unable to Stop Camera, Error: " + e.getMessage()); raise_log("StopLiveView failed, Unable to Stop Camera, Error: " + e.getMessage());
@@ -837,11 +872,8 @@ public class Cameradetail {
while(Capturing.get()){ while(Capturing.get()){
try { try {
qr_semaphore.acquire(); qr_semaphore.acquire();
UMat gray;
synchronized (lockObject){ String qr = DetectQRFromMat(GrayMat.clone());
gray = GrayMat;
}
String qr = DetectQRFromMat(gray);
if (ValidBarCode(qr)){ if (ValidBarCode(qr)){
if (event!=null) event.onDetectedQRCode(qr); if (event!=null) event.onDetectedQRCode(qr);
} }
@@ -866,18 +898,15 @@ public class Cameradetail {
while(Capturing.get()){ while(Capturing.get()){
try { try {
face_semaphore.acquire(); face_semaphore.acquire();
UMat gray;
synchronized (lockObject){
gray = GrayMat;
}
DetectorResult theface = null; DetectorResult theface = null;
boolean have_frontal_face = false; boolean have_frontal_face = false;
boolean have_left_45_face = false; boolean have_left_45_face = false;
int _face_width = 0; int _face_width = 0;
int _face_height = 0; int _face_height = 0;
List<DetectorResult> frontalfaces = detector.HaveFrontalFace(GrayMat.clone());
List<DetectorResult> frontalfaces = HaveFrontalFace(gray); //System.out.println("camera "+title+ (frontalfaces.isEmpty() ? " no frontal face detected" : " found "+frontalfaces.size()+" frontal faces"));
if (!frontalfaces.isEmpty()){ if (!frontalfaces.isEmpty()){
for(DetectorResult rect : frontalfaces){ for(DetectorResult rect : frontalfaces){
if (rect.haveFace() ){ if (rect.haveFace() ){
@@ -895,7 +924,8 @@ public class Cameradetail {
} else { } else {
// gak punya frontal face // gak punya frontal face
// coba cek punya profile left face 45 gak // coba cek punya profile left face 45 gak
List<DetectorResult> Left45Faces = HaveLeft45Face(gray); List<DetectorResult> Left45Faces = detector.HaveLeft45Face(GrayMat.clone());
//System.out.println("camera "+title+ (Left45Faces.isEmpty() ? " no left 45 face detected" : " found "+Left45Faces.size()+" left 45 faces"));
if (!Left45Faces.isEmpty()){ if (!Left45Faces.isEmpty()){
for(DetectorResult rect : Left45Faces){ for(DetectorResult rect : Left45Faces){
if (rect.haveFace()){ if (rect.haveFace()){
@@ -917,9 +947,10 @@ public class Cameradetail {
if (event!=null) event.onFrontalFaceDetector(true, _face_width, _face_height); if (event!=null) event.onFrontalFaceDetector(true, _face_width, _face_height);
LabelVisible(face_indicator,true); LabelVisible(face_indicator,true);
if (theface.getFace()!=null){ // Revisi 26/05/2025, LiveMatROI dihitung dari topcrop, bottomcrop, leftcrop dan rightcrop
LiveMatROI = new Rect(theface.getFace().x(), theface.getFace().y(), theface.getFace().width(), theface.getFace().height()); // if (theface.getFace()!=null){
} // LiveMatROI = new Rect(theface.getFace().x(), theface.getFace().y(), theface.getFace().width(), theface.getFace().height());
// }
if (theface.getEyesCount()>=2){ if (theface.getEyesCount()>=2){
// ada mata (buka mata) // ada mata (buka mata)
@@ -934,7 +965,7 @@ public class Cameradetail {
if (eye_state.get()!=1){ if (eye_state.get()!=1){
// transisi dari tutup mata ke buka mata // transisi dari tutup mata ke buka mata
if (eye_state.get()==-1) { if (eye_state.get()==-1) {
System.out.println("First Eye Detected from camera "+cameratitle.getText()); //System.out.println("First Eye Detected from camera "+title);
eye_state.set(1); eye_state.set(1);
} else { } else {
eye_state.set(1); eye_state.set(1);
@@ -948,13 +979,13 @@ public class Cameradetail {
long diff = now - last_blink.get(); long diff = now - last_blink.get();
// kalau beda waktu antara blink 1 dan blink 2 kurang dari 3 detik // kalau beda waktu antara blink 1 dan blink 2 kurang dari 3 detik
if (diff<=3000){ if (diff<=3000){
System.out.println("Double Blink Detected from camera "+cameratitle.getText()); //System.out.println("Double Blink Detected from camera "+title);
if (event!=null) event.onDoubleBlink((int)diff); if (event!=null) event.onDoubleBlink((int)diff);
} }
waiting_for_second_blink.set(false); waiting_for_second_blink.set(false);
} else { } else {
waiting_for_second_blink.set(true); waiting_for_second_blink.set(true);
System.out.println("First Blink Detected from camera "+cameratitle.getText()); //System.out.println("First Blink Detected from camera "+title);
} }
last_blink.set(now); last_blink.set(now);
} }
@@ -1008,13 +1039,19 @@ public class Cameradetail {
} }
if (LiveMat != null && !LiveMat.empty()){
Mat imgmat = new Mat(); Mat imgmat = new Mat();
LiveMat.copyTo(imgmat);
// copy back to CPU // copy back to CPU
LiveMat.copyTo(imgmat);
setCameraStream(MatToImage(imgmat)); setCameraStream(MatToImage(imgmat));
imgmat.release(); imgmat.release();
}
} catch (Exception e) { } catch (Exception e) {
//System.out.println(Thread.currentThread().getName()+" interrupted"); //System.out.println(Thread.currentThread().getName()+" interrupted");
} }
@@ -1030,11 +1067,11 @@ public class Cameradetail {
// 30 fps means 33 ms per frame // 30 fps means 33 ms per frame
// so if grab is quicker than 30 ms , its stale frame // so if grab is quicker than 30 ms , its stale frame
try{ try{
if (mGrabber==null) throw new FrameGrabber.Exception("Grabber is null"); if (mGrabber==null) throw new Exception("Grabber is null");
Frame frame = mGrabber.grab(); // grab frame Frame frame = mGrabber.grab(); // grab frame
delta = System.currentTimeMillis() - now; delta = System.currentTimeMillis() - now;
now = System.currentTimeMillis(); now = System.currentTimeMillis();
} catch (FrameGrabber.Exception ignored) { } catch (Exception ignored) {
} }
@@ -1060,11 +1097,10 @@ public class Cameradetail {
if (Capturing.get()) { if (Capturing.get()) {
try{ try{
if (mGrabber!=null) if (mGrabber==null) throw new Exception("Grabber is null");
frame = mGrabber.grab();
else throw new FrameGrabber.Exception("Grabber is null");
} catch (FrameGrabber.Exception e){ frame = mGrabber.grab();
} catch (Exception e){
if (Capturing.get()){ if (Capturing.get()){
// kalau ada exception padahal masih capturing. Kalau sudah tidak capturing, tidak peduli // kalau ada exception padahal masih capturing. Kalau sudah tidak capturing, tidak peduli
if (ValidString(e.getMessage())){ if (ValidString(e.getMessage())){
@@ -1118,15 +1154,29 @@ public class Cameradetail {
// rotate 90 degree counter clockwise karena kamera potrait // rotate 90 degree counter clockwise karena kamera potrait
opencv_core.rotate(originalmat, BestMat, opencv_core.ROTATE_90_COUNTERCLOCKWISE); opencv_core.rotate(originalmat, BestMat, opencv_core.ROTATE_90_COUNTERCLOCKWISE);
originalmat.release(); originalmat.release();
if (!BestMat.empty()) { if (!BestMat.empty()) {
// LiveMat and GrayMat are synchronized // LiveMat and GrayMat are synchronized
synchronized (lockObject){ synchronized (lockObject){
opencv_imgproc.resize(BestMat, LiveMat, LiveSize); // resize to LiveSize // resize BestMat to LiveSize
opencv_imgproc.cvtColor(LiveMat,GrayMat, COLOR_BGR2GRAY); // convert to grayscale UMat _liveMat = new UMat();
opencv_imgproc.resize(BestMat, _liveMat, LiveSize);
// crop LiveMat
UMat croppedLiveMat = CropMat(_liveMat);
if (croppedLiveMat!=null && !croppedLiveMat.empty()){
croppedLiveMat.copyTo(LiveMat);
croppedLiveMat.release();
} else {
_liveMat.copyTo(LiveMat);
_liveMat.release();
}
// convert to grayscale
Mat xx = new Mat();
LiveMat.copyTo(xx);
opencv_imgproc.cvtColor(xx,GrayMat, COLOR_BGR2GRAY);
} }
if (use_qr){ if (use_qr){
@@ -1140,19 +1190,73 @@ public class Cameradetail {
} }
} catch ( FrameGrabber.Exception fe){ } catch ( FrameGrabber.Exception fe){
System.out.println("FrameGrabber Exception in" + Thread.currentThread().getName() + " : " + fe.getMessage()); System.out.println("FrameGrabber Exception in" + Thread.currentThread().getName() + " : " + fe.getMessage());
fe.printStackTrace(); //fe.printStackTrace();
} catch (InterruptedException e) { } catch (InterruptedException e) {
System.out.println(Thread.currentThread().getName()+" interrupted"); System.out.println(Thread.currentThread().getName()+" interrupted");
} catch (Exception e){ } catch (Exception e){
System.out.println(Thread.currentThread().getName()+" exception : "+e.getMessage()); System.out.println(Thread.currentThread().getName()+" exception : "+e.getMessage());
e.printStackTrace(); //e.printStackTrace();
} }
} }
}); });
private UMat CropMat(UMat mat){
if (mat != null && !mat.empty()){
int width = mat.cols();
int height = mat.rows();
int x = (int) (width * leftcrop / 100);
int y = (int) (height * topcrop / 100);
int w = (int) (width * (100 - leftcrop - rightcrop) / 100);
int h = (int) (height * (100 - topcrop - bottomcrop) / 100);
// Ensure the crop dimensions are valid
if (x >= 0 && y >= 0 && w > 0 && h > 0 && x + w <= width && y + h <= height) {
Rect roi = new Rect(x, y, w, h);
UMat crop = new UMat();
mat.apply(roi).copyTo(crop);
return crop;
}
}
return null;
}
public void ChangeCropValue(){
if ("01".equals(title)){
topcrop = config.getCam1TopCrop();
leftcrop = config.getCam1LeftCrop();
rightcrop = config.getCam1RightCrop();
bottomcrop = config.getCam1BottomCrop();
} else if ("02".equals(title)){
topcrop = config.getCam2TopCrop();
leftcrop = config.getCam2LeftCrop();
rightcrop = config.getCam2RightCrop();
bottomcrop = config.getCam2BottomCrop();
} else if ("03".equals(title)){
topcrop = config.getCam3TopCrop();
leftcrop = config.getCam3LeftCrop();
rightcrop = config.getCam3RightCrop();
bottomcrop = config.getCam3BottomCrop();
} else if ("04".equals(title)){
topcrop = config.getCam4TopCrop();
leftcrop = config.getCam4LeftCrop();
rightcrop = config.getCam4RightCrop();
bottomcrop = config.getCam4BottomCrop();
} else if ("05".equals(title)){
topcrop = config.getCam5TopCrop();
leftcrop = config.getCam5LeftCrop();
rightcrop = config.getCam5RightCrop();
bottomcrop = config.getCam5BottomCrop();
}
}
public boolean StartLiveView(LiveCamEvent event, String cameratitle, boolean use_qr , boolean use_face) { public boolean StartLiveView(LiveCamEvent event, String cameratitle, boolean use_qr , boolean use_face) {
this.event = event; this.event = event;
this.title = cameratitle;
ChangeCropValue();
if (mGrabber != null) { if (mGrabber != null) {
try { try {
@@ -1180,6 +1284,7 @@ public class Cameradetail {
@Override @Override
public void run() { public void run() {
if (Capturing.get()){ if (Capturing.get()){
int fpsval = fps.getAndSet(0); int fpsval = fps.getAndSet(0);
if (fpsval!=LiveFPS){ if (fpsval!=LiveFPS){
LiveFPS = fpsval; LiveFPS = fpsval;
@@ -1229,83 +1334,78 @@ public class Cameradetail {
} }
// /**
/** // * Remap LiveMatROI to BestMatROI and ReducedMatROI Resolution
* Remap LiveMatROI to BestMatROI and ReducedMatROI Resolution // * @param scaleX scale factor for width
* @param scaleX scale factor for width // * @param scaleY scale factor for height
* @param scaleY scale factor for height // */
*/ // public void RemapROI(double scaleX, double scaleY, boolean printdebug){
public void RemapROI(double scaleX, double scaleY, boolean printdebug){ // BestMatROI = null;
BestMatROI = null; // ReducedMatROI = null;
ReducedMatROI = null; // if (ValidROI(LiveMatROI)){
if (ValidROI(LiveMatROI)){ // if (ROIInsideUMat(LiveMatROI, LiveMat)){
if (ROIInsideUMat(LiveMatROI, LiveMat)){ // if (printdebug) System.out.println("LiveMatROI camera "+cameratitle.getText()+" = "+RectToString(LiveMatROI));
if (printdebug) System.out.println("LiveMatROI camera "+cameratitle.getText()+" = "+RectToString(LiveMatROI)); //
// double scaleXBest = 1.0*BestSize.width()/LiveSize.width();
double scaleXBest = 1.0*BestSize.width()/LiveSize.width(); // double scaleYBest = 1.0*BestSize.height()/LiveSize.height();
double scaleYBest = 1.0*BestSize.height()/LiveSize.height(); // int XBest = (int) (LiveMatROI.x()*scaleXBest);
int XBest = (int) (LiveMatROI.x()*scaleXBest); // int YBest = (int) (LiveMatROI.y()*scaleYBest);
int YBest = (int) (LiveMatROI.y()*scaleYBest); // int WBest = (int) (LiveMatROI.width()*scaleXBest);
int WBest = (int) (LiveMatROI.width()*scaleXBest); // int HBest = (int) (LiveMatROI.height()*scaleYBest);
int HBest = (int) (LiveMatROI.height()*scaleYBest); // int deltaWBest = (int) (BestSize.width() * scaleX);
int deltaWBest = (int) (BestSize.width() * scaleX); // int deltaHBest = (int) (BestSize.height() * scaleY);
int deltaHBest = (int) (BestSize.height() * scaleY); // XBest = XBest - deltaWBest/2;
XBest = XBest - deltaWBest/2; // if (XBest<0) XBest = 0;
if (XBest<0) XBest = 0; // YBest = YBest - deltaHBest/2;
YBest = YBest - deltaHBest/2; // if (YBest<0) YBest = 0;
if (YBest<0) YBest = 0; // WBest = WBest + deltaWBest;
WBest = WBest + deltaWBest; // if (WBest>BestSize.width()) WBest = BestSize.width();
if (WBest>BestSize.width()) WBest = BestSize.width(); // HBest = HBest + deltaHBest;
HBest = HBest + deltaHBest; // if (HBest>BestSize.height()) HBest = BestSize.height();
if (HBest>BestSize.height()) HBest = BestSize.height(); // BestMatROI = new Rect(XBest, YBest, WBest, HBest);
BestMatROI = new Rect(XBest, YBest, WBest, HBest); //
// if (printdebug){
if (printdebug){ // System.out.println("scaleXBest = "+scaleXBest+" scaleYBest = "+scaleYBest);
System.out.println("scaleXBest = "+scaleXBest+" scaleYBest = "+scaleYBest); // System.out.println("BestMatROI camera "+cameratitle.getText()+" = "+RectToString(BestMatROI));
System.out.println("BestMatROI camera "+cameratitle.getText()+" = "+RectToString(BestMatROI)); // }
} //
// double scaleXReduced = 1.0*ReducedSize.width()/LiveSize.width();
double scaleXReduced = 1.0*ReducedSize.width()/LiveSize.width(); // double scaleYReduced = 1.0*ReducedSize.height()/LiveSize.height();
double scaleYReduced = 1.0*ReducedSize.height()/LiveSize.height(); // int XReduced = (int) (LiveMatROI.x()*scaleXReduced);
int XReduced = (int) (LiveMatROI.x()*scaleXReduced); // int YReduced = (int) (LiveMatROI.y()*scaleYReduced);
int YReduced = (int) (LiveMatROI.y()*scaleYReduced); // int WReduced = (int) (LiveMatROI.width()*scaleXReduced);
int WReduced = (int) (LiveMatROI.width()*scaleXReduced); // int HReduced = (int) (LiveMatROI.height()*scaleYReduced);
int HReduced = (int) (LiveMatROI.height()*scaleYReduced); // int deltaWReduced = (int) (ReducedSize.width() * scaleX);
int deltaWReduced = (int) (ReducedSize.width() * scaleX); // int deltaHReduced = (int) (ReducedSize.height() * scaleY);
int deltaHReduced = (int) (ReducedSize.height() * scaleY); // XReduced = XReduced - deltaWReduced/2;
XReduced = XReduced - deltaWReduced/2; // if (XReduced<0) XReduced = 0;
if (XReduced<0) XReduced = 0; // YReduced = YReduced - deltaHReduced/2;
YReduced = YReduced - deltaHReduced/2; // if (YReduced<0) YReduced = 0;
if (YReduced<0) YReduced = 0; // WReduced = WReduced + deltaWReduced;
WReduced = WReduced + deltaWReduced; // if (WReduced>ReducedSize.width()) WReduced = ReducedSize.width();
if (WReduced>ReducedSize.width()) WReduced = ReducedSize.width(); // HReduced = HReduced + deltaHReduced;
HReduced = HReduced + deltaHReduced; // if (HReduced>ReducedSize.height()) HReduced = ReducedSize.height();
if (HReduced>ReducedSize.height()) HReduced = ReducedSize.height(); // ReducedMatROI = new Rect(XReduced, YReduced, WReduced, HReduced);
ReducedMatROI = new Rect(XReduced, YReduced, WReduced, HReduced); // if (printdebug){
if (printdebug){ // System.out.println("scaleXReduced = "+scaleXReduced+" scaleYReduced = "+scaleYReduced);
System.out.println("scaleXReduced = "+scaleXReduced+" scaleYReduced = "+scaleYReduced); // System.out.println("ReducedMatROI camera "+cameratitle.getText()+" = "+RectToString(ReducedMatROI));
System.out.println("ReducedMatROI camera "+cameratitle.getText()+" = "+RectToString(ReducedMatROI)); // }
} // }
// }
//
} //else System.out.println("LiveMatROI is Outside LiveMat for camera "+cameratitle.getText()); // }
} //else System.out.println("LiveMatROI is invalid for camera "+cameratitle.getText());
}
/** /**
* Detect QR Code from Mat * Detect QR Code from Mat
* @param graymat Mat in Gray Scale * @param mat Mat in Gray Scale
* @return QR Code Text, or null if not detected * @return QR Code Text, or null if not detected
*/ */
private String DetectQRFromMat(UMat graymat){ private String DetectQRFromMat(Mat mat){
if (qrreader!=null){ if (qrreader!=null){
Mat mat = new Mat(); if (mat!=null && !mat.empty()){
graymat.copyTo(mat); // back to CPU, because zxing only accept BufferedImage
BufferedImage bufferedImage = matToBufferedImage(mat); BufferedImage bufferedImage = matToBufferedImage(mat);
String title = cameratitle.getText();
BinaryBitmap binaryBitmap = new BinaryBitmap(new HybridBinarizer(new BufferedImageLuminanceSource(bufferedImage))); BinaryBitmap binaryBitmap = new BinaryBitmap(new HybridBinarizer(new BufferedImageLuminanceSource(bufferedImage)));
try{ try{
Result result = qrreader.decode(binaryBitmap); Result result = qrreader.decode(binaryBitmap);
@@ -1316,6 +1416,8 @@ public class Cameradetail {
} }
mat.release(); mat.release();
} }
}
return null; return null;
} }

View File

@@ -7,10 +7,10 @@ import Config.CameraConfigEnum;
import Database.PhotoReviewClass; import Database.PhotoReviewClass;
import Database.Sqlite; import Database.Sqlite;
import ErhaAPI.ErhaAPI; import ErhaAPI.ErhaAPI;
import ErhaAPI.BarcodeResullt; import ErhaAPI.BarcodeHttpResult;
import ErhaAPI.UploadHttpResult;
import ErhaAPI.PhotoResult; import ErhaAPI.PhotoResult;
import ErhaAPI.PatientRecord; import ErhaAPI.PatientRecord;
import ErhaAPI.UploadResult;
import javafx.animation.KeyFrame; import javafx.animation.KeyFrame;
import javafx.animation.Timeline; import javafx.animation.Timeline;
@@ -90,7 +90,7 @@ public class CaptureView {
private final AtomicBoolean isTakingPhoto = new AtomicBoolean(false); private final AtomicBoolean isTakingPhoto = new AtomicBoolean(false);
private final ErhaAPI erhaAPI = new ErhaAPI(false); private final ErhaAPI erhaAPI = new ErhaAPI(config.isProduction());
// for timeout 180 detik // for timeout 180 detik
private final int timeout = 180; private final int timeout = 180;
@@ -144,6 +144,7 @@ public class CaptureView {
@FXML @FXML
private void TakePhotos(){ private void TakePhotos(){
if (audioTimeline!=null) audioTimeline.stop();
String directory = config.getPhotoDirectory(); String directory = config.getPhotoDirectory();
String prefix = RemoveSpaces(medicalRecordID.getText()) ; String prefix = RemoveSpaces(medicalRecordID.getText()) ;
boolean has_face = Arrays.stream(have_face).anyMatch(AtomicBoolean::get); boolean has_face = Arrays.stream(have_face).anyMatch(AtomicBoolean::get);
@@ -152,11 +153,7 @@ public class CaptureView {
isTakingPhoto.set(true); isTakingPhoto.set(true);
if (has_face){ if (has_face){
AutoCloseAlert.show("Pengambilan Foto", "Tahan Posisi Anda", "Proses ini kurang lebih 3 detik", 5, null); AutoCloseAlert.show("Pengambilan Foto", "Tahan Posisi Anda", "Proses ini kurang lebih 3 detik", 5, null);
if (audioPlayer!=null && audioPlayer.isInited()){ PlayFileWithEvent(audio_tahan_posisi, new PlaybackStatus() {
if (!Objects.equals(audioPlayer.getCurrentFile(), audio_tahan_posisi)){
audioPlayer.StopCurrentPlayback();
Wait(500);
audioPlayer.PlayFile(audio_tahan_posisi, new PlaybackStatus() {
@Override @Override
public void onPlaybackStarted(String filename) { public void onPlaybackStarted(String filename) {
@@ -164,9 +161,8 @@ public class CaptureView {
@Override @Override
public void onPlaybackFinished(String filename) { public void onPlaybackFinished(String filename) {
Wait(500); Wait(500);
audioPlayer.PlayFile(audio_countdown, new PlaybackStatus() { PlayFileWithEvent(audio_countdown, new PlaybackStatus() {
@Override @Override
public void onPlaybackStarted(String filename) { public void onPlaybackStarted(String filename) {
@@ -182,8 +178,6 @@ public class CaptureView {
} }
}); });
} }
@Override @Override
@@ -192,41 +186,21 @@ public class CaptureView {
} }
}); });
}
}
} else { } else {
AutoCloseAlert.show("Error", "No Face Detected", "No Face Detected", 5, null); AutoCloseAlert.show("Error", "No Face Detected", "No Face Detected", 5, null);
isTakingPhoto.set(false); isTakingPhoto.set(false);
if (audioPlayer!=null && audioPlayer.isInited()){ PlayFileWithoutEvent(audio_posisikan_muka);
if (!Objects.equals(audioPlayer.getCurrentFile(),audio_posisikan_muka)) {
audioPlayer.StopCurrentPlayback();
Wait(500);
audioPlayer.PlayFile(audio_posisikan_muka, null);
}
}
} }
} else { } else {
isTakingPhoto.set(false); 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)); clear();
if (!Objects.equals(AutoCloseAlert.shownBanner, AutoCloseAlert.banner_01)){ PlayFileWithoutEvent(audio_scan_barcode);
if (AutoCloseAlert.banner_01!=null){
AutoCloseAlert.showbanner(AutoCloseAlert.banner_01,0,null);
}
}
if (audioPlayer!=null && audioPlayer.isInited()){
if (!Objects.equals(audioPlayer.getCurrentFile(), audio_scan_barcode)) {
audioPlayer.StopCurrentPlayback();
Wait(500);
audioPlayer.PlayFile(audio_scan_barcode, null);
}
}
} }
} else { } else {
isTakingPhoto.set(false); 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)); 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, sx-> clear()));
} }
@@ -246,7 +220,7 @@ public class CaptureView {
@Override @Override
public PhotoResult call() { public PhotoResult call() {
if (image!=null){ if (image!=null){
image.RemapROI(0.1, 0.3, false); //image.RemapROI(0.1, 0.3, false);
double sharpness = CalculateSharpness(image.getGrayMat()); double sharpness = CalculateSharpness(image.getGrayMat());
image.setSharpness_indicator(sharpness); image.setSharpness_indicator(sharpness);
PhotoResult p = image.TakePhoto(directory, prefix); PhotoResult p = image.TakePhoto(directory, prefix);
@@ -264,7 +238,7 @@ public class CaptureView {
AtomicInteger takephoto_failure_counter = new AtomicInteger(0); AtomicInteger takephoto_failure_counter = new AtomicInteger(0);
private void take_photo_lanjutan(String directory, String prefix){ private void take_photo_lanjutan(String directory, String prefix){
audioPlayer.PlayFile(audio_camera_shutter, null); PlayFileWithoutEvent(audio_camera_shutter);
Size thumbsize = new Size(160,120); Size thumbsize = new Size(160,120);
PhotoReviewClass prc = new PhotoReviewClass(); PhotoReviewClass prc = new PhotoReviewClass();
@@ -337,21 +311,11 @@ public class CaptureView {
String lowest_string = String.format("%.2f", lowest); String lowest_string = String.format("%.2f", lowest);
AutoCloseAlert.show("Take Photos Failed", "Blurred Image Detected", "Blurred Image Detected at "+culprit+" with sharpness score "+lowest_string, 5, s -> isTakingPhoto.set(false)); AutoCloseAlert.show("Take Photos Failed", "Blurred Image Detected", "Blurred Image Detected at "+culprit+" with sharpness score "+lowest_string, 5, s -> isTakingPhoto.set(false));
takephoto_failure_counter.incrementAndGet(); takephoto_failure_counter.incrementAndGet();
if (audioPlayer!=null && audioPlayer.isInited()){
if (takephoto_failure_counter.get() < 2){ if (takephoto_failure_counter.get() < 2){
if (!Objects.equals(audio_pengambilan_gagal, audioPlayer.getCurrentFile())){ PlayFileWithoutEvent(audio_pengambilan_gagal);
audioPlayer.StopCurrentPlayback();
Wait(500);
audioPlayer.PlayFile(audio_pengambilan_gagal, null);
}
} else { } else {
if (!Objects.equals(audio_hubungi_staf_kami, audioPlayer.getCurrentFile())){ PlayFileWithoutEvent(audio_hubungi_staf_kami);
audioPlayer.StopCurrentPlayback(); clear();
Wait(500);
audioPlayer.PlayFile(audio_hubungi_staf_kami, null);
}
}
} }
return; return;
@@ -513,19 +477,7 @@ public class CaptureView {
String[] files = prc.compressed(); String[] files = prc.compressed();
if (files.length>0){ if (files.length>0){
if (audioPlayer!=null && audioPlayer.isInited()){ AutoCloseAlert.showpictures(prc.compressedcrop(),3, (s)->{
if (!Objects.equals(audioPlayer.getCurrentFile(),audio_pengambilan_berhasil)){
audioPlayer.StopCurrentPlayback();
Wait(500);
audioPlayer.PlayFile(audio_pengambilan_berhasil, new PlaybackStatus() {
@Override
public void onPlaybackStarted(String filename) {
}
@Override
public void onPlaybackFinished(String filename) {
AutoCloseAlert.showpictures(prc.compressed(),3, (s)->{
if (AutoCloseAlert.banner_02!=null) { if (AutoCloseAlert.banner_02!=null) {
AutoCloseAlert.showbanner(AutoCloseAlert.banner_02, 0, null); AutoCloseAlert.showbanner(AutoCloseAlert.banner_02, 0, null);
@@ -533,7 +485,14 @@ public class CaptureView {
if (runningTask!=null) runningTask.cancel(false); if (runningTask!=null) runningTask.cancel(false);
} }
}); });
PlayFileWithEvent(audio_pengambilan_berhasil, new PlaybackStatus() {
@Override
public void onPlaybackStarted(String filename) {
}
@Override
public void onPlaybackFinished(String filename) {
} }
@@ -541,42 +500,32 @@ public class CaptureView {
public void onPlaybackFailure(String filename) { public void onPlaybackFailure(String filename) {
} }
}); });
}
}
} else { } else {
if (audioPlayer!=null && audioPlayer.isInited()){ PlayFileWithoutEvent(audio_pengambilan_gagal);
if (!Objects.equals(audioPlayer.getCurrentFile(),audio_pengambilan_gagal)){
audioPlayer.StopCurrentPlayback();
Wait(500);
audioPlayer.PlayFile(audio_pengambilan_gagal, null);
}
}
clear(); clear();
if (runningTask!=null) runningTask.cancel(false); if (runningTask!=null) runningTask.cancel(false);
} }
} }
private void UploadFiles(PhotoReviewClass prc, String prefix){ private void UploadFiles(PhotoReviewClass prc, String prefix){
String[] files = prc.compressed(); String[] files = prc.compressedcrop();
if (files.length>0){ if (files.length>0){
InsertSQL(prc); InsertSQL(prc);
erhaAPI.setProduction(config.isProduction());
Task<Void> uploadtask = new Task<>() { Task<Void> uploadtask = new Task<>() {
@Override @Override
protected Void call() { protected Void call() {
int totalfiles = files.length; int totalfiles = files.length;
int counter = 0; int counter = 0;
for (String ff : files) { for (String ff : files) {
UploadResult ur = erhaAPI.Upload_File(prefix, ff,false);
if (ur != null) { UploadHttpResult ur = erhaAPI.Upload_File(prefix, ff,true);
if (ur.message.startsWith("Record has been created")) { if (ur.getResult().message.startsWith("Record has been created")) {
counter++; counter++;
updateMessage("Upload success for " + ff); updateMessage("Upload success for " + ff);
} else updateMessage("Upload failed for " + ff+", Message : "+ur.message); } else updateMessage("Upload failed for " + ff+", Message : "+ur.getResult().message);
} else updateMessage("Upload failed for " + ff+" because UploadResult is null");
} }
if (counter == totalfiles) { if (counter == totalfiles) {
super.succeeded(); super.succeeded();
@@ -593,13 +542,7 @@ public class CaptureView {
uploadtask.setOnSucceeded(e-> clear()); uploadtask.setOnSucceeded(e-> clear());
uploadtask.setOnFailed(e-> { uploadtask.setOnFailed(e-> {
if (audioPlayer!=null && audioPlayer.isInited()){ PlayFileWithoutEvent(audio_upload_gagal);
if (!Objects.equals(audioPlayer.getCurrentFile(), audio_upload_gagal)){
audioPlayer.StopCurrentPlayback();
Wait(500);
audioPlayer.PlayFile(audio_upload_gagal, null);
}
}
AutoCloseAlert.show("Upload Failed", "Upload Failed", "Upload Failed", 5, s -> clear()); AutoCloseAlert.show("Upload Failed", "Upload Failed", "Upload Failed", 5, s -> clear());
}); });
@@ -614,7 +557,7 @@ public class CaptureView {
@FXML @FXML
public void initialize(){ public void initialize(){
audio_posisikan_muka = ExtractResource("/posisikan_wajah.wav"); audio_posisikan_muka = ExtractResource("/posisikan_wajah.wav");
audio_pengambilan_berhasil = ExtractResource("/pengambilan_berhasil.wav"); audio_pengambilan_berhasil = ExtractResource("/pengambilan_berhasil_tunggu_lobby.wav");
audio_pengambilan_gagal = ExtractResource("/pengambilan_gagal.wav"); audio_pengambilan_gagal = ExtractResource("/pengambilan_gagal.wav");
audio_scan_barcode = ExtractResource("/scan_barcode.wav"); audio_scan_barcode = ExtractResource("/scan_barcode.wav");
audio_upload_gagal = ExtractResource("/upload_gagal.wav"); audio_upload_gagal = ExtractResource("/upload_gagal.wav");
@@ -664,11 +607,7 @@ public class CaptureView {
LoadCameraDetail(cam5, 5, CameraConfigEnum.CameraConfigRight90); LoadCameraDetail(cam5, 5, CameraConfigEnum.CameraConfigRight90);
Platform.runLater(()->{ Platform.runLater(()->{
if (!Objects.equals(AutoCloseAlert.shownBanner, AutoCloseAlert.banner_01)){ clear();
if (AutoCloseAlert.banner_01!=null){
AutoCloseAlert.showbanner(AutoCloseAlert.banner_01,0,null);
}
}
int indexleft90=-1; int indexleft90=-1;
int indexleft45=-1; int indexleft45=-1;
@@ -760,11 +699,23 @@ public class CaptureView {
} }
Timeline audioTimeline;
private void clear(){ private void clear(){
isTakingPhoto.set(false); isTakingPhoto.set(false);
TextAreaSetText(medicalRecordID,""); TextAreaSetText(medicalRecordID,"");
TextAreaSetText(PatientName,""); TextAreaSetText(PatientName,"");
if (image1!=null) image1.setSharpness_indicator(-1);
if (image2!=null) image2.setSharpness_indicator(-1);
if (image3!=null) image3.setSharpness_indicator(-1);
if (image4!=null) image4.setSharpness_indicator(-1);
if (image5!=null) image5.setSharpness_indicator(-1);
if (barcodeData.getText().isBlank()){
// pancing supaya addlistener barcodeData trigger
TextAreaSetText(barcodeData,"x");
Wait(10);
TextAreaSetText(barcodeData,""); TextAreaSetText(barcodeData,"");
} else TextAreaSetText(barcodeData,"");
} }
public void Unload(){ public void Unload(){
@@ -862,8 +813,8 @@ public class CaptureView {
photoheight = ObsbotMeet2.ModeBest.getHeight(); photoheight = ObsbotMeet2.ModeBest.getHeight();
livewidth = ObsbotMeet2.ModeLive.getWidth(); livewidth = ObsbotMeet2.ModeLive.getWidth();
liveheight = ObsbotMeet2.ModeLive.getHeight(); liveheight = ObsbotMeet2.ModeLive.getHeight();
reducewidth = ObsbotMeet2.Mode3.getWidth(); reducewidth = ObsbotMeet2.Mode2.getWidth();
reduceheight = ObsbotMeet2.Mode3.getHeight(); reduceheight = ObsbotMeet2.Mode2.getHeight();
} }
image.SetGrabber(devicenumber,grabber, livewidth,liveheight,photowidth,photoheight,reducewidth,reduceheight, true); image.SetGrabber(devicenumber,grabber, livewidth,liveheight,photowidth,photoheight,reducewidth,reduceheight, true);
@@ -872,7 +823,7 @@ public class CaptureView {
LiveCamEvent lce = new LiveCamEvent() { LiveCamEvent lce = new LiveCamEvent() {
Timeline audioTimeline;
@Override @Override
public void onDetectedQRCode(String barCode) { public void onDetectedQRCode(String barCode) {
@@ -888,6 +839,7 @@ public class CaptureView {
runningTask = timeoutExecutor.schedule(()->{ runningTask = timeoutExecutor.schedule(()->{
// timeout // timeout
clear(); clear();
System.out.println("180 seconds timeout");
}, timeout, TimeUnit.SECONDS); }, timeout, TimeUnit.SECONDS);
// Revisi 19/05/2025 // Revisi 19/05/2025
@@ -896,54 +848,120 @@ public class CaptureView {
// terakhir, PlayFile audio_hubungi_staf_kami // terakhir, PlayFile audio_hubungi_staf_kami
audioTimeline = new Timeline(); audioTimeline = new Timeline();
int file_duration = 7; // audio_posisikan_muka durasi 7 detik final int file_duration = 8; // audio_posisikan_muka durasi 7 detik
audioTimeline.getKeyFrames().add(new KeyFrame(Duration.seconds(0), k1 ->{ audioTimeline.getKeyFrames().add(new KeyFrame(Duration.seconds(0), k1 -> PlayFileWithEvent(audio_posisikan_muka, new PlaybackStatus() {
@Override
public void onPlaybackStarted(String filename) {
anti_bawel.set(true);
System.out.println("Peringatan pertama audio_posisikan_muka");
}
})); @Override
audioTimeline.getKeyFrames().add(new KeyFrame(Duration.seconds(5+file_duration), k2 ->{ public void onPlaybackFinished(String filename) {
anti_bawel.set(false);
}
})); @Override
audioTimeline.getKeyFrames().add(new KeyFrame(Duration.seconds(10+file_duration), k3 ->{ public void onPlaybackFailure(String filename) {
anti_bawel.set(false);
}
})));
})); audioTimeline.getKeyFrames().add(new KeyFrame(Duration.seconds(file_duration+5), k2 -> PlayFileWithEvent(audio_posisikan_muka, new PlaybackStatus() {
audioTimeline.getKeyFrames().add(new KeyFrame(Duration.seconds(15+file_duration), k4 ->{
@Override
public void onPlaybackStarted(String filename) {
anti_bawel.set(true);
System.out.println("Peringatan kedua audio_posisikan_muka");
}
@Override
public void onPlaybackFinished(String filename) {
anti_bawel.set(false);
}
@Override
public void onPlaybackFailure(String filename) {
anti_bawel.set(false);
}
})));
audioTimeline.getKeyFrames().add(new KeyFrame(Duration.seconds(3*file_duration+5), k3 -> PlayFileWithEvent(audio_posisikan_muka, new PlaybackStatus() {
@Override
public void onPlaybackStarted(String filename) {
anti_bawel.set(true);
System.out.println("Peringatan ketiga audio_posisikan_muka");
}
@Override
public void onPlaybackFinished(String filename) {
anti_bawel.set(false);
}
@Override
public void onPlaybackFailure(String filename) {
anti_bawel.set(false);
}
})));
audioTimeline.getKeyFrames().add(new KeyFrame(Duration.seconds(5*file_duration+5), k4 ->{
PlayFileWithEvent(audio_hubungi_staf_kami, new PlaybackStatus() {
@Override
public void onPlaybackStarted(String filename) {
anti_bawel.set(true);
System.out.println("Peringatan keempat audio_hubungi_staf_kami");
}
@Override
public void onPlaybackFinished(String filename) {
anti_bawel.set(false);
}
@Override
public void onPlaybackFailure(String filename) {
anti_bawel.set(false);
}
});
clear();
})); }));
Task<PatientRecord> checkpatientID = new Task<>() { Task<PatientRecord> checkpatientID = new Task<>() {
@Override @Override
protected PatientRecord call() throws Exception { protected PatientRecord call() throws Exception {
BarcodeResullt br = erhaAPI.Validate_Barcode(finalbarCode,false); erhaAPI.setProduction(config.isProduction());
if (br!=null){ BarcodeHttpResult br = erhaAPI.Validate_Barcode(finalbarCode,true);
if (br.message.startsWith("Records found")){ if (br.getStatusCode()==200){
if (br.data!=null && br.data.length>0){ if (br.getResult().message.startsWith("Records found")){
PatientRecord pr = br.data[0]; if (br.getResult().data!=null && br.getResult().data.length>0){
PatientRecord pr = br.getResult().data[0];
if (!pr.medical_record_detail_id.isBlank()){ if (!pr.medical_record_detail_id.isBlank()){
if (!pr.name.isBlank()){ if (!pr.name.isBlank()){
super.succeeded(); super.succeeded();
return pr; return pr;
} else { } else {
Logger.error("Barcode ",finalbarCode," PatientRecord name is empty"); Logger.error("Barcode ",finalbarCode," PatientRecord name is empty");
throw new Exception("Barcode "+finalbarCode+" PatientRecord name kosong"); throw new Exception("Barcode "+finalbarCode+" PatientRecord name kosong, code= "+br.getStatusCode());
} }
} else { } else {
Logger.error("Barcode ",finalbarCode," PatientRecord medical_record_detail_id is empty"); Logger.error("Barcode ",finalbarCode," PatientRecord medical_record_detail_id is empty");
throw new Exception("Data dengan barcode "+finalbarCode+", PatientRecord medical_record_detail_id kosong"); throw new Exception("Data dengan barcode "+finalbarCode+", PatientRecord medical_record_detail_id kosong, code= "+br.getStatusCode());
} }
} else { } else {
Logger.error("Record associated with barcode ",finalbarCode," is empty"); Logger.error("Record associated with barcode ",finalbarCode," is empty");
throw new Exception("Data dengan barcode "+finalbarCode+" ditemukan di server, tetapi kosong"); throw new Exception("Data dengan barcode "+finalbarCode+" ditemukan di server, tetapi kosong, code= "+br.getStatusCode());
} }
} else { } else {
Logger.error("Record associated with barcode ",finalbarCode," is not found"); Logger.error("Record associated with barcode ",finalbarCode," is not found");
throw new Exception("Data dengan barcode "+finalbarCode+" tidak ditemukan di server"); throw new Exception("Data dengan barcode "+finalbarCode+" tidak ditemukan di server, code= "+br.getStatusCode());
} }
} else { } else {
Logger.error("BarcodeResullt with barcode ",finalbarCode," is null"); Logger.error("HTTP code ", br.getStatusCode(), " for barcode ", finalbarCode);
throw new Exception("BarcodeResult dengan barcode "+finalbarCode+" menghasilkan null"); throw new Exception("Barcode "+finalbarCode+" failed, code= "+br.getStatusCode()+", message: "+br.getBody());
} }
} }
}; };
checkpatientID.setOnSucceeded(event -> { checkpatientID.setOnSucceeded(event -> {
@@ -953,55 +971,16 @@ public class CaptureView {
TextAreaSetText(medicalRecordID,""+medrecid); TextAreaSetText(medicalRecordID,""+medrecid);
TextAreaSetText(PatientName, pr.name); TextAreaSetText(PatientName, pr.name);
if (audioTimeline!=null) audioTimeline.play(); if (anti_bawel.get()) return; // sudah ada LiveCamEvent lain yang bunyiin, jadi tidak usah bunyiin lagi
if (audioTimeline!=null) audioTimeline.playFromStart();
if (audioPlayer!=null && audioPlayer.isInited()){
if (!Objects.equals(audioPlayer.getCurrentFile(),audio_posisikan_muka)) {
if (!anti_bawel.get()){
audioPlayer.StopCurrentPlayback();
Wait(500);
audioPlayer.PlayFile(audio_posisikan_muka, new PlaybackStatus() {
@Override
public void onPlaybackStarted(String filename) {
anti_bawel.set(true);
}
@Override
public void onPlaybackFinished(String filename) {
anti_bawel.set(false);
Thread antibawel = new Thread(()->{
try {
Thread.sleep(15*1000);
} catch (InterruptedException ignored) {
}
anti_bawel.set(false);
});
antibawel.setName("anti bawel");
antibawel.setDaemon(true);
antibawel.start();
}
@Override
public void onPlaybackFailure(String filename) {
anti_bawel.set(false);
}
});
}
}
}
} }
}); });
checkpatientID.setOnFailed(event -> { checkpatientID.setOnFailed(event -> {
if (audioPlayer!=null && audioPlayer.isInited()){ PlayFileWithoutEvent(audio_data_barcode_tidak_ditemukan);
if (!Objects.equals(audioPlayer.getCurrentFile(), audio_data_barcode_tidak_ditemukan)) { clear();
audioPlayer.StopCurrentPlayback();
audioPlayer.PlayFile(audio_data_barcode_tidak_ditemukan,null);
}
}
Task<?> failed = (Task<?>) event.getSource(); Task<?> failed = (Task<?>) event.getSource();
Throwable t = failed.getException(); Throwable t = failed.getException();
final String message = t.getMessage(); final String message = t.getMessage();
@@ -1031,44 +1010,33 @@ public class CaptureView {
} }
//update_status(image); //update_status(image);
if (hasface && audioTimeline!=null) audioTimeline.stop(); if (hasface){
// ada face terdeteksi
// instruksi scan barcode ketika welcomeUI masih muncul dan ada muka terdeteksi if (Objects.equals(AutoCloseAlert.shownBanner, AutoCloseAlert.banner_01)){
if (hasface && Objects.equals(AutoCloseAlert.shownBanner, AutoCloseAlert.banner_01)){ // WelcomeUI masih muncul
if (audioPlayer!=null && audioPlayer.isInited()){ if (anti_bawel.get()) return; // sudah ada LiveCamEvent lain yang bunyiin, jadi tidak usah bunyiin lagi
if (!Objects.equals(audioPlayer.getCurrentFile(), audio_scan_barcode)) { PlayFileWithEvent(audio_scan_barcode, new PlaybackStatus() {
audioPlayer.StopCurrentPlayback();
if (anti_bawel.get()) return;
audioPlayer.PlayFile(audio_scan_barcode, new PlaybackStatus() {
@Override @Override
public void onPlaybackStarted(String filename) { public void onPlaybackStarted(String filename) {
anti_bawel.set(true);
} }
@Override @Override
public void onPlaybackFinished(String filename) { public void onPlaybackFinished(String filename) {
Thread antibawel = new Thread(()->{ WaitAsync(15*1000, "anti bawel", x-> anti_bawel.set(false));
try {
Thread.sleep(15*1000);
} catch (InterruptedException ignored) {
}
anti_bawel.set(false);
});
antibawel.setName("anti bawel");
antibawel.setDaemon(true);
antibawel.start();
} }
@Override @Override
public void onPlaybackFailure(String filename) { public void onPlaybackFailure(String filename) {
anti_bawel.set(false);
}
});
} }
});
} else if (audioTimeline!=null){
// WelcomeUI sudah tidak muncul, tapi audioTimeLine masih ada
audioTimeline.stop();
} }
} }
} }
@@ -1107,7 +1075,7 @@ public class CaptureView {
@Override @Override
public void onDoubleBlink(int counter) { public void onDoubleBlink(int counter) {
if (audioPlayer!=null && audioPlayer.isPlaying()) return; // let audio finish playback if (audioPlayer!=null && audioPlayer.isPlaying()) return; // let audio finish playback
if (isTakingPhoto.get()) return; // other camera is taking picture if (isTakingPhoto.get()) return; // another camera is taking picture
// revisi 08/04/2025 // revisi 08/04/2025
TakePhotos(); TakePhotos();
} }
@@ -1135,8 +1103,11 @@ public class CaptureView {
}; };
image.setCameraStatus("Camera Starting"); image.setCameraStatus("Camera Starting");
if (image.StartLiveView(lce, title, use_qr_detector, use_face_detector)){ if (image.StartLiveView(lce, title, use_qr_detector, use_face_detector)){
//TODO Start Live View berhasil, apa lagi yang mau dikerjakan ? image.getDetector().setFaceMaxSize(config.getCascadeMaxSize());
image.getDetector().setFaceMinSize(config.getCascadeMinSize());
image.getDetector().setScaleFactor(config.getCascadeScaleFactor());
MainApplication.detectorsList.put(devicenumber, image.getDetector());
Logger.info("Camera "+cameraname+" started with device number "+devicenumber);
} else image.setCameraStatus("Unable to Set Grabber"); } else image.setCameraStatus("Unable to Set Grabber");
} else image.setCameraStatus("Camera not found, please check setting"); } else image.setCameraStatus("Camera not found, please check setting");
} }
@@ -1210,4 +1181,24 @@ public class CaptureView {
Sqlite sql = new Sqlite(); Sqlite sql = new Sqlite();
sql.Insert(prc); sql.Insert(prc);
} }
private void PlayFileWithoutEvent(String filename){
if (audioPlayer!=null && audioPlayer.isInited()){
if (!Objects.equals(audioPlayer.getCurrentFile(),filename)) {
audioPlayer.StopCurrentPlayback();
Wait(500);
audioPlayer.PlayFile(filename, null);
}
}
}
private void PlayFileWithEvent(String filename, PlaybackStatus ps){
if (audioPlayer!=null && audioPlayer.isInited()){
if (!Objects.equals(audioPlayer.getCurrentFile(),filename)) {
audioPlayer.StopCurrentPlayback();
Wait(500);
audioPlayer.PlayFile(filename, ps);
}
}
}
} }

View File

@@ -24,8 +24,14 @@ public class DetectorResult {
Eyes.add(eye); Eyes.add(eye);
} }
// trouble di sini
public void FaceRectangle(UMat mat){ public void FaceRectangle(UMat mat){
if (mat == null || mat.empty()) return;
if (Face == null || Face.width() <= 0 || Face.height() <= 0) return;
if (Face.x() < 0 || Face.y() < 0) return; // ignore negative coordinates
if (Face.x() + Face.width() > mat.cols() || Face.y() + Face.height() > mat.rows()) return; // ignore out of bounds
if (haveFace()){ if (haveFace()){
try{ try{
rectangle(mat, Face, Scalar.GREEN, linethickness, linetype, lineshift); rectangle(mat, Face, Scalar.GREEN, linethickness, linetype, lineshift);
@@ -33,9 +39,17 @@ public class DetectorResult {
} }
} }
// trouble di sini
public void EyesRectangle(UMat mat){ public void EyesRectangle(UMat mat){
if (mat == null || mat.empty()) return;
if (haveEyes()){ if (haveEyes()){
for(Rect eye : Eyes){ for(Rect eye : Eyes){
if (eye == null || eye.width() <= 0 || eye.height() <= 0) continue;
if (eye.x() < 0 || eye.y() < 0) continue; // ignore negative coordinates
if (eye.x() + eye.width() > mat.cols() || eye.y() + eye.height() > mat.rows()) continue; // ignore out of bounds
try{ try{
rectangle(mat, eye, Scalar.BLUE); rectangle(mat, eye, Scalar.BLUE);

View File

@@ -10,72 +10,55 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
public class Detectors { public class Detectors {
public static CascadeClassifier frontalfaceDetector; public CascadeClassifier frontalfaceDetector;
private static CascadeClassifier eyeDetector; private CascadeClassifier eyeDetector;
private static CascadeClassifier profilefaceDetector; private CascadeClassifier profilefaceDetector;
private static double scaleFactor = 1.2; // revisi 09/05/2025, dari nilai 1.05 private double scaleFactor = 1.05; // revisi 09/05/2025, dari nilai 1.05
private final static int minNeighbors = 5; // revisi 09/05/2025, dari nilai 3 private final int minNeighbors = 3; // revisi 09/05/2025, dari nilai 3
private final static int flags = 0; private final int flags = 0;
private static Size FaceminSize; private Size FaceminSize;
private static Size FacemaxSize; private Size FacemaxSize;
public static void LoadAllDetectors(){
public Detectors(){
LoadFrontalFaceDetector(); LoadFrontalFaceDetector();
LoadEyeDetector(); LoadEyeDetector();
LoadProfileFaceDetector(); LoadProfileFaceDetector();
} }
private static void LoadFrontalFaceDetector(){ private void LoadFrontalFaceDetector(){
// revisi 09/05/2025, dari filename = SomeCodes.ExtractResource("/haarcascade_frontalface_default.xml"); // revisi 09/05/2025, dari filename = SomeCodes.ExtractResource("/haarcascade_frontalface_default.xml");
String filename = SomeCodes.ExtractResource("/haarcascade_frontalface_alt.xml"); String filename = SomeCodes.ExtractResource("/haarcascade_frontalface_alt.xml");
if (filename!=null) { if (filename!=null) {
Logger.info("Face Detector file : " + filename);
if (frontalfaceDetector==null) {
try{ try{
frontalfaceDetector = new CascadeClassifier(filename); frontalfaceDetector = new CascadeClassifier(filename);
Logger.info("FaceDetector loaded");
} catch (Exception e){ } catch (Exception e){
Logger.error("Exception on loading FaceDetector : " + e.getMessage()); Logger.error("Exception on loading FaceDetector : " + e.getMessage());
} }
} else Logger.info("FaceDetector already loaded");
} else Logger.error("Unable to extract face detector file"); } else Logger.error("Unable to extract face detector file");
} }
private static void LoadProfileFaceDetector(){ private void LoadProfileFaceDetector(){
String filename = SomeCodes.ExtractResource("/haarcascade_profileface.xml"); String filename = SomeCodes.ExtractResource("/haarcascade_profileface.xml");
if (filename!=null) { if (filename!=null) {
Logger.info("Profile Face Detector file : " + filename);
if (profilefaceDetector==null) {
try{ try{
profilefaceDetector = new CascadeClassifier(filename); profilefaceDetector = new CascadeClassifier(filename);
Logger.info("ProfileFaceDetector loaded");
} catch (Exception e){ } catch (Exception e){
Logger.error("Exception on loading ProfileFaceDetector : " + e.getMessage()); Logger.error("Exception on loading ProfileFaceDetector : " + e.getMessage());
} }
} else Logger.info("ProfileFaceDetector already loaded");
} else Logger.error("Unable to extract profile face detector file"); } else Logger.error("Unable to extract profile face detector file");
} }
private static void LoadEyeDetector(){ private void LoadEyeDetector(){
String filename = SomeCodes.ExtractResource("/haarcascade_eye.xml"); String filename = SomeCodes.ExtractResource("/haarcascade_eye.xml");
if (filename!=null) { if (filename!=null) {
Logger.info("Eye Detector file : " + filename);
if (eyeDetector==null) {
try{ try{
eyeDetector = new CascadeClassifier(filename); eyeDetector = new CascadeClassifier(filename);
Logger.info("EyeDetector loaded");
} catch (Exception e){ } catch (Exception e){
Logger.error("Exception on loading EyeDetector : " + e.getMessage()); Logger.error("Exception on loading EyeDetector : " + e.getMessage());
} }
} else Logger.info("EyeDetector already loaded");
} else Logger.error("Unable to extract eye detector file"); } else Logger.error("Unable to extract eye detector file");
} }
@@ -84,16 +67,18 @@ public class Detectors {
* @param graymat Mat in Gray Scale * @param graymat Mat in Gray Scale
* @return List of Rect if face detected, otherwise empty list * @return List of Rect if face detected, otherwise empty list
*/ */
public static @NonNull List<DetectorResult> HaveFrontalFace(UMat graymat){ public @NonNull List<DetectorResult> HaveFrontalFace(Mat graymat){
List<DetectorResult> result = new ArrayList<>(); List<DetectorResult> result = new ArrayList<>();
//System.out.println("Detecting frontal from "+ graymat.size().width() + "x" + graymat.size().height());
RectVector faces = DetectFrontalFace(graymat); RectVector faces = DetectFrontalFace(graymat);
if (faces!=null && faces.size()>0){ if (faces!=null && faces.size()>0){
//System.out.println("faces size = " + faces.size());
for(Rect face : faces.get()){ for(Rect face : faces.get()){
RectVector eyes = DetectEye(graymat, face.width()); RectVector eyes = DetectEye(graymat, face.width());
DetectorResult dr = new DetectorResult(); DetectorResult dr = new DetectorResult();
dr.setFace(face); dr.setFace(face);
if (eyes!=null && eyes.size()>=2){ //System.out.println("eyes size = " + eyes.size());
if (eyes.size()>=2){
for(Rect eye : eyes.get()){ for(Rect eye : eyes.get()){
if (SomeCodes.IsInsideRect(eye, face)) { if (SomeCodes.IsInsideRect(eye, face)) {
dr.AddEye(eye); dr.AddEye(eye);
@@ -102,11 +87,11 @@ public class Detectors {
} }
result.add(dr); result.add(dr);
} }
} } //else System.out.println("faces size = 0");
return result; return result;
} }
public static @NonNull List<DetectorResult> HaveLeft45Face(UMat graymat){ public @NonNull List<DetectorResult> HaveLeft45Face(Mat graymat){
List<DetectorResult> result = new ArrayList<>(); List<DetectorResult> result = new ArrayList<>();
RectVector faces = DetectProfileFace(graymat); RectVector faces = DetectProfileFace(graymat);
if (faces!=null && faces.size()>0){ if (faces!=null && faces.size()>0){
@@ -115,7 +100,7 @@ public class Detectors {
RectVector eyes = DetectEye(graymat, face.width()); RectVector eyes = DetectEye(graymat, face.width());
DetectorResult dr = new DetectorResult(); DetectorResult dr = new DetectorResult();
dr.setFace(face); dr.setFace(face);
if (eyes!=null && eyes.size()>0){ if (eyes.size()>0){
for(Rect eye : eyes.get()){ for(Rect eye : eyes.get()){
if (SomeCodes.IsInsideRect(eye, face)) dr.AddEye(eye); if (SomeCodes.IsInsideRect(eye, face)) dr.AddEye(eye);
} }
@@ -129,38 +114,38 @@ public class Detectors {
public static void setScaleFactor(double value){ public void setScaleFactor(double value){
if (scaleFactor!=value) scaleFactor = value; if (scaleFactor!=value) scaleFactor = value;
} }
public static void setFaceMinSize(int value){ public void setFaceMinSize(int value){
if (FaceminSize!=null){ if (FaceminSize!=null){
if (FaceminSize.width()!=value || FaceminSize.height()!=value) { if (FaceminSize.width()!=value || FaceminSize.height()!=value) {
FaceminSize = new Size(value, value); FaceminSize = new Size(value, value);
//Logger.info("FaceMinSize changed to : " + FaceminSize.width()); Logger.info("FaceMinSize changed to : " + FaceminSize.width());
} }
} else { } else {
FaceminSize = new Size(value, value); FaceminSize = new Size(value, value);
//Logger.info("FaceMinSize created with value : " + FaceminSize.width()); Logger.info("FaceMinSize created with value : " + FaceminSize.width());
} }
} }
public static void setFaceMaxSize(int value){ public void setFaceMaxSize(int value){
if (FacemaxSize!=null){ if (FacemaxSize!=null){
if (FacemaxSize.width()!=value || FacemaxSize.height()!=value) { if (FacemaxSize.width()!=value || FacemaxSize.height()!=value) {
FacemaxSize = new Size(value, value); FacemaxSize = new Size(value, value);
//Logger.info("FaceMaxSize changed to : " + FacemaxSize.width()); Logger.info("FaceMaxSize changed to : " + FacemaxSize.width());
} }
} else { } else {
FacemaxSize = new Size(value, value); FacemaxSize = new Size(value, value);
//Logger.info("FaceMaxSize created with value : " + FacemaxSize.width()); Logger.info("FaceMaxSize created with value : " + FacemaxSize.width());
} }
} }
public static RectVector DetectProfileFace(UMat graymat){ public RectVector DetectProfileFace(Mat graymat){
return Detect(graymat, profilefaceDetector, scaleFactor, minNeighbors, flags, FaceminSize, FacemaxSize); return Detect(graymat, profilefaceDetector, scaleFactor, minNeighbors, flags, FaceminSize, FacemaxSize);
} }
@@ -169,7 +154,7 @@ public class Detectors {
* @param graymat Mat in Gray Scale * @param graymat Mat in Gray Scale
* @return RectVector if face detected, otherwise null * @return RectVector if face detected, otherwise null
*/ */
public static RectVector DetectFrontalFace(UMat graymat){ public RectVector DetectFrontalFace(Mat graymat){
return Detect(graymat, frontalfaceDetector, scaleFactor, minNeighbors, flags, FaceminSize, FacemaxSize); return Detect(graymat, frontalfaceDetector, scaleFactor, minNeighbors, flags, FaceminSize, FacemaxSize);
} }
@@ -179,38 +164,39 @@ public class Detectors {
* @param graymat Mat in Gray Scale * @param graymat Mat in Gray Scale
* @return RectVector if eye detected, otherwise null * @return RectVector if eye detected, otherwise null
*/ */
public static RectVector DetectEye(UMat graymat, int facewidth){ public RectVector DetectEye(Mat graymat, int facewidth){
//return Detect(graymat, eyeDetector); //return Detect(graymat, eyeDetector);
int minwidth = (int)(facewidth*0.2); int minwidth = (int)(facewidth*0.2);
int maxwidth = (int)(facewidth*0.4); int maxwidth = (int)(facewidth*0.4);
Size minsize = new Size(minwidth, minwidth); Size minsize = new Size(minwidth, minwidth);
Size maxsize = new Size(maxwidth, maxwidth); Size maxsize = new Size(maxwidth, maxwidth);
//System.out.println("Detecting Eye with minsize = " + minsize.width() + "x" + minsize.height() + ", maxsize = " + maxsize.width() + "x" + maxsize.height());
return Detect(graymat, eyeDetector, scaleFactor, minNeighbors, flags, minsize, maxsize); return Detect(graymat, eyeDetector, scaleFactor, minNeighbors, flags, minsize, maxsize);
} }
@SuppressWarnings("SameParameterValue") @SuppressWarnings("SameParameterValue")
private static RectVector Detect(UMat graymat, CascadeClassifier detector, double scaleFactor, int minNeighbors, int flags, Size minSize, Size maxSize){ private RectVector Detect(Mat graymat, CascadeClassifier detector, double scaleFactor, int minNeighbors, int flags, Size minSize, Size maxSize){
if (detector!=null){ if (detector!=null && !detector.empty()){
if (graymat!=null && graymat.channels()==1){ if (graymat!=null && graymat.channels()==1 && !graymat.empty()){
if (!graymat.empty()){ if (minSize!=null && maxSize!=null){
if (minSize!=null){ if (minSize.width()< maxSize.width() && minSize.height() < maxSize.height()){
if (maxSize!=null){ if (graymat.cols()> minSize.width() && graymat.rows() > minSize.height()) {
try{ try {
RectVector detected = new RectVector(); RectVector detected = new RectVector();
detector.detectMultiScale(graymat, detected, scaleFactor, minNeighbors, flags, minSize, maxSize); // try defaulting minSize and maxSize
detector.detectMultiScale(graymat, detected, scaleFactor, minNeighbors, flags, minSize, new Size());
return detected; return detected;
} catch (Exception e){ } catch (Exception e) {
System.out.println("Detectors Detect Error, Message : "+e.getMessage()); System.out.println("Detectors Detect Error, Message : " + e.getMessage());
} }
} else System.out.println("graymat is smaller than minSize");
} } else System.out.println("minSize is larger than maxSize");
} } else System.out.println("minSize or maxSize is null");
} else System.out.println("graymat is null, not 1 channel, or empty");
} } else System.out.println("detector empty");
}
}
return null; return null;
} }

View File

@@ -15,6 +15,7 @@ import javafx.stage.Stage;
import org.tinylog.Logger; import org.tinylog.Logger;
import java.io.IOException; import java.io.IOException;
import java.util.HashMap;
import java.util.Map; import java.util.Map;
import static Config.SomeCodes.ShowAlert; import static Config.SomeCodes.ShowAlert;
@@ -23,18 +24,21 @@ import static Config.SomeCodes.config;
public class MainApplication extends Application { public class MainApplication extends Application {
final String version = "27082025-PRODUCTION-1.0.7";
PhotoCleaner photoCleaner; PhotoCleaner photoCleaner;
public static Map<Integer, Detectors> detectorsList = new HashMap<>();
@Override @Override
public void start(Stage stage) throws IOException { public void start(Stage stage) throws IOException {
Runtime.getRuntime().addShutdownHook(new Thread(() -> { Runtime.getRuntime().addShutdownHook(new Thread(() -> {
System.out.println("ShutdownHook Checking running threads...");
Map<Thread, StackTraceElement[]> allThreads = Thread.getAllStackTraces();
for(Thread thread : allThreads.keySet()){
//System.out.println("Thread: " + thread.getName()+", State: " + thread.getState()+", Daemon: " + thread.isDaemon());
thread.interrupt();
}
if (photoCleaner!=null) photoCleaner.Stop(); if (photoCleaner!=null) photoCleaner.Stop();
System.out.println("ShutdownHook Checking running threads...");
// Map<Thread, StackTraceElement[]> allThreads = Thread.getAllStackTraces();
// for(Thread thread : allThreads.keySet()){
// System.out.println("Thread: " + thread.getName()+", State: " + thread.getState()+", Daemon: " + thread.isDaemon());
// thread.interrupt();
// }
})); }));
@@ -50,7 +54,7 @@ public class MainApplication extends Application {
Screen screen = Screen.getPrimary(); Screen screen = Screen.getPrimary();
Rectangle2D screenbound = screen.getBounds(); Rectangle2D screenbound = screen.getBounds();
Scene scene = new Scene(fxmlLoader.load(), screenbound.getWidth(), screenbound.getHeight()); Scene scene = new Scene(fxmlLoader.load(), screenbound.getWidth(), screenbound.getHeight());
stage.setTitle("MultiCam Capture App for ERHA 19052025-001"); stage.setTitle("MultiCam Capture App for ERHA "+version);
stage.setScene(scene); stage.setScene(scene);
stage.setResizable(true); stage.setResizable(true);
stage.setMaximized(true); stage.setMaximized(true);
@@ -62,13 +66,16 @@ public class MainApplication extends Application {
Logger.info("Application closed"); Logger.info("Application closed");
}); });
SomeCodes.LoadQRReader(); SomeCodes.LoadQRReader();
Detectors.LoadAllDetectors(); // buang, pindah ke Cameradetail
//Detectors.LoadAllDetectors();
stage.show(); stage.show();
AutoCloseAlert.init(); AutoCloseAlert.init();
Logger.info("Application started"); Logger.info("Application started");
System.out.println("Application version : " + version+" started");
System.out.println("Using "+(config.isProduction()?"Production":"Staging")+" API URL ");
sd.setEvent(new SecureDongleEvent() { sd.setEvent(new SecureDongleEvent() {
@Override @Override
public void onDongleMissing() { public void onDongleMissing() {
@@ -109,13 +116,6 @@ public class MainApplication extends Application {
} }
@Override
public void stop() throws Exception {
Logger.info("Application stopped");
super.stop();
}
public static void main(String[] args) { public static void main(String[] args) {
SomeCodes.ExtractResource("/tinylog.properties"); SomeCodes.ExtractResource("/tinylog.properties");
launch(); launch();

View File

@@ -3,10 +3,7 @@ package id.co.gtc.erhacam;
import FTP.FTPCheck; import FTP.FTPCheck;
import javafx.application.Platform; import javafx.application.Platform;
import javafx.fxml.FXML; import javafx.fxml.FXML;
import javafx.scene.control.Alert; import javafx.scene.control.*;
import javafx.scene.control.CheckBox;
import javafx.scene.control.ComboBox;
import javafx.scene.control.TextField;
import javafx.stage.DirectoryChooser; import javafx.stage.DirectoryChooser;
import javafx.stage.FileChooser; import javafx.stage.FileChooser;
import org.bytedeco.javacv.VideoInputFrameGrabber; import org.bytedeco.javacv.VideoInputFrameGrabber;
@@ -49,6 +46,158 @@ public class SettingView {
@FXML @FXML
private CheckBox FlipCamera; private CheckBox FlipCamera;
@FXML
private TextField Cam1TopCrop;
@FXML
private TextField Cam1BottomCrop;
@FXML
private TextField Cam1LeftCrop;
@FXML
private TextField Cam1RightCrop;
@FXML
private TextField Cam2TopCrop;
@FXML
private TextField Cam2BottomCrop;
@FXML
private TextField Cam2LeftCrop;
@FXML
private TextField Cam2RightCrop;
@FXML
private TextField Cam3TopCrop;
@FXML
private TextField Cam3BottomCrop;
@FXML
private TextField Cam3LeftCrop;
@FXML
private TextField Cam3RightCrop;
@FXML
private TextField Cam4TopCrop;
@FXML
private TextField Cam4BottomCrop;
@FXML
private TextField Cam4LeftCrop;
@FXML
private TextField Cam4RightCrop;
@FXML
private TextField Cam5TopCrop;
@FXML
private TextField Cam5BottomCrop;
@FXML
private TextField Cam5LeftCrop;
@FXML
private TextField Cam5RightCrop;
@FXML
private RadioButton apiStaging;
@FXML
private RadioButton apiProduction;
@FXML
public void changeAPIClick(){
if (apiStaging.isSelected()){
config.setAPI(false);
config.Save();
ShowAlert(Alert.AlertType.INFORMATION, "API Change", "API Change", "API Staging is selected, API will use Staging URL");
} else if (apiProduction.isSelected()){
config.setAPI(true);
config.Save();
ShowAlert(Alert.AlertType.INFORMATION, "API Change", "API Change", "API Production is selected, API will use Production URL");
} else {
ShowAlert(Alert.AlertType.ERROR, "API Change Error", "API Change Error", "Please select API Staging or API Production");
}
}
@FXML
private void ApplyCropClick(){
// Apply crop settings for each camera
boolean[] all_correct = {true};
CheckTextField(Cam1TopCrop,all_correct);
CheckTextField(Cam1BottomCrop,all_correct);
CheckTextField(Cam1LeftCrop,all_correct);
CheckTextField(Cam1RightCrop,all_correct);
CheckTextField(Cam2TopCrop,all_correct);
CheckTextField(Cam2BottomCrop,all_correct);
CheckTextField(Cam2LeftCrop,all_correct);
CheckTextField(Cam2RightCrop,all_correct);
CheckTextField(Cam3TopCrop,all_correct);
CheckTextField(Cam3BottomCrop,all_correct);
CheckTextField(Cam3LeftCrop,all_correct);
CheckTextField(Cam3RightCrop,all_correct);
CheckTextField(Cam4TopCrop,all_correct);
CheckTextField(Cam4BottomCrop,all_correct);
CheckTextField(Cam4LeftCrop,all_correct);
CheckTextField(Cam4RightCrop,all_correct);
CheckTextField(Cam5TopCrop,all_correct);
CheckTextField(Cam5BottomCrop,all_correct);
CheckTextField(Cam5LeftCrop,all_correct);
CheckTextField(Cam5RightCrop,all_correct);
if (all_correct[0]){
config.setCam1TopCrop(Cam1TopCrop.getText());
config.setCam1BottomCrop(Cam1BottomCrop.getText());
config.setCam1LeftCrop(Cam1LeftCrop.getText());
config.setCam1RightCrop(Cam1RightCrop.getText());
config.setCam2TopCrop(Cam2TopCrop.getText());
config.setCam2BottomCrop(Cam2BottomCrop.getText());
config.setCam2LeftCrop(Cam2LeftCrop.getText());
config.setCam2RightCrop(Cam2RightCrop.getText());
config.setCam3TopCrop(Cam3TopCrop.getText());
config.setCam3BottomCrop(Cam3BottomCrop.getText());
config.setCam3LeftCrop(Cam3LeftCrop.getText());
config.setCam3RightCrop(Cam3RightCrop.getText());
config.setCam4TopCrop(Cam4TopCrop.getText());
config.setCam4BottomCrop(Cam4BottomCrop.getText());
config.setCam4LeftCrop(Cam4LeftCrop.getText());
config.setCam4RightCrop(Cam4RightCrop.getText());
config.setCam5TopCrop(Cam5TopCrop.getText());
config.setCam5BottomCrop(Cam5BottomCrop.getText());
config.setCam5LeftCrop(Cam5LeftCrop.getText());
config.setCam5RightCrop(Cam5RightCrop.getText());
config.Save();
} else {
ShowAlert(Alert.AlertType.ERROR, "Crop Setting Error", "Crop Setting Error", "Please check your crop settings, some values are invalid");
}
}
private void CheckTextField(TextField tf, boolean[] value){
if (ValidCropValue(tf)){
tf.setStyle("");
} else {
tf.setStyle("-fx-border-color: red;");
value[0] = false;
}
}
private boolean ValidCropValue(TextField tf){
if (tf != null){
double value;
String name = tf.getId();
try {
value = Double.parseDouble(tf.getText());
if (value >= 0.0){
if (name.contains("Top")){
return value <= config.getTopcropmax();
} else if (name.contains("Bottom")){
return value <= config.getBottomcropmax();
} else if (name.contains("Left")){
return value <= config.getLeftcropmax();
} else if (name.contains("Right")){
return value <= config.getRightcropmax();
}
}
} catch (NumberFormatException ignored) {
}
}
return false;
}
final FileChooser jfc = new FileChooser(); final FileChooser jfc = new FileChooser();
@@ -108,9 +257,20 @@ public class SettingView {
config.setCascadeScaleFactor(scale); config.setCascadeScaleFactor(scale);
config.Save(); config.Save();
Detectors.setFaceMaxSize(max); if (MainApplication.detectorsList!=null){
Detectors.setFaceMinSize(min); MainApplication.detectorsList.forEach((i,d)-> {
Detectors.setScaleFactor(scale); d.setFaceMaxSize(max);
d.setFaceMinSize(min);
d.setScaleFactor(scale);
});
} else {
Logger.error("MainApplication.detectorsList is null, unable to set cascade settings");
}
// Detectors.setFaceMaxSize(max);
// Detectors.setFaceMinSize(min);
// Detectors.setScaleFactor(scale);
ShowAlert(Alert.AlertType.INFORMATION, "Cascade Setting", "Cascade Setting Saved", "Cascade Setting Saved Successfully"); ShowAlert(Alert.AlertType.INFORMATION, "Cascade Setting", "Cascade Setting Saved", "Cascade Setting Saved Successfully");
} else show_cascade_alert("Max Size must be greater than Min Size"); } else show_cascade_alert("Max Size must be greater than Min Size");
@@ -198,8 +358,31 @@ public class SettingView {
MirrorCamera.setSelected(config.isMirrorCamera()); MirrorCamera.setSelected(config.isMirrorCamera());
FlipCamera.setSelected(config.isFlipCamera()); FlipCamera.setSelected(config.isFlipCamera());
apiStaging.setSelected(!config.isProduction());
apiProduction.setSelected(config.isProduction());
TextFieldSetText(Sharpness,String.valueOf(config.getSharpnessThreshold())); TextFieldSetText(Sharpness,String.valueOf(config.getSharpnessThreshold()));
TextFieldSetText(Cam1TopCrop,String.valueOf(config.getCam1TopCrop()));
TextFieldSetText(Cam1BottomCrop,String.valueOf(config.getCam1BottomCrop()));
TextFieldSetText(Cam1LeftCrop,String.valueOf(config.getCam1LeftCrop()));
TextFieldSetText(Cam1RightCrop,String.valueOf(config.getCam1RightCrop()));
TextFieldSetText(Cam2TopCrop,String.valueOf(config.getCam2TopCrop()));
TextFieldSetText(Cam2BottomCrop,String.valueOf(config.getCam2BottomCrop()));
TextFieldSetText(Cam2LeftCrop,String.valueOf(config.getCam2LeftCrop()));
TextFieldSetText(Cam2RightCrop,String.valueOf(config.getCam2RightCrop()));
TextFieldSetText(Cam3TopCrop,String.valueOf(config.getCam3TopCrop()));
TextFieldSetText(Cam3BottomCrop,String.valueOf(config.getCam3BottomCrop()));
TextFieldSetText(Cam3LeftCrop,String.valueOf(config.getCam3LeftCrop()));
TextFieldSetText(Cam3RightCrop,String.valueOf(config.getCam3RightCrop()));
TextFieldSetText(Cam4TopCrop,String.valueOf(config.getCam4TopCrop()));
TextFieldSetText(Cam4BottomCrop,String.valueOf(config.getCam4BottomCrop()));
TextFieldSetText(Cam4LeftCrop,String.valueOf(config.getCam4LeftCrop()));
TextFieldSetText(Cam4RightCrop,String.valueOf(config.getCam4RightCrop()));
TextFieldSetText(Cam5TopCrop,String.valueOf(config.getCam5TopCrop()));
TextFieldSetText(Cam5BottomCrop,String.valueOf(config.getCam5BottomCrop()));
TextFieldSetText(Cam5LeftCrop,String.valueOf(config.getCam5LeftCrop()));
TextFieldSetText(Cam5RightCrop,String.valueOf(config.getCam5RightCrop()));
}); });
} }

View File

@@ -16,6 +16,7 @@ module id.co.gtc.erhacam {
requires java.sql; requires java.sql;
requires javafx.graphics; requires javafx.graphics;
requires java.net.http; requires java.net.http;
requires org.tinylog.impl;
opens id.co.gtc.erhacam to javafx.fxml; opens id.co.gtc.erhacam to javafx.fxml;

View File

@@ -39,7 +39,6 @@ import java.time.LocalDateTime;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import static Config.SomeCodes.*; import static Config.SomeCodes.*;
import static id.co.gtc.erhacam.Detectors.frontalfaceDetector;
import static org.bytedeco.opencv.global.opencv_core.CV_8UC3; 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_core.mean;
import static org.bytedeco.opencv.global.opencv_imgcodecs.imwrite; import static org.bytedeco.opencv.global.opencv_imgcodecs.imwrite;
@@ -776,11 +775,11 @@ public class Cameradetail_Arducam {
* @return true if face detected, otherwise false * @return true if face detected, otherwise false
*/ */
private RectVector DetectFace(UMat graymat){ private RectVector DetectFace(UMat graymat){
if (frontalfaceDetector!=null){ // if (frontalfaceDetector!=null){
val face = new RectVector(); // val face = new RectVector();
frontalfaceDetector.detectMultiScale(graymat, face); // frontalfaceDetector.detectMultiScale(graymat, face);
return face; // return face;
} // }
return null; return null;
} }

View File

@@ -30,20 +30,23 @@
</AnchorPane> </AnchorPane>
<AnchorPane prefHeight="200.0" prefWidth="200.0" GridPane.rowIndex="1"> <AnchorPane prefHeight="200.0" prefWidth="200.0" GridPane.rowIndex="1">
<children> <children>
<GridPane AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0"> <GridPane fx:id="camerassetting" prefHeight="300.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<columnConstraints> <columnConstraints>
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" /> <ColumnConstraints percentWidth="40.0" />
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" percentWidth="40.0" prefWidth="100.0" /> <ColumnConstraints percentWidth="40.0" />
<ColumnConstraints percentWidth="20.0" />
</columnConstraints> </columnConstraints>
<rowConstraints> <rowConstraints>
<RowConstraints minHeight="10.0" vgrow="SOMETIMES" /> <RowConstraints minHeight="10.0" vgrow="SOMETIMES" />
</rowConstraints> </rowConstraints>
<children> <children>
<GridPane> <AnchorPane>
<children>
<GridPane fx:id="cameraselection" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<columnConstraints> <columnConstraints>
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" percentWidth="25.0" prefWidth="100.0" /> <ColumnConstraints percentWidth="25" />
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" /> <ColumnConstraints percentWidth="50" />
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" percentWidth="15.0" prefWidth="100.0" /> <ColumnConstraints percentWidth="25" />
</columnConstraints> </columnConstraints>
<rowConstraints> <rowConstraints>
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" /> <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
@@ -53,124 +56,148 @@
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" /> <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" /> <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" /> <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" /> <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
</rowConstraints> </rowConstraints>
<children> <children>
<AnchorPane prefHeight="200.0" prefWidth="200.0">
<children>
<Label layoutX="30.0" layoutY="12.0" prefHeight="40.8" prefWidth="87.2" text="Left 90" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="5.0" AnchorPane.rightAnchor="5.0" AnchorPane.topAnchor="0.0" />
</children>
</AnchorPane>
<AnchorPane prefHeight="200.0" prefWidth="200.0" GridPane.rowIndex="1"> <AnchorPane prefHeight="200.0" prefWidth="200.0" GridPane.rowIndex="1">
<children> <children>
<Label layoutX="44.0" layoutY="12.0" prefHeight="40.8" prefWidth="87.2" text="Left 45" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="5.0" AnchorPane.rightAnchor="5.0" AnchorPane.topAnchor="0.0" /> <Label layoutX="30.0" layoutY="12.0" prefHeight="40.8" prefWidth="87.2" text="Cam 1" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="5.0" AnchorPane.rightAnchor="5.0" AnchorPane.topAnchor="0.0" />
</children> </children>
</AnchorPane> </AnchorPane>
<AnchorPane prefHeight="200.0" prefWidth="200.0" GridPane.rowIndex="2"> <AnchorPane prefHeight="200.0" prefWidth="200.0" GridPane.rowIndex="2">
<children> <children>
<Label layoutX="30.0" layoutY="6.0" prefHeight="40.8" prefWidth="87.2" text="Center" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="5.0" AnchorPane.rightAnchor="5.0" AnchorPane.topAnchor="0.0" /> <Label layoutX="44.0" layoutY="12.0" prefHeight="40.8" prefWidth="87.2" text="Cam 2" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="5.0" AnchorPane.rightAnchor="5.0" AnchorPane.topAnchor="0.0" />
</children> </children>
</AnchorPane> </AnchorPane>
<AnchorPane prefHeight="200.0" prefWidth="200.0" GridPane.rowIndex="3"> <AnchorPane prefHeight="200.0" prefWidth="200.0" GridPane.rowIndex="3">
<children> <children>
<Label layoutX="24.0" layoutY="6.0" prefHeight="40.0" prefWidth="87.2" text="Right 45" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="5.0" AnchorPane.rightAnchor="5.0" AnchorPane.topAnchor="0.0" /> <Label layoutX="30.0" layoutY="6.0" prefHeight="40.8" prefWidth="87.2" text="Cam 3" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="5.0" AnchorPane.rightAnchor="5.0" AnchorPane.topAnchor="0.0" />
</children> </children>
</AnchorPane> </AnchorPane>
<AnchorPane prefHeight="200.0" prefWidth="200.0" GridPane.rowIndex="4"> <AnchorPane prefHeight="200.0" prefWidth="200.0" GridPane.rowIndex="4">
<children> <children>
<Label layoutX="36.0" layoutY="11.0" prefHeight="40.0" prefWidth="87.2" text="Right 90" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="5.0" AnchorPane.rightAnchor="5.0" AnchorPane.topAnchor="0.0" /> <Label layoutX="24.0" layoutY="6.0" prefHeight="40.0" prefWidth="87.2" text="Cam 4" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="5.0" AnchorPane.rightAnchor="5.0" AnchorPane.topAnchor="0.0" />
</children>
</AnchorPane>
<AnchorPane prefHeight="200.0" prefWidth="200.0" GridPane.columnIndex="1">
<children>
<ComboBox fx:id="CameraLeft90" layoutX="54.0" layoutY="8.0" prefHeight="40.8" prefWidth="408.0" AnchorPane.bottomAnchor="2.0" AnchorPane.leftAnchor="2.0" AnchorPane.rightAnchor="2.0" AnchorPane.topAnchor="2.0" />
</children>
</AnchorPane>
<AnchorPane prefHeight="200.0" prefWidth="200.0" GridPane.columnIndex="1" GridPane.rowIndex="1">
<children>
<ComboBox fx:id="CameraLeft45" layoutX="26.0" layoutY="14.0" prefHeight="40.8" prefWidth="408.0" AnchorPane.bottomAnchor="2.0" AnchorPane.leftAnchor="2.0" AnchorPane.rightAnchor="2.0" AnchorPane.topAnchor="2.0" />
</children>
</AnchorPane>
<AnchorPane prefHeight="200.0" prefWidth="200.0" GridPane.columnIndex="1" GridPane.rowIndex="2">
<children>
<ComboBox fx:id="CameraCenter" layoutX="88.0" layoutY="8.0" prefHeight="40.8" prefWidth="408.0" AnchorPane.bottomAnchor="2.0" AnchorPane.leftAnchor="2.0" AnchorPane.rightAnchor="2.0" AnchorPane.topAnchor="2.0" />
</children>
</AnchorPane>
<AnchorPane prefHeight="200.0" prefWidth="200.0" GridPane.columnIndex="1" GridPane.rowIndex="3">
<children>
<ComboBox fx:id="CameraRight45" layoutX="54.0" layoutY="8.0" prefHeight="40.0" prefWidth="408.0" AnchorPane.bottomAnchor="2.0" AnchorPane.leftAnchor="2.0" AnchorPane.rightAnchor="2.0" AnchorPane.topAnchor="2.0" />
</children>
</AnchorPane>
<AnchorPane prefHeight="200.0" prefWidth="200.0" GridPane.columnIndex="1" GridPane.rowIndex="4">
<children>
<ComboBox fx:id="CameraRight90" layoutX="75.0" layoutY="1.0" prefHeight="40.0" prefWidth="408.0" AnchorPane.bottomAnchor="2.0" AnchorPane.leftAnchor="2.0" AnchorPane.rightAnchor="2.0" AnchorPane.topAnchor="2.0" />
</children>
</AnchorPane>
<AnchorPane prefHeight="200.0" prefWidth="200.0" GridPane.columnIndex="2">
<children>
<Button layoutY="8.0" mnemonicParsing="false" onAction="#ApplyCameraLeft90" prefHeight="40.8" prefWidth="88.0" text="Apply" AnchorPane.bottomAnchor="2.0" AnchorPane.leftAnchor="2.0" AnchorPane.rightAnchor="2.0" AnchorPane.topAnchor="2.0" />
</children>
</AnchorPane>
<AnchorPane prefHeight="200.0" prefWidth="200.0" GridPane.columnIndex="2" GridPane.rowIndex="1">
<children>
<Button layoutX="22.0" layoutY="2.0" mnemonicParsing="false" onAction="#ApplyCameraLeft45" prefHeight="40.8" prefWidth="88.0" text="Apply" AnchorPane.bottomAnchor="2.0" AnchorPane.leftAnchor="2.0" AnchorPane.rightAnchor="2.0" AnchorPane.topAnchor="2.0" />
</children>
</AnchorPane>
<AnchorPane prefHeight="200.0" prefWidth="200.0" GridPane.columnIndex="2" GridPane.rowIndex="2">
<children>
<Button layoutX="18.0" layoutY="2.0" mnemonicParsing="false" onAction="#ApplyCameraFront" prefHeight="40.8" prefWidth="88.0" text="Apply" AnchorPane.bottomAnchor="2.0" AnchorPane.leftAnchor="2.0" AnchorPane.rightAnchor="2.0" AnchorPane.topAnchor="2.0" />
</children>
</AnchorPane>
<AnchorPane prefHeight="200.0" prefWidth="200.0" GridPane.columnIndex="2" GridPane.rowIndex="3">
<children>
<Button layoutX="22.0" layoutY="8.0" mnemonicParsing="false" onAction="#ApplyCameraRight45" prefHeight="40.0" prefWidth="88.0" text="Apply" AnchorPane.bottomAnchor="2.0" AnchorPane.leftAnchor="2.0" AnchorPane.rightAnchor="2.0" AnchorPane.topAnchor="2.0" />
</children>
</AnchorPane>
<AnchorPane prefHeight="200.0" prefWidth="200.0" GridPane.columnIndex="2" GridPane.rowIndex="4">
<children>
<Button layoutX="22.0" layoutY="8.0" mnemonicParsing="false" onAction="#ApplyCameraRight90" prefHeight="40.0" prefWidth="88.0" text="Apply" AnchorPane.bottomAnchor="2.0" AnchorPane.leftAnchor="2.0" AnchorPane.rightAnchor="2.0" AnchorPane.topAnchor="2.0" />
</children> </children>
</AnchorPane> </AnchorPane>
<AnchorPane prefHeight="200.0" prefWidth="200.0" GridPane.rowIndex="5"> <AnchorPane prefHeight="200.0" prefWidth="200.0" GridPane.rowIndex="5">
<children> <children>
<Label layoutX="60.0" prefHeight="40.0" prefWidth="175.0" text="Mirror Camera" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="5.0" AnchorPane.rightAnchor="5.0" AnchorPane.topAnchor="0.0" /> <Label layoutX="36.0" layoutY="11.0" prefHeight="40.0" prefWidth="87.2" text="Cam 5" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="5.0" AnchorPane.rightAnchor="5.0" AnchorPane.topAnchor="0.0" />
</children>
</AnchorPane>
<AnchorPane prefHeight="200.0" prefWidth="200.0" GridPane.columnIndex="1" GridPane.rowIndex="1">
<children>
<ComboBox fx:id="CameraLeft90" layoutX="54.0" layoutY="8.0" prefHeight="40.8" prefWidth="408.0" AnchorPane.bottomAnchor="2.0" AnchorPane.leftAnchor="2.0" AnchorPane.rightAnchor="2.0" AnchorPane.topAnchor="2.0" />
</children>
</AnchorPane>
<AnchorPane prefHeight="200.0" prefWidth="200.0" GridPane.columnIndex="1" GridPane.rowIndex="2">
<children>
<ComboBox fx:id="CameraLeft45" layoutX="26.0" layoutY="14.0" prefHeight="40.8" prefWidth="408.0" AnchorPane.bottomAnchor="2.0" AnchorPane.leftAnchor="2.0" AnchorPane.rightAnchor="2.0" AnchorPane.topAnchor="2.0" />
</children>
</AnchorPane>
<AnchorPane prefHeight="200.0" prefWidth="200.0" GridPane.columnIndex="1" GridPane.rowIndex="3">
<children>
<ComboBox fx:id="CameraCenter" layoutX="88.0" layoutY="8.0" prefHeight="40.8" prefWidth="408.0" AnchorPane.bottomAnchor="2.0" AnchorPane.leftAnchor="2.0" AnchorPane.rightAnchor="2.0" AnchorPane.topAnchor="2.0" />
</children>
</AnchorPane>
<AnchorPane prefHeight="200.0" prefWidth="200.0" GridPane.columnIndex="1" GridPane.rowIndex="4">
<children>
<ComboBox fx:id="CameraRight45" layoutX="54.0" layoutY="8.0" prefHeight="40.0" prefWidth="408.0" AnchorPane.bottomAnchor="2.0" AnchorPane.leftAnchor="2.0" AnchorPane.rightAnchor="2.0" AnchorPane.topAnchor="2.0" />
</children> </children>
</AnchorPane> </AnchorPane>
<AnchorPane prefHeight="200.0" prefWidth="200.0" GridPane.columnIndex="1" GridPane.rowIndex="5"> <AnchorPane prefHeight="200.0" prefWidth="200.0" GridPane.columnIndex="1" GridPane.rowIndex="5">
<children> <children>
<CheckBox fx:id="MirrorCamera" layoutX="30.0" layoutY="6.0" mnemonicParsing="false" prefHeight="40.0" prefWidth="175.0" AnchorPane.bottomAnchor="2.0" AnchorPane.leftAnchor="2.0" AnchorPane.rightAnchor="2.0" AnchorPane.topAnchor="2.0" /> <ComboBox fx:id="CameraRight90" layoutX="75.0" layoutY="1.0" prefHeight="40.0" prefWidth="408.0" AnchorPane.bottomAnchor="2.0" AnchorPane.leftAnchor="2.0" AnchorPane.rightAnchor="2.0" AnchorPane.topAnchor="2.0" />
</children>
</AnchorPane>
<AnchorPane prefHeight="200.0" prefWidth="200.0" GridPane.columnIndex="2" GridPane.rowIndex="1">
<children>
<Button layoutY="8.0" mnemonicParsing="false" onAction="#ApplyCameraLeft90" prefHeight="40.8" prefWidth="88.0" text="Apply" AnchorPane.bottomAnchor="2.0" AnchorPane.leftAnchor="2.0" AnchorPane.rightAnchor="2.0" AnchorPane.topAnchor="2.0" />
</children>
</AnchorPane>
<AnchorPane prefHeight="200.0" prefWidth="200.0" GridPane.columnIndex="2" GridPane.rowIndex="2">
<children>
<Button layoutX="22.0" layoutY="2.0" mnemonicParsing="false" onAction="#ApplyCameraLeft45" prefHeight="40.8" prefWidth="88.0" text="Apply" AnchorPane.bottomAnchor="2.0" AnchorPane.leftAnchor="2.0" AnchorPane.rightAnchor="2.0" AnchorPane.topAnchor="2.0" />
</children>
</AnchorPane>
<AnchorPane prefHeight="200.0" prefWidth="200.0" GridPane.columnIndex="2" GridPane.rowIndex="3">
<children>
<Button layoutX="18.0" layoutY="2.0" mnemonicParsing="false" onAction="#ApplyCameraFront" prefHeight="40.8" prefWidth="88.0" text="Apply" AnchorPane.bottomAnchor="2.0" AnchorPane.leftAnchor="2.0" AnchorPane.rightAnchor="2.0" AnchorPane.topAnchor="2.0" />
</children>
</AnchorPane>
<AnchorPane prefHeight="200.0" prefWidth="200.0" GridPane.columnIndex="2" GridPane.rowIndex="4">
<children>
<Button layoutX="22.0" layoutY="8.0" mnemonicParsing="false" onAction="#ApplyCameraRight45" prefHeight="40.0" prefWidth="88.0" text="Apply" AnchorPane.bottomAnchor="2.0" AnchorPane.leftAnchor="2.0" AnchorPane.rightAnchor="2.0" AnchorPane.topAnchor="2.0" />
</children>
</AnchorPane>
<AnchorPane prefHeight="200.0" prefWidth="200.0" GridPane.columnIndex="2" GridPane.rowIndex="5">
<children>
<Button layoutX="22.0" layoutY="8.0" mnemonicParsing="false" onAction="#ApplyCameraRight90" prefHeight="40.0" prefWidth="88.0" text="Apply" AnchorPane.bottomAnchor="2.0" AnchorPane.leftAnchor="2.0" AnchorPane.rightAnchor="2.0" AnchorPane.topAnchor="2.0" />
</children> </children>
</AnchorPane> </AnchorPane>
<AnchorPane prefHeight="200.0" prefWidth="200.0" GridPane.rowIndex="6"> <AnchorPane prefHeight="200.0" prefWidth="200.0" GridPane.rowIndex="6">
<children> <children>
<Label prefHeight="40.0" prefWidth="175.0" text="Flip Camera" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="5.0" AnchorPane.rightAnchor="5.0" AnchorPane.topAnchor="0.0" /> <Label layoutX="60.0" prefHeight="40.0" prefWidth="175.0" text="Mirror" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="5.0" AnchorPane.rightAnchor="5.0" AnchorPane.topAnchor="0.0" />
</children> </children>
</AnchorPane> </AnchorPane>
<AnchorPane prefHeight="200.0" prefWidth="200.0" GridPane.columnIndex="1" GridPane.rowIndex="6"> <AnchorPane prefHeight="200.0" prefWidth="200.0" GridPane.columnIndex="1" GridPane.rowIndex="6">
<children> <children>
<CheckBox fx:id="FlipCamera" mnemonicParsing="false" prefHeight="40.0" prefWidth="175.0" AnchorPane.bottomAnchor="2.0" AnchorPane.leftAnchor="2.0" AnchorPane.rightAnchor="2.0" AnchorPane.topAnchor="2.0" /> <CheckBox fx:id="MirrorCamera" layoutX="30.0" layoutY="6.0" mnemonicParsing="false" prefHeight="40.0" prefWidth="175.0" AnchorPane.bottomAnchor="2.0" AnchorPane.leftAnchor="2.0" AnchorPane.rightAnchor="2.0" AnchorPane.topAnchor="2.0" />
</children> </children>
</AnchorPane> </AnchorPane>
<AnchorPane prefHeight="200.0" prefWidth="200.0" GridPane.rowIndex="7"> <AnchorPane prefHeight="200.0" prefWidth="200.0" GridPane.rowIndex="7">
<children> <children>
<Label layoutX="32.0" layoutY="19.0" prefHeight="37.0" prefWidth="92.0" text="Sharpness Threshold" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="5.0" AnchorPane.rightAnchor="5.0" AnchorPane.topAnchor="0.0" /> <Label prefHeight="40.0" prefWidth="175.0" text="Flip" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="5.0" AnchorPane.rightAnchor="5.0" AnchorPane.topAnchor="0.0" />
</children> </children>
</AnchorPane> </AnchorPane>
<AnchorPane prefHeight="200.0" prefWidth="200.0" GridPane.columnIndex="1" GridPane.rowIndex="7"> <AnchorPane prefHeight="200.0" prefWidth="200.0" GridPane.columnIndex="1" GridPane.rowIndex="7">
<children>
<CheckBox fx:id="FlipCamera" mnemonicParsing="false" prefHeight="40.0" prefWidth="175.0" AnchorPane.bottomAnchor="2.0" AnchorPane.leftAnchor="2.0" AnchorPane.rightAnchor="2.0" AnchorPane.topAnchor="2.0" />
</children>
</AnchorPane>
<AnchorPane prefHeight="200.0" prefWidth="200.0">
<children>
<Label layoutX="32.0" layoutY="19.0" prefHeight="37.0" prefWidth="92.0" text="Sharpness" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="5.0" AnchorPane.rightAnchor="5.0" AnchorPane.topAnchor="0.0" />
</children>
</AnchorPane>
<AnchorPane prefHeight="200.0" prefWidth="200.0" GridPane.columnIndex="1">
<children> <children>
<TextField fx:id="Sharpness" layoutX="35.0" layoutY="2.0" prefHeight="37.0" prefWidth="368.0" promptText="if Sharpness below this number, photos considered as blurred" AnchorPane.bottomAnchor="2.0" AnchorPane.leftAnchor="2.0" AnchorPane.rightAnchor="2.0" AnchorPane.topAnchor="2.0" /> <TextField fx:id="Sharpness" layoutX="35.0" layoutY="2.0" prefHeight="37.0" prefWidth="368.0" promptText="if Sharpness below this number, photos considered as blurred" AnchorPane.bottomAnchor="2.0" AnchorPane.leftAnchor="2.0" AnchorPane.rightAnchor="2.0" AnchorPane.topAnchor="2.0" />
</children> </children>
</AnchorPane> </AnchorPane>
<AnchorPane GridPane.columnIndex="2" GridPane.rowIndex="7"> <AnchorPane GridPane.columnIndex="2">
<children> <children>
<Button mnemonicParsing="false" onAction="#SharpnessApply" prefHeight="37.0" prefWidth="92.0" text="Apply" AnchorPane.bottomAnchor="2.0" AnchorPane.leftAnchor="2.0" AnchorPane.rightAnchor="2.0" AnchorPane.topAnchor="2.0" /> <Button mnemonicParsing="false" onAction="#SharpnessApply" prefHeight="37.0" prefWidth="92.0" text="Apply" AnchorPane.bottomAnchor="2.0" AnchorPane.leftAnchor="2.0" AnchorPane.rightAnchor="2.0" AnchorPane.topAnchor="2.0" />
</children> </children>
</AnchorPane>
<AnchorPane GridPane.rowIndex="8">
<children>
<Label text="API Type" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="5.0" AnchorPane.rightAnchor="5.0" AnchorPane.topAnchor="0.0" />
</children>
</AnchorPane>
<AnchorPane GridPane.columnIndex="1" GridPane.rowIndex="8">
<children>
<RadioButton fx:id="apiStaging" layoutX="14.0" layoutY="8.0" mnemonicParsing="false" text="Staging" AnchorPane.bottomAnchor="2.0" AnchorPane.leftAnchor="2.0" AnchorPane.topAnchor="2.0">
<toggleGroup>
<ToggleGroup fx:id="apiselect" />
</toggleGroup></RadioButton>
<RadioButton fx:id="apiProduction" layoutX="103.0" layoutY="11.0" mnemonicParsing="false" text="Production" toggleGroup="$apiselect" AnchorPane.bottomAnchor="2.0" AnchorPane.rightAnchor="2.0" AnchorPane.topAnchor="2.0" />
</children>
</AnchorPane>
<AnchorPane GridPane.columnIndex="2" GridPane.rowIndex="8">
<children>
<Button mnemonicParsing="false" onAction="#changeAPIClick" prefHeight="33.6" prefWidth="102.4" text="Apply" AnchorPane.bottomAnchor="2.0" AnchorPane.leftAnchor="2.0" AnchorPane.rightAnchor="2.0" AnchorPane.topAnchor="2.0" />
</children>
</AnchorPane> </AnchorPane>
</children> </children>
</GridPane> </GridPane>
<GridPane GridPane.columnIndex="1"> </children>
</AnchorPane>
<AnchorPane maxWidth="400.0" GridPane.columnIndex="2">
<children>
<GridPane AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<columnConstraints> <columnConstraints>
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" /> <ColumnConstraints prefWidth="300" />
</columnConstraints> </columnConstraints>
<rowConstraints> <rowConstraints>
<RowConstraints maxHeight="-Infinity" minHeight="-Infinity" prefHeight="30.0" vgrow="SOMETIMES" /> <RowConstraints maxHeight="-Infinity" minHeight="-Infinity" prefHeight="30.0" vgrow="SOMETIMES" />
@@ -190,14 +217,10 @@
</AnchorPane> </AnchorPane>
<AnchorPane GridPane.rowIndex="1"> <AnchorPane GridPane.rowIndex="1">
<children> <children>
<ScrollPane layoutX="5.0" prefHeight="200.8" prefWidth="410.4" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="5.0" AnchorPane.rightAnchor="5.0" AnchorPane.topAnchor="0.0"> <GridPane prefHeight="200.0" prefWidth="300.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="5.0" AnchorPane.rightAnchor="5.0" AnchorPane.topAnchor="0.0">
<content>
<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="200.0" minWidth="0.0" prefWidth="350.0">
<children>
<GridPane prefHeight="200.0" prefWidth="300.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<columnConstraints> <columnConstraints>
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" /> <ColumnConstraints percentWidth="70.0" />
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" /> <ColumnConstraints percentWidth="30.0" />
</columnConstraints> </columnConstraints>
<rowConstraints> <rowConstraints>
<RowConstraints maxHeight="-Infinity" minHeight="-Infinity" prefHeight="40.0" vgrow="SOMETIMES" /> <RowConstraints maxHeight="-Infinity" minHeight="-Infinity" prefHeight="40.0" vgrow="SOMETIMES" />
@@ -207,27 +230,27 @@
<children> <children>
<AnchorPane> <AnchorPane>
<children> <children>
<Label prefHeight="40.0" prefWidth="175.2" text="Scale Factor" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" /> <Label prefHeight="40.0" text="Scale Factor" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" />
</children> </children>
</AnchorPane> </AnchorPane>
<AnchorPane GridPane.rowIndex="1"> <AnchorPane GridPane.rowIndex="1">
<children> <children>
<Label prefHeight="40.0" prefWidth="175.2" text="Minimum Size" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" /> <Label prefHeight="40.0" text="Minimum Size" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" />
</children> </children>
</AnchorPane> </AnchorPane>
<AnchorPane GridPane.rowIndex="2"> <AnchorPane GridPane.rowIndex="2">
<children> <children>
<Label prefHeight="40.0" prefWidth="175.2" text="Maximum Size" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" /> <Label prefHeight="40.0" text="Maximum Size" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" />
</children> </children>
</AnchorPane> </AnchorPane>
<AnchorPane GridPane.columnIndex="1"> <AnchorPane GridPane.columnIndex="1">
<children> <children>
<TextField fx:id="cascadeScaleFactor" alignment="CENTER" prefHeight="40.0" prefWidth="175.2" text="1.2" AnchorPane.bottomAnchor="5.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" /> <TextField fx:id="cascadeScaleFactor" alignment="CENTER" prefHeight="40.0" prefWidth="175.0" text="1.2" AnchorPane.bottomAnchor="5.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" />
</children> </children>
</AnchorPane> </AnchorPane>
<AnchorPane GridPane.columnIndex="1" GridPane.rowIndex="1"> <AnchorPane GridPane.columnIndex="1" GridPane.rowIndex="1">
<children> <children>
<TextField fx:id="cascadeMinSize" alignment="CENTER" prefHeight="40.0" prefWidth="175.2" text="200" AnchorPane.bottomAnchor="5.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" /> <TextField fx:id="cascadeMinSize" alignment="CENTER" prefHeight="40.0" prefWidth="175.0" text="200" AnchorPane.bottomAnchor="5.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" />
</children> </children>
</AnchorPane> </AnchorPane>
<AnchorPane GridPane.columnIndex="1" GridPane.rowIndex="2"> <AnchorPane GridPane.columnIndex="1" GridPane.rowIndex="2">
@@ -239,13 +262,252 @@
</GridPane> </GridPane>
</children> </children>
</AnchorPane> </AnchorPane>
</content> </children>
</ScrollPane> </GridPane>
</children>
</AnchorPane>
<AnchorPane GridPane.columnIndex="1">
<children>
<GridPane fx:id="cropsetting" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="5.0" AnchorPane.rightAnchor="5.0" AnchorPane.topAnchor="0.0">
<columnConstraints>
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
</columnConstraints>
<rowConstraints>
<RowConstraints maxHeight="-Infinity" minHeight="-Infinity" prefHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints prefHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints prefHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints prefHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints prefHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints prefHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints maxHeight="-Infinity" minHeight="-Infinity" prefHeight="50.0" vgrow="SOMETIMES" />
</rowConstraints>
<children>
<AnchorPane prefHeight="200.0" prefWidth="200.0">
<children>
<GridPane layoutX="-21.0" layoutY="-31.0" prefHeight="30.4" prefWidth="399.2" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<columnConstraints>
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
</columnConstraints>
<rowConstraints>
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
</rowConstraints>
<children>
<AnchorPane prefHeight="200.0" prefWidth="200.0">
<children>
<Label alignment="CENTER" layoutX="36.0" layoutY="6.0" prefHeight="30.4" prefWidth="100.0" text="Top" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" />
</children>
</AnchorPane>
<AnchorPane prefHeight="200.0" prefWidth="200.0" GridPane.columnIndex="1">
<children>
<Label alignment="CENTER" text="Bottom" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" />
</children>
</AnchorPane>
<AnchorPane prefHeight="200.0" prefWidth="200.0" GridPane.columnIndex="2">
<children>
<Label alignment="CENTER" layoutX="7.0" layoutY="8.0" text="Left" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" />
</children></AnchorPane>
<AnchorPane prefHeight="200.0" prefWidth="200.0" GridPane.columnIndex="3">
<children>
<Label alignment="CENTER" layoutX="36.0" layoutY="8.0" text="Right" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" />
</children></AnchorPane>
</children>
</GridPane>
</children>
</AnchorPane>
<AnchorPane prefHeight="200.0" prefWidth="200.0" GridPane.rowIndex="1">
<children>
<GridPane prefHeight="50.4" prefWidth="399.2" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<columnConstraints>
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
</columnConstraints>
<rowConstraints>
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
</rowConstraints>
<children>
<AnchorPane prefHeight="200.0" prefWidth="200.0">
<children>
<TextField fx:id="Cam1TopCrop" alignment="CENTER" promptText="crop percent" text="10" AnchorPane.bottomAnchor="3.0" AnchorPane.leftAnchor="3.0" AnchorPane.rightAnchor="3.0" AnchorPane.topAnchor="3.0" />
</children>
</AnchorPane>
<AnchorPane prefHeight="200.0" prefWidth="200.0" GridPane.columnIndex="1">
<children>
<TextField fx:id="Cam1BottomCrop" alignment="CENTER" promptText="crop percent" text="10" AnchorPane.bottomAnchor="3.0" AnchorPane.leftAnchor="3.0" AnchorPane.rightAnchor="3.0" AnchorPane.topAnchor="3.0" />
</children>
</AnchorPane>
<AnchorPane prefHeight="200.0" prefWidth="200.0" GridPane.columnIndex="2">
<children>
<TextField fx:id="Cam1LeftCrop" alignment="CENTER" promptText="crop percent" text="10" AnchorPane.bottomAnchor="3.0" AnchorPane.leftAnchor="3.0" AnchorPane.rightAnchor="3.0" AnchorPane.topAnchor="3.0" />
</children>
</AnchorPane>
<AnchorPane prefHeight="200.0" prefWidth="200.0" GridPane.columnIndex="3">
<children>
<TextField fx:id="Cam1RightCrop" alignment="CENTER" promptText="crop percent" text="10" AnchorPane.bottomAnchor="3.0" AnchorPane.leftAnchor="3.0" AnchorPane.rightAnchor="3.0" AnchorPane.topAnchor="3.0" />
</children> </children>
</AnchorPane> </AnchorPane>
</children> </children>
</GridPane> </GridPane>
</children> </children>
</AnchorPane>
<AnchorPane prefHeight="200.0" prefWidth="200.0" GridPane.rowIndex="2">
<children>
<GridPane layoutX="-27.0" layoutY="-31.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<columnConstraints>
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
</columnConstraints>
<rowConstraints>
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
</rowConstraints>
<children>
<AnchorPane prefHeight="200.0" prefWidth="200.0">
<children>
<TextField fx:id="Cam2TopCrop" alignment="CENTER" promptText="crop percent" text="10" AnchorPane.bottomAnchor="3.0" AnchorPane.leftAnchor="3.0" AnchorPane.rightAnchor="3.0" AnchorPane.topAnchor="3.0" />
</children>
</AnchorPane>
<AnchorPane prefHeight="200.0" prefWidth="200.0" GridPane.columnIndex="1">
<children>
<TextField fx:id="Cam2BottomCrop" alignment="CENTER" promptText="crop percent" text="10" AnchorPane.bottomAnchor="3.0" AnchorPane.leftAnchor="3.0" AnchorPane.rightAnchor="3.0" AnchorPane.topAnchor="3.0" />
</children>
</AnchorPane>
<AnchorPane prefHeight="200.0" prefWidth="200.0" GridPane.columnIndex="2">
<children>
<TextField fx:id="Cam2LeftCrop" alignment="CENTER" promptText="crop percent" text="10" AnchorPane.bottomAnchor="3.0" AnchorPane.leftAnchor="3.0" AnchorPane.rightAnchor="3.0" AnchorPane.topAnchor="3.0" />
</children>
</AnchorPane>
<AnchorPane prefHeight="200.0" prefWidth="200.0" GridPane.columnIndex="3">
<children>
<TextField fx:id="Cam2RightCrop" alignment="CENTER" promptText="crop percent" text="10" AnchorPane.bottomAnchor="3.0" AnchorPane.leftAnchor="3.0" AnchorPane.rightAnchor="3.0" AnchorPane.topAnchor="3.0" />
</children>
</AnchorPane>
</children>
</GridPane>
</children></AnchorPane>
<AnchorPane prefHeight="200.0" prefWidth="200.0" GridPane.rowIndex="3">
<children>
<GridPane AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<columnConstraints>
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
</columnConstraints>
<rowConstraints>
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
</rowConstraints>
<children>
<AnchorPane prefHeight="200.0" prefWidth="200.0">
<children>
<TextField fx:id="Cam3TopCrop" alignment="CENTER" promptText="crop percent" text="10" AnchorPane.bottomAnchor="3.0" AnchorPane.leftAnchor="3.0" AnchorPane.rightAnchor="3.0" AnchorPane.topAnchor="3.0" />
</children>
</AnchorPane>
<AnchorPane prefHeight="200.0" prefWidth="200.0" GridPane.columnIndex="1">
<children>
<TextField fx:id="Cam3BottomCrop" alignment="CENTER" promptText="crop percent" text="10" AnchorPane.bottomAnchor="3.0" AnchorPane.leftAnchor="3.0" AnchorPane.rightAnchor="3.0" AnchorPane.topAnchor="3.0" />
</children>
</AnchorPane>
<AnchorPane prefHeight="200.0" prefWidth="200.0" GridPane.columnIndex="2">
<children>
<TextField fx:id="Cam3LeftCrop" alignment="CENTER" promptText="crop percent" text="10" AnchorPane.bottomAnchor="3.0" AnchorPane.leftAnchor="3.0" AnchorPane.rightAnchor="3.0" AnchorPane.topAnchor="3.0" />
</children>
</AnchorPane>
<AnchorPane prefHeight="200.0" prefWidth="200.0" GridPane.columnIndex="3">
<children>
<TextField fx:id="Cam3RightCrop" alignment="CENTER" promptText="crop percent" text="10" AnchorPane.bottomAnchor="3.0" AnchorPane.leftAnchor="3.0" AnchorPane.rightAnchor="3.0" AnchorPane.topAnchor="3.0" />
</children>
</AnchorPane>
</children>
</GridPane>
</children></AnchorPane>
<AnchorPane prefHeight="200.0" prefWidth="200.0" GridPane.rowIndex="4">
<children>
<GridPane AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<columnConstraints>
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
</columnConstraints>
<rowConstraints>
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
</rowConstraints>
<children>
<AnchorPane prefHeight="200.0" prefWidth="200.0">
<children>
<TextField fx:id="Cam4TopCrop" alignment="CENTER" promptText="crop percent" text="10" AnchorPane.bottomAnchor="3.0" AnchorPane.leftAnchor="3.0" AnchorPane.rightAnchor="3.0" AnchorPane.topAnchor="3.0" />
</children>
</AnchorPane>
<AnchorPane prefHeight="200.0" prefWidth="200.0" GridPane.columnIndex="1">
<children>
<TextField fx:id="Cam4BottomCrop" alignment="CENTER" promptText="crop percent" text="10" AnchorPane.bottomAnchor="3.0" AnchorPane.leftAnchor="3.0" AnchorPane.rightAnchor="3.0" AnchorPane.topAnchor="3.0" />
</children>
</AnchorPane>
<AnchorPane prefHeight="200.0" prefWidth="200.0" GridPane.columnIndex="2">
<children>
<TextField fx:id="Cam4LeftCrop" alignment="CENTER" promptText="crop percent" text="10" AnchorPane.bottomAnchor="3.0" AnchorPane.leftAnchor="3.0" AnchorPane.rightAnchor="3.0" AnchorPane.topAnchor="3.0" />
</children>
</AnchorPane>
<AnchorPane prefHeight="200.0" prefWidth="200.0" GridPane.columnIndex="3">
<children>
<TextField fx:id="Cam4RightCrop" alignment="CENTER" promptText="crop percent" text="10" AnchorPane.bottomAnchor="3.0" AnchorPane.leftAnchor="3.0" AnchorPane.rightAnchor="3.0" AnchorPane.topAnchor="3.0" />
</children>
</AnchorPane>
</children>
</GridPane>
</children></AnchorPane>
<AnchorPane prefHeight="200.0" prefWidth="200.0" GridPane.rowIndex="5">
<children>
<GridPane AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<columnConstraints>
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
</columnConstraints>
<rowConstraints>
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
</rowConstraints>
<children>
<AnchorPane prefHeight="200.0" prefWidth="200.0">
<children>
<TextField fx:id="Cam5TopCrop" alignment="CENTER" promptText="crop percent" text="10" AnchorPane.bottomAnchor="3.0" AnchorPane.leftAnchor="3.0" AnchorPane.rightAnchor="3.0" AnchorPane.topAnchor="3.0" />
</children>
</AnchorPane>
<AnchorPane prefHeight="200.0" prefWidth="200.0" GridPane.columnIndex="1">
<children>
<TextField fx:id="Cam5BottomCrop" alignment="CENTER" promptText="crop percent" text="10" AnchorPane.bottomAnchor="3.0" AnchorPane.leftAnchor="3.0" AnchorPane.rightAnchor="3.0" AnchorPane.topAnchor="3.0" />
</children>
</AnchorPane>
<AnchorPane prefHeight="200.0" prefWidth="200.0" GridPane.columnIndex="2">
<children>
<TextField fx:id="Cam5LeftCrop" alignment="CENTER" promptText="crop percent" text="10" AnchorPane.bottomAnchor="3.0" AnchorPane.leftAnchor="3.0" AnchorPane.rightAnchor="3.0" AnchorPane.topAnchor="3.0" />
</children>
</AnchorPane>
<AnchorPane prefHeight="200.0" prefWidth="200.0" GridPane.columnIndex="3">
<children>
<TextField fx:id="Cam5RightCrop" alignment="CENTER" promptText="crop percent" text="10" AnchorPane.bottomAnchor="3.0" AnchorPane.leftAnchor="3.0" AnchorPane.rightAnchor="3.0" AnchorPane.topAnchor="3.0" />
</children>
</AnchorPane>
</children>
</GridPane>
</children></AnchorPane>
<AnchorPane GridPane.rowIndex="6">
<children>
<Button fx:id="btnApplyCrop" mnemonicParsing="false" onAction="#ApplyCropClick" prefHeight="40.0" prefWidth="399.2" text="Apply Crop" AnchorPane.bottomAnchor="5.0" AnchorPane.leftAnchor="5.0" AnchorPane.rightAnchor="5.0" AnchorPane.topAnchor="5.0" />
</children>
</AnchorPane>
</children>
</GridPane>
</children>
</AnchorPane>
</children>
</GridPane> </GridPane>
</children> </children>
</AnchorPane> </AnchorPane>