first commit

This commit is contained in:
2024-11-09 08:55:17 +07:00
commit f6ee4817e6
98 changed files with 85493 additions and 0 deletions

View File

@@ -0,0 +1,760 @@
package id.co.gtc.erhacam;
import Camera.ArducamIMX477Preset;
import Camera.CameraProperty;
import Camera.LiveCamEvent;
import Config.CameraConfigEnum;
import com.google.zxing.BinaryBitmap;
import com.google.zxing.NotFoundException;
import com.google.zxing.Result;
import com.google.zxing.client.j2se.BufferedImageLuminanceSource;
import com.google.zxing.common.HybridBinarizer;
import javafx.application.Platform;
import javafx.concurrent.Task;
import javafx.fxml.FXML;
import javafx.scene.control.CheckBox;
import javafx.scene.control.Slider;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.control.Label;
import javafx.scene.image.PixelFormat;
import javafx.scene.image.WritableImage;
import javafx.scene.layout.AnchorPane;
import lombok.Getter;
import lombok.Setter;
import lombok.val;
import org.bytedeco.javacv.Frame;
import org.bytedeco.javacv.OpenCVFrameGrabber;
import org.bytedeco.opencv.global.opencv_imgproc;
import org.bytedeco.opencv.opencv_core.*;
import org.opencv.videoio.Videoio;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferByte;
import java.nio.ByteBuffer;
import java.nio.file.Path;
import java.time.LocalDateTime;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import static Config.SomeCodes.*;
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_imgcodecs.imwrite;
import static org.bytedeco.opencv.global.opencv_imgproc.*;
@SuppressWarnings({"unused"})
public class Cameradetail {
private final AtomicBoolean Capturing = new AtomicBoolean(false);
private final AtomicBoolean TakingPhoto = new AtomicBoolean(false);
private final AtomicBoolean IsGrabbingLiveView = new AtomicBoolean(false);
private OpenCVFrameGrabber mGrabber = null;
private LiveCamEvent event = null;
private @Getter @Setter CameraConfigEnum cameraConfigEnum = CameraConfigEnum.CameraConfigCenter;
private @Getter int LiveFPS = 0;
/**
* Get detected QR text from Live View
*/
private @Getter String qrtext = null;
@FXML
private Label cameratitle;
@FXML
private ImageView camerastream;
@FXML
private AnchorPane streamanchor;
@FXML
private Label camerastatus;
@FXML
private Slider brightnessSlider;
@FXML
private Slider contrastSlider;
@FXML
private Slider saturationSlider;
@FXML
private Slider hueSlider;
@FXML
private Slider gainSlider;
@FXML
private Slider exposureSlider;
@FXML
private CheckBox AutoExposure;
@FXML
private CheckBox AutoWhiteBalance;
@FXML
private CheckBox AutoFocus;
private final UMat BestMat = new UMat();
private final UMat LiveMat = new UMat();
private Size LiveSize = new Size(640, 480);
private Size PhotoSize = new Size(1920, 1080);
private void setSliderValue(Slider sld, CameraProperty prop, double value){
sld.setMin(prop.Min);
sld.setMax(prop.Max);
sld.setValue(value);
}
@FXML
public void initialize(){
camerastream.fitHeightProperty().bind(streamanchor.heightProperty());
//camerastream.fitWidthProperty().bind(streamanchor.widthProperty());
camerastream.setPreserveRatio(true);
Platform.runLater(()->{
setSliderValue(brightnessSlider, ArducamIMX477Preset.Brightness, config.getBrightness(cameraConfigEnum));
setSliderValue(contrastSlider, ArducamIMX477Preset.Contrast, config.getContrast(cameraConfigEnum));
setSliderValue(saturationSlider, ArducamIMX477Preset.Saturation, config.getSaturation(cameraConfigEnum));
setSliderValue(hueSlider, ArducamIMX477Preset.Hue, config.getHue(cameraConfigEnum));
setSliderValue(gainSlider, ArducamIMX477Preset.Gain, config.getGain(cameraConfigEnum));
setSliderValue(exposureSlider, ArducamIMX477Preset.ExposureTime, config.getExposure(cameraConfigEnum));
AutoExposure.setSelected(config.getAutoExposure(cameraConfigEnum));
AutoWhiteBalance.setSelected(config.getAutoWhiteBalance(cameraConfigEnum));
AutoFocus.setSelected(config.getAutoFocus(cameraConfigEnum));
});
AutoExposure.selectedProperty().addListener((obs, oldVal, newVal) -> {
setAutoExposure(newVal);
config.setAutoExposure(cameraConfigEnum, newVal);
raise_log("AutoExposure for "+getCameraTitle()+" changed to " + newVal);
});
AutoWhiteBalance.selectedProperty().addListener((obs, oldVal, newVal) -> {
setAutoWB(newVal);
config.setAutoWhiteBalance(cameraConfigEnum, newVal);
raise_log("AutoWhiteBalance for "+getCameraTitle()+" changed to "+newVal);
});
AutoFocus.selectedProperty().addListener((obs, oldVal, newVal) -> {
setAutoFocus(newVal);
config.setAutoFocus(cameraConfigEnum, newVal);
raise_log("AutoFocus for "+getCameraTitle()+" changed to "+newVal);
});
brightnessSlider.valueProperty().addListener((obs, oldVal, newVal) -> {
setBrightness(newVal.doubleValue());
config.setBrightness(cameraConfigEnum, newVal.doubleValue());
raise_log("Brightness for "+getCameraTitle()+" changed to "+newVal);
});
contrastSlider.valueProperty().addListener((obs, oldVal, newVal) -> {
setContrast(newVal.doubleValue());
config.setContrast(cameraConfigEnum, newVal.doubleValue());
raise_log("Contrast for "+getCameraTitle()+" changed to "+newVal);
});
saturationSlider.valueProperty().addListener((obs, oldVal, newVal) -> {
setSaturation(newVal.doubleValue());
config.setSaturation(cameraConfigEnum, newVal.doubleValue());
raise_log("Saturation for "+getCameraTitle()+" changed to "+newVal);
});
hueSlider.valueProperty().addListener((obs, oldVal, newVal) -> {
setHue(newVal.doubleValue());
config.setHue(cameraConfigEnum, newVal.doubleValue());
raise_log("Hue for "+getCameraTitle()+" changed to "+newVal);
});
gainSlider.valueProperty().addListener((obs, oldVal, newVal) -> {
setGain(newVal.doubleValue());
config.setGain(cameraConfigEnum, newVal.doubleValue());
raise_log("Gain for "+getCameraTitle()+" changed to "+newVal);
});
exposureSlider.valueProperty().addListener((obs, oldVal, newVal) -> {
setExposure(newVal.doubleValue());
config.setExposure(cameraConfigEnum, newVal.doubleValue());
raise_log("Exposure for "+getCameraTitle()+" changed to "+newVal);
});
}
@FXML
public void resetClick(){
brightnessSlider.adjustValue(ArducamIMX477Preset.Brightness.Default);
contrastSlider.adjustValue(ArducamIMX477Preset.Contrast.Default);
saturationSlider.adjustValue(ArducamIMX477Preset.Saturation.Default);
hueSlider.adjustValue(ArducamIMX477Preset.Hue.Default);
gainSlider.adjustValue(ArducamIMX477Preset.Gain.Default);
exposureSlider.adjustValue(ArducamIMX477Preset.ExposureTime.Default);
AutoExposure.setSelected(true);
AutoFocus.setSelected(true);
AutoWhiteBalance.setSelected(true);
}
public boolean isCapturing(){
return Capturing.get();
}
/**
* Set Camera Title
* @param title Title of the Camera
*/
public void setCameraTitle(String title){
if (ValidString(title)){
if (cameratitle!=null){
cameratitle.setText(title);
}
}
}
public void setSaturation(double value){
if (mGrabber!=null){
mGrabber.setOption(Videoio.CAP_PROP_SATURATION, value);
}
}
public double getSaturation(){
if (mGrabber!=null){
return mGrabber.getOption(Videoio.CAP_PROP_SATURATION);
}
return 0;
}
public void setHue(double value){
if (mGrabber!=null){
mGrabber.setOption(Videoio.CAP_PROP_HUE, value);
}
}
public double getHue(){
if (mGrabber!=null){
return mGrabber.getOption(Videoio.CAP_PROP_HUE);
}
return 0;
}
public void setGain(double value){
if (mGrabber!=null){
mGrabber.setOption(Videoio.CAP_PROP_GAIN, value);
}
}
public double getGain(){
if (mGrabber!=null){
return mGrabber.getOption(Videoio.CAP_PROP_GAIN);
}
return 0;
}
/**
* Get Camera Title
* @return Title of the Camera, or empty string if not set
*/
public String getCameraTitle(){
if (cameratitle!=null){
return cameratitle.getText();
}
return "";
}
/**
* Set Camera Status
* @param status Status of the Camera
*/
public void setCameraStatus(String status){
if (ValidString(status)){
if (camerastatus!=null){
camerastatus.setText(status);
}
}
}
/**
* Get Camera Status
* @return Status of the Camera, or empty string if not set
*/
public String getCameraStatus(){
if (camerastatus!=null){
return camerastatus.getText();
}
return "";
}
/**
* Set Camera Stream
* @param image Image to be displayed
*/
public void setCameraStream(Image image){
if (image!=null){
if (camerastream!=null){
camerastream.setImage(image);
}
}
}
/**
* Get Camera Stream
* @return Image of the Camera Stream, or null if not set
*/
public Image getCameraStream(){
if (camerastream!=null){
return camerastream.getImage();
}
return null;
}
public void setFPS(double value){
if (mGrabber!=null){
mGrabber.setOption(Videoio.CAP_PROP_FPS, value);
}
}
public double getFPS(){
if (mGrabber!=null){
return mGrabber.getOption(Videoio.CAP_PROP_FPS);
}
return 0;
}
/**
* Set Camera Grabber and Target Width and Height
* @param grabber Camera Grabber
* @param livewidth Width used on live view
* @param liveheight Height used on live view
* @param photowidth Width used on photo capture
* @param photoheight Height used on photo capture
*/
public void SetGrabber(OpenCVFrameGrabber grabber, int livewidth, int liveheight, int photowidth, int photoheight){
if (mGrabber!=null) {
StopLiveView();
}
LiveSize = new Size(livewidth, liveheight);
PhotoSize = new Size(photowidth, photoheight);
mGrabber = grabber;
}
//Exposure and Focus Tricks :
// https://stackoverflow.com/questions/53545945/how-to-set-camera-to-auto-exposure-with-opencv-3-4-2
// https://github.com/opencv/opencv/issues/9738
/**
* Set Auto Exposure Mode
* @param ON if true, set autoexposure on, otherwise off
*/
public void setAutoExposure(boolean ON){
if (mGrabber!=null){
mGrabber.setOption(Videoio.CAP_PROP_AUTO_EXPOSURE, ON?ArducamIMX477Preset.AutoExposure.On:ArducamIMX477Preset.AutoExposure.Off);
}
}
/**
* Get Auto Exposure Mode
* @return true if autoexposure is on, otherwise off
*/
public boolean getAutoExposure(){
if (mGrabber!=null){
return mGrabber.getOption(Videoio.CAP_PROP_AUTO_EXPOSURE)==ArducamIMX477Preset.AutoExposure.On;
}
return false;
}
/**
* Set Exposure when Auto Exposure is Off
* @param value exposure value
*/
public void setExposure(double value){
if (mGrabber!=null){
mGrabber.setOption(Videoio.CAP_PROP_EXPOSURE, value);
}
}
/**
* Get Exposure when Auto Exposure is Off
* @return exposure value
*/
public double getExposure(){
if (mGrabber!=null){
return mGrabber.getOption(Videoio.CAP_PROP_EXPOSURE);
}
return 0;
}
/**
* Set Auto Focus
* @param ON if true, set autofocus on, otherwise off
*/
public void setAutoFocus(boolean ON){
if (mGrabber!=null){
mGrabber.setOption(Videoio.CAP_PROP_AUTOFOCUS, ON?ArducamIMX477Preset.AutoFocus.On:ArducamIMX477Preset.AutoFocus.Off);
}
}
/**
* Get Auto Focus
* @return true if autofocus is on, otherwise off
*/
public boolean getAutoFocus(){
if (mGrabber!=null){
return mGrabber.getOption(Videoio.CAP_PROP_AUTOFOCUS)==ArducamIMX477Preset.AutoFocus.On;
}
return false;
}
public void setAutoWB(boolean ON){
if (mGrabber!=null){
mGrabber.setOption(Videoio.CAP_PROP_AUTO_WB, ON?ArducamIMX477Preset.AutoWhiteBalance.On:ArducamIMX477Preset.AutoWhiteBalance.Off);
}
}
public boolean getAutoWB(){
if (mGrabber!=null){
return mGrabber.getOption(Videoio.CAP_PROP_AUTO_WB)==ArducamIMX477Preset.AutoWhiteBalance.On;
}
return false;
}
/**
* Set Focus when Auto Focus is Off
* @param value focus value
*/
public void setFocus(double value){
if (mGrabber!=null){
mGrabber.setOption(Videoio.CAP_PROP_FOCUS, value);
}
}
/**
* Get Focus when Auto Focus is Off
* @return focus value
*/
public double getFocus(){
if (mGrabber!=null){
return mGrabber.getOption(Videoio.CAP_PROP_FOCUS);
}
return 0;
}
public void setBrightness(double value){
if (mGrabber!=null){
mGrabber.setOption(Videoio.CAP_PROP_BRIGHTNESS, value);
}
}
public double getBrightness(){
if (mGrabber!=null){
return mGrabber.getOption(Videoio.CAP_PROP_BRIGHTNESS);
}
return 0;
}
public void setContrast(double value){
if (mGrabber!=null){
mGrabber.setOption(Videoio.CAP_PROP_CONTRAST, value);
}
}
public double getContrast(){
if (mGrabber!=null){
return mGrabber.getOption(Videoio.CAP_PROP_CONTRAST);
}
return 0;
}
public void setFrameWidth(int width){
if (mGrabber!=null){
mGrabber.setOption(Videoio.CAP_PROP_FRAME_WIDTH, width);
}
}
public double getFrameWidth(){
if (mGrabber!=null){
return mGrabber.getOption(Videoio.CAP_PROP_FRAME_WIDTH);
}
return 0;
}
public void setFrameHeight(int height){
if (mGrabber!=null){
mGrabber.setOption(Videoio.CAP_PROP_FRAME_HEIGHT, height);
}
}
public double getFrameHeight(){
if (mGrabber!=null){
return mGrabber.getOption(Videoio.CAP_PROP_FRAME_HEIGHT);
}
return 0;
}
public void setSharpness(double value){
if (mGrabber!=null){
mGrabber.setOption(Videoio.CAP_PROP_SHARPNESS, value);
}
}
public double getSharpness(){
if (mGrabber!=null){
return mGrabber.getOption(Videoio.CAP_PROP_SHARPNESS);
}
return 0;
}
public void setGamma(double value){
if (mGrabber!=null){
mGrabber.setOption(Videoio.CAP_PROP_GAMMA, value);
}
}
public double getGamma(){
if (mGrabber!=null){
return mGrabber.getOption(Videoio.CAP_PROP_GAMMA);
}
return 0;
}
/**
* Take Photo from Camera
* @param directory directory to save the photo, if null, will use default directory
* @param prefix filename prefix
* @return filename path of the saved photo, or null if failed
*/
@SuppressWarnings("BusyWait")
public String TakePhoto(String directory, String prefix) throws InterruptedException {
String result = null;
if (!ValidDirectory(directory)) directory = currentDirectory;
if (mGrabber!=null){
while(IsGrabbingLiveView.get()){
Thread.sleep(10);
}
TakingPhoto.set(true);
if (!BestMat.empty()){
Size sz = BestMat.size();
raise_log("TakePhoto got frame with width: " + sz.width() + " and height: " + sz.height());
String filename = Path.of(directory, makeFileName(prefix)).toString();
if (imwrite(filename, BestMat)){
raise_log("TakePhoto success, Photo saved to " + filename);
result = filename;
} else raise_log("TakePhoto failed, Unable to Save Photo");
} else raise_log("TakePhoto failed, Live View is Empty");
} else raise_log("TakePhoto failed, Grabber is null");
TakingPhoto.set(false);
return result;
}
private String makeFileName(String prefix){
//make filename with prefix_POSITION_YYYY-MM-DD_HH-MM-SS
LocalDateTime ldt = LocalDateTime.now();
String timetag = ldt.getYear() + "-" + ldt.getMonthValue() + "-" + ldt.getDayOfMonth() + "_" + ldt.getHour() + "-" + ldt.getMinute() + "-" + ldt.getSecond();
return prefix+"_"
+ switch(cameratitle.getText()){
case "Camera Left 90" -> "LEFT90";
case "Camera Left 45" -> "LEFT45";
case "Camera Center" -> "CENTER";
case "Camera Right 45" -> "RIGHT45";
case "Camera Right 90" -> "RIGHT90";
default -> "UNKNOWN";
}
+ "_" + timetag + ".jpg";
}
public void StopLiveView(){
Capturing.set(false);
if (mGrabber!=null){
try{
mGrabber.stop();
Platform.runLater(()->setCameraStatus("Camera Stopped"));
} catch (Exception e){
raise_log("StopLiveView failed, Unable to Stop Camera, Error: " + e.getMessage());
}
}
TakingPhoto.set(false);
IsGrabbingLiveView.set(false);
}
public boolean StartLiveView(LiveCamEvent event, String cameratitle, final boolean use_qr , final boolean use_face) {
this.event = event;
if (mGrabber != null) {
try {
if (use_qr) raise_log("QR Reader loaded");
if (use_face) raise_log("Face detector loaded");
// capture with best resolution
setFrameHeight(PhotoSize.height());
setFrameWidth(PhotoSize.width());
LiveFPS = 0;
mGrabber.start();
Capturing.set(true);
// just information
String ss = String.format("Camera Started with resolution %dx%d@%d", PhotoSize.width(), PhotoSize.height(),LiveFPS);
Platform.runLater(()->setCameraStatus(ss));
raise_log(ss);
AutoExposure.setSelected(true);
AutoFocus.setSelected(true);
AutoWhiteBalance.setSelected(true);
Task<Image> task = new Task<>() {
@SuppressWarnings("BusyWait")
@Override
protected Image call() {
// repeat until capturing is false
AtomicInteger fps = new AtomicInteger(0);
TimerTask timerTask = new TimerTask() {
@Override
public void run() {
LiveFPS = fps.getAndSet(0);
}
};
Timer timer = new java.util.Timer();
timer.scheduleAtFixedRate(timerTask, 1000, 1000);
while (Capturing.get()) {
try {
// selama proses pengambilan foto, jangan ambil frame
while(TakingPhoto.get() && Capturing.get()){
Thread.sleep(10);
}
if (!Capturing.get()) return null;
IsGrabbingLiveView.set(true);
Frame frame = mGrabber.grab(); // grab frame
Mat mat = matconverter.convert(frame); // convert to Mat
mat.copyTo(BestMat); // copy to BestMat for using OpenCL
fps.addAndGet(1);
IsGrabbingLiveView.set(false);
if (frame != null) {
opencv_imgproc.resize(BestMat, LiveMat, LiveSize); // resize to LiveSize
UMat graymat = new UMat(); // use OpenCL for grayscale
opencv_imgproc.cvtColor(LiveMat,graymat, COLOR_BGR2GRAY); // convert to grayscale
if (use_qr){
String qr = DetectQRFromMat(graymat);
if (qr!=null) {
if (!qr.equals(qrtext)){
qrtext = qr;
raise_log("QR Detected: " + qr);
if (event!=null) event.onDetectedQRCode(qr);
}
}
}
if (use_face){
RectVector face = DetectFace(graymat);
if (face!=null && face.size()>0){
if (event!=null) event.onFaceDetector(true, PhotoSize.width(), PhotoSize.height());
for(int i=0; i<face.size(); i++){
Rect rect = face.get(i);
rectangle(LiveMat, rect, Scalar.GREEN);
}
} else if (event!=null) event.onFaceDetector(false, PhotoSize.width(), PhotoSize.height());
}
UMat rgbmat = new UMat(LiveMat.size(), CV_8UC3);
cvtColor(LiveMat, rgbmat, COLOR_BGR2RGB);
Mat imgmat = new Mat();
rgbmat.copyTo(imgmat); // copy back to CPU
// Update Task Value usign matToWritableImage
updateValue(matToWritableImage(imgmat, imgmat.cols(), imgmat.rows()));
}
} catch (Exception e) {
raise_log("Unable to Grab Frame, Error: " + e.getMessage());
//if (!Capturing.get()) Platform.runLater(this::StopLiveView);
}
}
timer.cancel();
return null;
}
};
// value dari task, yaitu image, akan diupdate ke camerastream
task.valueProperty().addListener((obs, oldVal, newVal) -> {
if (newVal != null) {
setCameraStream(newVal);
}
});
// start task
new Thread(task).start();
return true;
} catch (Exception e) {
raise_log("StartLiveView failed, Unable to Start Camera, Error: " + e.getMessage());
}
} else raise_log("StartLiveView failed, grabber is null");
return false;
}
/**
* Detect QR Code from Mat
* @param graymat Mat in Gray Scale
* @return QR Code Text, or null if not detected
*/
private String DetectQRFromMat(UMat graymat){
if (qrreader!=null){
Mat mat = new Mat();
graymat.copyTo(mat); // back to CPU, because zxing only accept BufferedImage
BufferedImage bufferedImage = matToBufferedImage(mat);
String title = cameratitle.getText();
BinaryBitmap binaryBitmap = new BinaryBitmap(new HybridBinarizer(new BufferedImageLuminanceSource(bufferedImage)));
try{
Result result = qrreader.decode(binaryBitmap);
if (result!=null){
return result.getText();
}
} catch (NotFoundException ignored) {
}
}
return null;
}
/**
* Detect Face from Mat
* @param graymat Mat in Gray Scale
* @return RectVector if face detected, otherwise false
*/
private RectVector DetectFace(UMat graymat){
if (faceDetector!=null){
RectVector face = new RectVector();
faceDetector.detectMultiScale(graymat, face);
return face;
}
return null;
}
private double getBrightnessFromGrayMat(Mat graymat){
Scalar mean = mean(graymat);
return mean.get(0);
}
private WritableImage matToWritableImage(Mat mat, int cols, int rows){
WritableImage writableImage = new WritableImage(cols, rows);
ByteBuffer buffer = mat.createBuffer();
PixelFormat<ByteBuffer> pixelFormat = PixelFormat.getByteRgbInstance();
writableImage.getPixelWriter().setPixels(0, 0, mat.cols(), mat.rows(), pixelFormat, buffer, mat.cols() * 3);
return writableImage;
}
private BufferedImage matToBufferedImage(Mat mat){
int type = BufferedImage.TYPE_BYTE_GRAY;
if (mat.channels() > 1) {
type = BufferedImage.TYPE_3BYTE_BGR;
}
BufferedImage image = new BufferedImage(mat.cols(), mat.rows(), type);
byte[] data = ((DataBufferByte) image.getRaster().getDataBuffer()).getData();
mat.data().get(data);
return image;
}
private void raise_log(String msg){
if (event!=null) event.onLog(msg);
}
}

