Overhaul cropping #5
8 changed files with 97 additions and 70 deletions
|
@ -20,6 +20,10 @@ This is a personal/professional project, which makes use of JavaCV, OpenCV, Tess
|
||||||
- [x] modify number of iterations for test suite
|
- [x] modify number of iterations for test suite
|
||||||
- [ ] JavaFX GUI (see `gui` branch)
|
- [ ] JavaFX GUI (see `gui` branch)
|
||||||
|
|
||||||
|
## Known Bugs
|
||||||
|
|
||||||
|
- As of v4.1.0, cropping images is done by a temporary window created by OpenCV. This window will crash after being used correctly. This can be safely killed when prompted, without affecting the rest of the project. Investigation into how to fix this crashing is ongoing, but it has not yet been resolved.
|
||||||
|
|
||||||
## Dependencies
|
## Dependencies
|
||||||
|
|
||||||
To install this project, you must have the following:
|
To install this project, you must have the following:
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
<groupId>org.baxter.disco</groupId>
|
<groupId>org.baxter.disco</groupId>
|
||||||
<artifactId>ocr</artifactId>
|
<artifactId>ocr</artifactId>
|
||||||
<name>Disco OCR Accuracy Over Life Testing</name>
|
<name>Disco OCR Accuracy Over Life Testing</name>
|
||||||
<version>4.0.0</version>
|
<version>4.1.0</version>
|
||||||
<description>Testing Discos for long-term accuracy, using automated optical character recognition.</description>
|
<description>Testing Discos for long-term accuracy, using automated optical character recognition.</description>
|
||||||
<organization>
|
<organization>
|
||||||
<name>Baxter International</name>
|
<name>Baxter International</name>
|
||||||
|
@ -108,7 +108,6 @@
|
||||||
<poi.version>5.2.3</poi.version>
|
<poi.version>5.2.3</poi.version>
|
||||||
<run.with.java.module>true</run.with.java.module>
|
<run.with.java.module>true</run.with.java.module>
|
||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
<javafx.version>19</javafx.version>
|
|
||||||
<junit.version>5.9.1</junit.version>
|
<junit.version>5.9.1</junit.version>
|
||||||
<beanutils.version>1.9.4</beanutils.version>
|
<beanutils.version>1.9.4</beanutils.version>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
4
pom.xml
4
pom.xml
|
@ -3,7 +3,7 @@
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<groupId>org.baxter.disco</groupId>
|
<groupId>org.baxter.disco</groupId>
|
||||||
<artifactId>ocr</artifactId>
|
<artifactId>ocr</artifactId>
|
||||||
<version>4.0.1</version>
|
<version>4.1.0</version>
|
||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
<name>Disco OCR Accuracy Over Life Testing</name>
|
<name>Disco OCR Accuracy Over Life Testing</name>
|
||||||
<description>Testing Discos for long-term accuracy, using automated optical character recognition.</description>
|
<description>Testing Discos for long-term accuracy, using automated optical character recognition.</description>
|
||||||
|
@ -25,7 +25,7 @@
|
||||||
<jvmargs.debug></jvmargs.debug>
|
<jvmargs.debug></jvmargs.debug>
|
||||||
|
|
||||||
<pi4j.version>2.1.0</pi4j.version>
|
<pi4j.version>2.1.0</pi4j.version>
|
||||||
<javafx.version>19</javafx.version>
|
<!--<javafx.version>19</javafx.version>-->
|
||||||
<poi.version>5.2.3</poi.version>
|
<poi.version>5.2.3</poi.version>
|
||||||
<commons.version>2.8.0</commons.version>
|
<commons.version>2.8.0</commons.version>
|
||||||
<opencv.version>1.5.6</opencv.version>
|
<opencv.version>1.5.6</opencv.version>
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
#! /usr/bin/env sh
|
#! /usr/bin/env sh
|
||||||
sudo java -jar discoTesting-4.0.0-rc3.jar
|
sudo java -jar discoTesting-4.1.0.jar 2>/dev/null
|
||||||
|
|
|
@ -18,7 +18,7 @@ module org.baxter.disco.ocr {
|
||||||
requires org.apache.commons.lang3;
|
requires org.apache.commons.lang3;
|
||||||
|
|
||||||
requires org.bytedeco.javacv.platform;
|
requires org.bytedeco.javacv.platform;
|
||||||
requires java.desktop;
|
//requires java.desktop;
|
||||||
|
|
||||||
uses com.pi4j.extension.Extension;
|
uses com.pi4j.extension.Extension;
|
||||||
uses com.pi4j.provider.Provider;
|
uses com.pi4j.provider.Provider;
|
||||||
|
|
|
@ -17,7 +17,7 @@ import java.util.concurrent.locks.ReentrantLock;
|
||||||
* classes).
|
* classes).
|
||||||
*
|
*
|
||||||
* @author Blizzard Finnegan
|
* @author Blizzard Finnegan
|
||||||
* @version 1.4.1, 09 Feb. 2023
|
* @version 1.5.0, 10 Feb. 2023
|
||||||
*/
|
*/
|
||||||
public class Cli
|
public class Cli
|
||||||
{
|
{
|
||||||
|
@ -25,7 +25,7 @@ public class Cli
|
||||||
* Complete build version number
|
* Complete build version number
|
||||||
*/
|
*/
|
||||||
|
|
||||||
private static final String version = "4.0.0";
|
private static final String version = "4.1.0";
|
||||||
/**
|
/**
|
||||||
* Currently saved iteration count.
|
* Currently saved iteration count.
|
||||||
*/
|
*/
|
||||||
|
@ -239,7 +239,9 @@ public class Cli
|
||||||
"\n\tfor use in OCR. Can modify"+
|
"\n\tfor use in OCR. Can modify"+
|
||||||
"\n\tconfig file, if requested." +
|
"\n\tconfig file, if requested." +
|
||||||
"\n\tAvailable variables to change:"+
|
"\n\tAvailable variables to change:"+
|
||||||
"\n\t\tCrop dimensions");
|
"\n\t\tCrop dimensions"+
|
||||||
|
"\n\t\tComposite frame count"+
|
||||||
|
"\n\t\tThreshold value");
|
||||||
println("----------------------------------------");
|
println("----------------------------------------");
|
||||||
println("3. Set serial numbers: Set the serial " +
|
println("3. Set serial numbers: Set the serial " +
|
||||||
"\n\tnumber for the device under test." +
|
"\n\tnumber for the device under test." +
|
||||||
|
@ -373,17 +375,6 @@ public class Cli
|
||||||
println("====================================");
|
println("====================================");
|
||||||
println("Camera Config Menu:");
|
println("Camera Config Menu:");
|
||||||
println("------------------------------------");
|
println("------------------------------------");
|
||||||
println("Current Crop values: ");
|
|
||||||
println("************************************");
|
|
||||||
print("X: " + ConfigFacade.getValue(cameraName,
|
|
||||||
ConfigProperties.CROP_X));
|
|
||||||
print(" | Y: " + ConfigFacade.getValue(cameraName,
|
|
||||||
ConfigProperties.CROP_Y));
|
|
||||||
print(" | Width: " + ConfigFacade.getValue(cameraName,
|
|
||||||
ConfigProperties.CROP_W));
|
|
||||||
println(" | Height: " + ConfigFacade.getValue(cameraName,
|
|
||||||
ConfigProperties.CROP_H));
|
|
||||||
println("************************************");
|
|
||||||
println("Current composite frame count: " +
|
println("Current composite frame count: " +
|
||||||
ConfigFacade.getValue(cameraName,ConfigProperties.COMPOSITE_FRAMES));
|
ConfigFacade.getValue(cameraName,ConfigProperties.COMPOSITE_FRAMES));
|
||||||
println("Current threshold value: " +
|
println("Current threshold value: " +
|
||||||
|
@ -392,20 +383,14 @@ public class Cli
|
||||||
println("Will the image be cropped? " + cropValue);
|
println("Will the image be cropped? " + cropValue);
|
||||||
String thresholdImage = ((ConfigFacade.getValue(cameraName,ConfigProperties.THRESHOLD) != 0) ? "yes" : "no");
|
String thresholdImage = ((ConfigFacade.getValue(cameraName,ConfigProperties.THRESHOLD) != 0) ? "yes" : "no");
|
||||||
println("Will the image be thresholded? " + thresholdImage);
|
println("Will the image be thresholded? " + thresholdImage);
|
||||||
String cameraActive = ((ConfigFacade.getValue(cameraName,ConfigProperties.ACTIVE) != 0) ? "yes" : "no");
|
|
||||||
println("Will the camera be used when running tests? " + cameraActive);
|
|
||||||
println("Tesseract parsed value for camera " + cameraName + ": " + tesseractValue);
|
println("Tesseract parsed value for camera " + cameraName + ": " + tesseractValue);
|
||||||
println("------------------------------------");
|
println("------------------------------------");
|
||||||
println("1. Change Crop X");
|
println("1. Change Crop Point");
|
||||||
println("2. Change Crop Y");
|
println("2. Change Composite Frame Count");
|
||||||
println("3. Change Crop Width");
|
println("3. Change Threshold Value");
|
||||||
println("4. Change Crop Height");
|
println("4. Toggle crop");
|
||||||
println("5. Change Composite Frame Count");
|
println("5. Toggle threshold");
|
||||||
println("6. Change Threshold Value");
|
println("6. Exit");
|
||||||
println("7. Toggle crop");
|
|
||||||
println("8. Toggle threshold");
|
|
||||||
println("9. Toggle camera");
|
|
||||||
println("10. Exit");
|
|
||||||
println("====================================");
|
println("====================================");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -541,53 +526,48 @@ public class Cli
|
||||||
modifiedProperty = ConfigProperties.CROP_X;
|
modifiedProperty = ConfigProperties.CROP_X;
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
modifiedProperty = ConfigProperties.CROP_Y;
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
modifiedProperty = ConfigProperties.CROP_W;
|
|
||||||
break;
|
|
||||||
case 4:
|
|
||||||
modifiedProperty = ConfigProperties.CROP_H;
|
|
||||||
break;
|
|
||||||
case 5:
|
|
||||||
modifiedProperty = ConfigProperties.COMPOSITE_FRAMES;
|
modifiedProperty = ConfigProperties.COMPOSITE_FRAMES;
|
||||||
break;
|
break;
|
||||||
case 6:
|
case 3:
|
||||||
modifiedProperty = ConfigProperties.THRESHOLD_VALUE;
|
modifiedProperty = ConfigProperties.THRESHOLD_VALUE;
|
||||||
break;
|
break;
|
||||||
case 7:
|
case 4:
|
||||||
modifiedProperty = ConfigProperties.CROP;
|
modifiedProperty = ConfigProperties.CROP;
|
||||||
break;
|
break;
|
||||||
case 8:
|
case 5:
|
||||||
modifiedProperty = ConfigProperties.THRESHOLD;
|
modifiedProperty = ConfigProperties.THRESHOLD;
|
||||||
break;
|
break;
|
||||||
case 9:
|
case 6:
|
||||||
modifiedProperty = ConfigProperties.ACTIVE;
|
|
||||||
break;
|
|
||||||
case 0:
|
|
||||||
case 10:
|
|
||||||
modifiedProperty = ConfigProperties.PRIME;
|
modifiedProperty = ConfigProperties.PRIME;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
} while(modifiedProperty == null);
|
} while(modifiedProperty == null);
|
||||||
|
|
||||||
|
//Toggle threshold/crop
|
||||||
if(modifiedProperty == ConfigProperties.THRESHOLD ||
|
if(modifiedProperty == ConfigProperties.THRESHOLD ||
|
||||||
modifiedProperty == ConfigProperties.CROP ||
|
modifiedProperty == ConfigProperties.CROP)
|
||||||
modifiedProperty == ConfigProperties.ACTIVE)
|
|
||||||
{
|
{
|
||||||
double newValue = ConfigFacade.getValue(cameraName,modifiedProperty);
|
double newValue = ConfigFacade.getValue(cameraName,modifiedProperty);
|
||||||
newValue = Math.abs(newValue - 1);
|
newValue = Math.abs(newValue - 1);
|
||||||
ConfigFacade.setValue(cameraName,modifiedProperty,newValue);
|
ConfigFacade.setValue(cameraName,modifiedProperty,newValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Redefine crop points
|
||||||
|
else if(modifiedProperty == ConfigProperties.CROP_X)
|
||||||
|
{ OpenCVFacade.setCrop(cameraName); }
|
||||||
|
|
||||||
|
//Modify config values
|
||||||
else if(modifiedProperty != ConfigProperties.PRIME)
|
else if(modifiedProperty != ConfigProperties.PRIME)
|
||||||
{
|
{
|
||||||
prompt("Enter new value for this property (" + modifiedProperty.toString() + ", currently : " +
|
prompt("Enter new value for this property (" + modifiedProperty.toString() + ", currently : " +
|
||||||
ConfigFacade.getValue(cameraName,modifiedProperty) + "): ");
|
(int)ConfigFacade.getValue(cameraName,modifiedProperty) + "): ");
|
||||||
userInput = inputFiltering(inputScanner.nextLine());
|
userInput = inputFiltering(inputScanner.nextLine());
|
||||||
ConfigFacade.setValue(cameraName,modifiedProperty,userInput);
|
ConfigFacade.setValue(cameraName,modifiedProperty,userInput);
|
||||||
//if(canvas != null) canvas.dispose();
|
//if(canvas != null) canvas.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Exit loop
|
||||||
else break;
|
else break;
|
||||||
} while(true);
|
} while(true);
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@ import java.util.Set;
|
||||||
|
|
||||||
import static org.bytedeco.opencv.global.opencv_imgproc.*;
|
import static org.bytedeco.opencv.global.opencv_imgproc.*;
|
||||||
import static org.bytedeco.opencv.global.opencv_imgcodecs.*;
|
import static org.bytedeco.opencv.global.opencv_imgcodecs.*;
|
||||||
|
import static org.bytedeco.opencv.global.opencv_highgui.*;
|
||||||
import static org.bytedeco.opencv.global.opencv_core.*;
|
import static org.bytedeco.opencv.global.opencv_core.*;
|
||||||
|
|
||||||
import org.bytedeco.javacv.*;
|
import org.bytedeco.javacv.*;
|
||||||
|
@ -22,7 +23,7 @@ import java.util.List;
|
||||||
* Performs image capture, as well as image manipulation.
|
* Performs image capture, as well as image manipulation.
|
||||||
*
|
*
|
||||||
* @author Blizzard Finnegan
|
* @author Blizzard Finnegan
|
||||||
* @version 1.4.1, 09 Feb. 2023
|
* @version 1.5.0, 10 Feb. 2023
|
||||||
*/
|
*/
|
||||||
public class OpenCVFacade
|
public class OpenCVFacade
|
||||||
{
|
{
|
||||||
|
@ -36,7 +37,7 @@ public class OpenCVFacade
|
||||||
/**
|
/**
|
||||||
* Object used to convert between Mats and Frames
|
* Object used to convert between Mats and Frames
|
||||||
*/
|
*/
|
||||||
private static final OpenCVFrameConverter.ToMat MAT_CONVERTER = new OpenCVFrameConverter.ToMat();
|
public static final OpenCVFrameConverter.ToMat MAT_CONVERTER = new OpenCVFrameConverter.ToMat();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Width of the image created by the camera.
|
* Width of the image created by the camera.
|
||||||
|
@ -228,15 +229,68 @@ public class OpenCVFacade
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Crop the given image to the dimensions in the configuration.
|
* Set crop size and location by GUI means.
|
||||||
*
|
*
|
||||||
|
* @param cameraName The name of the camera being configured
|
||||||
|
*/
|
||||||
|
public static void setCrop(String cameraName)
|
||||||
|
{
|
||||||
|
Mat uncroppedImage = takePicture(cameraName);
|
||||||
|
Rect roi = selectROI("Pick Crop Location", uncroppedImage);
|
||||||
|
ConfigFacade.setValue(cameraName,ConfigProperties.CROP_X, roi.x());
|
||||||
|
ConfigFacade.setValue(cameraName,ConfigProperties.CROP_Y, roi.y());
|
||||||
|
ConfigFacade.setValue(cameraName,ConfigProperties.CROP_W, roi.width());
|
||||||
|
ConfigFacade.setValue(cameraName,ConfigProperties.CROP_H, roi.height());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Crop a given image, based on dimensions in the configuration.
|
||||||
|
*
|
||||||
|
* @param image Frame taken from the camera
|
||||||
|
* @param cameraName Name of the camera the frame is from
|
||||||
|
*/
|
||||||
|
public static Mat crop(Mat image, String cameraName)
|
||||||
|
{
|
||||||
|
int x = (int)ConfigFacade.getValue(cameraName,ConfigProperties.CROP_X);
|
||||||
|
int y = (int)ConfigFacade.getValue(cameraName,ConfigProperties.CROP_Y);
|
||||||
|
int width = (int)ConfigFacade.getValue(cameraName,ConfigProperties.CROP_W);
|
||||||
|
int height = (int)ConfigFacade.getValue(cameraName,ConfigProperties.CROP_H);
|
||||||
|
Rect roi = new Rect(x,y,width,height);
|
||||||
|
return crop(image, roi,cameraName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Crop the given image, based on dimensions defined in a {@link Rect}
|
||||||
|
*
|
||||||
|
* @param image Frame taken from the camera
|
||||||
|
* @param roi The region of interest to crop the image to
|
||||||
|
*
|
||||||
|
* @return Frame of the cropped image
|
||||||
|
*/
|
||||||
|
public static Mat crop(Mat image, Rect roi, String cameraName)
|
||||||
|
{
|
||||||
|
Mat output = null;
|
||||||
|
output = image.apply(roi);
|
||||||
|
String fileLocation = ConfigFacade.getImgSaveLocation() + "/debug/"
|
||||||
|
+ ErrorLogging.fileDatetime.format(LocalDateTime.now()) +
|
||||||
|
"." + cameraName + "-preProcess.jpg";
|
||||||
|
cvSaveImage(fileLocation,MAT_CONVERTER.convertToIplImage(
|
||||||
|
MAT_CONVERTER.convert(output)));
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Crop the given image to the dimensions in the configuration;
|
||||||
|
* deprecated in favour of {@link #crop(Mat, Rect)}
|
||||||
|
*
|
||||||
|
* @deprecated
|
||||||
* @param image Frame taken from the camera.
|
* @param image Frame taken from the camera.
|
||||||
* @param cameraName Name of the camera taking the picture
|
* @param cameraName Name of the camera taking the picture
|
||||||
*
|
*
|
||||||
* @return Frame of the cropped image
|
* @return Frame of the cropped image
|
||||||
*/
|
*/
|
||||||
public static Mat crop(Mat image, String cameraName)
|
private static Mat cropImage(Mat image, String cameraName)
|
||||||
{
|
{
|
||||||
Mat output = null;
|
Mat output = null;
|
||||||
int x = (int)ConfigFacade.getValue(cameraName,ConfigProperties.CROP_X);
|
int x = (int)ConfigFacade.getValue(cameraName,ConfigProperties.CROP_X);
|
||||||
|
|
16
toDoList.md
16
toDoList.md
|
@ -4,31 +4,21 @@
|
||||||
|
|
||||||
## Cli(?)
|
## Cli(?)
|
||||||
|
|
||||||
- [ ] enable camera toggling
|
- [x] enable camera toggling
|
||||||
- may require more classes to be modified
|
|
||||||
|
|
||||||
### MovementFacade
|
|
||||||
|
|
||||||
- [x] Implement multithreading for physical Run switch\*
|
|
||||||
- Currently questionably implemented, consider looking into more
|
|
||||||
|
|
||||||
### OpenCVFacade
|
### OpenCVFacade
|
||||||
|
|
||||||
- [x] Overload takeBurst with a default framecount from config
|
|
||||||
- [x] Overload crop with default values from config
|
|
||||||
- [ ] completeProcess should have more robust file output checking
|
- [ ] completeProcess should have more robust file output checking
|
||||||
|
|
||||||
### Gui
|
### Gui
|
||||||
|
|
||||||
- [x] rebuild menus with current feature set
|
- [ ] Debug and ensure GUI is written properly
|
||||||
- [x] Complete implementation
|
|
||||||
- currently questionably implemented; debugging...
|
|
||||||
|
|
||||||
## Low-priority improvements
|
## Low-priority improvements
|
||||||
|
|
||||||
### All
|
### All
|
||||||
|
|
||||||
- [ ] denote overrided functions in first sentence of function
|
- [x] denote overrided functions in first sentence of function
|
||||||
- [ ] reduce Javadoc linking to one link per reference per class
|
- [ ] reduce Javadoc linking to one link per reference per class
|
||||||
- [ ] Use generated Javadocs to improve Javadocs (perpetual)
|
- [ ] Use generated Javadocs to improve Javadocs (perpetual)
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue