diff --git a/README.md b/README.md
index 679cde4..f7354cf 100644
--- a/README.md
+++ b/README.md
@@ -61,4 +61,5 @@ This app makes usage of
* NetBeans Platform:
* CHDK-PTP-Java Library:
-* usb4java-javax:
\ No newline at end of file
+* usb4java-javax:
+* USB IDs:
\ No newline at end of file
diff --git a/eazy-bookscanner-swing/README.md b/eazy-bookscanner-swing/README.md
index bf14154..37135f3 100644
--- a/eazy-bookscanner-swing/README.md
+++ b/eazy-bookscanner-swing/README.md
@@ -1,3 +1,11 @@
+# Eazy BookScanner
+
+OpenSource Book Scanner application for digitizing books with two cameras.
+Usage for two digital cameras like described at .
+
+![Screenshot GUI Eazy BookScanner](./screenshot-20201221.jpg)
+
+## References
* Swing: https://zetcode.com/javaswing/
* Icons: https://www.javacodegeeks.com/2020/03/javafx-tip-32-need-icons-use-ikonli.html
\ No newline at end of file
diff --git a/eazy-bookscanner-swing/pom.xml b/eazy-bookscanner-swing/pom.xml
index c71f7af..5459d26 100644
--- a/eazy-bookscanner-swing/pom.xml
+++ b/eazy-bookscanner-swing/pom.xml
@@ -3,16 +3,21 @@
4.0.0
- com.datazuul
- eazy-bookscanner-parent
- 0.2.1-SNAPSHOT
+ org.springframework.boot
+ spring-boot-starter-parent
+ 2.6.7
+ Eazy BookScanner (Swing)
com.datazuul.eazy.bookscanner
eazy-bookscanner-swing
- 1.0-SNAPSHOT
+ 1.0.0-SNAPSHOT
+ jar
+ 1.8
+ 1.8
+ 1.8
UTF-8
com.datazuul.eazy.bookscanner.App
@@ -23,6 +28,11 @@
CHDK-PTP-Java
0.7.1-SNAPSHOT
+
+ commons-cli
+ commons-cli
+ 1.5.0
+
org.imgscalr
imgscalr-lib
@@ -38,5 +48,20 @@
ikonli-swing
12.3.1
+
+ org.springframework.boot
+ spring-boot-starter
+
+
+
+ eazy-bookscanner-${project.version}
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
\ No newline at end of file
diff --git a/eazy-bookscanner-swing/src/main/java/com/datazuul/eazy/bookscanner/App.java b/eazy-bookscanner-swing/src/main/java/com/datazuul/eazy/bookscanner/App.java
index 7a8d097..52db74c 100644
--- a/eazy-bookscanner-swing/src/main/java/com/datazuul/eazy/bookscanner/App.java
+++ b/eazy-bookscanner-swing/src/main/java/com/datazuul/eazy/bookscanner/App.java
@@ -1,5 +1,6 @@
package com.datazuul.eazy.bookscanner;
+import com.datazuul.eazy.bookscanner.devices.CamerasProperties;
import com.datazuul.eazy.bookscanner.gui.ThumbnailsAndScanPanel;
import java.awt.Color;
import java.awt.EventQueue;
@@ -11,10 +12,22 @@
import static javax.swing.WindowConstants.EXIT_ON_CLOSE;
import org.kordamp.ikonli.material2.Material2AL;
import org.kordamp.ikonli.swing.FontIcon;
-
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.builder.SpringApplicationBuilder;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.ConfigurableApplicationContext;
+
+/**
+ * Implementing ApplicationRunner interface tells Spring Boot to automatically
+ * call the run method AFTER the application context has been loaded.
+ */
+@SpringBootApplication
+@EnableConfigurationProperties(CamerasProperties.class)
public class App extends JFrame {
public static final int ICON_SIZE = 20;
+
+ public static ConfigurableApplicationContext CONTEXT;
public App() {
initUI();
@@ -71,9 +84,17 @@ private void initUI() {
}
public static void main(String[] args) {
+ CONTEXT = new SpringApplicationBuilder(App.class).headless(false).run(args);
EventQueue.invokeLater(() -> {
- App ex = new App();
+ App ex = CONTEXT.getBean(App.class);
ex.setVisible(true);
});
+
+ logConfig();
+ }
+
+ private static void logConfig() {
+ CamerasProperties props = CONTEXT.getBean(CamerasProperties.class);
+ System.out.println(props);
}
}
diff --git a/eazy-bookscanner-swing/src/main/java/com/datazuul/eazy/bookscanner/devices/CameraFactory.java b/eazy-bookscanner-swing/src/main/java/com/datazuul/eazy/bookscanner/devices/CameraFactory.java
index c2591d7..7dee982 100644
--- a/eazy-bookscanner-swing/src/main/java/com/datazuul/eazy/bookscanner/devices/CameraFactory.java
+++ b/eazy-bookscanner-swing/src/main/java/com/datazuul/eazy/bookscanner/devices/CameraFactory.java
@@ -1,21 +1,51 @@
package com.datazuul.eazy.bookscanner.devices;
import chdk.ptp.java.ICamera;
-import chdk.ptp.java.SupportedCamera;
import chdk.ptp.java.connection.CameraUsbDevice;
-import javax.usb.UsbDevice;
+import com.datazuul.eazy.bookscanner.App;
+import java.util.List;
+import java.util.Objects;
+import org.springframework.stereotype.Component;
+@Component
public class CameraFactory {
public static ICamera getCamera(CameraUsbDevice cameraUsbDevice) {
- SupportedCamera sc = SupportedCamera.getCamera(cameraUsbDevice.getIdVendor(), cameraUsbDevice.getIdProduct());
- if (sc != null) {
- // known camera - create specified class
- try {
- UsbDevice usbDevice = cameraUsbDevice.getDevice();
- return sc.getClazz().getConstructor(UsbDevice.class).newInstance(usbDevice);
- } catch (Exception ex) {
- return null;
+ CamerasProperties props = App.CONTEXT.getBean(CamerasProperties.class);
+
+ Short idVendor = cameraUsbDevice.getIdVendor();
+ Short idProduct = cameraUsbDevice.getIdProduct();
+
+ ConfiguredCamera camera = getCamera(props, idVendor, idProduct, cameraUsbDevice);
+ return camera;
+
+// SupportedCamera sc = SupportedCamera.getCamera(idVendor, idProduct);
+// if (sc != null) {
+// // known camera - create specified class
+// try {
+// UsbDevice usbDevice = cameraUsbDevice.getDevice();
+// return sc.getClazz().getConstructor(UsbDevice.class).newInstance(usbDevice);
+// } catch (Exception ex) {
+// return null;
+// }
+// }
+// return null;
+ }
+
+ private static ConfiguredCamera getCamera(CamerasProperties props, Short idVendor, Short idProduct, CameraUsbDevice cameraUsbDevice) {
+ List vendors = props.getVendors();
+ if (vendors != null) {
+ Vendor vendor = vendors.stream().filter(v -> Objects.equals(idVendor, v.getId())).findAny().orElse(null);
+ if (vendor != null) {
+ System.out.println("found vendor: " + vendor);
+ List products = vendor.getProducts();
+ if (products != null) {
+ Product product = products.stream().filter(p -> Objects.equals(idProduct, p.getId())).findAny().orElse(null);
+ if (product != null) {
+ System.out.println("found product: " + product);
+ return new ConfiguredCamera(cameraUsbDevice.getDevice(), product);
+ }
+ }
}
}
return null;
diff --git a/eazy-bookscanner-swing/src/main/java/com/datazuul/eazy/bookscanner/devices/CamerasProperties.java b/eazy-bookscanner-swing/src/main/java/com/datazuul/eazy/bookscanner/devices/CamerasProperties.java
new file mode 100644
index 0000000..03204d6
--- /dev/null
+++ b/eazy-bookscanner-swing/src/main/java/com/datazuul/eazy/bookscanner/devices/CamerasProperties.java
@@ -0,0 +1,25 @@
+package com.datazuul.eazy.bookscanner.devices;
+
+import java.util.List;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.boot.context.properties.ConfigurationPropertiesScan;
+
+@ConfigurationProperties(prefix = "cameras")
+@ConfigurationPropertiesScan
+public class CamerasProperties {
+
+ private List vendors;
+
+ public List getVendors() {
+ return vendors;
+ }
+
+ public void setVendors(List vendors) {
+ this.vendors = vendors;
+ }
+
+ @Override
+ public String toString() {
+ return "CamerasProperties{" + "vendors=" + vendors + '}';
+ }
+}
diff --git a/eazy-bookscanner-swing/src/main/java/com/datazuul/eazy/bookscanner/devices/ConfiguredCamera.java b/eazy-bookscanner-swing/src/main/java/com/datazuul/eazy/bookscanner/devices/ConfiguredCamera.java
new file mode 100644
index 0000000..17b7a3b
--- /dev/null
+++ b/eazy-bookscanner-swing/src/main/java/com/datazuul/eazy/bookscanner/devices/ConfiguredCamera.java
@@ -0,0 +1,99 @@
+package com.datazuul.eazy.bookscanner.devices;
+
+import chdk.ptp.java.camera.FailSafeCamera;
+import chdk.ptp.java.exception.GenericCameraException;
+import chdk.ptp.java.exception.PTPTimeoutException;
+import chdk.ptp.java.model.FocusMode;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import javax.usb.UsbDevice;
+
+public class ConfiguredCamera extends FailSafeCamera {
+
+ private Logger log = Logger.getLogger(ConfiguredCamera.class.getName());
+
+ private final Product product;
+
+ public ConfiguredCamera(UsbDevice device, Product product) {
+ super(device);
+ this.product = product;
+ }
+
+ @Override
+ public void setFocusMode(FocusMode mode) throws GenericCameraException, PTPTimeoutException {
+ try {
+ Thread.sleep(500);
+ } catch (InterruptedException e) {
+ log.log(Level.SEVERE, e.getLocalizedMessage(), e);
+ throw new GenericCameraException(e.getLocalizedMessage());
+ }
+
+ // currently only supporting autofocus mode
+ if (FocusMode.AUTO.equals(mode)) {
+ List autofocus = product.getAutofocus();
+ int currentFocusMode = getFocusMode().getValue();
+ for (FocusModeChange focusModeChange : autofocus) {
+ if (currentFocusMode == focusModeChange.getCurrentMode()) {
+ List commands = focusModeChange.getExecute();
+ if (commands != null) {
+ for (String command : commands) {
+ switch (command) {
+ case "LEFT":
+ this.executeLuaCommand("click('left');");
+ break;
+ case "RIGHT":
+ this.executeLuaCommand("click('right');");
+ break;
+ case "SET":
+ this.executeLuaCommand("click('set');");
+ break;
+ default:
+ break;
+ }
+ if (command.startsWith("sleep")) {
+ long milliseconds = Long.valueOf(command.substring(5));
+ try {
+ Thread.sleep(milliseconds);
+ } catch (InterruptedException ex) {
+ log.log(Level.SEVERE, ex.getLocalizedMessage(), ex);
+ throw new GenericCameraException(ex.getLocalizedMessage());
+ }
+ }
+ }
+ }
+ // activate auto focus and focus
+ try {
+ this.executeLuaCommand("set_aflock(0);");
+ this.executeLuaCommand("press('shoot_half');");
+ Thread.sleep(800);
+ this.executeLuaCommand("release('shoot_half');");
+ this.executeLuaCommand("set_aflock(1);");
+ Thread.sleep(1000);
+ } catch (InterruptedException ex) {
+ log.log(Level.SEVERE, ex.getLocalizedMessage(), ex);
+ throw new GenericCameraException(ex.getLocalizedMessage());
+ }
+ }
+ }
+ }
+ }
+
+ @Override
+ public void setZoom(int zoomPosition) throws PTPTimeoutException, GenericCameraException {
+ Zoom zoom = product.getZoom();
+ if (zoom != null) {
+ List preExecute = zoom.getPreExecute();
+ for (String command : preExecute) {
+ switch (command) {
+ case "SET_AUTOFOCUS":
+ setFocusMode(FocusMode.AUTO);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ super.setZoom(zoomPosition);
+ }
+}
diff --git a/eazy-bookscanner-swing/src/main/java/com/datazuul/eazy/bookscanner/devices/FocusModeChange.java b/eazy-bookscanner-swing/src/main/java/com/datazuul/eazy/bookscanner/devices/FocusModeChange.java
new file mode 100644
index 0000000..0673461
--- /dev/null
+++ b/eazy-bookscanner-swing/src/main/java/com/datazuul/eazy/bookscanner/devices/FocusModeChange.java
@@ -0,0 +1,30 @@
+package com.datazuul.eazy.bookscanner.devices;
+
+import java.util.List;
+
+public class FocusModeChange {
+
+ private int currentMode;
+ private List execute;
+
+ public int getCurrentMode() {
+ return currentMode;
+ }
+
+ public List getExecute() {
+ return execute;
+ }
+
+ public void setCurrentMode(int currentMode) {
+ this.currentMode = currentMode;
+ }
+
+ public void setExecute(List execute) {
+ this.execute = execute;
+ }
+
+ @Override
+ public String toString() {
+ return "FocusModeChange{" + "currentMode=" + currentMode + ", execute=" + execute + '}';
+ }
+}
diff --git a/eazy-bookscanner-swing/src/main/java/com/datazuul/eazy/bookscanner/devices/Product.java b/eazy-bookscanner-swing/src/main/java/com/datazuul/eazy/bookscanner/devices/Product.java
new file mode 100644
index 0000000..cd62353
--- /dev/null
+++ b/eazy-bookscanner-swing/src/main/java/com/datazuul/eazy/bookscanner/devices/Product.java
@@ -0,0 +1,48 @@
+package com.datazuul.eazy.bookscanner.devices;
+
+import java.util.List;
+
+public class Product {
+
+ private List autofocus;
+ private Short id;
+ private String name;
+ private Zoom zoom;
+
+ public List getAutofocus() {
+ return autofocus;
+ }
+
+ public Short getId() {
+ return id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public Zoom getZoom() {
+ return zoom;
+ }
+
+ public void setAutofocus(List autofocus) {
+ this.autofocus = autofocus;
+ }
+
+ public void setId(Short id) {
+ this.id = id;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public void setZoom(Zoom zoom) {
+ this.zoom = zoom;
+ }
+
+ @Override
+ public String toString() {
+ return "Product{" + "autofocus=" + autofocus + ", id=" + id + ", name=" + name + ", zoom=" + zoom + '}';
+ }
+}
diff --git a/eazy-bookscanner-swing/src/main/java/com/datazuul/eazy/bookscanner/devices/Vendor.java b/eazy-bookscanner-swing/src/main/java/com/datazuul/eazy/bookscanner/devices/Vendor.java
new file mode 100644
index 0000000..c5dedbf
--- /dev/null
+++ b/eazy-bookscanner-swing/src/main/java/com/datazuul/eazy/bookscanner/devices/Vendor.java
@@ -0,0 +1,39 @@
+package com.datazuul.eazy.bookscanner.devices;
+
+import java.util.List;
+
+public class Vendor {
+
+ private Short id;
+ private String name;
+ private List products;
+
+ public Short getId() {
+ return id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public List getProducts() {
+ return products;
+ }
+
+ public void setId(Short id) {
+ this.id = id;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public void setProducts(List products) {
+ this.products = products;
+ }
+
+ @Override
+ public String toString() {
+ return "Vendor{" + "id=" + id + ", name=" + name + ", products=" + products + '}';
+ }
+}
diff --git a/eazy-bookscanner-swing/src/main/java/com/datazuul/eazy/bookscanner/devices/Zoom.java b/eazy-bookscanner-swing/src/main/java/com/datazuul/eazy/bookscanner/devices/Zoom.java
new file mode 100644
index 0000000..c8ffc8a
--- /dev/null
+++ b/eazy-bookscanner-swing/src/main/java/com/datazuul/eazy/bookscanner/devices/Zoom.java
@@ -0,0 +1,21 @@
+package com.datazuul.eazy.bookscanner.devices;
+
+import java.util.List;
+
+public class Zoom {
+
+ private List preExecute;
+
+ public List getPreExecute() {
+ return preExecute;
+ }
+
+ public void setPreExecute(List preExecute) {
+ this.preExecute = preExecute;
+ }
+
+ @Override
+ public String toString() {
+ return "Zoom{" + "preExecute=" + preExecute + '}';
+ }
+}
diff --git a/eazy-bookscanner-swing/src/main/java/com/datazuul/eazy/bookscanner/gui/ThumbnailsAndScanPanel.form b/eazy-bookscanner-swing/src/main/java/com/datazuul/eazy/bookscanner/gui/ThumbnailsAndScanPanel.form
index edaf5a4..2b64b94 100644
--- a/eazy-bookscanner-swing/src/main/java/com/datazuul/eazy/bookscanner/gui/ThumbnailsAndScanPanel.form
+++ b/eazy-bookscanner-swing/src/main/java/com/datazuul/eazy/bookscanner/gui/ThumbnailsAndScanPanel.form
@@ -179,6 +179,11 @@
+
+
+
+
+
diff --git a/eazy-bookscanner-swing/src/main/java/com/datazuul/eazy/bookscanner/gui/ThumbnailsAndScanPanel.java b/eazy-bookscanner-swing/src/main/java/com/datazuul/eazy/bookscanner/gui/ThumbnailsAndScanPanel.java
index de00c9b..49d52cd 100644
--- a/eazy-bookscanner-swing/src/main/java/com/datazuul/eazy/bookscanner/gui/ThumbnailsAndScanPanel.java
+++ b/eazy-bookscanner-swing/src/main/java/com/datazuul/eazy/bookscanner/gui/ThumbnailsAndScanPanel.java
@@ -232,7 +232,6 @@ public void stateChanged(javax.swing.event.ChangeEvent evt) {
scanPanels.setLayout(new javax.swing.BoxLayout(scanPanels, javax.swing.BoxLayout.X_AXIS));
- leftScanPanel.setAlignmentX(0.5F);
leftScanPanel.setPreferredSize(new java.awt.Dimension(511, 800));
scanPanels.add(leftScanPanel);
@@ -253,6 +252,7 @@ public void actionPerformed(java.awt.event.ActionEvent evt) {
add(scanPanels, java.awt.BorderLayout.CENTER);
+ bottomPanel.setPreferredSize(new java.awt.Dimension(59, 70));
bottomPanel.setLayout(new java.awt.BorderLayout());
shootButton.setBackground(new java.awt.Color(204, 255, 204));
diff --git a/eazy-bookscanner-swing/src/main/resources/application.yml b/eazy-bookscanner-swing/src/main/resources/application.yml
new file mode 100644
index 0000000..d83c34c
--- /dev/null
+++ b/eazy-bookscanner-swing/src/main/resources/application.yml
@@ -0,0 +1,24 @@
+cameras:
+ vendors:
+ - id: 0x04a9
+ name: 'Canon, Inc.'
+ products:
+ - id: 0x322a
+ name: 'PowerShot A2200'
+ # The A2200 only has three focus modes (in this order on display):
+ # currentFocusMode in: "Macro" = 4 - "Normal" = 0 - "Infinity" = 3
+ # FocusMode.MACRO (4), "Normal" = FocusMode.AUTO (0), "Infinity" = FocusMode.INF (3)
+ autofocus:
+ # set to "Normal" (there is no AUTO mode) and handle auto focus later during shoot
+ # see https://chdk.fandom.com/wiki/Script_commands#set_aflock
+ # macro
+ - current-mode: 4
+ execute: [LEFT, sleep1000, RIGHT, sleep500, SET, sleep1000]
+ # normal
+ - current-mode: 0
+ execute: []
+ # infinity
+ - current-mode: 3
+ execute: [LEFT, sleep1000, LEFT, sleep500, SET, sleep1000]
+ zoom:
+ pre-execute: [SET_AUTOFOCUS]
diff --git a/eazy-bookscanner-swing/src/main/resources/images/logo-datazuul-bookscan-16x16.png b/eazy-bookscanner-swing/src/main/resources/images/logo-datazuul-bookscan-16x16.png
new file mode 100644
index 0000000..ce310fd
Binary files /dev/null and b/eazy-bookscanner-swing/src/main/resources/images/logo-datazuul-bookscan-16x16.png differ
diff --git a/eazy-bookscanner-swing/src/main/resources/images/logo-datazuul-bookscan-32x32.png b/eazy-bookscanner-swing/src/main/resources/images/logo-datazuul-bookscan-32x32.png
new file mode 100644
index 0000000..7281c19
Binary files /dev/null and b/eazy-bookscanner-swing/src/main/resources/images/logo-datazuul-bookscan-32x32.png differ
diff --git a/eazy-bookscanner-swing/src/main/resources/images/logo-datazuul-bookscan-48x48.png b/eazy-bookscanner-swing/src/main/resources/images/logo-datazuul-bookscan-48x48.png
new file mode 100644
index 0000000..c356954
Binary files /dev/null and b/eazy-bookscanner-swing/src/main/resources/images/logo-datazuul-bookscan-48x48.png differ
diff --git a/eazy-bookscanner-swing/src/main/resources/images/splash-screen-bookscan-638x320.png b/eazy-bookscanner-swing/src/main/resources/images/splash-screen-bookscan-638x320.png
new file mode 100644
index 0000000..ba253e2
Binary files /dev/null and b/eazy-bookscanner-swing/src/main/resources/images/splash-screen-bookscan-638x320.png differ