View File

@@ -0,0 +1,633 @@
package id.co.gtc.erhacam;
import BASS.AudioPlayer;
import BASS.PlaybackStatus;
import Camera.*;
import Config.CameraConfigEnum;
import Config.SomeCodes;
import Database.PhotoReviewClass;
import Database.Sqlite;
import FTP.FTPUpload;
import FTP.FTPUploadEvent;
import javafx.application.Platform;
import javafx.concurrent.Task;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.control.Alert;
import javafx.scene.control.TextArea;
import javafx.scene.layout.AnchorPane;
import javafx.stage.DirectoryChooser;
import lombok.val;
import java.util.*;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import javafx.scene.control.Alert.AlertType;
import org.bytedeco.javacv.OpenCVFrameConverter;
import org.bytedeco.javacv.OpenCVFrameGrabber;
import org.bytedeco.javacv.VideoInputFrameGrabber;
import org.bytedeco.opencv.opencv_core.Size;
import org.tinylog.Logger;
import static Config.SomeCodes.*;
public class CaptureView {
@FXML
private AnchorPane cam1, cam2, cam3, cam4, cam5, controlpane;
private Cameradetail image1, image2, image3, image4, image5;
@FXML
private TextArea directorypath, prefixfile;
@FXML
private AnchorPane progressanchor;
private AudioPlayer audioPlayer;
private String audio_posisikan_muka = "satu.wav";
private String audio_posisi_diam = "dua.wav";
private String audio_foto_selesai = "tiga.wav";
private String audio_ke_ruangtunggu = "empat.wav";
private List<String> cams;
@FXML
private void ChangeDirectory(){
DirectoryChooser dc = new DirectoryChooser();
dc.setTitle("Select Directory");
String path = dc.showDialog(null).getAbsolutePath();
config.SetPhotoDirectory(path);
config.Save();
directorypath.setText(path);
}
private void trigger_autofocus(Cameradetail image) throws InterruptedException {
if (image!=null){
if (image.isCapturing()){
image.setAutoFocus(false);
Thread.sleep(2);
image.setFocus(0.9);
Thread.sleep(2);
image.setAutoFocus(true);
Thread.sleep(2);
}
}
}
@FXML
private void AutoFocus() throws InterruptedException {
trigger_autofocus(image1);
trigger_autofocus(image2);
trigger_autofocus(image3);
trigger_autofocus(image4);
trigger_autofocus(image5);
}
@SuppressWarnings("resource")
@FXML
private void TakePhotos(){
Size thumbsize = new Size(160,120);
String directory = directorypath.getText();
String prefix = RemoveSpaces(prefixfile.getText()) ;
if (ValidDirectory(directory)){
if (ValidString(prefix)){
audioPlayer.PlayFile(audio_posisi_diam, ps);
PhotoReviewClass prc = new PhotoReviewClass();
prc.setPrefix(prefix);
long nanostart = System.nanoTime(); // for performance measurement
ExecutorService executor = Executors.newFixedThreadPool(5);
Callable<String> task1 = ()->{
if (image1!=null) {
String p1 = image1.TakePhoto(directory,prefix);
if (ValidFile(p1)) {
Platform.runLater(()->image1.setCameraStatus("Photo: "+ SomeCodes.GetFileName(p1)));
prc.setFileLeft90(p1);
String thumb1 = MakeThumbfile(p1, thumbsize);
if (ValidFile(thumb1)) prc.setThumbLeft90(thumb1);
}
}
return "Task 1 Done";
};
Callable<String> task2 = ()->{
if (image2!=null) {
String p2 = image2.TakePhoto(directory,prefix);
if (ValidFile(p2)) {
Platform.runLater(()->image2.setCameraStatus("Photo: "+ SomeCodes.GetFileName(p2)));
prc.setFileLeft45(p2);
String thumb2 = MakeThumbfile(p2, thumbsize);
if (ValidFile(thumb2)) prc.setThumbLeft45(thumb2);
}
}
return "Task 2 Done";
};
Callable<String> task3 = ()->{
if (image3!=null) {
String p3 = image3.TakePhoto(directory,prefix);
if (ValidFile(p3)) {
Platform.runLater(()->image3.setCameraStatus("Photo: "+ SomeCodes.GetFileName(p3)));
prc.setFileCenter(p3);
String thumb3 = MakeThumbfile(p3, thumbsize);
if (ValidFile(thumb3)) prc.setThumbCenter(thumb3);
}
}
return "Task 3 Done";
};
Callable<String> task4 = ()->{
if (image4!=null) {
String p4 = image4.TakePhoto(directory,prefix);
if (ValidFile(p4)) {
Platform.runLater(()->image4.setCameraStatus("Photo: "+ SomeCodes.GetFileName(p4)));
prc.setFileRight45(p4);
String thumb4 = MakeThumbfile(p4, thumbsize);
if (ValidFile(thumb4)) prc.setThumbRight45(thumb4);
}
}
return "Task 4 Done";
};
Callable<String> task5 = ()->{
if (image5!=null) {
String p5 = image5.TakePhoto(directory,prefix);
if (ValidFile(p5)) {
Platform.runLater(()->image5.setCameraStatus("Photo: "+ SomeCodes.GetFileName(p5)));
prc.setFileRight90(p5);
String thumb5 = MakeThumbfile(p5, thumbsize);
if (ValidFile(thumb5)) prc.setThumbRight90(thumb5);
}
}
return "Task 5 Done";
};
try{
Future<String> f1 = executor.submit(task1);
Future<String> f2 = executor.submit(task2);
Future<String> f3 = executor.submit(task3);
Future<String> f4 = executor.submit(task4);
Future<String> f5 = executor.submit(task5);
f1.get();
f2.get();
f3.get();
f4.get();
f5.get();
} catch (Exception e){
Logger.error("Error TakePhotos: " + e.getMessage());
} finally {
executor.shutdown();
}
long duration = (System.nanoTime() - nanostart) / 1000000; // in milliseconds
System.out.println("TakePhotos duration: "+duration+" ms");
audioPlayer.PlayFile(audio_foto_selesai, ps);
String[] files = prc.files();
if (files!=null && files.length>0){
InsertSQL(prc);
progressanchor.getChildren().clear();
prefixfile.setText("");
new Thread(()-> UploadToFTP(files)).start();
} else {
Alert Alert = new Alert(AlertType.ERROR);
Alert.setTitle("Error");
Alert.setHeaderText("No Photos Taken");
Alert.setContentText("No Photos Taken, please check camera");
Alert.showAndWait();
}
} else {
Alert Alert = new Alert(AlertType.ERROR);
Alert.setTitle("Error");
Alert.setHeaderText("Invalid Prefix");
Alert.setContentText("Please input valid prefix or scan QR Code");
Alert.showAndWait();
}
} else {
Alert Alert = new Alert(AlertType.ERROR);
Alert.setTitle("Error");
Alert.setHeaderText("Invalid Directory");
Alert.setContentText("Please select valid directory");
Alert.showAndWait();
}
}
@FXML
public void initialize(){
audio_posisikan_muka = ExtractResource("/satu.wav");
audio_posisi_diam = ExtractResource("/dua.wav");
audio_foto_selesai = ExtractResource("/tiga.wav");
audio_ke_ruangtunggu = ExtractResource("/empat.wav");
audioPlayer = new AudioPlayer(1,48000);
Logger.info("Audio Player : "+(audioPlayer.isInited()? "Inited" : "Not Inited"));
cams = null;
try{
String[] xxx = VideoInputFrameGrabber.getDeviceDescriptions();
if (xxx!=null && xxx.length>0){
cams = Arrays.asList(xxx);
}
} catch (Exception e){
Logger.error("Error getting camera list: "+e.getMessage());
}
LoadCameraDetail(cam1, 1, CameraConfigEnum.CameraConfigLeft90);
LoadCameraDetail(cam2, 2, CameraConfigEnum.CameraConfigLeft45);
LoadCameraDetail(cam3, 3, CameraConfigEnum.CameraConfigCenter);
LoadCameraDetail(cam4, 4, CameraConfigEnum.CameraConfigRight45);
LoadCameraDetail(cam5, 5, CameraConfigEnum.CameraConfigRight90);
Platform.runLater(()->{
int indexleft90=-1;
int indexleft45=-1;
int indexcenter=-1;
int indexright45=-1;
int indexright90;
if (cams!=null && !cams.isEmpty()){
String camleft90 = config.getCameraLeft90();
if (ValidString(camleft90)){
int[] indexes = FindIndexes(cams, camleft90);
if (indexes.length>0){
indexleft90 = indexes[0];
if (indexleft90!=1){
final int finalindex = indexleft90;
new Thread(()-> SetupCameraWithController(image1, camleft90, finalindex)).start();
}
}
}
String camleft45 = config.getCameraLeft45();
if (ValidString(camleft45)){
int[] indexes = FindIndexes(cams, camleft45);
if (indexes.length>0){
indexleft45 = FindFirstIndex(cams, camleft45, indexleft90);
if (indexleft45!=-1) {
final int finalindex = indexleft45;
new Thread(()-> SetupCameraWithController(image2, camleft45, finalindex)).start();
}
}
}
String camcenter = config.getCameraCenter();
if (ValidString(camcenter)){
int[] indexes = FindIndexes(cams, camcenter);
if (indexes.length>0){
indexcenter = FindFirstIndex(cams, camcenter, indexleft90, indexleft45);
if (indexcenter!=-1) {
final int finalindex = indexcenter;
new Thread(()-> SetupCameraWithController(image3, camcenter, finalindex)).start();
}
}
}
String camright45 = config.getCameraRight45();
if (ValidString(camright45)){
int[] indexes = FindIndexes(cams, camright45);
if (indexes.length>0){
indexright45 = FindFirstIndex(cams, camright45, indexleft90, indexleft45, indexcenter);
if (indexright45!=-1) {
final int finalindex = indexright45;
new Thread(()-> SetupCameraWithController(image4, camright45, finalindex)).start();
}
}
}
String camright90 = config.getCameraRight90();
if (ValidString(camright90)){
int[] indexes = FindIndexes(cams, camright90);
if (indexes.length>0){
indexright90 = FindFirstIndex(cams, camright90, indexleft90, indexleft45, indexcenter, indexright45);
if (indexright90!=-1) {
final int finalindex = indexright90;
new Thread(()-> SetupCameraWithController(image5, camright90, finalindex)).start();
}
}
}
}
directorypath.setText(config.getPhotoDirectory());
progressanchor.prefWidthProperty().bind(controlpane.widthProperty());
});
}
public void Unload(){
if (image1!=null) {
image1.StopLiveView();
}
if (image2!=null) {
image2.StopLiveView();
}
if (image3!=null) {
image3.StopLiveView();
}
if (image4!=null) {
image4.StopLiveView();
}
if (image5!=null) {
image5.StopLiveView();
}
config.Save();
}
final PlaybackStatus ps = new PlaybackStatus(){
@Override
public void onPlaybackStarted(String filename) {
if (filename.contains(audio_posisikan_muka)){
Logger.info("Audio Positikan Muka Started");
} else if (filename.contains(audio_posisi_diam)){
Logger.info("Audio Posisi Diam Started");
} else if (filename.contains(audio_foto_selesai)){
Logger.info("Audio Foto Selesai Started");
} else if (filename.contains(audio_ke_ruangtunggu)){
Logger.info("Audio Ke Ruang Tunggu Started");
}
}
@Override
public void onPlaybackFinished(String filename) {
if (filename.contains(audio_posisikan_muka)){
Logger.info("Audio Positikan Muka Finished");
} else if (filename.contains(audio_posisi_diam)){
Logger.info("Audio Posisi Diam Finished");
} else if (filename.contains(audio_foto_selesai)){
Logger.info("Audio Foto Selesai Finished");
} else if (filename.contains(audio_ke_ruangtunggu)){
Logger.info("Audio Ke Ruang Tunggu Finished");
}
}
@Override
public void onPlaybackFailure(String filename) {
if (filename.contains(audio_posisikan_muka)){
Logger.info("Audio Positikan Muka Failure");
} else if (filename.contains(audio_posisi_diam)){
Logger.info("Audio Posisi Diam Failure");
} else if (filename.contains(audio_foto_selesai)){
Logger.info("Audio Foto Selesai Failure");
} else if (filename.contains(audio_ke_ruangtunggu)){
Logger.info("Audio Ke Ruang Tunggu Failure");
}
}
};
private void SetupCameraWithController(Cameradetail image, String cameraname, int devicenumber){
if (image!=null){
String title = switch(image.getCameraConfigEnum()){
case CameraConfigCenter -> "Camera Center";
case CameraConfigLeft45 -> "Camera Left 45";
case CameraConfigLeft90 -> "Camera Left 90";
case CameraConfigRight45 -> "Camera Right 45";
case CameraConfigRight90 -> "Camera Right 90";
};
Platform.runLater(()-> image.setCameraTitle(title));
if (devicenumber!=-1){
OpenCVFrameGrabber grabber = new OpenCVFrameGrabber(devicenumber);
// default
int livewidth = 640;
int liveheight = 480;
int photowidth = 640;
int photoheight = 480;
// mode1 selalu paling tinggi
if (cameraname.contains("ACER QHD")){
photowidth = AcerQHD.ModeBest.getWidth();
photoheight = AcerQHD.ModeBest.getHeight();
livewidth = AcerQHD.ModeLive.getWidth();
liveheight = AcerQHD.ModeLive.getHeight();
} else if (cameraname.contains("AVerVision M15W")){
photowidth = AverVisionM15W.ModeBest.getWidth();
photoheight = AverVisionM15W.ModeBest.getHeight();
livewidth = AverVisionM15W.ModeLive.getWidth();
liveheight = AverVisionM15W.ModeLive.getHeight();
} else if (cameraname.contains("Arducam IMX477")){
// mode1 terbaik 16:9
// mode5 terbaik 4:3
photowidth = ArducamIMX477.ModeBest.getWidth();
photoheight = ArducamIMX477.ModeBest.getHeight();
livewidth = ArducamIMX477.ModeLive.getWidth();
liveheight = ArducamIMX477.ModeLive.getHeight();
} else if (cameraname.contains("OBSBOT Meet 2")){
photowidth = ObsbotMeet2.ModeBest.getWidth();
photoheight = ObsbotMeet2.ModeBest.getHeight();
livewidth = ObsbotMeet2.ModeLive.getWidth();
liveheight = ObsbotMeet2.ModeLive.getHeight();
}
image.SetGrabber(grabber, livewidth,liveheight,photowidth,photoheight);
//TODO reconfirm requirement again
boolean use_face_detector = true;
boolean use_qr_detector = true;
LiveCamEvent lce = new LiveCamEvent() {
@Override
public void onDetectedQRCode(String qrCode) {
Platform.runLater(()->prefixfile.setText(RemoveSpaces(qrCode)));
if (ValidString(qrCode)) audioPlayer.PlayFile(audio_posisikan_muka, ps);
}
@Override
public void onFaceDetector(boolean hasface, int width, int height) {
Platform.runLater(()-> {
String ss = hasface ? String.format("Camera Started, %dx%d@%d, Face Detected", width, height, image.getLiveFPS()) : String.format("Camera Started, %dx%d@%d", width, height, image.getLiveFPS());
image.setCameraStatus(ss);
//String qr = prefixfile.getText();
//if (ValidString(qr) && hasface) audioPlayer.PlayFile(audio_posisikan_muka, ps);
});
}
@Override
public void onLog(String log) {
String ss = String.format("[%s] : %s", title, log);
Logger.info(ss);
}
};
Platform.runLater(()-> image.setCameraStatus("Camera Starting"));
if (image.StartLiveView(lce, title, use_qr_detector, use_face_detector)){
//TODO Start Live View berhasil, apa lagi yang mau dikerjakan ?
} else Platform.runLater(()->image.setCameraStatus("Unable to Set Grabber"));
} else Platform.runLater(()->image.setCameraStatus("Camera not found, please check setting"));
}
}
private void LoadCameraDetail(AnchorPane cam, int camid, CameraConfigEnum cc){
try{
FXMLLoader loader = new FXMLLoader(getClass().getResource("cameradetail.fxml"));
AnchorPane child = loader.load();
AnchorPane.setTopAnchor(child, 0.0);
AnchorPane.setBottomAnchor(child, 0.0);
AnchorPane.setLeftAnchor(child, 0.0);
AnchorPane.setRightAnchor(child, 0.0);
cam.getChildren().clear();
cam.getChildren().add(child);
switch(camid){
case 1:
image1 = loader.getController();
image1.setCameraConfigEnum(cc);
break;
case 2:
image2 = loader.getController();
image2.setCameraConfigEnum(cc);
break;
case 3:
image3 = loader.getController();
image3.setCameraConfigEnum(cc);
break;
case 4:
image4 = loader.getController();
image4.setCameraConfigEnum(cc);
break;
case 5:
image5 = loader.getController();
image5.setCameraConfigEnum(cc);
break;
}
} catch (Exception e){
Logger.error("Error LoadCameraDetail: " + e.getMessage());
}
}
private void UploadToFTP(String[] files){
final double uploadprogressheight = 50;
Map<String, UploadProgress> progressmap = new HashMap<>();
for (String filetoupload : files){
Task<AnchorPane> loadtask = new Task<>() {
@Override
protected AnchorPane call() throws Exception {
FXMLLoader loader = new FXMLLoader(getClass().getResource("uploadprogress.fxml"));
AnchorPane pane = loader.load();
pane.prefWidthProperty().bind(progressanchor.widthProperty());
pane.setPrefHeight(uploadprogressheight);
UploadProgress up = loader.getController();
up.SetFile(filetoupload);
up.SetStatus("Initialized");
up.SetProgress(0,0);
int ii = progressmap.size();
AnchorPane.setTopAnchor(pane, (ii*uploadprogressheight)+10);
progressmap.put(GetFileName(filetoupload), up);
return pane;
}
};
loadtask.setOnSucceeded(e-> progressanchor.getChildren().add(loadtask.getValue()));
loadtask.setOnFailed(e-> Logger.error("Error LoadTask: {}",e.getSource().getMessage()));
new Thread(loadtask).start();
}
FTPUpload ftp = new FTPUpload(config.getFTPHost(), toInt(config.getFTPPort()), config.getFTPUser(), config.getFTPPass(), config.getFTPPath());
ftp.UploadFile(new FTPUploadEvent() {
@Override
public void onUploadSuccess(String file) {
Logger.info("Upload Success: {}" ,file);
UploadProgress up = progressmap.get(GetFileName(file));
if (up!=null){
Platform.runLater(()->{
up.SetStatus("Success");
up.SetProgress(1,1);
});
}
}
@Override
public void onUploadFailed(String file) {
Logger.info("Upload Failed: {}",file);
UploadProgress up = progressmap.get(GetFileName(file));
if (up!=null){
Platform.runLater(()->{
up.SetStatus("Failed");
up.SetProgress(0,1);
});
}
}
@Override
public void onUploadProgress(String file, long bytes, long total) {
UploadProgress up = progressmap.get(GetFileName(file));
if (up!=null){
Platform.runLater(()->up.SetProgress(bytes, total));
}
}
@Override
public void onUploadStarted(String file) {
Logger.info("Upload Started: {}",file);
UploadProgress up = progressmap.get(GetFileName(file));
if (up!=null){
Platform.runLater(()->{
up.SetStatus("Started");
up.SetProgress(0,0);
});
}
}
@Override
public void uploadLog(String msg) {
Logger.info("Upload Log: {}",msg);
}
@Override
public void onUploadFinished(int total, int success, int failed, String[] files) {
Logger.info("Upload Finished, Total: {}, Success: {}, Failed: {}", total, success, failed);
Platform.runLater(()->{
audioPlayer.PlayFile(audio_ke_ruangtunggu, ps);
Alert Alert = new Alert(AlertType.INFORMATION);
Alert.setTitle("Upload Finished");
Alert.setHeaderText("Upload Finished");
Alert.setContentText("Total: "+total+"\nSuccess: "+success+"\nFailed: "+failed);
Alert.showAndWait();
});
}
}, files);
}
// private void Load_UploadProgress(Map<String, UploadProgress> progressmap, String filename, double uploadprogressheight){
// try{
// FXMLLoader loader = new FXMLLoader(getClass().getResource("uploadprogress.fxml"));
// AnchorPane pane = loader.load();
// pane.prefWidthProperty().bind(progressanchor.widthProperty());
// pane.setPrefHeight(uploadprogressheight);
// UploadProgress up = loader.getController();
// up.SetFile(filename);
// up.SetStatus("Started");
// up.SetProgress(0,0);
// int ii = progressmap.size();
// AnchorPane.setTopAnchor(pane, (ii*uploadprogressheight)+10);
// progressanchor.getChildren().add(pane);
// progressmap.put(filename, up);
//
// } catch (Exception e){
// Logger.error("Error loading uploadprogress.fxml: "+e.getMessage());
// }
// }
private void InsertSQL(PhotoReviewClass prc){
Sqlite sql = new Sqlite();
sql.Insert(prc);
}
}

View File

@@ -0,0 +1,143 @@
package id.co.gtc.erhacam;
import Database.TinyLogRow;
import javafx.application.Platform;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.scene.control.*;
import javafx.scene.control.cell.PropertyValueFactory;
import lombok.val;
import org.tinylog.Logger;
import java.io.IOException;
import java.nio.file.Files;
import java.util.concurrent.atomic.AtomicInteger;
import static Config.SomeCodes.GetLogsPath;
public class LogsView {
@FXML
private ComboBox<String> datePicker;
@FXML
private TextField searchField;
@FXML
private TableView<TinyLogRow> logTable;
@FXML
private Button PopulateButton;
private ObservableList<TinyLogRow> tablerows ;
@FXML
private void initialize(){
tablerows = FXCollections.observableArrayList();
logTable.setItems(tablerows);
initialize_tableview();
datePicker.onActionProperty().set(e->{
String selected = datePicker.getValue();
tablerows.clear();
try {
AtomicInteger index = new AtomicInteger(1);
Files.lines(GetLogsPath().resolve(selected+".log"))
.forEachOrdered(l->{
TinyLogRow row = TinyLogRow.Regex(l);
if (row != null && row.HaveContent()) {
row.setIndex(index.getAndIncrement());
tablerows.add(row);
}
});
} catch (IOException ex) {
Logger.error("datePicker error: {}", ex.getMessage());
}
});
PopulateButton.onActionProperty().set(e->{
datePicker.getItems().clear();
try {
Files.list(GetLogsPath())
.filter(p -> p.toFile().isFile())
.filter(p -> p.getFileName().toString().endsWith(".log"))
.forEachOrdered(p->{
String ss = p.getFileName().toString();
datePicker.getItems().add(ss.substring(0, ss.lastIndexOf('.')));
});
} catch (IOException ex) {
Logger.error("PopulateButton error: {}", ex.getMessage());
}
});
searchField.onKeyTypedProperty().set(e->{
String search = searchField.getText().toLowerCase();
if (search.length() > 0){
ObservableList<TinyLogRow> filtered = FXCollections.observableArrayList();
tablerows.forEach(r->{
if (r.getMessage().toLowerCase().contains(search)){
filtered.add(r);
}
});
logTable.setItems(filtered);
} else {
logTable.setItems(tablerows);
}
});
Platform.runLater(()->{
PopulateButton.fire(); // trigger PopulateButton on show
});
}
private void initialize_tableview(){
logTable.getColumns().clear();
TableColumn<TinyLogRow,Integer> indexCol = new TableColumn<>("No");
indexCol.setCellValueFactory(new PropertyValueFactory<>("index"));
TableColumn<TinyLogRow,String> datetimeCol = new TableColumn<>("DateTime");
datetimeCol.setCellValueFactory(new PropertyValueFactory<>("DateTime"));
TableColumn<TinyLogRow,String> categoryCol = new TableColumn<>("Category");
categoryCol.setCellValueFactory(new PropertyValueFactory<>("Category"));
TableColumn<TinyLogRow,String> messageCol = new TableColumn<>("Message");
messageCol.setCellValueFactory(new PropertyValueFactory<>("Message"));
logTable.getColumns().add(indexCol);
logTable.getColumns().add(datetimeCol);
logTable.getColumns().add(categoryCol);
logTable.getColumns().add(messageCol);
logTable.widthProperty().addListener((obs, oldVal, newVal)->{
double width = (double)newVal;
if (width > (75+150+75+100)){
// cukup besar, pake patokan
indexCol.setPrefWidth(75);
datetimeCol.setPrefWidth(150);
categoryCol.setPrefWidth(75);
messageCol.setPrefWidth(width-300);
} else {
// kecil, pake persen
indexCol.setPrefWidth(width*0.075);
datetimeCol.setPrefWidth(width*0.15);
categoryCol.setPrefWidth(width*0.075);
messageCol.setPrefWidth(width*0.7);
}
});
}
public void Unload(){
}
}

View File

@@ -0,0 +1,45 @@
package id.co.gtc.erhacam;
import Config.SomeCodes;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.geometry.Rectangle2D;
import javafx.scene.Scene;
import javafx.stage.Screen;
import javafx.stage.Stage;
import org.tinylog.Logger;
import java.io.IOException;
import static Config.SomeCodes.config;
public class MainApplication extends Application {
@Override
public void start(Stage stage) throws IOException {
FXMLLoader fxmlLoader = new FXMLLoader(MainApplication.class.getResource("main-view.fxml"));
Screen screen = Screen.getPrimary();
Rectangle2D screenbound = screen.getVisualBounds();
Scene scene = new Scene(fxmlLoader.load(), screenbound.getWidth(), screenbound.getHeight());
stage.setTitle("MultiCam Capture App for ERHA");
stage.setScene(scene);
stage.setResizable(false);
stage.setMaximized(true);
stage.setOnCloseRequest(e->{
config.Save();
MainView mainView = fxmlLoader.getController();
mainView.Unload();
Logger.info("Application closed");
});
SomeCodes.LoadQRReader();
SomeCodes.LoadFaceDetector();
stage.show();
Logger.info("Application started");
}
public static void main(String[] args) {
SomeCodes.ExtractResource("/tinylog.properties");
launch();
}
}

