v4.3.6 push #15

Merged
blizzardfinnegan merged 2 commits from devel into stable 2023-03-10 12:21:03 -05:00
9 changed files with 70 additions and 294 deletions

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.5</version>
<version>4.3.6</version>
<description>Testing Discos for long-term accuracy, using automated optical character recognition.</description>
<organization>
<name>Baxter International</name>
@ -91,9 +91,6 @@
<plugin>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.4.1</version>
<configuration>
<show>private</show>
</configuration>
</plugin>
</plugins>
</reporting>

38
pom.xml
View file

@ -3,7 +3,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>org.baxter.disco</groupId>
<artifactId>ocr</artifactId>
<version>4.3.5</version>
<version>4.3.6</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>
@ -47,7 +47,6 @@
<artifactId>javacv-platform</artifactId>
<version>${opencv.version}</version>
</dependency>
<!-- So... turns out, the showImage call requires JavaFX Swing, but doesn't actually *say* it requires that. -->
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-swing</artifactId>
@ -91,13 +90,6 @@
<artifactId>pi4j-plugin-pigpio</artifactId>
<version>${pi4j.version}</version>
</dependency>
<!-- JavaFX Import -->
<!--<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-fxml</artifactId>
<version>${javafx.version}</version>
</dependency>-->
</dependencies>
<build>
@ -184,30 +176,6 @@
</execution>
</executions>
</plugin>
<!--<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>3.3.0</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
<configuration>
<archive>
<manifest>
<addClasspath>true</addClasspath>
<mainClass>org.baxter.disco.ocr.GuiStarter</mainClass>
</manifest>
</archive>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
</execution>
</executions>
</plugin>-->
</plugins>
</build>
<reporting>
@ -216,9 +184,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

@ -18,14 +18,14 @@ import java.util.concurrent.locks.ReentrantLock;
* classes).
*
* @author Blizzard Finnegan
* @version 1.7.0, 06 Mar. 2023
* @version 1.7.1, 10 Mar. 2023
*/
public class Cli
{
/**
* Complete build version number
*/
private static final String version = "4.3.5";
private static final String version = "4.3.6";
/**
* Currently saved iteration count.
@ -65,12 +65,10 @@ public class Cli
*/
public static final Lock LOCK = new ReentrantLock();
//private static Thread safeThread;
//Start of program message; always runs first
static
{
ErrorLogging.logError("START OF PROGRAM");
ErrorLogging.logError("DEBUG: START OF PROGRAM");
}
public static void main(String[] args)
@ -354,13 +352,10 @@ public class Cli
MovementFacade.iterationMovement(true);
double tesseractValue = 0.0;
//Main camera config loop
do
{
//Show the menu
printCameraMenu(cameraList);
//Pick a camera to configure
int userInput;
String cameraName = "";
do
@ -370,12 +365,10 @@ public class Cli
userInput--;
} while (cameraList.size() < userInput && userInput < 0);
//Leave do-while loop if the user asks to
if(userInput == (cameraList.size())) break;
else if(userInput < 0) continue;
else cameraName = cameraList.get((userInput));
//Single camera config loop
do
{
//Press button twice, to make sure the DUT is awake
@ -384,17 +377,12 @@ public class Cli
MovementFacade.pressButton();
try{ Thread.sleep(2000); } catch(Exception e){ ErrorLogging.logError(e); }
//Show image
File image = OpenCVFacade.showImage(cameraName);
//Parse the image with Tesseract, to show user what the excel output will be
tesseractValue = TesseractFacade.imageToDouble(image);
//User input parsing
ConfigProperties modifiedProperty = null;
do
{
//list configurable settings
printCameraConfigMenu(cameraName,tesseractValue);
userInput = (int)inputFiltering(inputScanner.nextLine(),Menus.CAMERA);
@ -422,7 +410,6 @@ public class Cli
}
} while(modifiedProperty == null);
//Toggle threshold/crop
if(modifiedProperty == ConfigProperties.THRESHOLD ||
modifiedProperty == ConfigProperties.CROP)
{
@ -431,11 +418,9 @@ public class Cli
ConfigFacade.setValue(cameraName,modifiedProperty,newValue);
}
//Redefine crop points
else if(modifiedProperty == ConfigProperties.CROP_X)
{ OpenCVFacade.setCrop(cameraName); }
//Modify number of composite frames, or threshold value
else if(modifiedProperty == ConfigProperties.COMPOSITE_FRAMES ||
modifiedProperty == ConfigProperties.THRESHOLD_VALUE)
{
@ -447,7 +432,6 @@ public class Cli
ConfigFacade.setValue(cameraName,modifiedProperty,userInput);
}
//Exit loop
else
{
ConfigFacade.saveCurrentConfig();
@ -457,7 +441,6 @@ public class Cli
} while(true);
//Save the current config to the config file
ConfigFacade.saveCurrentConfig();
println("Configuration complete!");
}
@ -467,15 +450,11 @@ public class Cli
*/
private static void setDUTSerials()
{
//Get a list of available cameras
List<String> cameraList = new ArrayList<>(OpenCVFacade.getCameraNames());
//Main serial setting loop
do
{
//Main menu
printSerialMenu(cameraList);
//Pick a camera to configure
int userInput;
String cameraName = "";
do
@ -486,12 +465,9 @@ public class Cli
userInput--;
} while (cameraList.size() < userInput || userInput < 0);
//Leave do-while loop if the user asks to
if(userInput == (cameraList.size())) break;
else cameraName = cameraList.get((userInput));
//Save the serial number.
//No parsing is ever done on this serial number.
prompt("Enter the serial number you wish to use for this camera: ");
ConfigFacade.setSerial(cameraName,inputScanner.nextLine());
@ -518,16 +494,12 @@ public class Cli
*/
private static void setActiveCameras()
{
//Get available cameras
List<String> cameraList = new ArrayList<>(OpenCVFacade.getCameraNames());
//Main loop
do
{
//Print menu
printActiveToggleMenu(cameraList);
//Pick a camera to configure
int userInput;
String cameraName = "";
do
@ -537,11 +509,9 @@ public class Cli
userInput--;
} while (cameraList.size() < userInput || userInput < 0);
//Leave do-while loop if the user asks to
if(userInput == (cameraList.size())) break;
else cameraName = cameraList.get((userInput));
//Toggle whether the camera is active, at the config level
double newValue = ConfigFacade.getValue(cameraName,ConfigProperties.ACTIVE);
newValue = Math.abs(newValue - 1);
ConfigFacade.setValue(cameraName,ConfigProperties.ACTIVE,newValue);
@ -561,22 +531,18 @@ public class Cli
//useful for multithreading, which isn't necessary in CLI
final int localIterations = iterationCount;
//Save whether to prime the devices; defaults to false
//Hide legacy functionality
boolean prime = false;
//Create a List of *active* cameras.
List<String> cameraList = new ArrayList<>();
for(String cameraName : OpenCVFacade.getCameraNames())
{
prime = (ConfigFacade.getValue(cameraName,ConfigProperties.PRIME) != 0) || prime;
if(ConfigFacade.getValue(cameraName,ConfigProperties.ACTIVE) != 0)
{
cameraList.add(cameraName);
}
}
//Initialise the workbook, with the number of cameras and the final output location
DataSaving.initWorkbook(ConfigFacade.getOutputSaveLocation(),cameraList.size());
//Wake the device, then wait to ensure they're awake before continuing
@ -584,7 +550,6 @@ public class Cli
MovementFacade.pressButton();
try{ Thread.sleep(2000); } catch(Exception e){ ErrorLogging.logError(e); }
//Create final maps for result images, result values, and camera names
Map<File,Double> resultMap = new HashMap<>();
Map<String,File> cameraToFile = new HashMap<>();
@ -595,7 +560,7 @@ public class Cli
}
ErrorLogging.logError("DEBUG: Starting tests...");
//Start actually running tests
//All portions of the test check with the GPIO Run/Pause switch before
//continuing, using the Lock object.
for(int i = 0; i < localIterations; i++)
@ -611,7 +576,6 @@ public class Cli
do
{
fail = false;
//Move the fixture for one iteration, with whether or not the DUTs need to be primed
while(!LOCK.tryLock()) {}
MovementFacade.iterationMovement(prime);
LOCK.unlock();
@ -619,9 +583,6 @@ public class Cli
//Wait for the DUT to display an image
try{ Thread.sleep(2000); } catch(Exception e){ ErrorLogging.logError(e); }
//For all available cameras:
// take an image, process it, and save it to a file
// put that file into the camera name file Map
for(String cameraName : cameraList)
{
while(!LOCK.tryLock()) {}
@ -633,9 +594,6 @@ public class Cli
LOCK.unlock();
}
//ONCE ALL IMAGES ARE CREATED
//Re-iterate over list of cameras, parse the images with Tesseract, then add
//the parsed value to the map
for(String cameraName : cameraList)
{
while(!LOCK.tryLock()) {}
@ -666,16 +624,13 @@ public class Cli
}
while(fail);
//Write all given values to the Excel file
while(!LOCK.tryLock()) {}
DataSaving.writeValues(i,resultMap,cameraToFile);
LOCK.unlock();
//Clear the result map
//DO NOT CLEAR camera to file Map. This will change the order of the objects within it
resultMap.clear();
}
//Alert the user to testing being complete
println("=======================================");
println("Testing complete!");
}

View file

