Version bump

- Update all classes to more verbosely log what's going on.
- Update readme and udev rule for automatic camera checking
- Add dependency-checking plugin to pom.xml
- Add GPIO check on initialisation, and override to not drive GPIO if
  not plugged in
- Continue updating functions to be closer and more accurate to
  documentation
This commit is contained in:
Blizzard Finnegan 2023-03-15 11:10:09 -04:00
parent 65eaec4035
commit 5796163068
No known key found for this signature in database
GPG key ID: 86DC1B09AC2E1807
11 changed files with 294 additions and 117 deletions

View file

@ -1,2 +1,2 @@
##EXAMPLE: ACTION=="add", KERNEL=="video[0-9]*", SUBSYSTEM=="video4linux", SUBSYSTEMS=="usb", ENV{ID_PATH}=="platform-fd500000.pcie-pci-0000:01:00.0-usb-0:1.4:1.0", SYMLINK+="video-cam1"
ACTION=="add", KERNEL=="video[0-9]*", SUBSYSTEM=="video4linux", SUBSYSTEMS=="usb", ENV{ID_PATH}=="fillerText", SYMLINK+="video-cam1"
##EXAMPLE: ACTION=="add", KERNEL=="video[0-9]*", SUBSYSTEM=="video4linux", SUBSYSTEMS=="usb", ENV{ID_PATH}=="platform-fd500000.pcie-pci-0000:01:00.0-usb-0:1.4:1.0", SYMLINK+="video-cam-left"
ACTION=="add", KERNEL=="video[0-9]*", SUBSYSTEM=="video4linux", SUBSYSTEMS=="usb", ENV{ID_PATH}=="fillerText", SYMLINK+="video-cam-left"

View file

@ -94,7 +94,7 @@ ls /dev/video-*
## Installation
The project is then built from source, and the output final binary (located in `target/discoTesting.jar`) is copied to the Raspberry Pi for use.
The project is then built from source, and the output final binary (located in `target/discoTesting-[version].jar`) is copied to the Raspberry Pi for use.
This project requires use of `udev` rules to ensure that cameras are in the proper location, by default. This can be modified in this project (camera initialisation is done in the initialisation of `OpenCVFacade`).

View file

@ -4,7 +4,7 @@
<groupId>org.baxter.disco</groupId>
<artifactId>ocr</artifactId>
<name>Disco OCR Accuracy Over Life Testing</name>
<version>4.3.7</version>
<version>4.3.8</version>
<description>Testing Discos for long-term accuracy, using automated optical character recognition.</description>
<organization>
<name>Baxter International</name>
@ -26,11 +26,6 @@
<plugin>
<artifactId>maven-project-info-reports-plugin</artifactId>
<version>3.0.0</version>
<configuration>
<suppressionFiles>
<suppressionFile>./dependency-check-suppressions.xml</suppressionFile>
</suppressionFiles>
</configuration>
</plugin>
<plugin>
<artifactId>maven-javadoc-plugin</artifactId>
@ -100,6 +95,9 @@
<plugin>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.4.1</version>
<configuration>
<show>private</show>
</configuration>
</plugin>
</plugins>
</reporting>

10
pom.xml
View file

@ -3,7 +3,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>org.baxter.disco</groupId>
<artifactId>ocr</artifactId>
<version>4.3.7</version>
<version>4.3.8</version>
<packaging>jar</packaging>
<name>Disco OCR Accuracy Over Life Testing</name>
<description>Testing Discos for long-term accuracy, using automated optical character recognition.</description>
@ -106,11 +106,11 @@
<groupId>org.owasp</groupId>
<artifactId>dependency-check-maven</artifactId>
<version>8.1.2</version>
<configuration>
<!--<configuration>
<suppressionFiles>
<suppressionFile>dependency-check-suppressions.xml</suppressionFile>
</suppressionFiles>
</configuration>
</configuration>-->
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
@ -192,9 +192,9 @@
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.4.1</version>
<!--<configuration>
<configuration>
<show>private</show>
</configuration>-->
</configuration>
</plugin>
</plugins>
</reporting>