View File

@@ -0,0 +1,99 @@
package id.co.gtc.erhacam;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.control.Button;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.Pane;
import org.tinylog.Logger;
import static Config.SomeCodes.ValidString;
@SuppressWarnings("unused")
public class MainView {
private String currentselected = "";
@FXML
private Pane mainpane;
@FXML
private Button ReviewButton;
@FXML
private Button CaptureButton;
@FXML
private Button SettingButton;
@FXML
private Button LogsButton;
private Object currentcontroller;
@FXML
private void ReviewClick(ActionEvent event){
if (currentselected.equals("review-view.fxml")) return;
loadContent("review-view.fxml");
}
@FXML
private void CaptureClick(ActionEvent event){
if (currentselected.equals("capture-view.fxml")) return;
loadContent("capture-view.fxml");
}
@FXML
private void SettingClick(ActionEvent event){
if (currentselected.equals("setting-view.fxml")) return;
loadContent("setting-view.fxml");
}
@FXML
private void LogsClick(ActionEvent event){
if (currentselected.equals("logs-view.fxml")) return;
loadContent("logs-view.fxml");
}
@FXML
private void initialize(){
ReviewClick(null);
}
public void Unload(){
loadContent("");
}
private void loadContent(String fxmlfile){
if (currentcontroller!=null){
switch (currentcontroller) {
case CaptureView captureView -> captureView.Unload();
case SettingView settingView -> settingView.Unload();
case ReviewView reviewView -> reviewView.Unload();
case LogsView logsView -> logsView.Unload();
default -> {
}
}
}
if (ValidString(fxmlfile)){
try {
FXMLLoader loader = new FXMLLoader(getClass().getResource(fxmlfile));
AnchorPane child = loader.load();
AnchorPane.setTopAnchor(child, 0.0);
AnchorPane.setRightAnchor(child, 0.0);
AnchorPane.setLeftAnchor(child, 0.0);
AnchorPane.setBottomAnchor(child, 0.0);
mainpane.getChildren().clear();
mainpane.getChildren().add(child);
currentselected = fxmlfile;
currentcontroller = loader.getController();
} catch (Exception e) {
Logger.error("Unable to load " + fxmlfile + ", exception : " + e.getMessage());
}
} else Logger.info("Not loading empty fxml file");
}
}

View File

@@ -0,0 +1,91 @@
package id.co.gtc.erhacam;
import Config.SomeCodes;
import javafx.fxml.FXML;
import javafx.scene.control.Alert;
import javafx.scene.control.Label;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.HBox;
import org.bytedeco.opencv.global.opencv_imgcodecs;
import org.bytedeco.opencv.opencv_core.Mat;
import org.tinylog.Logger;
import java.io.File;
import java.nio.file.Path;
import static Config.SomeCodes.config;
public class PhotoRow {
@FXML
private Label datetime;
@FXML
private Label prefix;
@FXML
private HBox photos;
private final String borderstyle = "-fx-border-color: black; -fx-border-width: 1px;";
public void setDatetime(String datetime){
this.datetime.setText(datetime);
this.datetime.setStyle(borderstyle);
}
public void setPrefix(String prefix){
this.prefix.setText(prefix);
this.prefix.setStyle(borderstyle);
}
public void setPhotos(int width, int height, String... thumbnails){
photos.setSpacing(10);
for(String photopath : thumbnails){
ImageView imgview = createImageView(loadImage(photopath), width, height);
if (imgview!=null){
photos.getChildren().add(imgview);
//HBox.setMargin(imgview, new Insets(5, 5, 5, 5));
imgview.setStyle(borderstyle);
imgview.setOnMouseClicked(e->{
if (e.getClickCount()>=2){
File ff = new File(photopath);
String hires = Path.of(config.getPhotoDirectory(), ff.getName()).toString();
File hiresfile = new File(hires);
if (hiresfile.isFile()){
SomeCodes.OpenPictureInDefaultViewer(hires);
} else {
Alert alert = new Alert(Alert.AlertType.ERROR);
alert.setTitle("Error");
alert.setHeaderText("File not found");
alert.setContentText("File not found: "+hires);
alert.showAndWait();
}
e.consume();
}
});
}
}
}
private ImageView createImageView(Image img, int width, int height){
if (img!=null){
ImageView imgview = new ImageView(img);
imgview.prefWidth(width);
imgview.prefHeight(height);
imgview.setFitHeight(height);
imgview.setFitWidth(width);
imgview.setPreserveRatio(true);
return imgview;
}
return null;
}
private Image loadImage(String photopath){
try{
Mat mat = opencv_imgcodecs.imread(photopath);
return SomeCodes.ConvertToImage(mat, 640,480);
} catch (Exception e){
Logger.error("Error loading image: " + photopath + ", Msg : " + e.getMessage());
}
return null;
}
}