@ -24,7 +24,7 @@ import org.apache.commons.configuration2.builder.fluent.Parameters;
* Can write to file when requested, reads from file on initial start.
*
* @author Blizzard Finnegan
* @version 1.3.1, 09 Feb. 2023
* @version 1.3.2, 10 Mar. 2023
*/
public class ConfigFacade
{
@ -44,7 +44,7 @@ public class ConfigFacade
/**
* Location to save output XLSX file to.
*/
public static String outputSaveLocation = "outputData/" +
private static String outputSaveLocation = "outputData/" +
(LocalDateTime.now().format(ErrorLogging.fileDatetime)) + ".xlsx";
/**
@ -73,16 +73,14 @@ public class ConfigFacade
*/
private static INIConfiguration CONFIG_STORE;
//This block will ALWAYS run first.
static
{
//Get config values
ErrorLogging.logError("Starting configuration setup...");
//Give CONFIG_STORE an intentionally bad value
CONFIG_STORE = null;
//See if a config file already exists
File configFile = new File(configFileLocation);
boolean newConfig = true;
try{ newConfig = configFile.createNewFile(); }
catch(Exception e){ ErrorLogging.logError(e); }
@ -90,7 +88,6 @@ public class ConfigFacade
CONFIG_BUILDER = new FileBasedConfigurationBuilder<>(INIConfiguration.class)
.configure(new Parameters().fileBased().setFile(configFile));
//Try to import the config
ErrorLogging.logError("Attempting to import config...");
if(!newConfig)
{
@ -111,8 +108,6 @@ public class ConfigFacade
}
}
//If there isn't a config file yet (or rather, if we just made a new one)
//save the default values to it
else
{
ErrorLogging.logError("Unable to import config. Loading defaults...");
@ -121,24 +116,19 @@ public class ConfigFacade
else ErrorLogging.logError("Configuration settings set up!");
}
//Make necessary directories, if not already available
ErrorLogging.logError("Creating image storage directories...");
File imageLocation = new File(imageSaveLocation);
imageLocation.mkdir();
File debugImageLocation = new File(imageSaveLocation + "/debug");
debugImageLocation.mkdir();
File configImageLocation = new File(imageSaveLocation + "/config");
configImageLocation.mkdir();
File setupImageLocation = new File(imageSaveLocation + "/config");
setupImageLocation.mkdir();
File outputFileDirectory = new File("outputData");
outputFileDirectory.mkdir();
//Create a new output file
ErrorLogging.logError("Creating output file....");
File outputFile = new File(outputSaveLocation);
try{ outputFile.createNewFile(); }
catch(Exception e){ ErrorLogging.logError(e); }
//Autosave the config
CONFIG_BUILDER.setAutoSave(true);
}
@ -161,7 +151,6 @@ public class ConfigFacade
double output = 0.0;
if(!configMap.keySet().contains(cameraName))
{
//Log failure information
ErrorLogging.logError("CONFIG ERROR!!! - Invalid camera name: " + cameraName);
ErrorLogging.logError("\tKey set: " + configMap.keySet().toString());
ErrorLogging.logError("\tProperty: " + property.getConfig());
@ -170,9 +159,6 @@ public class ConfigFacade
}
Map<ConfigProperties,Double> cameraConfig = configMap.get(cameraName);
output = cameraConfig.get(property);
//Debug logger.
//ErrorLogging.logError("DEBUG: getValue - return value: " + cameraName
// + "/" + property.getConfig() + " = " + output);
return output;
}
@ -240,12 +226,16 @@ public class ConfigFacade
public static boolean setValue(String cameraName, ConfigProperties property, double propertyValue)
{
boolean output = false;
List<String> activeCameras = new ArrayList<>(OpenCVFacade.getCameraNames());
if(!activeCameras.contains(cameraName)) return output;
Map<ConfigProperties,Double> cameraConfig = configMap.get(cameraName);
if(cameraConfig == null) return output;
Double oldValue = cameraConfig.get(property);
output = cameraConfig.replace(property,oldValue,propertyValue);
saveCurrentConfig();
return output;
}
@ -288,21 +278,15 @@ public class ConfigFacade
*/
public static boolean saveDefaultConfig(String filename)
{
//Get set of camera names
boolean output = false;
Set<String> cameraNames = OpenCVFacade.getCameraNames();
//Set the config builder to a file-based, INI configuration,
//with the given filename
CONFIG_BUILDER = new FileBasedConfigurationBuilder<>(INIConfiguration.class)
.configure(new Parameters().fileBased()
.setFile(new File(filename)));
//Set CONFIG_STORE to the Configuration created by the Builder
try { CONFIG_STORE = CONFIG_BUILDER.getConfiguration(); }
catch(Exception e) { ErrorLogging.logError(e); }
//If the save default fails, warn the user that something is wrong
finally
{
if(CONFIG_STORE == null)
@ -312,10 +296,6 @@ public class ConfigFacade
}
}
//Iterate over every camera
// Create a map of the default values
// Save the default values to the CONFIG_STORE
// save the map to the main config map
for(String camera : cameraNames)
{
Map<ConfigProperties,Double> cameraConfig = new HashMap<>();
@ -324,14 +304,11 @@ public class ConfigFacade
String propertyName = camera + "." + property.getConfig();
double propertyValue = property.getDefaultValue();
cameraConfig.put(property,propertyValue);
//ErrorLogging.logError("DEBUG: Attempting to save to config: ");
//ErrorLogging.logError("DEBUG: " + propertyName + ", " + propertyValue);
CONFIG_STORE.setProperty(propertyName,propertyValue);
}
configMap.put(camera,cameraConfig);
}
//Save out to the file
try
{
CONFIG_BUILDER.save();
@ -442,7 +419,6 @@ public class ConfigFacade
for(ConfigProperties configState : ConfigProperties.values())
{
Double configValue = CONFIG_STORE.getDouble(sectionName + "." + configState.getConfig());
//ErrorLogging.logError("DEBUG: Imported config value: " + Double.toString(configValue));
savedSection.put(configState,configValue);
}
}
@ -456,7 +432,6 @@ public class ConfigFacade
{
for(String key : configMap.keySet())
{
//ErrorLogging.logError("DEBUG: configMap Key: " + key + " ?= " + sectionName);
if( key.equals(sectionName))
{ configMap.put(key,savedSection); }
}
@ -493,8 +468,6 @@ public class ConfigFacade
String propertyName = sectionName + "." + property.getConfig();
double propertyValue = property.getDefaultValue();
cameraConfig.put(property,propertyValue);
//ErrorLogging.logError("DEBUG: Attempting to save to config: ");
//ErrorLogging.logError("DEBUG: " + propertyName + ", " + propertyValue);
CONFIG_STORE.setProperty(propertyName,propertyValue);
}
configMap.put(sectionName,cameraConfig);