View file

@ -1,2 +1,2 @@
#! /usr/bin/env sh
sudo java -jar discoTesting-4.3.7.jar 2>/dev/null
sudo java -jar discoTesting-4.3.8.jar 2>/dev/null

View file

@ -25,7 +25,7 @@ public class Cli
/**
* Complete build version number
*/
private static final String version = "4.3.7";
private static final String version = "4.3.8";
/**
* Currently saved iteration count.
@ -50,6 +50,11 @@ public class Cli
*/
private static boolean camerasConfigured = false;
/**
* Whether GPIO is safe to use
*/
private static boolean safeGPIO = false;
/**
* Number of options currently available in the main menu.
*/
@ -85,7 +90,31 @@ public class Cli
ErrorLogging.logError("Calibrating motor movement. ");
ErrorLogging.logError("The piston will fire momentarily when the motor calibration is complete.");
MovementFacade.pressButton();
boolean testedGPIO = false;
do
{
try
{
MovementFacade.init();
ErrorLogging.logError("Motor movement successfully calibrated!");
testedGPIO = true;
safeGPIO = true;
}
catch(Exception e)
{
ErrorLogging.logError(e);
ErrorLogging.logError("GPIO initialisation error! Please check GPIO connections.");
println("");
prompt("Press enter to try again.");
String userInputString = inputScanner.nextLine();
if(userInputString.length() > 0)
{
testedGPIO = userInputString.contains("override");
ErrorLogging.logError("DEBUG: WARNING! - User override of GPIO check.");
}
}
}while(!testedGPIO);
do
{
@ -188,6 +217,7 @@ public class Cli
*/
private static void printHelp()
{
ErrorLogging.logError("DEBUG: User asked for help at main menu.");
println("\n\n");
println("========================================");
println("Explanations:");
@ -348,6 +378,7 @@ public class Cli
*/
private static void printCameraConfigHelpMenu()
{
ErrorLogging.logError("DEBUG: User asked for help at camera config menu.");
println("\n\n");
println("============================================================");
println("Camera Config Menu options:");
@ -389,10 +420,11 @@ public class Cli
*/
private static void configureCameras()
{
ErrorLogging.logError("DEBUG: Configuing cameras...");
List<String> cameraList = new ArrayList<>(OpenCVFacade.getCameraNames());
//Always wake the camera, to ensure that the image is useful
MovementFacade.iterationMovement(true);
if(safeGPIO) MovementFacade.iterationMovement(true);
double tesseractValue = 0.0;
do
@ -414,11 +446,14 @@ public class Cli
do
{
//Press button twice, to make sure the DUT is awake
MovementFacade.pressButton();
try{ Thread.sleep(2000); } catch(Exception e){ ErrorLogging.logError(e); }
MovementFacade.pressButton();
try{ Thread.sleep(2000); } catch(Exception e){ ErrorLogging.logError(e); }
if(safeGPIO)
{
//Press button twice, to make sure the DUT is awake
MovementFacade.pressButton();
try{ Thread.sleep(2000); } catch(Exception e){ ErrorLogging.logError(e); }
MovementFacade.pressButton();
try{ Thread.sleep(2000); } catch(Exception e){ ErrorLogging.logError(e); }
}
File image = OpenCVFacade.showImage(cameraName);
tesseractValue = TesseractFacade.imageToDouble(image);
@ -497,6 +532,7 @@ public class Cli
*/
private static void setDUTSerials()
{
ErrorLogging.logError("DEBUG: Setting DUT serials...");
List<String> cameraList = new ArrayList<>(OpenCVFacade.getCameraNames());
do
{
@ -534,6 +570,7 @@ public class Cli
input = (int)inputFiltering(inputScanner.nextLine());
} while(input == -1);
iterationCount = input;
ErrorLogging.logError("DEBUG: Iteration count modified! Iteration count is now: " + iterationCount);
}
/**
@ -541,6 +578,7 @@ public class Cli
*/
private static void setActiveCameras()
{
ErrorLogging.logError("DEBUG: User modified active cameras.");
List<String> cameraList = new ArrayList<>(OpenCVFacade.getCameraNames());
do
@ -564,6 +602,13 @@ public class Cli
ConfigFacade.setValue(cameraName,ConfigProperties.ACTIVE,newValue);
} while(true);
ErrorLogging.logError("DEBUG: Currently active cameras:");
for(String camera : OpenCVFacade.getCameraNames())
{
if(ConfigFacade.getValue(camera,ConfigProperties.ACTIVE) != 0)
ErrorLogging.logError("DEBUG: - " + camera);
}
ErrorLogging.logError("DEBUG:");
}
/**
@ -594,8 +639,11 @@ public class Cli
//Wake the device, then wait to ensure they're awake before continuing
ErrorLogging.logError("DEBUG: Waking devices...");
MovementFacade.pressButton();
try{ Thread.sleep(2000); } catch(Exception e){ ErrorLogging.logError(e); }
if(safeGPIO)
{
MovementFacade.pressButton();
try{ Thread.sleep(2000); } catch(Exception e){ ErrorLogging.logError(e); }
}
Map<File,Double> resultMap = new HashMap<>();
Map<String,File> cameraToFile = new HashMap<>();
@ -623,12 +671,15 @@ public class Cli
do
{
fail = false;
while(!LOCK.tryLock()) {}
MovementFacade.iterationMovement(prime);
LOCK.unlock();
if(safeGPIO)
{
while(!LOCK.tryLock()) {}
MovementFacade.iterationMovement(prime);
LOCK.unlock();
//Wait for the DUT to display an image
try{ Thread.sleep(2000); } catch(Exception e){ ErrorLogging.logError(e); }
//Wait for the DUT to display an image
try{ Thread.sleep(2000); } catch(Exception e){ ErrorLogging.logError(e); }
}
for(String cameraName : cameraList)
{
@ -692,7 +743,7 @@ public class Cli
ErrorLogging.logError("DEBUG: PROGRAM CLOSING.");
ErrorLogging.logError("DEBUG: =================");
if(inputScanner != null) inputScanner.close();
MovementFacade.closeGPIO();
if(safeGPIO) MovementFacade.closeGPIO();
ErrorLogging.logError("DEBUG: END OF PROGRAM.");
ErrorLogging.closeLogs();
println("The program has exited successfully. Please press Ctrl-c to return to the terminal prompt.");

View file

@ -148,6 +148,7 @@ public class ConfigFacade
*/
public static double getValue(String cameraName, ConfigProperties property)
{
ErrorLogging.logError("DEBUG: Getting " + property.getConfig() + " for " + cameraName + "camera...");
double output = 0.0;
if(!CONFIG_MAP.keySet().contains(cameraName))
{
@ -163,7 +164,7 @@ public class ConfigFacade
}
/**
* Called to force early calling of the static block
* Initialises ConfigFacade and OpenCVFacade values
*/
public static void init() {}
@ -182,12 +183,18 @@ public class ConfigFacade
*/
public static boolean setOutputSaveLocation(String path)
{
ErrorLogging.logError("DEBUG: Output save location modified!");
boolean output = false;
if(Files.exists(Paths.get(path)))
{
outputSaveLocation = path;
ErrorLogging.logError("DEBUG: New output save location: " + outputSaveLocation);
output = true;
}
else
{
ErrorLogging.logError("DEBUG: Output save location modification failed.");
}
return output;
}
@ -206,12 +213,18 @@ public class ConfigFacade
*/
public static boolean setImgSaveLocation(String path)
{
ErrorLogging.logError("DEBUG: Image save location modified!");
boolean output = false;
if(Files.exists(Paths.get(path)))
{
imageSaveLocation = path;
ErrorLogging.logError("DEBUG: New image save location: " + outputSaveLocation);
output = true;
}
else
{
ErrorLogging.logError("DEBUG: Output save location modification failed.");
}
return output;
}
@ -225,6 +238,7 @@ public class ConfigFacade
*/
public static boolean setValue(String cameraName, ConfigProperties property, double propertyValue)
{
ErrorLogging.logError("DEBUG: Setting " + property.getConfig() + " to " + propertyValue + "for " + cameraName);
boolean output = false;
List<String> activeCameras = new ArrayList<>(OpenCVFacade.getCameraNames());
@ -278,6 +292,7 @@ public class ConfigFacade
*/
public static boolean saveDefaultConfig(String filename)
{
ErrorLogging.logError("DEBUG: Saving default config to " + filename);
boolean output = false;
Set<String> cameraNames = OpenCVFacade.getCameraNames();
@ -335,6 +350,7 @@ public class ConfigFacade
*/
public static boolean saveCurrentConfig(String filename)
{
ErrorLogging.logError("DEBUG: Saving current config to " + filename);
boolean output = false;
//Get a list of all cameras
@ -379,6 +395,7 @@ public class ConfigFacade
*/
public static boolean loadConfig(String filename)
{
ErrorLogging.logError("DEBUG: Attempting to load config from " + filename);
//Check if the current CONFIG_MAP is empty
boolean emptyMap = CONFIG_MAP.keySet().size() == 0;
boolean output = false;
@ -386,7 +403,14 @@ public class ConfigFacade
//If the config file we're trying to load from doesn't exist, failover to saving
//the default values to a new file with that name
File file = new File(filename);
try{ if(file.createNewFile()) return saveDefaultConfig(); }
try
{
if(file.createNewFile())
{
ErrorLogging.logError("DEBUG: Listed file does not exist!");
return saveDefaultConfig();
}
}
catch(Exception e)
{
ErrorLogging.logError(e);
@ -461,6 +485,7 @@ public class ConfigFacade
*/
private static boolean saveSingleDefault(String sectionName)
{
ErrorLogging.logError("DEBUG: Section unsuccessfully imported! Saving defaults to " + sectionName);
boolean output = false;
Map<ConfigProperties,Double> cameraConfig = new HashMap<>();
for(ConfigProperties property : ConfigProperties.values())

View file

@ -91,6 +91,7 @@ public class DataSaving
*/
public static boolean initWorkbook(String filename, int camCount, double targetTemp, double failRange)
{
ErrorLogging.logError("DEBUG: Initialising workbook...");
DataSaving.targetTemp = targetTemp;
DataSaving.failRange = failRange;
boolean output = false;
@ -148,8 +149,15 @@ public class DataSaving
passPercentCell.setCellValue("Pass %");
try (FileOutputStream outputStream = new FileOutputStream(outputFile))
{ outputWorkbook.write(outputStream); }
catch(Exception e) {ErrorLogging.logError(e);}
{
outputWorkbook.write(outputStream);
ErrorLogging.logError("DEBUG: Initialising workbook completed successfully.");
}
catch(Exception e)
{
ErrorLogging.logError("DEBUG: Workbook save failed!");
ErrorLogging.logError(e);
}
return output;
}
@ -161,6 +169,7 @@ public class DataSaving
*/
private static void updateFormulas(int cameraCount)
{
ErrorLogging.logError("DEBUG: Updating formulas in Excel sheet...");
int rowIndex = 0;
FormulaEvaluator formulaEvaluator = outputWorkbook.getCreationHelper().createFormulaEvaluator();
int lastColumnOfData = outputSheet.getRow(rowIndex).getLastCellNum();
@ -224,6 +233,7 @@ public class DataSaving
*/
public static boolean writeValues(int cycle, Map<File,Double> inputMap, Map<String,File> cameraToFile)
{
ErrorLogging.logError("Writing values for " + cycle + " cycle to worksheet.");
boolean output = false;
int cellnum = 0;
int startingRow = outputSheet.getLastRowNum();
@ -283,8 +293,16 @@ public class DataSaving
row.createCell(cellnum++);
}
try (FileOutputStream outputStream = new FileOutputStream(outputFile))
{ outputWorkbook.write(outputStream); output = true; }
catch(Exception e) {ErrorLogging.logError(e);}
{
outputWorkbook.write(outputStream);
output = true;
ErrorLogging.logError("DEBUG: Writing values to Excel sheet was successful.");
}
catch(Exception e)
{
ErrorLogging.logError("DEBUG: Writing values to Excel sheet failed!");
ErrorLogging.logError(e);
}
updateFormulas(cameraNames.size());
return output;
}

View file

@ -39,11 +39,24 @@ public class MovementFacade
/**
* Amount of distance to travel.
* Measured in... seemingly arbitrary units? Not sure on the math here.
* Measured in polls (~10ms intervals currently)
* Set in {@link #findDistance()}
*/
private static double TRAVEL_DIST;
/**
* How many milliseconds to wait before polling the GPIO
*/
private static final int POLL_WAIT = 10;
/**
* How many polls before assuming bad GPIO connection.
* Fixture movement takes approximately 2.5s, or 250 polls.
* Double this to account for errors, or slower fixtures.
*/
private static final int TIMEOUT = 500;
//PWM Addresses
//All addresses are in BCM format.
@ -77,11 +90,6 @@ public class MovementFacade
*/
private static final int LOWER_LIMIT_ADDR = 24;
/**
* How many milliseconds to wait before polling the GPIO
*/
private static final int POLL_WAIT = 10;
//Pi GPIO pin objects
/**
@ -149,8 +157,32 @@ public class MovementFacade
*/
private static Context pi4j;
static
public static void init() throws Exception
{
pi4j = Pi4J.newAutoContext();
ErrorLogging.logError("DEBUG: Opening input GPIO pins...");
upperLimit = inputBuilder(UPPER_LIMIT_ADDR);
lowerLimit = inputBuilder(LOWER_LIMIT_ADDR);
runSwitch = inputBuilder(RUN_SWITCH_ADDR);
ErrorLogging.logError("DEBUG: Opening output GPIO pins...");
motorEnable = outputBuilder(MOTOR_ENABLE_ADDR);
motorDirection = outputBuilder(MOTOR_DIRECTION_ADDR);
pistonActivate = outputBuilder(PISTON_ADDR);
try
{
findDistance();
pressButton();
}
catch(Exception e)
{
ErrorLogging.logError(e);
pi4j.shutdown();
throw new Exception("GPIO init error!!! Check GPIO connections.");
}
ErrorLogging.logError("DEBUG: Starting lock thread...");
runSwitchThread = new Thread(() ->
{
@ -175,17 +207,6 @@ public class MovementFacade
}, "Run switch monitor.");
runSwitchThread.start();
pi4j = Pi4J.newAutoContext();
upperLimit = inputBuilder(UPPER_LIMIT_ADDR);
lowerLimit = inputBuilder(LOWER_LIMIT_ADDR);
runSwitch = inputBuilder(RUN_SWITCH_ADDR);
motorEnable = outputBuilder(MOTOR_ENABLE_ADDR);
motorDirection = outputBuilder(MOTOR_DIRECTION_ADDR);
pistonActivate = outputBuilder(PISTON_ADDR);
findDistance();
}
@ -230,27 +251,23 @@ public class MovementFacade
public static int resetArm()
{
ErrorLogging.logError("DEBUG: --------------------------------------");
ErrorLogging.logError("DEBUG: Resetting arm...");
int counter;
ErrorLogging.logError("DEBUG: Setting minimum frequency of PWM...");
if(upperLimit.isHigh())
{
ErrorLogging.logError("DEBUG: Motor at highest point! Lowering to reset.");
motorDirection.low();
ErrorLogging.logError("DEBUG: Motor offset on.");
motorEnable.on();
motorDirectionDown();
motorOn();
try{ Thread.sleep(500); }
catch (Exception e){ ErrorLogging.logError(e); }
motorEnable.off();
ErrorLogging.logError("DEBUG: Motor offset off.");
motorOff();
}
ErrorLogging.logError("DEBUG: Moving motor to highest point.");
motorDirection.high();
motorDirectionUp();
ErrorLogging.logError("DEBUG: Motor return on.");
motorEnable.on();
motorOn();
ErrorLogging.logError("DEBUG: Is the upper limit switch reached? " + upperLimit.isHigh());
for(counter = 0; counter < Integer.MAX_VALUE; counter++)
for(counter = 0; counter < TIMEOUT; counter++)
{
try{ Thread.sleep(POLL_WAIT); } catch(Exception e){ ErrorLogging.logError(e); }
if(upperLimit.isOn())
@ -259,23 +276,33 @@ public class MovementFacade
if(upperLimit.isOn()) break;
}
}
motorEnable.off();
ErrorLogging.logError("DEBUG: Motor returned after " + counter + " polls.");
ErrorLogging.logError("DEBUG: --------------------------------------");
return counter;
motorOff();
if(counter < TIMEOUT)
{
ErrorLogging.logError("DEBUG: Motor returned after " + counter + " polls.");
ErrorLogging.logError("DEBUG: --------------------------------------");
return counter;
}
else
{
ErrorLogging.logError("DEBUG: No motor return after 30 seconds.");
ErrorLogging.logError("DEBUG: --------------------------------------");
return -1;
}
}
/**
* Used to programmatically find the distance between the upper and lower limit switches.
*/
private static void findDistance()
private static void findDistance() throws Exception
{
resetArm();
ErrorLogging.logError("DEBUG: Measuring travel time to buttons...");
int safeGPIO = resetArm();
if(safeGPIO < 0) throw new Exception("Failed GPIO initialisation!");
int downTravelCounter = 0;
int upTravelCounter = 0;
//pwm.on(DUTY_CYCLE, MIN_FREQUENCY);
motorDirection.low();
motorEnable.on();
motorDirectionDown();
motorOn();
for(downTravelCounter = 0; downTravelCounter < Integer.MAX_VALUE; downTravelCounter++)
{
try{ Thread.sleep(POLL_WAIT); } catch(Exception e){ ErrorLogging.logError(e); }
@ -285,13 +312,13 @@ public class MovementFacade
if(lowerLimit.isOn()) break;
}
}
motorEnable.off();
motorOff();
if(lowerLimit.isOff()) ErrorLogging.logError("DEBUG: False positive on findDistance down!");
ErrorLogging.logError("DEBUG: Down travel count: " + downTravelCounter);
motorDirection.high();
motorEnable.on();
motorDirectionUp();
motorOn();
for(upTravelCounter = 0; upTravelCounter < Integer.MAX_VALUE; upTravelCounter++)
{
try{ Thread.sleep(POLL_WAIT); } catch(Exception e){ ErrorLogging.logError(e); }
@ -301,7 +328,7 @@ public class MovementFacade
if(upperLimit.isOn()) break;
}
}
motorEnable.off();
motorOff();
if(upperLimit.isOff()) ErrorLogging.logError("DEBUG: False positive on findDistance up!");
ErrorLogging.logError("DEBUG: Up travel count: " + downTravelCounter);
@ -327,15 +354,13 @@ public class MovementFacade
DigitalInput limitSense;
if(moveUp)
{
motorDirection.high();
motorDirectionUp();
limitSense = upperLimit;
ErrorLogging.logError("DEBUG: Sending fixture up...");
}
else
{
motorDirection.low();
motorDirectionDown();
limitSense = lowerLimit;
ErrorLogging.logError("DEBUG: Sending fixture down...");
}
if(limitSense.isOn()) return FinalState.SAFE;
@ -346,17 +371,17 @@ public class MovementFacade
ErrorLogging.logError("DEBUG: Travel time: " + totalPollCount);
ErrorLogging.logError("DEBUG: High speed poll count: " + highSpeedPolls);
ErrorLogging.logError("DEBUG: =============================");
motorEnable.on();
motorOn();
for(int i = 0; i < highSpeedPolls; i++)
{
try{ Thread.sleep(POLL_WAIT); } catch(Exception e){ ErrorLogging.logError(e); }
if(limitSense.isOn())
{
motorEnable.off();
motorOff();
break;
}
}
motorEnable.off();
motorOff();
output = (limitSense.isOn() ? FinalState.UNSAFE : FinalState.SAFE);
@ -417,6 +442,33 @@ public class MovementFacade
pressButton();
}
private static void motorOn()
{
ErrorLogging.logError("DEBUG: Motor on.");
motorEnable.on();
}
private static void motorOff()
{
ErrorLogging.logError("DEBUG: Motor off.");
motorEnable.off();
}
private static void motorDirectionUp()
{
ErrorLogging.logError("DEBUG: Motor travelling up.");
motorDirection.high();
}
private static void motorDirectionDown()
{
ErrorLogging.logError("DEBUG: Motor travelling down.");
motorDirection.low();
}
/**
* Possible states for motor control movement.
*/
public enum FinalState
{ UNSAFE, SAFE, FAILED; }
}

View file

@ -27,6 +27,7 @@ import java.util.Map;
import java.util.Set;
import java.io.File;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
@ -155,6 +156,7 @@ public class OpenCVFacade
*/
private static Mat takePicture(String cameraName)
{
ErrorLogging.logError("DEBUG: Taking picture...");
Mat output = null;
Frame temp = null;
@ -162,12 +164,16 @@ public class OpenCVFacade
{
try{ temp = cameraMap.get(cameraName).grab(); }
catch(Exception e) { ErrorLogging.logError(e); }
}
//Convert to grayscale
Mat in = MAT_CONVERTER.convertToMat(temp);
output = MAT_CONVERTER.convertToMat(temp);
cvtColor(in,output,CV_BGR2GRAY);
//Convert to grayscale
Mat in = MAT_CONVERTER.convertToMat(temp);
output = MAT_CONVERTER.convertToMat(temp);
cvtColor(in,output,CV_BGR2GRAY);
}
else
{
ErrorLogging.logError("DEBUG: Invalid camera!");
}
return output;
}
@ -181,6 +187,7 @@ public class OpenCVFacade
*/
public static File showImage(String cameraName)
{
ErrorLogging.logError("DEBUG: Showing preview...");
File imageLocation = completeProcess(cameraName,ConfigFacade.getImgSaveLocation() + "/config");
if(imageLocation == null) return null;
Frame outputImage = MAT_CONVERTER.convert(imread(imageLocation.getAbsolutePath()));
@ -213,6 +220,7 @@ public class OpenCVFacade
*/
private static List<Mat> takeBurst(String cameraName, int frameCount)
{
ErrorLogging.logError("DEBUG: Taking burst of " + frameCount + " images...");
List<Mat> output = null;
if(getCameraNames().contains(cameraName))
{
@ -222,6 +230,10 @@ public class OpenCVFacade
output.add(takePicture(cameraName));
}
}
else
{
ErrorLogging.logError("DEBUG: Invalid camera!");
}
return output;
}
@ -233,21 +245,25 @@ public class OpenCVFacade
public static void setCrop(String cameraName)
{
Mat uncroppedImage = takePicture(cameraName);
Rect roi = selectROI("Pick Crop Location", uncroppedImage);
if(roi.x() == 0 && roi.y() == 0 && roi.width() == 0 && roi.height() == 0)
if(uncroppedImage != null)
{
ErrorLogging.logError("Crop error! - Invalid crop selection.");
ErrorLogging.logError("If the crop region did not have a box indicating is location, please restart the program.");
ConfigFacade.setValue(cameraName,ConfigProperties.CROP_X,ConfigProperties.CROP_X.getDefaultValue());
ConfigFacade.setValue(cameraName,ConfigProperties.CROP_Y,ConfigProperties.CROP_Y.getDefaultValue());
ConfigFacade.setValue(cameraName,ConfigProperties.CROP_W,ConfigProperties.CROP_W.getDefaultValue());
ConfigFacade.setValue(cameraName,ConfigProperties.CROP_H,ConfigProperties.CROP_H.getDefaultValue());
return;
ErrorLogging.logError("DEBUG: Allowing user to modify crop.");
Rect roi = selectROI("Pick Crop Location", uncroppedImage);
if(roi.x() == 0 && roi.y() == 0 && roi.width() == 0 && roi.height() == 0)
{
ErrorLogging.logError("Crop error! - Invalid crop selection.");
ErrorLogging.logError("If the crop region did not have a box indicating is location, please restart the program.");
ConfigFacade.setValue(cameraName,ConfigProperties.CROP_X,ConfigProperties.CROP_X.getDefaultValue());
ConfigFacade.setValue(cameraName,ConfigProperties.CROP_Y,ConfigProperties.CROP_Y.getDefaultValue());
ConfigFacade.setValue(cameraName,ConfigProperties.CROP_W,ConfigProperties.CROP_W.getDefaultValue());
ConfigFacade.setValue(cameraName,ConfigProperties.CROP_H,ConfigProperties.CROP_H.getDefaultValue());
return;
}
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());
}
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());
}
/**
@ -322,6 +338,7 @@ public class OpenCVFacade
*/
private static File saveImage(Mat image, String fileLocation, String cameraName)
{
ErrorLogging.logError("DEBUG: Saving image from " + cameraName + " to " + fileLocation);
File output = null;
IplImage temp = MAT_CONVERTER.convertToIplImage(MAT_CONVERTER.convert(image));
fileLocation = fileLocation + "/" + ErrorLogging.fileDatetime.format(LocalDateTime.now()) + "-" + cameraName + ".png";
@ -337,30 +354,21 @@ public class OpenCVFacade
* from {@link ConfigFacade}.
*
* @param images List of images to be composed
* @param threshold Whether to put the image through a binary threshold
* @param crop Whether to crop the image
* @param cameraName Name of the camera the images came from (used to determine crop sizing and threshold value)
*
* @return A single image, found by boolean AND-ing together all parsed images.
*/
private static Mat compose(List<Mat> images, boolean threshold,
boolean crop, String cameraName)
private static Mat compose(List<Mat> images)
{
ErrorLogging.logError("DEBUG: Attempting to compose " + images.size() + " images...");
Mat output = null;
int iterationCount = 1;
for(Mat image : images)
{
Mat processedImage = image.clone();
image.copyTo(processedImage);
if(crop) processedImage = crop(processedImage,cameraName);
if(threshold) processedImage = thresholdImage(processedImage,cameraName);
if(iterationCount == 1) output = processedImage.clone();
if(output == null) output = processedImage.clone();
bitwise_and((iterationCount == 1 ? processedImage : output),processedImage, output);
bitwise_and(output,processedImage, output);
iterationCount++;
}
if(output != null) ErrorLogging.logError("DEBUG: Compositing successful!");
@ -391,8 +399,32 @@ public class OpenCVFacade
return output;
}
List<Mat> imageList = takeBurst(cameraName, compositeFrames);
List<Mat> processedImageList = new ArrayList<>();
Mat finalImage = compose(imageList, threshold, crop, cameraName);
Mat finalImage = null;
if(crop) ErrorLogging.logError("Cropping is enabled.");
if(threshold) ErrorLogging.logError("Thresholding is enabled.");
for(Mat image : imageList)
{
Mat processedImage = image.clone();
image.copyTo(processedImage);
if(crop)
processedImage = crop(processedImage,cameraName);
if(threshold)
processedImage = thresholdImage(processedImage,cameraName);
if(finalImage == null)
finalImage = processedImage.clone();
processedImageList.add(processedImage);
}
if(compositeFrames > 1)
finalImage = compose(processedImageList);
//Mat finalImage = compose(imageList, threshold, crop, cameraName);
output = saveImage(finalImage, saveLocation,cameraName);
return output;
}

View file

@ -101,6 +101,7 @@ public class TesseractFacade
else ErrorLogging.logError("OCR ERROR!!! - OCR output is not a Double.");
}
}
else { ErrorLogging.logError("OCR Failed to retrieve information!"); }
return output;
}
}