View File

@@ -0,0 +1,83 @@
package id.co.gtc.erhacam;
import Database.PhotoReviewClass;
import Database.Sqlite;
import javafx.application.Platform;
import javafx.concurrent.Task;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.layout.AnchorPane;
import org.tinylog.Logger;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ReviewView {
@FXML
private AnchorPane mainpane;
@FXML
private AnchorPane reviewpane;
@FXML
public void initialize(){
Platform.runLater(()->{
reviewpane.prefWidthProperty().bind(mainpane.widthProperty());
int height = 120;
double factor = 4.0/3.0;
Sqlite sql = new Sqlite();
PhotoReviewClass[] prcs = sql.GetAll();
if (prcs!=null){
ExecutorService executor = Executors.newSingleThreadExecutor();
for(int ii=0;ii<prcs.length;ii++){
PhotoReviewClass prc = prcs[ii];
Thumbloader tl = new Thumbloader(prc,ii,height,factor);
tl.setOnSucceeded(e->{
AnchorPane row = tl.getValue();
if (row!=null) reviewpane.getChildren().add(row);
});
tl.setOnFailed(e-> Logger.error("Thumbloader for "+prc.getPrefix()+" failed, error: "+e.getSource().getException().getMessage()));
executor.submit(tl);
}
executor.shutdown();
}
});
}
// somehow this code is not working, it's not showing the thumbnails
private static class Thumbloader extends Task<AnchorPane> {
private final PhotoReviewClass prc;
private final int height;
private final double factor;
private final int ii;
public Thumbloader(PhotoReviewClass prc, int ii, int height, double factor){
this.prc = prc;
this.height = height;
this.factor = factor;
this.ii = ii;
}
@Override
protected AnchorPane call() {
try{
FXMLLoader loader = new FXMLLoader(getClass().getResource("PhotoRow.fxml"));
AnchorPane row = loader.load();
row.setPrefHeight(height);
PhotoRow pr = loader.getController();
AnchorPane.setTopAnchor(row, (1.0*ii*height)+5.0);
pr.setDatetime(prc.getDateTime());
pr.setPrefix(prc.getPrefix());
pr.setPhotos((int)(factor*height), height, prc.thumbnails());
return row;
} catch (Exception e) {
System.out.println("Error loading PhotoRow.fxml, error: "+e.getMessage());
return null;
}
}
}
public void Unload(){
}
}