View file

@ -1,10 +1,10 @@
package org.baxter.disco.ocr;
import java.io.DataInputStream;
//Standard imports
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.DataInputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
@ -32,7 +32,7 @@ import static org.apache.poi.hssf.util.HSSFColor.HSSFColorPredefined;
* Facade for saving data out to a file.
*
* @author Blizzard Finnegan
* @version 5.0.0, 07 Mar. 2023
* @version 5.0.1, 10 Mar. 2023
*/
public class DataSaving
{
@ -99,16 +99,13 @@ public class DataSaving
//Create workbook, Sheet, and DataFormat object
//HSSF objects are used, as these are compatible with Microsoft Excel
//XSSF objects were initially used, but caused issues.
outputWorkbook = new HSSFWorkbook();
outputSheet = outputWorkbook.createSheet();
format = outputWorkbook.createDataFormat();
//Create a default style for values.
defaultStyle = outputWorkbook.createCellStyle();
defaultStyle.setDataFormat(format.getFormat("0.0"));
//Create a style for the final percentage values
finalValuesStyle = outputWorkbook.createCellStyle();
finalValuesStyle.setDataFormat(format.getFormat("0.000%"));
@ -124,19 +121,16 @@ public class DataSaving
failStyle.setDataFormat(format.getFormat("0.0"));
failStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
//Create a style for error-ed, but not out-of-range, values
errorStyle = outputWorkbook.createCellStyle();
errorStyle.setFillForegroundColor(HSSFColorPredefined.YELLOW.getIndex());
errorStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
//Create the header
int startingRow = outputSheet.getLastRowNum();
HSSFRow row = outputSheet.createRow(++startingRow);
int cellnum = 0;
HSSFCell cell = row.createCell(cellnum++);
cell.setCellValue("Iteration");
//Create a section for every camera
for(int i = 0; i < camCount; i++)
{
cell = row.createCell(cellnum++);
@ -153,7 +147,6 @@ public class DataSaving
HSSFCell passPercentCell = row.createCell(cellnum++);
passPercentCell.setCellValue("Pass %");
//Save to file
try (FileOutputStream outputStream = new FileOutputStream(outputFile))
{ outputWorkbook.write(outputStream); }
catch(Exception e) {ErrorLogging.logError(e);}
@ -175,10 +168,8 @@ public class DataSaving
int serialColumn = lastColumnOfData - 2;
int percentColumn = lastColumnOfData - 1;
//Get the last row, add another row below it, and name the first cell "Totals:"
int lastRowOfData = outputSheet.getLastRowNum();
//For each camera, create a unique total line
int column = 1;
for(int i = 0; i < cameraCount; i++)
{
@ -219,7 +210,6 @@ public class DataSaving
column += 4;
}
//Once all totals have been created, write to the file
try (FileOutputStream outputStream = new FileOutputStream(outputFile))
{ outputWorkbook.write(outputStream); }
catch(Exception e) {ErrorLogging.logError(e);}
@ -238,10 +228,8 @@ public class DataSaving
boolean output = false;
int cellnum = 0;
int startingRow = outputSheet.getLastRowNum();
HSSFRow row = (startingRow == 1) ? outputSheet.getRow(++startingRow) : outputSheet.createRow(++startingRow);
HSSFRow row = (cycle == 1) ? outputSheet.getRow(startingRow) : outputSheet.createRow(++startingRow);
List<String> cameraNames = new ArrayList<>(cameraToFile.keySet());
//ErrorLogging.logError("DEBUG: image locations: " + imageLocations.toString());
//List<Object> objectArray = new LinkedList<>();
cycle++;
@ -249,12 +237,10 @@ public class DataSaving
indexCell.setCellValue(cycle);
for(String cameraName : cameraNames)
{
//put serial number into sheet
String serialNumber = ConfigFacade.getSerial(cameraName);
HSSFCell serialCell = row.createCell(cellnum++);
serialCell.setCellValue(serialNumber);
//Put the generated image into the spreadsheet
File file = cameraToFile.get(cameraName);
HSSFCell imageCell = row.createCell(cellnum++);
try

View file

@ -61,10 +61,8 @@ public class ErrorLogging
//Make sure the filename formatter is compatible with Windows and Linux
fileDatetime = DateTimeFormatter.ofPattern("yyyy-MM-dd_HH.mm.ss");
//Local formatter for logs
datetime = DateTimeFormatter.ISO_LOCAL_DATE_TIME;
//Create a new log file on every run; put them all in a common folder
logFile = "logs/" + fileDatetime.format(LocalDateTime.now()) + "-log.txt";
File logDirectory = new File("logs");
File outFile = new File(logFile);

View file

@ -20,7 +20,7 @@ import com.pi4j.io.pwm.PwmType;
* Currently missing Run switch compatibility.
*
* @author Blizzard Finnegan
* @version 3.0.0, 06 Mar. 2023
* @version 3.0.1, 10 Mar. 2023
*/
public class MovementFacade
{
@ -44,7 +44,7 @@ public class MovementFacade
/**
* Amount of buffer between the found absolute speed, and used speed.
*/
private static final int SPEED_BUFFER = 5000;
private static final int SPEED_BUFFER = 7500;
/**
* Minimum allowed speed of the fixture arm; also used for reset travels.
@ -190,7 +190,7 @@ public class MovementFacade
static
{
//ErrorLogging.logError("DEBUG: Starting lock thread...");
ErrorLogging.logError("DEBUG: Starting lock thread...");
runSwitchThread = new Thread(() ->
{
boolean unlock = false;
@ -198,7 +198,7 @@ public class MovementFacade
{
if(runSwitch.isOn())
{
ErrorLogging.logError("Run switch turned off!");
ErrorLogging.logError("DEBUG: Run switch turned off!");
while(!Cli.LOCK.tryLock())
{}
unlock = true;
@ -214,24 +214,19 @@ public class MovementFacade
}, "Run switch monitor.");
runSwitchThread.start();
//Initialise Pi4J
pi4j = Pi4J.newAutoContext();
//Initialise input pins
upperLimit = inputBuilder("upperLimit", "Upper Limit Switch", UPPER_LIMIT_ADDR);
lowerLimit = inputBuilder("lowerLimit", "Lower Limit Switch", LOWER_LIMIT_ADDR);
runSwitch = inputBuilder("runSwitch" , "Run Switch" , RUN_SWITCH_ADDR);
//Initialise output pins
motorEnable = outputBuilder("motorEnable" , "Motor Enable" , MOTOR_ENABLE_ADDR);
motorDirection = outputBuilder("motorDirection", "Motor Direction", MOTOR_DIRECTION_ADDR);
pistonActivate = outputBuilder("piston" , "Piston Activate", PISTON_ADDR);
//Initialise PWM object.
pwm = pwmBuilder("pwm","PWM Pin",PWM_PIN_ADDR);
pwm.on(DUTY_CYCLE, FREQUENCY);
//Find Distance and max speeds
calibrate();
}
@ -267,7 +262,6 @@ public class MovementFacade
.frequency(FREQUENCY)
.provider("pigpio-pwm")
.initial(1)
//On program close, turn off PWM.
.shutdown(0);
break;
//Any pin not listed above must be software PWM controlled.
@ -280,7 +274,6 @@ public class MovementFacade
.frequency(FREQUENCY)
.provider("pigpio-pwm")
.initial(1)
//On program close, turn off PWM.
.shutdown(0);
}
return pi4j.create(configBuilder);
@ -299,7 +292,6 @@ public class MovementFacade
{
DigitalInputConfigBuilder configBuilder = DigitalInput.newConfigBuilder(pi4j)
.id(id)
//.name(name)
.address(address)
.pull(PullResistance.PULL_DOWN)
.debounce(3000L)
@ -320,7 +312,6 @@ public class MovementFacade
{
DigitalOutputConfigBuilder configBuilder = DigitalOutput.newConfigBuilder(pi4j)
.id(id)
.name(name)
.address(address)
.shutdown(DigitalState.LOW)
.initial(DigitalState.LOW)
@ -382,8 +373,6 @@ public class MovementFacade
resetArm();
ErrorLogging.logError("Calibrating...");
FREQUENCY = calib(MIN_FREQUENCY, MAX_FREQUENCY, 10000);
//ErrorLogging.logError("Fine calibrating...");
//FREQUENCY = calib(FREQUENCY,(FREQUENCY+10000),1000);
ErrorLogging.logError("Calibration complete!");
ErrorLogging.logError("DEBUG: Speed set to " + (FREQUENCY - SPEED_BUFFER));
setFrequency(FREQUENCY - SPEED_BUFFER);
@ -454,7 +443,6 @@ public class MovementFacade
*/
private static int calib(int start, int max, int iterate)
{
//start -= iterate;
for(int i = start; i < max; i+=iterate)
{
if(!setFrequency(i))

View file

@ -27,7 +27,6 @@ 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;
@ -37,11 +36,10 @@ import java.util.List;
* Performs image capture, as well as image manipulation.
*
* @author Blizzard Finnegan
* @version 2.1.0, 06 Mar. 2023
* @version 3.0.0, 10 Mar. 2023
*/
public class OpenCVFacade
{
//Local variable instantiation
/**
* Storage of all cameras as Map.
* To get available camera names, getKeys.
@ -51,7 +49,7 @@ public class OpenCVFacade
/**
* Object used to convert between Mats and Frames
*/
public static final OpenCVFrameConverter.ToMat MAT_CONVERTER = new OpenCVFrameConverter.ToMat();
private static final OpenCVFrameConverter.ToMat MAT_CONVERTER = new OpenCVFrameConverter.ToMat();
/**
* Width of the image created by the camera.
@ -129,7 +127,6 @@ public class OpenCVFacade
*/
private static void newCamera(String name, String location, int width, int height, String codec)
{
//ErrorLogging.logError("DEBUG: Attempting to create new camera " + name + " from location " + location + "...");
ErrorLogging.logError("Initialising camera : " + name + "...");
File cameraLocation = new File(location);
if (cameraLocation.exists())
@ -171,7 +168,7 @@ public class OpenCVFacade
* @return null if camera doesn't exist, or if capture fails;
* otherwise, Frame of the taken image
*/
public static Mat takePicture(String cameraName)
private static Mat takePicture(String cameraName)
{
Mat output = null;
Frame temp = null;
@ -199,12 +196,8 @@ public class OpenCVFacade
*/
public static File showImage(String cameraName)
{
//ErrorLogging.logError("DEBUG: Showing image from camera: " + cameraName);
//ErrorLogging.logError("DEBUG: camera location: " + cameraMap.get(cameraName).toString());
File imageLocation = completeProcess(cameraName,ConfigFacade.getImgSaveLocation() + "/config");
if(imageLocation == null) return null;
//ErrorLogging.logError("DEBUG: Image processed successfully.");
//ErrorLogging.logError("DEBUG: Image location: " + imageLocation.getAbsolutePath());
Frame outputImage = MAT_CONVERTER.convert(imread(imageLocation.getAbsolutePath()));
String canvasTitle = "Camera " + cameraName + " Preview";
final CanvasFrame canvas = new CanvasFrame(canvasTitle);
@ -212,20 +205,18 @@ public class OpenCVFacade
return imageLocation;
}
/**
* Show current processed image to the GUI user.
*
* @param cameraName The name of the camera to be previewed
*
* @return The {@link CanvasFrame} that is being opened. This is returned so it can be closed by the program.
*/
public static String showImage(String cameraName, Object object)
{
//ErrorLogging.logError("DEBUG: Showing image from camera: " + cameraName);
//ErrorLogging.logError("DEBUG: camera location: " + cameraMap.get(cameraName).toString());
File imageLocation = completeProcess(cameraName,ConfigFacade.getImgSaveLocation() + "/config");
return imageLocation.getPath();
}
///**
// * Show current processed image to the GUI user.
// *
// * @param cameraName The name of the camera to be previewed
// *
// * @return The {@link CanvasFrame} that is being opened. This is returned so it can be closed by the program.
// */
//private static String showImage(String cameraName, Object object)
//{
// File imageLocation = completeProcess(cameraName,ConfigFacade.getImgSaveLocation() + "/config");
// return imageLocation.getPath();
//}
/**
* Take multiple pictures in quick succession.
@ -235,11 +226,9 @@ public class OpenCVFacade
*
* @return List of Frames taken from the camera. List is in order
*/
public static List<Mat> takeBurst(String cameraName, int frameCount)
private static List<Mat> takeBurst(String cameraName, int frameCount)
{
List<Mat> output = null;
//ErrorLogging.logError("DEBUG: takeBurst - Camera Name: " + cameraName);
//ErrorLogging.logError("DEBUG: takeBurst - Possible camera names: " + getCameraNames().toString());
if(getCameraNames().contains(cameraName))
{
output = new LinkedList<>();
@ -272,7 +261,7 @@ public class OpenCVFacade
* @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)
private 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);
@ -290,15 +279,9 @@ public class OpenCVFacade
*
* @return Frame of the cropped image
*/
public static Mat crop(Mat image, Rect roi, String cameraName)
private static Mat crop(Mat image, Rect roi, String cameraName)
{
Mat output = image.apply(roi).clone();
//IplImage croppedImage = MAT_CONVERTER.convertToIplImage(MAT_CONVERTER.convert(output));
//String fileLocation = ConfigFacade.getImgSaveLocation() + "/debug/"
// + ErrorLogging.fileDatetime.format(LocalDateTime.now()) +
// "." + cameraName + "-preProcess.jpg";
//cvSaveImage(fileLocation,croppedImage);
return output;
}
@ -311,7 +294,7 @@ public class OpenCVFacade
*
* @return Frame of the thresholded image
*/
public static Mat thresholdImage(Mat image,String cameraName)
private static Mat thresholdImage(Mat image,String cameraName)
{
Mat output = image;
Mat in = image;
@ -328,7 +311,7 @@ public class OpenCVFacade
*
* @return File if save was successful, otherwise null
*/
public static File saveImage(Mat image, String fileLocation, String cameraName)
private static File saveImage(Mat image, String fileLocation, String cameraName)
{
File output = null;
IplImage temp = MAT_CONVERTER.convertToIplImage(MAT_CONVERTER.convert(image));
@ -350,64 +333,32 @@ public class OpenCVFacade
*
* @return A single image, found by boolean AND-ing together all parsed images.
*/
public static Mat compose(List<Mat> images, boolean threshold,
private static Mat compose(List<Mat> images, boolean threshold,
boolean crop, String cameraName)
{
ErrorLogging.logError("DEBUG: Attempting to compose " + images.size() + " images...");
Mat output = null;
int iterationCount = 1;
for(Mat image : images)
{ //crop and threshold, based on booleans
Mat processedImage = image.clone();
image.copyTo(processedImage);
if(crop)
{
//ErrorLogging.logError("DEBUG: Cropping image " + iterationCount + "...");
processedImage = crop(processedImage,cameraName);
//String fileLocation = ConfigFacade.getImgSaveLocation() + "/debug/"
// + ErrorLogging.fileDatetime.format(LocalDateTime.now()) +
// "." + iterationCount + "-post-crop.jpg";
//cvSaveImage(fileLocation,MAT_CONVERTER.convertToIplImage(
// MAT_CONVERTER.convert(processedImage)));
}
if(threshold)
{
//ErrorLogging.logError("DEBUG: Thresholding image " + iterationCount + "...");
processedImage = thresholdImage(processedImage,cameraName);
//String fileLocation = ConfigFacade.getImgSaveLocation() + "/debug/"
// + ErrorLogging.fileDatetime.format(LocalDateTime.now()) +
// "." + iterationCount + "-post-threshold.jpg";
//cvSaveImage(fileLocation,MAT_CONVERTER.convertToIplImage(
// MAT_CONVERTER.convert(processedImage)));
}
//String fileLocation = ConfigFacade.getImgSaveLocation() + "/debug/"
// + ErrorLogging.fileDatetime.format(LocalDateTime.now()) +
// "." + iterationCount + "-pre.compose.jpg";
//cvSaveImage(fileLocation,MAT_CONVERTER.convertToIplImage(
// MAT_CONVERTER.convert(processedImage)));
//ErrorLogging.logError("DEBUG: Post-threshold/crop image: " + fileLocation);
//ErrorLogging.logError("DEBUG: Image " + iterationCount + " complete!");
//ErrorLogging.logError("DEBUG: -----------------");
//ErrorLogging.logError("DEBUG: Post-threshold/crop width: " + processedImage.cols());
{
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(iterationCount == 1)
{
output = processedImage.clone();
}
//ErrorLogging.logError("DEBUG: Thresholding image " + iterationCount + "...");
bitwise_and((iterationCount == 1 ? processedImage : output),processedImage, output);
iterationCount++;
}
if(output != null)
ErrorLogging.logError("DEBUG: Compositing successful!");
else
ErrorLogging.logError("ERROR: Final output image is null!");
if(output != null) ErrorLogging.logError("DEBUG: Compositing successful!");
else ErrorLogging.logError("ERROR: Final output image is null!");
return output;
}
/**TODO: More robust file output checking;
/**
* Processes image from defined camera, using the config defaults.
*
*
@ -429,18 +380,8 @@ public class OpenCVFacade
ErrorLogging.logError("OPENCV ERROR!!! - Invalid camera name.");
return output;
}
//ErrorLogging.logError("DEBUG: Camera to take picture from: " + cameraName);
//ErrorLogging.logError("DEBUG: Composite frame count: " + compositeFrames);
List<Mat> imageList = takeBurst(cameraName, compositeFrames);
//Debug save of pre-processing image
//String fileLocation = ConfigFacade.getImgSaveLocation() + "/debug/"
// + ErrorLogging.fileDatetime.format(LocalDateTime.now()) +
// "." + cameraName + "-preProcess.jpg";
//cvSaveImage(fileLocation,MAT_CONVERTER.convertToIplImage(
// MAT_CONVERTER.convert(imageList.get(0))));
ErrorLogging.logError("DEBUG: Size of output image list: " + imageList.size());
Mat finalImage = compose(imageList, threshold, crop, cameraName);
output = saveImage(finalImage, saveLocation,cameraName);
return output;
@ -458,21 +399,21 @@ public class OpenCVFacade
public static File completeProcess(String cameraName, String saveLocation)
{
File output = null;
if(!getCameraNames().contains(cameraName))
{
ErrorLogging.logError("OPENCV ERROR!!! - Invalid camera name.");
return output;
}
int compositeFrames = (int)ConfigFacade.getValue(cameraName,ConfigProperties.COMPOSITE_FRAMES);
//boolean threshold = false;
boolean threshold = (ConfigFacade.getValue(cameraName,ConfigProperties.THRESHOLD) != 0.0);
//ErrorLogging.logError("DEBUG: Threshold config value: " + threshold);
//boolean crop = false;
boolean crop = (ConfigFacade.getValue(cameraName,ConfigProperties.CROP) != 0.0);
//ErrorLogging.logError("DEBUG: Crop config value: " + crop);
boolean threshold = (ConfigFacade.getValue(cameraName,ConfigProperties.THRESHOLD) != 0.0);
boolean crop = (ConfigFacade.getValue(cameraName,ConfigProperties.CROP) != 0.0);
output = completeProcess(cameraName,crop,threshold,compositeFrames,saveLocation);
if(output == null)
ErrorLogging.logError("OPENCV ERROR!!!: Final processed image is null!");
if(output == null) ErrorLogging.logError("OPENCV ERROR!!!: Final processed image is null!");
return output;
}
@ -485,23 +426,5 @@ public class OpenCVFacade
* @return null if any error occurs; otherwise File of output image
*/
public static File completeProcess(String cameraName)
{
return completeProcess(cameraName,ConfigFacade.getImgSaveLocation());
}
/**
* Collect images from all cameras and save them, using the config defaults.
*
* @return List of Files, as defined by {@code #completeProcess(String, String)}
*/
public static List<File> singleIteration()
{
List<File> output = new ArrayList<>();
for(String cameraName : getCameraNames())
{
output.add(completeProcess(cameraName, ConfigFacade.getImgSaveLocation()));
ErrorLogging.logError("DEBUG: ---------------------------------");
}
return output;
}
{ return completeProcess(cameraName,ConfigFacade.getImgSaveLocation()); }
}

View file

@ -69,23 +69,16 @@ public class TesseractFacade
*/
public static double imageToDouble(File file)
{
//Set default output
double output = Double.NEGATIVE_INFINITY;
//Import image, parse image
PIX importedImage = pixRead(file.getAbsolutePath());
api.SetImage(importedImage);
String stringOutput = api.GetUTF8Text().getString();
//Determine whether the OCR output is actually a double
if(!stringOutput.isEmpty())
{
try(Scanner sc = new Scanner(stringOutput.trim());)
try( Scanner sc = new Scanner(stringOutput.trim()); )
{
/*
* Discos have error messages (LO, HI, POS, ?). Consider parsing as well.
* Update on above note: Requires retraining Tesseract
*/
if(sc.hasNextDouble())
{
output = sc.nextDouble();
@ -98,19 +91,14 @@ public class TesseractFacade
output = output / 10;
ErrorLogging.logError("OCR output saved, as value appears to be real. Value needs to be verified.");
}
else if(output >= 200)
ErrorLogging.logError("OCR WARNING - OCR output is too high for DUT, potential misread.");
else
ErrorLogging.logError("OCR output successfully adjusted. Disregard warning.");
else if(output >= 200) ErrorLogging.logError("OCR WARNING - OCR output is too high for DUT, potential misread.");
else ErrorLogging.logError("OCR output successfully adjusted. Disregard warning.");
}
if(output <= -10)
ErrorLogging.logError("OCR ERROR!!! - OCR output is too low for DUT, potential misread.");
if(output <= -10) ErrorLogging.logError("OCR ERROR!!! - OCR output is too low for DUT, potential misread.");
}
else ErrorLogging.logError("OCR ERROR!!! - OCR output is not a Double.");
}
}
//Return output
return output;
}
}