View File

@@ -0,0 +1,215 @@
package id.co.gtc.erhacam;
import FTP.FTPCheck;
import javafx.application.Platform;
import javafx.fxml.FXML;
import javafx.scene.control.Alert;
import javafx.scene.control.ComboBox;
import javafx.scene.control.TextField;
import javafx.stage.FileChooser;
import lombok.val;
import org.bytedeco.javacv.VideoInputFrameGrabber;
import org.tinylog.Logger;
import java.io.File;
import static Config.SomeCodes.config;
public class SettingView {
@FXML
private TextField AudioPhase1;
@FXML
private TextField AudioPhase2;
@FXML
private TextField AudioPhase3;
@FXML
private TextField AudioPhase4;
@FXML
private TextField AudioPhase5;
@FXML
private ComboBox<String> CameraLeft90;
@FXML
private ComboBox<String> CameraLeft45;
@FXML
private ComboBox<String> CameraCenter;
@FXML
private ComboBox<String> CameraRight45;
@FXML
private ComboBox<String> CameraRight90;
@FXML
private TextField FTPHost;
@FXML
private TextField FTPPort;
@FXML
private TextField FTPUser;
@FXML
private TextField FTPPass;
@FXML
private TextField FTPPath;
final FileChooser jfc = new FileChooser();
String[] cameranames = null;
@FXML
public void initialize(){
FileChooser.ExtensionFilter filter = new FileChooser.ExtensionFilter("Audio File", "wav","mp3");
jfc.setSelectedExtensionFilter(filter);
jfc.setTitle("Select Audio File");
try{
cameranames = VideoInputFrameGrabber.getDeviceDescriptions();
Logger.info("Found "+cameranames.length+" Cameras");
} catch (Exception e){
Logger.error("Unable to detect Cameras, Msg : "+e.getMessage());
}
Platform.runLater(()->{
AudioPhase1.setText(config.getAudioPhase1());
AudioPhase2.setText(config.getAudioPhase2());
AudioPhase3.setText(config.getAudioPhase3());
AudioPhase4.setText(config.getAudioPhase4());
AudioPhase5.setText(config.getAudioPhase5());
CameraLeft90.getItems().clear();
CameraLeft45.getItems().clear();
CameraCenter.getItems().clear();
CameraRight45.getItems().clear();
CameraRight90.getItems().clear();
CameraLeft90.getItems().add("");
CameraLeft45.getItems().add("");
CameraCenter.getItems().add("");
CameraRight45.getItems().add("");
CameraRight90.getItems().add("");
for(String camera: cameranames){
Logger.info("adding camera : "+camera+" to camera list");
CameraLeft90.getItems().add(camera);
CameraLeft45.getItems().add(camera);
CameraCenter.getItems().add(camera);
CameraRight45.getItems().add(camera);
CameraRight90.getItems().add(camera);
}
CameraLeft90.setValue(config.getCameraLeft90());
CameraLeft45.setValue(config.getCameraLeft45());
CameraCenter.setValue(config.getCameraCenter());
CameraRight45.setValue(config.getCameraRight45());
CameraRight90.setValue(config.getCameraRight90());
FTPHost.setText(config.getFTPHost());
FTPPort.setText(config.getFTPPort());
FTPUser.setText(config.getFTPUser());
FTPPass.setText(config.getFTPPass());
FTPPath.setText(config.getFTPPath());
});
}
public void Unload(){
config.Save();
}
@FXML
private void BrowseAudioPhase1(){
File file = jfc.showOpenDialog(null);
if (file!=null){
config.SetAudioPhase1(file.getAbsolutePath());
AudioPhase1.setText(config.getAudioPhase1());
}
}
@FXML
private void BrowseAudioPhase2(){
File file = jfc.showOpenDialog(null);
if (file!=null){
config.SetAudioPhase2(file.getAbsolutePath());
AudioPhase2.setText(config.getAudioPhase2());
}
}
@FXML
private void BrowseAudioPhase3(){
File file = jfc.showOpenDialog(null);
if (file!=null){
config.SetAudioPhase3(file.getAbsolutePath());
AudioPhase3.setText(config.getAudioPhase3());
}
}
@FXML
private void BrowseAudioPhase4(){
File file = jfc.showOpenDialog(null);
if (file!=null){
config.SetAudioPhase4(file.getAbsolutePath());
AudioPhase4.setText(config.getAudioPhase4());
}
}
@FXML
private void BrowseAudioPhase5(){
File file = jfc.showOpenDialog(null);
if (file!=null){
config.SetAudioPhase5(file.getAbsolutePath());
AudioPhase5.setText(config.getAudioPhase5());
}
}
@FXML
private void ApplyCameraLeft90(){
config.SetCameraLeft90(CameraLeft90.getValue());
}
@FXML
private void ApplyCameraLeft45(){
config.SetCameraLeft45(CameraLeft45.getValue());
}
@FXML
private void ApplyCameraFront(){
config.SetCameraCenter(CameraCenter.getValue());
}
@FXML
private void ApplyCameraRight45(){
config.SetCameraRight45(CameraRight45.getValue());
}
@FXML
private void ApplyCameraRight90(){
config.SetCameraRight90(CameraRight90.getValue());
}
@FXML
private void SaveFTP(){
boolean passive = false;
FTPCheck ftp = new FTPCheck(FTPHost.getText(),Integer.parseInt(FTPPort.getText()),FTPUser.getText(),FTPPass.getText(),FTPPath.getText(), passive);
if (ftp.IsCorrect()){
config.SetFTPHost(FTPHost.getText());
config.SetFTPPort(FTPPort.getText());
config.SetFTPUser(FTPUser.getText());
config.SetFTPPass(FTPPass.getText());
config.SetFTPPath(FTPPath.getText());
val alert = new Alert(Alert.AlertType.INFORMATION);
alert.setTitle("FTP Configuration");
alert.setHeaderText("FTP Configuration Saved");
alert.setContentText("FTP Configuration Saved Successfully");
alert.showAndWait();
} else {
val alert = new Alert(Alert.AlertType.ERROR);
alert.setTitle("FTP Error");
alert.setHeaderText("FTP Configuration Error");
alert.setContentText("FTP Configuration is incorrect, please check your FTP Configuration");
alert.showAndWait();
}
}
}

View File

@@ -0,0 +1,60 @@
package id.co.gtc.erhacam;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.fxml.FXML;
import javafx.scene.control.Label;
import javafx.scene.control.ProgressBar;
import static Config.SomeCodes.ValidString;
public class UploadProgress {
@FXML
private Label labelfile;
@FXML
private Label labelstatus;
@FXML
private ProgressBar progressbar;
@FXML
public void initialize(){
}
/**
* Set the filename to be displayed
* @param filename the filename to be displayed
*/
public void SetFile(String filename){
if (ValidString(filename)){
labelfile.setText(filename);
}
}
/**
* Set the status to be displayed
* @param status the status to be displayed
*/
public void SetStatus(String status){
if (ValidString(status)){
labelstatus.setText(status);
}
}
/**
* Set the progress of the upload
* @param current the current progress in Bytes
* @param total the total progress in Bytes
*/
public void SetProgress(long current, long total){
if (total > 0){
progressbar.setProgress((double)current / (double)total);
}
}
}