From c1bb771cff3ec628456a6769570b8a26ba8d7c72 Mon Sep 17 00:00:00 2001 From: Jamie Mac Date: Thu, 13 Jun 2024 08:56:25 +0100 Subject: [PATCH 01/10] Working on the exporter --- .../menuOptions/RExportOverlayMenu.java | 2 +- src/export/CSVExport/CSVExportManager.java | 9 + src/export/MLExport/MLDetectionsManager.java | 6 + src/export/PamDataUnitExporter.java | 12 +- src/export/PamExporterManager.java | 21 ++- src/export/RExport/RExportManager.java | 28 ++- src/export/swing/ExportProcessDialog.java | 176 +++++++++++++++++- src/export/swing/ExportTask.java | 42 +++-- .../wavExport/WavFileExportManager.java | 7 + src/offlineProcessing/OfflineTaskGroup.java | 7 +- .../WhistleBinaryDataSource.java | 8 +- 11 files changed, 271 insertions(+), 47 deletions(-) diff --git a/src/dataPlotsFX/overlaymark/menuOptions/RExportOverlayMenu.java b/src/dataPlotsFX/overlaymark/menuOptions/RExportOverlayMenu.java index fd6f6a0f..53083c39 100644 --- a/src/dataPlotsFX/overlaymark/menuOptions/RExportOverlayMenu.java +++ b/src/dataPlotsFX/overlaymark/menuOptions/RExportOverlayMenu.java @@ -44,7 +44,7 @@ public class RExportOverlayMenu extends ExportOverlayMenu { * Create the export overlay */ public RExportOverlayMenu(){ - rExportManger= new RExportManager(); + rExportManger= new RExportManager(null); buttonNode = createButton(); diff --git a/src/export/CSVExport/CSVExportManager.java b/src/export/CSVExport/CSVExportManager.java index bf011e30..95e8520d 100644 --- a/src/export/CSVExport/CSVExportManager.java +++ b/src/export/CSVExport/CSVExportManager.java @@ -6,6 +6,10 @@ import java.util.List; import PamguardMVC.PamDataUnit; import export.PamDataUnitExporter; + +/** + * Export to CSV files which are RAVEN compatible. + */ public class CSVExportManager implements PamDataUnitExporter{ @Override @@ -41,4 +45,9 @@ public class CSVExportManager implements PamDataUnitExporter{ } + @Override + public boolean isNeedsNewFile() { + return false; + } + } diff --git a/src/export/MLExport/MLDetectionsManager.java b/src/export/MLExport/MLDetectionsManager.java index fe05cd39..3cdb1a9e 100644 --- a/src/export/MLExport/MLDetectionsManager.java +++ b/src/export/MLExport/MLDetectionsManager.java @@ -269,6 +269,12 @@ public class MLDetectionsManager implements PamDataUnitExporter { } + @Override + public boolean isNeedsNewFile() { + // TODO Auto-generated method stub + return false; + } + diff --git a/src/export/PamDataUnitExporter.java b/src/export/PamDataUnitExporter.java index 8ab6fa56..01e4e163 100644 --- a/src/export/PamDataUnitExporter.java +++ b/src/export/PamDataUnitExporter.java @@ -41,15 +41,21 @@ public interface PamDataUnitExporter { public String getIconString(); /** - * Get the name of the exporter - * @return + * Get the name of the exporter. + * @return the name of the exporter. */ public String getName(); /** - * Clsoe the exporter + * Close the exporter. */ public void close(); + /** + * Check whether and exporter needs a new file + * @return true if we need a new file. + */ + public boolean isNeedsNewFile(); + } diff --git a/src/export/PamExporterManager.java b/src/export/PamExporterManager.java index 61631f1a..5d21daac 100644 --- a/src/export/PamExporterManager.java +++ b/src/export/PamExporterManager.java @@ -56,11 +56,12 @@ public class PamExporterManager { "yyyy_MM_dd_HHmmss"); public PamExporterManager() { + pamExporters = new ArrayList(); //add the MATLAB export pamExporters.add(new MLDetectionsManager()); - pamExporters.add(new RExportManager()); + pamExporters.add(new RExportManager(this)); pamExporters.add(new WavFileExportManager()); pamExporters.add(new CSVExportManager()); } @@ -74,7 +75,7 @@ public class PamExporterManager { if (dataUnit==null) { if (force) { - System.out.println("Write data 1!!" + dataUnitBuffer.size() ); +// System.out.println("Write data 1!!" + dataUnitBuffer.size() ); //finish off saving any buffered data exportOK = pamExporters.get(exportParams.exportChoice).exportData(currentFile, dataUnitBuffer, true); dataUnitBuffer.clear(); @@ -83,10 +84,10 @@ public class PamExporterManager { } //if file is null or too large create another a file for saving. - if (currentFile == null || isFileSizeMax(currentFile)) { + if (currentFile == null || isNeedsNewFile(currentFile, pamExporters.get(exportParams.exportChoice))) { Date date = new Date(dataUnit.getTimeMilliseconds()); - String newFileName = "PAM_" + dataFormat.format(date); + String newFileName = "PAM_" + dataFormat.format(date) + dataUnit.getParentDataBlock().getDataName().replace(" ", "_"); //create a new file - note each exporter is responsible for closing the file after writing //so previous files should already be closed @@ -98,10 +99,10 @@ public class PamExporterManager { dataUnitBuffer.add(dataUnit); - System.out.println("Write data unit " + dataUnitBuffer.size() + " to: "+ currentFile); +// System.out.println("Write data unit " + dataUnitBuffer.size() + " to: "+ currentFile); if (dataUnitBuffer.size()>=BUFFER_SIZE || force) { - System.out.println("Write data 2!!" + dataUnitBuffer.size()); +// System.out.println("Write data 2!!" + dataUnitBuffer.size()); exportOK = pamExporters.get(exportParams.exportChoice).exportData(currentFile, dataUnitBuffer, true); dataUnitBuffer.clear(); } @@ -118,10 +119,14 @@ public class PamExporterManager { /** * Check whether the current file is greater than the maximum allowed file size. * @param currentFile2 - the current file + * @param pamDataUnitExporter * @return true of greater than or equal to the maximum file size. */ - private boolean isFileSizeMax(File currentFile2) { - return getFileSizeMegaBytes(currentFile2) >= MAX_FILE_SIZE_MB; + private boolean isNeedsNewFile(File currentFile2, PamDataUnitExporter pamDataUnitExporter) { + if( getFileSizeMegaBytes(currentFile2) >= exportParams.maximumFileSize) { + return true; + }; + return pamDataUnitExporter.isNeedsNewFile(); } /** diff --git a/src/export/RExport/RExportManager.java b/src/export/RExport/RExportManager.java index a6cf1add..826e96ae 100644 --- a/src/export/RExport/RExportManager.java +++ b/src/export/RExport/RExportManager.java @@ -18,6 +18,7 @@ import org.renjin.sexp.PairList.Builder; import PamUtils.PamArrayUtils; import PamguardMVC.PamDataUnit; import export.PamDataUnitExporter; +import export.PamExporterManager; import export.MLExport.MLDetectionsManager; /** @@ -29,7 +30,6 @@ import export.MLExport.MLDetectionsManager; public class RExportManager implements PamDataUnitExporter { /** - * * All the possible RDataUnit export classes. */ ArrayList rDataExport = new ArrayList(); @@ -37,10 +37,13 @@ public class RExportManager implements PamDataUnitExporter { private File currentFileName ; - private Builder allData; + private Builder allData; + + private PamExporterManager pamExporterManager; - public RExportManager(){ + public RExportManager(PamExporterManager pamExporterManager){ + this.pamExporterManager=pamExporterManager; /***Add more options here to export data units****/ rDataExport.add(new RClickExport()); rDataExport.add(new RWhistleExport()); @@ -55,17 +58,14 @@ public class RExportManager implements PamDataUnitExporter { * Note - there is no way to save data units to R files wothout loading the file into memory. * So everything is stored in memory until saved. */ - // then PamDataUnit minByTime = PamArrayUtils.getMinTimeMillis(dataUnits); - //matlab struct must start with a letter. + //MATLAB struct must start with a letter. Date date = new Date(minByTime.getTimeMilliseconds()); String entryName = "det_" + MLDetectionsManager.dataFormat.format( date); // System.out.println("Save R data! "+ dataUnits.size()); - // System.out.println("Export R file!!" + dataUnits.size()); - //is there an existing writer? Is that writer writing to the correct file? if (allData==null || !fileName.equals(currentFileName)) { @@ -259,5 +259,19 @@ public class RExportManager implements PamDataUnitExporter { } + @Override + public boolean isNeedsNewFile() { + //Rdata can't be appended to a file so we cannot check file sizes. + +// pamExporterManager.getExportParams().maximumFileSize; + + //TODO + //check file size against the export params. + System.out.println("RData length: " + allData.length()); + + return false; + } + + } diff --git a/src/export/swing/ExportProcessDialog.java b/src/export/swing/ExportProcessDialog.java index fd62ec6e..08dff701 100644 --- a/src/export/swing/ExportProcessDialog.java +++ b/src/export/swing/ExportProcessDialog.java @@ -9,7 +9,6 @@ import java.awt.Window; import java.awt.event.ActionListener; import java.io.File; import java.util.ArrayList; - import javax.swing.BoxLayout; import javax.swing.ButtonGroup; import javax.swing.JFileChooser; @@ -38,7 +37,6 @@ import export.PamExporterManager; import export.layoutFX.ExportParams; import offlineProcessing.OLProcessDialog; import offlineProcessing.OfflineTaskGroup; -import offlineProcessing.TaskStatus; /** * Handles an offline dialog for processing offline data and exporting to bespoke file types. @@ -87,7 +85,7 @@ public class ExportProcessDialog { dlOfflineGroup.addTask(new ExportTask(dataBlocks.get(i), exportManager)); } } - + } ////---Swing stuff----/// should not be here but this is how PG works. @@ -129,6 +127,9 @@ public class ExportProcessDialog { */ private JFileChooser fc; + /** + * S hows the folder stuff is going to export to. + */ private JTextField exportTo; /** @@ -268,7 +269,7 @@ public class ExportProcessDialog { Ikon icon = null; /** - * This is nasty but we won't have many exporters and this is the only + * This is NASTY but we won't have many exporters and this is the only * good way to get this to work in Swing. */ switch (iconString) { @@ -353,11 +354,10 @@ public class ExportProcessDialog { } - class ExportTaskGroup extends OfflineTaskGroup{ + class ExportTaskGroup extends OfflineTaskGroup { public ExportTaskGroup(String settingsName) { super(null, settingsName); - // TODO Auto-generated constructor stub } @@ -365,6 +365,170 @@ public class ExportProcessDialog { public String getUnitType() { return "Export Data"; } + + + /** + * Override the tasks o it runs through all tasks for each datablock. Usually + * task groups deal with just one parent datablock but exporters export from + * different data blocks. The only way to deal with this is to let the task run + * again and again through all tasks and letting tasks themselves check the + * correct data units are being exported. + */ + @Override + public boolean runTasks() { + boolean OK = true; + for (int i=0; i it = primaryDataBlock.getListIterator(0); +// long procDataStart = Long.MAX_VALUE; +// long procDataEnd = 0; +// int nToProcess = 0; +// while (it.hasNext()) { +// dataUnit = it.next(); +// /** +// * Make sure we only process data units within the current time interval. +// */ +// if (dataUnit.getTimeMilliseconds() < processStartTime) { +// continue; +// } +// if (dataUnit.getTimeMilliseconds() > processEndTime) { +// break; +// } +//// if (shouldProcess(dataUnit) == false) { +//// continue; +//// } +// procDataStart = Math.min(procDataStart, dataUnit.getTimeMilliseconds()); +// procDataEnd = Math.max(procDataEnd, dataUnit.getEndTimeInMilliseconds()); +// // do this one too - just to make sure in case end time returns zero. +// procDataEnd = Math.max(procDataEnd, dataUnit.getTimeMilliseconds()); +// nToProcess++; // increase toprocess counter +// } +// if (nToProcess == 0) { +// return; +// } +// PamDataBlock aDataBlock; +// RequiredDataBlockInfo blockInfo; +// /* +// * if the data interval is < 1 hour, then load it all now +// * otherwise we'll do it on a data unit basis. +// */ +////// long maxSecondaryLoad = 1800L*1000L; +////// if (procDataEnd - procDataStart < maxSecondaryLoad) { +//// loadSecondaryData(procDataStart, procDataEnd); +////// } +// // remember the end time of the data so we can use the "new data" selection flag. +// taskGroupParams.lastDataTime = Math.min(primaryDataBlock.getCurrentViewDataEnd(),processEndTime); +// // synchronized(primaryDataBlock) { +// /* +// * Call newDataLoaded for each task before getting on with processing individual data units. +// */ +// +// /** +// * Now process the data +// */ +// it = primaryDataBlock.getListIterator(0); +// unitChanged = false; +// int totalUnits = 0; +// int unitsChanged = 0; +// boolean doTasks = false; +// while (it.hasNext()) { +// dataUnit = it.next(); +// totalUnits++; +// doTasks = true; +// /** +// * Make sure we only process data units within the current time interval. +// */ +// if (dataUnit.getTimeMilliseconds() < processStartTime) { +// continue; +// } +// if (dataUnit.getTimeMilliseconds() > processEndTime) { +// break; +// } +// +// if (shouldProcess(dataUnit) == false) { +// doTasks = false; +// } +// +// if (doTasks) { +// /* +// * load the secondary datablock data. this can be called even if +// * it was called earlier on since it wont' reload if data are already +// * in memory. +// */ +//// loadSecondaryData(dataUnit.getTimeMilliseconds(), dataUnit.getEndTimeInMilliseconds()); +// +// for (int iTask = 0; iTask < nTasks; iTask++) { +// aTask = getTask(iTask); +// if (aTask.isDoRun() == false || !isInTimeChunk(dataUnit, taskGroupParams.timeChunks)) { +// continue; +// } +// cpuMonitor.start(); +// unitChanged |= aTask.processDataUnit(dataUnit); +// cpuMonitor.stop(); +// } +// if (unitChanged) { +// fileInfo = dataUnit.getDataUnitFileInformation(); +// if (fileInfo != null) { +// fileInfo.setNeedsUpdate(true); +// } +// dataUnit.updateDataUnit(System.currentTimeMillis()); +// } +// dataUnit.freeData(); +// } +// if (instantKill) { +// break; +// } +// unitsChanged++; +// if (totalUnits%nSay == 0) { +// publish(new TaskMonitorData(TaskStatus.RUNNING, TaskActivity.PROCESSING, nToProcess, totalUnits, dataName, +// dataUnit.getTimeMilliseconds())); +// } +// } +// for (int iTask = 0; iTask < nTasks; iTask++) { +// aTask = getTask(iTask); +// if (aTask.isDoRun() == false) { +// continue; +// } +// aTask.loadedDataComplete(); +// } +// // } +// publish(new TaskMonitorData(TaskStatus.RUNNING, TaskActivity.SAVING, nToProcess, totalUnits, dataName, +// processEndTime)); +// for (int i = 0; i < affectedDataBlocks.size(); i++) { +// //System.out.println("SAVE VIEWER DATA FOR: " + affectedDataBlocks.get(i) ); +// aDataBlock = affectedDataBlocks.get(i); +// aDataBlock.saveViewerData(); +// } +// Debug.out.printf("Processd %d out of %d data units at " + mapPoint + "\n", unitsChanged, totalUnits); +// commitDatabase(); +// } } diff --git a/src/export/swing/ExportTask.java b/src/export/swing/ExportTask.java index 665d9ded..1342e729 100644 --- a/src/export/swing/ExportTask.java +++ b/src/export/swing/ExportTask.java @@ -16,12 +16,12 @@ import offlineProcessing.OfflineTask; * */ public class ExportTask extends OfflineTask>{ - + /** * Reference to the data exporter which manages exporting of data. */ private PamExporterManager exporter; - + /** * The data selector for the data block */ @@ -32,8 +32,8 @@ public class ExportTask extends OfflineTask>{ public ExportTask(PamDataBlock> parentDataBlock, PamExporterManager exporter) { super(parentDataBlock); this.exporter = exporter; - dataSelector=parentDataBlock.getDataSelectCreator().getDataSelector(this.getUnitName() +"_clicks", false, null); - + dataSelector=parentDataBlock.getDataSelectCreator().getDataSelector(this.getUnitName() +"_export", false, null); + } @@ -44,9 +44,15 @@ public class ExportTask extends OfflineTask>{ @Override public boolean processDataUnit(PamDataUnit dataUnit) { - if (dataSelector==null) exporter.exportDataUnit(dataUnit, false); - else if (dataSelector.scoreData(dataUnit)>0) { - exporter.exportDataUnit(dataUnit, false); + + System.out.println("Huh? " + this.getDataBlock().getDataName() + " " + dataUnit + " " + dataUnit.getParentDataBlock().equals(this.getDataBlock())); + if (dataUnit.getParentDataBlock().equals(this.getDataBlock())) { + //this is very important because the way the expoeter works is that it iterates through multiple parent data blocks + System.out.println(this.getDataBlock().getDataName() + " " + dataUnit); + if (dataSelector==null) exporter.exportDataUnit(dataUnit, false); + else if (dataSelector.scoreData(dataUnit)>0) { + exporter.exportDataUnit(dataUnit, false); + } } return false; //we don't need to indicate that anything has changed - we are just exporting. } @@ -54,18 +60,18 @@ public class ExportTask extends OfflineTask>{ @Override public void newDataLoad(long startTime, long endTime, OfflineDataMapPoint mapPoint) { // TODO Auto-generated method stub -// System.out.println("EXPORTER: new data load"); + // System.out.println("EXPORTER: new data load"); + } @Override public void loadedDataComplete() { - System.out.println("EXPORTER: loaded data complete"); - + // System.out.println("EXPORTER: loaded data complete"); //force the exporter so save any renaming data units in the buffer exporter.exportDataUnit(null, true); exporter.close(); exporter.setCurrentFile(null); - + } /** * task has settings which can be called @@ -80,13 +86,13 @@ public class ExportTask extends OfflineTask>{ * @return true if settings may have changed. */ public boolean callSettings() { - + dataSelector.getDialogPanel().setParams(); - + DataSelectDialog dataSelectDialog = new DataSelectDialog(PamController.getMainFrame(), this.getDataBlock(), dataSelector, null); return dataSelectDialog.showDialog(); - + } /** @@ -96,17 +102,17 @@ public class ExportTask extends OfflineTask>{ public boolean canExport(int exportSelection) { return exporter.getExporter(exportSelection).hasCompatibleUnits(getDataBlock().getUnitClass()); } - - + + @Override public boolean canRun() { boolean can = getDataBlock() != null; - + if (can) { //check whether we can export based on the export selection can = canExport(exporter.getExportParams().exportChoice); } - + return can; } diff --git a/src/export/wavExport/WavFileExportManager.java b/src/export/wavExport/WavFileExportManager.java index a765a0c7..67ffcc48 100644 --- a/src/export/wavExport/WavFileExportManager.java +++ b/src/export/wavExport/WavFileExportManager.java @@ -465,6 +465,13 @@ public class WavFileExportManager implements PamDataUnitExporter { + @Override + public boolean isNeedsNewFile() { + return false; + } + + + // hello(){ diff --git a/src/offlineProcessing/OfflineTaskGroup.java b/src/offlineProcessing/OfflineTaskGroup.java index 40c37c66..e3c8f4ca 100644 --- a/src/offlineProcessing/OfflineTaskGroup.java +++ b/src/offlineProcessing/OfflineTaskGroup.java @@ -142,7 +142,7 @@ public class OfflineTaskGroup implements PamSettings { private ArrayList offlineTasks = new ArrayList(); - private TaskGroupWorker worker; + protected TaskGroupWorker worker; private OfflineSuperDetFilter superDetectionFilter; @@ -377,7 +377,7 @@ public class OfflineTaskGroup implements PamSettings { * @author Doug Gillespie * */ - class TaskGroupWorker extends SwingWorker implements ViewLoadObserver { + public class TaskGroupWorker extends SwingWorker implements ViewLoadObserver { volatile boolean instantKill = false; @@ -437,6 +437,7 @@ public class OfflineTaskGroup implements PamSettings { /** * Process all data for a list of time chunks. This is robust to the list * not being in chronological order. + * * @param timeChunks - the time chunks. */ private void processAllData(ArrayList timeChunks){ @@ -976,4 +977,6 @@ public class OfflineTaskGroup implements PamSettings { offlineTasks.clear(); } + + } diff --git a/src/whistlesAndMoans/WhistleBinaryDataSource.java b/src/whistlesAndMoans/WhistleBinaryDataSource.java index eaa47a1a..bfe24f24 100644 --- a/src/whistlesAndMoans/WhistleBinaryDataSource.java +++ b/src/whistlesAndMoans/WhistleBinaryDataSource.java @@ -151,11 +151,15 @@ public class WhistleBinaryDataSource extends BinaryDataSource { /** * Bit of mess sorted out on 15/5/2020. Was working because module version went from 1 to 2 at same time - * as file version went from 3 to 4. May have been some middly stuff where file version and module + * as file version went from 3 to 4. May have been some middle stuff where file version and module * There is some FV 3 with MV 1, in which case data were probably duplicated. */ if (fileVersion > 3) { // basic data now in standard format. - firstSliceSample = startSample = binaryObjectData.getDataUnitBaseData().getStartSample(); + if (binaryObjectData.getDataUnitBaseData().getStartSample()==null) { + //some very rare circumstances + firstSliceSample =0; + } + else firstSliceSample = startSample = binaryObjectData.getDataUnitBaseData().getStartSample(); // if the DataUnitBaseData contains a sequence map, use it in place of the channel map if (binaryObjectData.getDataUnitBaseData().getSequenceBitmap()!=null) { From a0399fce84b0dca7e67f1d262c1028c156f6e4f6 Mon Sep 17 00:00:00 2001 From: Jamie Mac Date: Thu, 13 Jun 2024 16:55:47 +0100 Subject: [PATCH 02/10] Working out on exporter --- readme.md | 22 +- src/export/MLExport/MLDetectionsManager.java | 9 +- src/export/MLExport/MLWhistleMoanExport.java | 2 +- src/export/PamExporterManager.java | 8 +- src/export/swing/ExportProcessDialog.java | 219 ++++++------------- src/export/swing/ExportTask.java | 18 +- 6 files changed, 94 insertions(+), 184 deletions(-) diff --git a/readme.md b/readme.md index 721b10db..10d133e1 100644 --- a/readme.md +++ b/readme.md @@ -1,14 +1,15 @@ # PAMGuard + +## Introduction PAMGuard is a bioacoustics analysis program designed for use in real time research contexts and for the processing of large datasets. PAMGuard provides users access to a suite of state-of-the-art auotmated analysis algorithms alongside displays for visualisation data and a comprehensive data management systems. -# Why do we need PAMGuard? +## Why do we need PAMGuard? PAMGuard fufills two main requirements within marine bioacoustics -1) **Real time operation** - Almost all PAMGuard features and modules work in real time - this allows scientists and industry to detect, classify and loclaise animals in real time on a standard consumer laptop, enabling mitigations and research survey without expensive bespoke software solutions and the transparncy of open source software. +1) **Real time operation**: Almost all PAMGuard features and modules work in real time - this allows scientists and industry to detect, classify and loclaise animals in real time on a standard consumer laptop, enabling mitigations and research survey without expensive bespoke software solutions and the transparncy of open source software. -2) **Processing and visuslisation of large datasets** - +2) **Processing and visuslisation of large datasets**: Off-the-shelf autonomous PAM devices, large scale surveys involving drifters, towed arrays and bottom mounted devices and real time continuous monitoring system all generate huge volumes of data whcih requires automated analysis approaches. PAMGuard allows the processing of very large passive acoustic datasets using automated algorothms and provides visualisation tools for a manual analyst to check the results. - ## Installation PAMGuard is available on Windows and can be downloaded from the [PAMGuard website](www.pamguard.org). Note that we are considering MacOS installers but they are not available at this time. @@ -20,7 +21,6 @@ Upon opening PAMGuard for the first time you are greeted with a blank screen. Yo Make sure to add the database and binary file storage modules **_File->Add Modules->Utilities->..._**) to save data then press the run button (red button) and data will process. PAMGuard can handle huge datasets so runing might take hours or even days. Progress is shown on the bottom of the screen. ## Features - ### Hardware integration PAMGuard connects with hardware such as various GPS and AIS systems and a multitude of different sound cards (e.g. [National Instruments](www.ni.com) devices, [SAIL DAQ cards](www.smruconsulting.com/contact-us), almost all ASIO sound cards and standard computer sound cards) for real time data collection and processing. PAMGuard also works with some very bespoke hardware such as [DIFAR Sonobuoys](); @@ -36,24 +36,26 @@ PAMGuard is designed to collect/process data from large acosutic datasets. PAMGu ### Access to detection and classification algorithms PAMGuard allows users to inegrate automated detection and classification algorithms directly into their acosutic workflow. There are a multitude of differwent algorothms to choose from, including a basic click detector, whislte and moan detector, GPL detector, click train detectors and many others. The idea behind PAMGuard is allow researchers to access open source state-of-the-art algorithms devleoped within the scientific community - if you want to contribute and get your algorithm into PAMGuard get in touch. -###Localisation +### Localisation PAMGuard has a mutltude of different options for acoustic loclaisation. There's a comprehesnive beam forming module for beam forming arrays, a large aperture localiser for 3D loclaisation and target motion analysis for towed hydrophone arrays. -###Soundscape analysis +### Soundscape analysis PAMGuard has a noise band (which supports third octave noise bands) and long term spectral average module for soundscape analysis. ### GIS Almsot all detection data can be visualised on a map. PAMGaurd also supports plotting GPS and AIS data. ### Suite of data visualisation tools -An important aspect of PAMGuard is the ability for users to explore porcessed data. This is +An important aspect of PAMGuard is the ability for users to explore porcessed data. PAMGuard allows users to visualise data at multiple different times scales, from inspecting individual waveforms microseconds long to datagrams showing detector output or soundscape metrics over days, weeks or even years. ### Advanced manual annotation -The displays within PAMGuard support a variety of manual annottion tools. A simple spectrogram +The displays within PAMGuard support a variety of manual annottion tools. ### Deep learning integration +PAMGuard allows users to run their own deep learning models using the deep learning module. AI can therfore be integrated into PAMGuard workflows, allowing for much more efficient analysis of data. -### Meatadata standard and Tethys compatibility +### Metadata standard and Tethys compatibility +PAMGuard Integrates with Tethys ## Feature roadmap There's lots of features we would like to add to PAMGuard. If you want to add a feature you can either code it up yourself in Java and submit a pull request or get in touch with us to discuss how to it might be integrated. Some smaller features might be in our roadmap anyway but larger features usually require funding. Some features we are thinking about (but do not necassarily have time for yet) are; diff --git a/src/export/MLExport/MLDetectionsManager.java b/src/export/MLExport/MLDetectionsManager.java index 3cdb1a9e..eb6a1fbc 100644 --- a/src/export/MLExport/MLDetectionsManager.java +++ b/src/export/MLExport/MLDetectionsManager.java @@ -4,14 +4,11 @@ import java.io.File; import java.io.IOException; import java.text.SimpleDateFormat; import java.util.ArrayList; -import java.util.Comparator; import java.util.Date; import java.util.List; -import java.util.NoSuchElementException; import java.util.zip.Deflater; import PamUtils.PamArrayUtils; -import PamUtils.PamCalendar; import PamguardMVC.PamDataUnit; import export.PamDataUnitExporter; import us.hebi.matlab.mat.format.Mat5; @@ -21,7 +18,6 @@ import us.hebi.matlab.mat.types.Matrix; import us.hebi.matlab.mat.types.Sink; import us.hebi.matlab.mat.types.Sinks; import us.hebi.matlab.mat.types.Struct; -import us.hebi.matlab.mat.util.Casts; /** @@ -106,10 +102,11 @@ public class MLDetectionsManager implements PamDataUnitExporter { matFile.writeTo(sink); - matFile.close(); +// matFile.close(); //CAUSES AN EXCEPTION IF WRITING AGAIN } else { + //write to the mat file without loading all contents into memory. Mat5Writer writer = Mat5.newWriter(sink); @@ -257,7 +254,7 @@ public class MLDetectionsManager implements PamDataUnitExporter { @Override public void close() { - //handled in the mian funtion + //handled in the main function if (sink!=null) { try { sink.close(); diff --git a/src/export/MLExport/MLWhistleMoanExport.java b/src/export/MLExport/MLWhistleMoanExport.java index 124628ca..cdaba823 100644 --- a/src/export/MLExport/MLWhistleMoanExport.java +++ b/src/export/MLExport/MLWhistleMoanExport.java @@ -82,7 +82,7 @@ public class MLWhistleMoanExport extends MLDataUnitExport dataUnit, boolean force) { boolean exportOK = true; + System.out.println("Add data unit " + dataUnit + " to: "+ currentFile); + if (dataUnit==null) { if (force) { -// System.out.println("Write data 1!!" + dataUnitBuffer.size() ); + System.out.println("Write data 1!!" + dataUnitBuffer.size() ); //finish off saving any buffered data exportOK = pamExporters.get(exportParams.exportChoice).exportData(currentFile, dataUnitBuffer, true); dataUnitBuffer.clear(); @@ -87,7 +89,7 @@ public class PamExporterManager { if (currentFile == null || isNeedsNewFile(currentFile, pamExporters.get(exportParams.exportChoice))) { Date date = new Date(dataUnit.getTimeMilliseconds()); - String newFileName = "PAM_" + dataFormat.format(date) + dataUnit.getParentDataBlock().getDataName().replace(" ", "_"); + String newFileName = "PAM_" + dataFormat.format(date) + "_" + dataUnit.getParentDataBlock().getDataName().replace(" ", "_"); //create a new file - note each exporter is responsible for closing the file after writing //so previous files should already be closed @@ -99,7 +101,7 @@ public class PamExporterManager { dataUnitBuffer.add(dataUnit); -// System.out.println("Write data unit " + dataUnitBuffer.size() + " to: "+ currentFile); + System.out.println("Write data unit " + dataUnitBuffer.size() + " to: "+ currentFile); if (dataUnitBuffer.size()>=BUFFER_SIZE || force) { // System.out.println("Write data 2!!" + dataUnitBuffer.size()); diff --git a/src/export/swing/ExportProcessDialog.java b/src/export/swing/ExportProcessDialog.java index 08dff701..7008b7f8 100644 --- a/src/export/swing/ExportProcessDialog.java +++ b/src/export/swing/ExportProcessDialog.java @@ -37,6 +37,9 @@ import export.PamExporterManager; import export.layoutFX.ExportParams; import offlineProcessing.OLProcessDialog; import offlineProcessing.OfflineTaskGroup; +import offlineProcessing.TaskMonitor; +import offlineProcessing.TaskMonitorData; +import offlineProcessing.TaskStatus; /** * Handles an offline dialog for processing offline data and exporting to bespoke file types. @@ -352,20 +355,69 @@ public class ExportProcessDialog { } + + class ExportTaskMonitor implements TaskMonitor { + + private int taskIndex; + + private ExportTaskGroup exportTaskGroup; + + private boolean started = false; - - class ExportTaskGroup extends OfflineTaskGroup { - - public ExportTaskGroup(String settingsName) { - super(null, settingsName); - + public ExportTaskMonitor(int i, ExportTaskGroup exportTaskGroup) { + this.taskIndex = i; + this.exportTaskGroup = exportTaskGroup; } + + @Override + public void setTaskStatus(TaskMonitorData taskMonitorData) { + if (taskMonitorData.taskStatus== TaskStatus.COMPLETE && !started) { + System.out.println(" TASK COMPLETE:"); + if (taskIndex it = primaryDataBlock.getListIterator(0); -// long procDataStart = Long.MAX_VALUE; -// long procDataEnd = 0; -// int nToProcess = 0; -// while (it.hasNext()) { -// dataUnit = it.next(); -// /** -// * Make sure we only process data units within the current time interval. -// */ -// if (dataUnit.getTimeMilliseconds() < processStartTime) { -// continue; -// } -// if (dataUnit.getTimeMilliseconds() > processEndTime) { -// break; -// } -//// if (shouldProcess(dataUnit) == false) { -//// continue; -//// } -// procDataStart = Math.min(procDataStart, dataUnit.getTimeMilliseconds()); -// procDataEnd = Math.max(procDataEnd, dataUnit.getEndTimeInMilliseconds()); -// // do this one too - just to make sure in case end time returns zero. -// procDataEnd = Math.max(procDataEnd, dataUnit.getTimeMilliseconds()); -// nToProcess++; // increase toprocess counter -// } -// if (nToProcess == 0) { -// return; -// } -// PamDataBlock aDataBlock; -// RequiredDataBlockInfo blockInfo; -// /* -// * if the data interval is < 1 hour, then load it all now -// * otherwise we'll do it on a data unit basis. -// */ -////// long maxSecondaryLoad = 1800L*1000L; -////// if (procDataEnd - procDataStart < maxSecondaryLoad) { -//// loadSecondaryData(procDataStart, procDataEnd); -////// } -// // remember the end time of the data so we can use the "new data" selection flag. -// taskGroupParams.lastDataTime = Math.min(primaryDataBlock.getCurrentViewDataEnd(),processEndTime); -// // synchronized(primaryDataBlock) { -// /* -// * Call newDataLoaded for each task before getting on with processing individual data units. -// */ -// -// /** -// * Now process the data -// */ -// it = primaryDataBlock.getListIterator(0); -// unitChanged = false; -// int totalUnits = 0; -// int unitsChanged = 0; -// boolean doTasks = false; -// while (it.hasNext()) { -// dataUnit = it.next(); -// totalUnits++; -// doTasks = true; -// /** -// * Make sure we only process data units within the current time interval. -// */ -// if (dataUnit.getTimeMilliseconds() < processStartTime) { -// continue; -// } -// if (dataUnit.getTimeMilliseconds() > processEndTime) { -// break; -// } -// -// if (shouldProcess(dataUnit) == false) { -// doTasks = false; -// } -// -// if (doTasks) { -// /* -// * load the secondary datablock data. this can be called even if -// * it was called earlier on since it wont' reload if data are already -// * in memory. -// */ -//// loadSecondaryData(dataUnit.getTimeMilliseconds(), dataUnit.getEndTimeInMilliseconds()); -// -// for (int iTask = 0; iTask < nTasks; iTask++) { -// aTask = getTask(iTask); -// if (aTask.isDoRun() == false || !isInTimeChunk(dataUnit, taskGroupParams.timeChunks)) { -// continue; -// } -// cpuMonitor.start(); -// unitChanged |= aTask.processDataUnit(dataUnit); -// cpuMonitor.stop(); -// } -// if (unitChanged) { -// fileInfo = dataUnit.getDataUnitFileInformation(); -// if (fileInfo != null) { -// fileInfo.setNeedsUpdate(true); -// } -// dataUnit.updateDataUnit(System.currentTimeMillis()); -// } -// dataUnit.freeData(); -// } -// if (instantKill) { -// break; -// } -// unitsChanged++; -// if (totalUnits%nSay == 0) { -// publish(new TaskMonitorData(TaskStatus.RUNNING, TaskActivity.PROCESSING, nToProcess, totalUnits, dataName, -// dataUnit.getTimeMilliseconds())); -// } -// } -// for (int iTask = 0; iTask < nTasks; iTask++) { -// aTask = getTask(iTask); -// if (aTask.isDoRun() == false) { -// continue; -// } -// aTask.loadedDataComplete(); -// } -// // } -// publish(new TaskMonitorData(TaskStatus.RUNNING, TaskActivity.SAVING, nToProcess, totalUnits, dataName, -// processEndTime)); -// for (int i = 0; i < affectedDataBlocks.size(); i++) { -// //System.out.println("SAVE VIEWER DATA FOR: " + affectedDataBlocks.get(i) ); -// aDataBlock = affectedDataBlocks.get(i); -// aDataBlock.saveViewerData(); -// } -// Debug.out.printf("Processd %d out of %d data units at " + mapPoint + "\n", unitsChanged, totalUnits); -// commitDatabase(); -// } + } diff --git a/src/export/swing/ExportTask.java b/src/export/swing/ExportTask.java index 1342e729..61858455 100644 --- a/src/export/swing/ExportTask.java +++ b/src/export/swing/ExportTask.java @@ -4,6 +4,7 @@ import PamController.PamController; import PamguardMVC.PamDataBlock; import PamguardMVC.PamDataUnit; import PamguardMVC.dataSelector.DataSelectDialog; +import PamguardMVC.dataSelector.DataSelectParams; import PamguardMVC.dataSelector.DataSelector; import dataMap.OfflineDataMapPoint; import export.PamExporterManager; @@ -45,12 +46,17 @@ public class ExportTask extends OfflineTask>{ @Override public boolean processDataUnit(PamDataUnit dataUnit) { - System.out.println("Huh? " + this.getDataBlock().getDataName() + " " + dataUnit + " " + dataUnit.getParentDataBlock().equals(this.getDataBlock())); +// System.out.println("Huh? " + this.getDataBlock().getDataName() + " " + dataUnit + " " + dataUnit.getParentDataBlock().equals(this.getDataBlock())); + if (dataUnit.getParentDataBlock().equals(this.getDataBlock())) { - //this is very important because the way the expoeter works is that it iterates through multiple parent data blocks - System.out.println(this.getDataBlock().getDataName() + " " + dataUnit); - if (dataSelector==null) exporter.exportDataUnit(dataUnit, false); - else if (dataSelector.scoreData(dataUnit)>0) { + //this is very important because the way the exporter works is that it iterates through multiple parent data blocks + System.out.println(this.getDataBlock().getDataName() + " " + dataUnit + " " + dataSelector.scoreData(dataUnit)); + if (dataSelector==null) { +// System.out.println("Data selector null: " + this.getDataBlock().getDataName() + " " + dataUnit); + exporter.exportDataUnit(dataUnit, false); + } + else if (dataSelector.scoreData(dataUnit)>=0 || dataSelector.getParams().getCombinationFlag() == DataSelectParams.DATA_SELECT_DISABLE) { +// System.out.println("Data selector OK: " + this.getDataBlock().getDataName() + " " + dataUnit); exporter.exportDataUnit(dataUnit, false); } } @@ -66,7 +72,7 @@ public class ExportTask extends OfflineTask>{ @Override public void loadedDataComplete() { - // System.out.println("EXPORTER: loaded data complete"); + System.out.println("EXPORTER: loaded data complete"); //force the exporter so save any renaming data units in the buffer exporter.exportDataUnit(null, true); exporter.close(); From 9628b69f3b3b5fa81e07b725d2451ef951672eb4 Mon Sep 17 00:00:00 2001 From: Jamie Mac Date: Mon, 8 Jul 2024 16:26:51 +0100 Subject: [PATCH 03/10] Working on new symbol options for deep learning and sud file options in SoundAcquisition GUI --- src/Acquisition/FileInputSystem.java | 4 +- src/Acquisition/FolderInputSystem.java | 81 ++++- src/Acquisition/layoutFX/FolderInputPane.java | 2 +- .../offlineFuncs/OfflineWavFileServer.java | 2 +- src/Acquisition/pamAudio/FlacAudioFile.java | 6 + .../pamAudio/PamAudioFileLoader.java | 6 + .../pamAudio/PamAudioFileManager.java | 19 +- .../pamAudio/PamAudioSettingsPane.java | 21 ++ src/Acquisition/pamAudio/SudAudioFile.java | 14 + src/Acquisition/pamAudio/WavAudioFile.java | 6 + src/Acquisition/sud/SudAudioSettingsPane.java | 41 +++ src/PamUtils/PamArrayUtils.java | 49 +++ .../fxNodes/utilsFX/ColourArray.java | 27 +- src/pamViewFX/fxNodes/utilsFX/PamUtilsFX.java | 38 ++- .../dataPlotFX/DLSymbolModifier.java | 69 ++++- .../dataPlotFX/DLSymbolModifierParams.java | 84 +++++- .../dataPlotFX/DLSymbolOptionPane.java | 279 +++++++++++++++--- 17 files changed, 665 insertions(+), 83 deletions(-) create mode 100644 src/Acquisition/pamAudio/PamAudioSettingsPane.java create mode 100644 src/Acquisition/sud/SudAudioSettingsPane.java diff --git a/src/Acquisition/FileInputSystem.java b/src/Acquisition/FileInputSystem.java index 33779534..62ffa038 100644 --- a/src/Acquisition/FileInputSystem.java +++ b/src/Acquisition/FileInputSystem.java @@ -180,7 +180,6 @@ public class FileInputSystem extends DaqSystem implements ActionListener, PamSe this.acquisitionDialog = acquisitionDialog; - return getDialogPanel(); } @@ -197,7 +196,7 @@ public class FileInputSystem extends DaqSystem implements ActionListener, PamSe protected JPanel createDaqDialogPanel() { - JPanel p = new JPanel(); + PamPanel p = new PamPanel(); p.setBorder(new TitledBorder("Select sound file")); GridBagLayout layout = new GridBagLayout(); layout.columnWidths = new int[]{100, 100, 10}; @@ -252,6 +251,7 @@ public class FileInputSystem extends DaqSystem implements ActionListener, PamSe // constraints.gridwidth = 2; // addComponent(p, fileDateText = new JTextField(), constraints); // fileDateText.setEnabled(false); + return p; } diff --git a/src/Acquisition/FolderInputSystem.java b/src/Acquisition/FolderInputSystem.java index c8c2d872..fab8e961 100644 --- a/src/Acquisition/FolderInputSystem.java +++ b/src/Acquisition/FolderInputSystem.java @@ -32,6 +32,7 @@ import javafx.application.Platform; import pamguard.GlobalArguments; import Acquisition.pamAudio.PamAudioFileManager; import Acquisition.pamAudio.PamAudioFileFilter; +import Acquisition.pamAudio.PamAudioFileLoader; import Acquisition.pamAudio.PamAudioSystem; import PamController.DataInputStore; import PamController.InputStoreInfo; @@ -92,6 +93,12 @@ public class FolderInputSystem extends FileInputSystem implements PamSettings, D * Text field for skipping initial few seconds of a file. */ private JTextField skipSecondsField; + + /** + * Panel which shows bespoke settings for certain audio loaders. Contains nothing + * if the audio loader has no settings or no file is selected. + */ + protected PamPanel audioLoaderHolder; @Override public boolean runFileAnalysis() { @@ -170,6 +177,8 @@ public class FolderInputSystem extends FileInputSystem implements PamSettings, D } } + + @Override protected JPanel createDaqDialogPanel() { JPanel p = new JPanel(); @@ -228,6 +237,7 @@ public class FolderInputSystem extends FileInputSystem implements PamSettings, D constraints.gridwidth = 2; constraints.fill = GridBagConstraints.NONE; constraints.anchor = GridBagConstraints.WEST; + addComponent(p, mergeFiles = new JCheckBox("Merge contiguous files"), constraints); if (PamController.getInstance().getRunMode() == PamController.RUN_PAMVIEW) { constraints.gridx+=2; @@ -236,18 +246,28 @@ public class FolderInputSystem extends FileInputSystem implements PamSettings, D checkFiles.addActionListener(new CheckFiles()); } -// if (SMRUEnable.isEnable()) { + // if (SMRUEnable.isEnable()) { // no reason to hide this option from users. - constraints.gridy++; - constraints.gridx = 0; - constraints.gridwidth = 1; - addComponent(p, new JLabel("Skip initial :"), constraints); - constraints.gridx++; - addComponent(p, skipSecondsField = new JTextField(4), constraints); - constraints.gridx++; - addComponent(p, new JLabel("seconds"), constraints); - constraints.anchor = GridBagConstraints.EAST; -// } + constraints.gridy++; + constraints.gridx = 0; + constraints.gridwidth = 1; + addComponent(p, new JLabel("Skip initial :"), constraints); + constraints.gridx++; + addComponent(p, skipSecondsField = new JTextField(4), constraints); + constraints.gridx++; + addComponent(p, new JLabel("seconds"), constraints); + constraints.anchor = GridBagConstraints.EAST; + // } + + + //panel to show bespoke settings for certain audio loaders. + constraints.gridx = 0; + constraints.gridy++; + constraints.gridwidth = 3; + addComponent(p, audioLoaderHolder = new PamPanel(), constraints); + + GridBagLayout layout2 = new GridBagLayout(); + audioLoaderHolder.setLayout(layout2); return p; } @@ -525,10 +545,14 @@ public class FolderInputSystem extends FileInputSystem implements PamSettings, D List asList = allFiles; setSelectedFileTypes(acquisitionControl.soundFileTypes.getUsedTypes(allFiles)); + //set the date of the first file. setFileDateText(); + + //set any bespoke options for the files to be laoded. + setFileOptionPanel(); + // also open up the first file and get the sample rate and number of channels from it // and set these - File file = getCurrentFile(); if (file == null) return; AudioInputStream audioStream; @@ -558,7 +582,7 @@ public class FolderInputSystem extends FileInputSystem implements PamSettings, D /****FX GUI stuff****/ if (folderInputPane!=null) { Platform.runLater(()->{ - folderInputPane.newFileList(fileListData); + folderInputPane.newFileList(fileListData); }); } } @@ -572,7 +596,38 @@ public class FolderInputSystem extends FileInputSystem implements PamSettings, D protected int fudgeNumChannels(int nChannels) { return nChannels; } + + /** + * Set bespoke options for certain file types. + */ + public void setFileOptionPanel() { + getDialogPanel(); // make sure panel is created + audioLoaderHolder.removeAll(); + + if (allFiles.size() > 0) { + //Get all the audio file laoders that will be used for this list of files. Usually + //just one but possible that there can be mixed files. + ArrayList loaders = PamAudioFileManager.getInstance().getAudioFileLoaders(allFiles); + + GridBagConstraints constraints = new GridBagConstraints(); + constraints.gridx = 0; + constraints.gridy = 0; + for (PamAudioFileLoader loader : loaders) { + if (loader.getSettingsPane()!=null) { +// System.out.println("ADD AUDIO PANEL: " +loader.getSettingsPane().getAudioLoaderPanel()); + //gridbag layout + addComponent(audioLoaderHolder, loader.getSettingsPane().getAudioLoaderPanel(), constraints); + constraints.gridy++; + } + } + } + + } + + /** + * Show the date of the first file in the dialog. + */ public void setFileDateText() { if (allFiles.size() > 0) { long fileTime = getFileStartTime(getCurrentFile()); diff --git a/src/Acquisition/layoutFX/FolderInputPane.java b/src/Acquisition/layoutFX/FolderInputPane.java index cf0b0e3e..48e7cb16 100644 --- a/src/Acquisition/layoutFX/FolderInputPane.java +++ b/src/Acquisition/layoutFX/FolderInputPane.java @@ -48,7 +48,7 @@ import pamViewFX.fxNodes.PamProgressBar; import pamViewFX.fxNodes.PamVBox; /** - * Pane for the folder input of the sound acquisition. + * JavaFX pane for the folder input of the sound acquisition. * * @author Jamie Macaulay * diff --git a/src/Acquisition/offlineFuncs/OfflineWavFileServer.java b/src/Acquisition/offlineFuncs/OfflineWavFileServer.java index b10b27dc..582c4bdc 100644 --- a/src/Acquisition/offlineFuncs/OfflineWavFileServer.java +++ b/src/Acquisition/offlineFuncs/OfflineWavFileServer.java @@ -178,7 +178,7 @@ public class OfflineWavFileServer extends OfflineFileServer { return false; } - PamAudioFileLoader audioFile = PamAudioFileManager.getInstance().getAudioLoader(mapPoint.getSoundFile()); + PamAudioFileLoader audioFile = PamAudioFileManager.getInstance().getAudioFileLoader(mapPoint.getSoundFile()); if (audioFile==null) { System.err.println("OfflineWavFileServer: could not find audio loader for mapped sound file: " + mapPoint.getSoundFile()); diff --git a/src/Acquisition/pamAudio/FlacAudioFile.java b/src/Acquisition/pamAudio/FlacAudioFile.java index 13d95b87..0330ef3d 100644 --- a/src/Acquisition/pamAudio/FlacAudioFile.java +++ b/src/Acquisition/pamAudio/FlacAudioFile.java @@ -233,4 +233,10 @@ public class FlacAudioFile implements PamAudioFileLoader { return (end.equals(".flac")); } + @Override + public PamAudioSettingsPane getSettingsPane() { + // TODO Auto-generated method stub + return null; + } + } diff --git a/src/Acquisition/pamAudio/PamAudioFileLoader.java b/src/Acquisition/pamAudio/PamAudioFileLoader.java index 90cc524d..077e1f6e 100644 --- a/src/Acquisition/pamAudio/PamAudioFileLoader.java +++ b/src/Acquisition/pamAudio/PamAudioFileLoader.java @@ -48,5 +48,11 @@ public interface PamAudioFileLoader { public boolean loadAudioData(OfflineFileServer offlineFileServer, PamDataBlock dataBlock, OfflineDataLoadInfo offlineDataLoadInfo, ViewLoadObserver loadObserver); + /** + * Get a settings pane for the audio loader + * @return settings pane for audio loader - can be null. + */ + public PamAudioSettingsPane getSettingsPane(); + } diff --git a/src/Acquisition/pamAudio/PamAudioFileManager.java b/src/Acquisition/pamAudio/PamAudioFileManager.java index bf13f904..3805e738 100644 --- a/src/Acquisition/pamAudio/PamAudioFileManager.java +++ b/src/Acquisition/pamAudio/PamAudioFileManager.java @@ -56,7 +56,7 @@ public class PamAudioFileManager { * @param soundFile - the sound file * @return the audio file loader. */ - public PamAudioFileLoader getAudioLoader(File soundFile) { + public PamAudioFileLoader getAudioFileLoader(File soundFile) { for (int i = 0; i < pamAudioFileTypes.size(); i++) { if (isExtension(soundFile, pamAudioFileTypes.get(i))) { return pamAudioFileTypes.get(i); @@ -169,6 +169,23 @@ public class PamAudioFileManager { public ArrayList getAudioFileLoaders() { return this.pamAudioFileTypes; } + + /** + * Get the loaders which are needed to open a list of files + * @param files - the files to find audio loaders for. + * @return a list of the audio loaders required for the file list + */ + public ArrayList getAudioFileLoaders(ArrayList files) { + ArrayList audioLoaders = new ArrayList(); + PamAudioFileLoader loader; + for (int i=0; i worker; private volatile SudMapWorker sudMapWorker; + /** + * Settings pane to allow users to set some additional options. + */ + private SudAudioSettingsPane sudAudioSettingsPane; + public SudAudioFile() { super(); fileExtensions = new ArrayList(Arrays.asList(new String[] { ".sud" })); @@ -216,5 +222,13 @@ public class SudAudioFile extends WavAudioFile { } } + + @Override + public PamAudioSettingsPane getSettingsPane() { + if (sudAudioSettingsPane==null) { + sudAudioSettingsPane = new SudAudioSettingsPane(this); + } + return sudAudioSettingsPane; + } } \ No newline at end of file diff --git a/src/Acquisition/pamAudio/WavAudioFile.java b/src/Acquisition/pamAudio/WavAudioFile.java index e8789da0..7df37862 100644 --- a/src/Acquisition/pamAudio/WavAudioFile.java +++ b/src/Acquisition/pamAudio/WavAudioFile.java @@ -296,5 +296,11 @@ public class WavAudioFile implements PamAudioFileLoader { } } + @Override + public PamAudioSettingsPane getSettingsPane() { + // TODO Auto-generated method stub + return null; + } + } diff --git a/src/Acquisition/sud/SudAudioSettingsPane.java b/src/Acquisition/sud/SudAudioSettingsPane.java new file mode 100644 index 00000000..5d3e1e87 --- /dev/null +++ b/src/Acquisition/sud/SudAudioSettingsPane.java @@ -0,0 +1,41 @@ +package Acquisition.sud; + +import javax.swing.JLabel; + +import Acquisition.pamAudio.PamAudioSettingsPane; +import Acquisition.pamAudio.SudAudioFile; +import PamView.panel.PamPanel; +import javafx.scene.layout.Pane; + +public class SudAudioSettingsPane implements PamAudioSettingsPane { + + private SudAudioFile sudAudioFile; + + private PamPanel sudAudioPanel; + + public SudAudioSettingsPane(SudAudioFile sudAudioFile) { + this.sudAudioFile=sudAudioFile; + } + + @Override + public Pane getAudioLoaderPane() { + // TODO Auto-generated method stub + return null; + } + + @Override + public PamPanel getAudioLoaderPanel() { + if (sudAudioPanel==null) { + createSudAudioPanel(); + } + return sudAudioPanel; + } + + private void createSudAudioPanel() { + sudAudioPanel = new PamPanel(); + + sudAudioPanel.add(new JLabel("Hello")); + + } + +} diff --git a/src/PamUtils/PamArrayUtils.java b/src/PamUtils/PamArrayUtils.java index 37909be0..b6a4da62 100644 --- a/src/PamUtils/PamArrayUtils.java +++ b/src/PamUtils/PamArrayUtils.java @@ -564,6 +564,55 @@ public class PamArrayUtils { return index; } + /** + * Get the index of the maximum value in an array + * @param arr - the array to find the position of the maximum value. + * m value of. + * @return the index of the maximum value + */ + public static int maxPos(float[] arr) { + double max = Double.NEGATIVE_INFINITY; + int index = -1; + + int count = 0; + for(float cur: arr) { + if (cur>max) { + index = count; + max=cur; + } + count++; + } + + + return index; + } + + /** + * Get the index of the maximum value within a 2D matrix + * @param arr - the array to find the position of the maximum value. + * m value of. + * @return the index i and j of the maximum value + */ + public static int[] maxPos(float[][] arr) { + float max = Float.NEGATIVE_INFINITY; + int[] index = new int[] {-1,-1}; + + // int count = 0; + float cur; + for(int i=0; imax) { + index[0]=i; + index[1]=j; + max=cur; + } + // count++; + } + } + return index; + } + /** * Get the minimum index of an array * @param arr - the array to find the position of the maximum value. diff --git a/src/pamViewFX/fxNodes/utilsFX/ColourArray.java b/src/pamViewFX/fxNodes/utilsFX/ColourArray.java index 536fa9d3..3d60628f 100644 --- a/src/pamViewFX/fxNodes/utilsFX/ColourArray.java +++ b/src/pamViewFX/fxNodes/utilsFX/ColourArray.java @@ -244,7 +244,6 @@ public class ColourArray implements Cloneable, Serializable{ colors[4]=Color.web("#0000ff"); colors[5]=Color.web("#ff00ff"); colors[6]=Color.web("#ff0000"); - return colors; case PATRIOTIC: colors=new Color[3]; @@ -254,22 +253,22 @@ public class ColourArray implements Cloneable, Serializable{ return colors; case MATLAB: colors=new Color[7]; - colors[0]= Color.color( 0, 0.4470, 0.7410); - colors[1]= Color.color(0.8500 , 0.3250 , 0.0980); - colors[2]= Color.color(0.9290 , 0.6940 , 0.1250); - colors[3]= Color.color( 0.4940 , 0.1840 , 0.5560); - colors[4]= Color.color(0.4660 , 0.6740 , 0.1880); - colors[5]= Color.color( 0.3010 , 0.7450 , 0.9330); - colors[6]= Color.color( 0.6350 , 0.0780 , 0.1840); + colors[0]= Color.color(0, 0.4470, 0.7410); + colors[1]= Color.color(0.8500, 0.3250, 0.0980); + colors[2]= Color.color(0.9290, 0.6940, 0.1250); + colors[3]= Color.color(0.4940, 0.1840, 0.5560); + colors[4]= Color.color(0.4660, 0.6740, 0.1880); + colors[5]= Color.color(0.3010, 0.7450, 0.9330); + colors[6]= Color.color(0.6350, 0.0780, 0.1840); return colors; case PARULA: colors=new Color[6]; - colors[0]= Color.color( 0.24220, 0.150400, 0.66030); - colors[1]= Color.color( 0.264700 ,0.403000 , 0.993500); - colors[2]= Color.color( 0.108500, 0.66690 , 0.873400); - colors[3]= Color.color( 0.280900 , 0.796400, 0.526600); - colors[4]= Color.color( 0.918400, 0.73080 , 0.18900); - colors[5]= Color.color( 0.9769 , 0.98390 ,0.0805000); + colors[0]= Color.color(0.24220, 0.150400, 0.66030); + colors[1]= Color.color(0.264700, 0.403000, 0.993500); + colors[2]= Color.color(0.108500, 0.66690, 0.873400); + colors[3]= Color.color(0.280900, 0.796400, 0.526600); + colors[4]= Color.color(0.918400, 0.73080, 0.18900); + colors[5]= Color.color(0.9769, 0.98390, 0.0805000); return colors; case INFERNO: return InfernoColorMap.getInfernoColourMap(); diff --git a/src/pamViewFX/fxNodes/utilsFX/PamUtilsFX.java b/src/pamViewFX/fxNodes/utilsFX/PamUtilsFX.java index a0d976b2..98efc95b 100644 --- a/src/pamViewFX/fxNodes/utilsFX/PamUtilsFX.java +++ b/src/pamViewFX/fxNodes/utilsFX/PamUtilsFX.java @@ -632,13 +632,37 @@ public class PamUtilsFX { * @param color - the color. * @return the color. */ - public static String toRGBCode( Color color ) - { - return String.format( "#%02X%02X%02X", - (int)( color.getRed() * 255 ), - (int)( color.getGreen() * 255 ), - (int)( color.getBlue() * 255 ) ); - } + public static String toRGBCode( Color color ){ + return String.format( "#%02X%02X%02X", + (int)( color.getRed() * 255 ), + (int)( color.getGreen() * 255 ), + (int)( color.getBlue() * 255 ) ); + } + + + /** + * Convert a colour to an int. + * @param c - the colour to change. + * @return the int representation of the colour + */ + public static int colorToInt(Color c) { + int r = (int) Math.round(c.getRed() * 255); + int g = (int) Math.round(c.getGreen() * 255); + int b = (int) Math.round(c.getBlue() * 255); + return (r << 16) | (g << 8) | b; + } + + /** + * Convert an int encoded with a colour to a Color object. + * @param value - the int to convert to colour + * @return the Color object for the int + */ + public static Color intToColor(int value) { + int r = (value >>> 16) & 0xFF; + int g = (value >>> 8) & 0xFF; + int b = value & 0xFF; + return Color.rgb(r,g,b); + } } diff --git a/src/rawDeepLearningClassifier/dataPlotFX/DLSymbolModifier.java b/src/rawDeepLearningClassifier/dataPlotFX/DLSymbolModifier.java index ce94692a..6e6bc752 100644 --- a/src/rawDeepLearningClassifier/dataPlotFX/DLSymbolModifier.java +++ b/src/rawDeepLearningClassifier/dataPlotFX/DLSymbolModifier.java @@ -2,6 +2,7 @@ package rawDeepLearningClassifier.dataPlotFX; import java.awt.Color; +import PamUtils.PamArrayUtils; import PamView.GeneralProjector; import PamView.PamSymbolType; import PamView.symbol.PamSymbolChooser; @@ -20,7 +21,8 @@ import rawDeepLearningClassifier.logging.DLAnnotationType; /** - * The DL symbol modifier. Colours symbols by prediction. + * The DL symbol modifier. Colours symbols by eother the vlaue of the prediction by a user selected class + * or by the class with the highest prediction value. * * @author Jamie Macaulay. * @@ -94,7 +96,70 @@ public class DLSymbolModifier extends SymbolModifier { return null; } - //System.out.println("Class index: " + dlSymbolOptions.classIndex); + + //modify the default symbol + if (dlSymbolOptions.colTypeSelection == DLSymbolModifierParams.PREDICITON_COL) { + getSymbolDataPred(annotation); + } + + else if (dlSymbolOptions.colTypeSelection == DLSymbolModifierParams.CLASS_COL) { + getSymbolDataClass(annotation); + } + + return symbolData; + } + + /** + * Get symbol data for colouring by the species class with the maximum prediction + * @param annotation - the annotation + * @return symbol data for colouring by class maximum. + */ + private SymbolData getSymbolDataClass(DLAnnotation annotation ) { + + boolean passed = false; + int colIndex = -1; + + float[][] results = new float[ annotation.getModelResults().size()][]; + + //A detection might have multiple prediction results, i.e. predictions are a matrix. Need + //to iterate through all the predictions and then work out whihc is the maximum. That index is then then] + //class colour. + int i=0; + for (PredictionResult modelResult: annotation.getModelResults()) { + if (modelResult.isBinaryClassification()) passed = true; + results[i] = modelResult.getPrediction(); + i++; + } + + int[] indexBest = PamArrayUtils.maxPos(results); + + + if (passed || !dlSymbolOptions.showOnlyBinary) { + //work out the class colour... + + javafx.scene.paint.Color color = PamUtilsFX.intToColor(dlSymbolOptions.classColors[indexBest[1]]); + + Color colorAWT = PamUtilsFX.fxToAWTColor(color); + + symbolData.setFillColor(colorAWT); + symbolData.setLineColor(colorAWT); + + return symbolData; + } + else { + //has data but we have only show binary option selected. + return null; + } + + } + + + /** + * Get symbol data for colouring by the prediction value for a selected species class. + * @param annotation - the annotation + * @return symbol data for colouring by prediction value for a selected species class. + */ + private SymbolData getSymbolDataPred(DLAnnotation annotation ) { if (dlSymbolOptions.classIndex<0) { dlSymbolOptions.classIndex=0; diff --git a/src/rawDeepLearningClassifier/dataPlotFX/DLSymbolModifierParams.java b/src/rawDeepLearningClassifier/dataPlotFX/DLSymbolModifierParams.java index 718b6378..45e775a8 100644 --- a/src/rawDeepLearningClassifier/dataPlotFX/DLSymbolModifierParams.java +++ b/src/rawDeepLearningClassifier/dataPlotFX/DLSymbolModifierParams.java @@ -1,7 +1,10 @@ package rawDeepLearningClassifier.dataPlotFX; + import PamView.symbol.modifier.SymbolModifierParams; +import javafx.scene.paint.Color; import pamViewFX.fxNodes.utilsFX.ColourArray.ColourArrayType; +import pamViewFX.fxNodes.utilsFX.PamUtilsFX; /** * Parameters for colouring symbols by deep learning probability. @@ -10,11 +13,28 @@ import pamViewFX.fxNodes.utilsFX.ColourArray.ColourArrayType; * */ public class DLSymbolModifierParams extends SymbolModifierParams { + + public DLSymbolModifierParams() { + setDefaultClassColors(32); + } /** * */ - private static final long serialVersionUID = 1L; + private static final long serialVersionUID = 3L; + + + public static final int PREDICITON_COL = 0; + + public static final int CLASS_COL = 1; + + /** + * The way to colour the predictions. + */ + public int colTypeSelection = PREDICITON_COL; + + + /****Prediction colours****/ /** * The colour limits. @@ -27,16 +47,72 @@ public class DLSymbolModifierParams extends SymbolModifierParams { */ public int classIndex = 0; + + /*******Class colours******/ + /** - * Show only binary. + * The current colours for each */ - public boolean showOnlyBinary = false; - + public int[] classColors = new int[32]; + + /** + * The currently selected class for colour picker- just so the user sees the same selection. + */ + public int classIndex2 = 0; + /** * The colour array to show. */ public ColourArrayType colArray = ColourArrayType.FIRE; + /***************************/ + + + /** + * Show only detections which have passed a decision threshold. + */ + public boolean showOnlyBinary = false; + + /** + * Set the default colours. + * @param num - the number of colours to set. + */ + public void setDefaultClassColors(int num) { + if (classColors==null || classColors.length classNameBox2; + + /** + * Pane which holds controls for changing the colour based on prediciton value + */ + private Pane probPane; + + /** + * Pane which holds controls for changing the colour based on the highest prediction value + */ + private Pane classPane; + + + private PamBorderPane holder; + + /** + * Button to select how to colour. + */ + private ToggleButton b1, b2; public DLSymbolOptionPane(SymbolModifier symbolModifer) { super(symbolModifer, Orientation.HORIZONTAL, true, 0); - this.setBottom(createProbPane()); + + probPane = createProbPane(); + classPane = createClassPane(); + classPane.setPadding(new Insets(0,0,5,0)); + + b1 = new ToggleButton("Prediction"); + b1.setPrefWidth(80); + b1.setStyle("-fx-border-radius: 5 0 0 5; -fx-background-radius: 5 0 0 5;"); + + b2 = new ToggleButton("Class"); + b2.setPrefWidth(80); + + b1.setOnAction((a)->{ + setSettingsPane(); + //change the colour of the colour range slider. + notifySettingsListeners(); + }); + b2.setStyle("-fx-border-radius: 0 5 5 0; -fx-background-radius: 0 5 5 0;"); + + + b2.setOnAction((a)->{ + setSettingsPane(); + //change the colour of the colour range slider. + notifySettingsListeners(); + }); + + SegmentedButton segmentedButton = new SegmentedButton(); + segmentedButton.getButtons().addAll(b1, b2); + segmentedButton.setPadding(new Insets(5,0,5,0)); + + BorderPane.setAlignment(segmentedButton, Pos.CENTER); + b1.setSelected(true); + + showOnlyBinary = new CheckBox("Show only binary classificaiton"); + showOnlyBinary.setOnAction((action)->{ + getParams(); + //change the colour of the colour range slider. + notifySettingsListeners(); + }); + showOnlyBinary.setTooltip(new Tooltip("Colour detections only if they passed decision threshold")); + + holder = new PamBorderPane(); + + holder.setTop(segmentedButton); + holder.setCenter(probPane); + holder.setBottom(showOnlyBinary); + + this.setBottom(holder); + this.dlSymbolModifier = (DLSymbolModifier) symbolModifer; initialised=true; } + + private void setSettingsPane() { + if (b1.isSelected()) { + holder.setCenter(probPane); + } + if (b2.isSelected()) { + holder.setCenter(classPane); + } + } + + + private Pane createClassPane() { + + classNameBox2 = new ComboBox(); + classNameBox2.setOnAction((action)->{ + getParams(); + //change the colour of the colour range slider. + notifySettingsListeners(); + }); + classNameBox2.setMaxWidth(Double.MAX_VALUE); + + classNameBox2.setOnAction((action)->{ + colourPicker.setValue(PamUtilsFX.intToColor(dlSymbolModifier.getSymbolModifierParams().classColors[classNameBox2.getSelectionModel().getSelectedIndex()])); + }); + + classNameBox2.setPrefWidth(CLASS_NAME_BOX_WIDTH); + + colourPicker = new ColorPicker(); + colourPicker.setPrefWidth(60); + colourPicker.setOnAction((action)->{ + getParams(); + notifySettingsListeners(); + }); + + HBox.setHgrow(classNameBox, Priority.ALWAYS); + + PamHBox classHolder = new PamHBox(); + classHolder.setSpacing(5); + classHolder.setAlignment(Pos.CENTER_LEFT); + classHolder.getChildren().addAll(classNameBox2, colourPicker); + + return classHolder; + + } + private Pane createProbPane() { @@ -82,6 +211,8 @@ public class DLSymbolOptionPane extends StandardSymbolModifierPane { //change the colour of the colour range slider. notifySettingsListeners(); }); + classNameBox.setPrefWidth(CLASS_NAME_BOX_WIDTH); + colourBox = new ColorComboBox(ColorComboBox.COLOUR_ARRAY_BOX); colourBox.setPrefWidth(50); @@ -92,17 +223,10 @@ public class DLSymbolOptionPane extends StandardSymbolModifierPane { colourRangeSlider.setColourArrayType( dlSymbolModifier.getSymbolModifierParams().colArray); }); - showOnlyBinary = new CheckBox("Show only binary classificaiton"); - showOnlyBinary.setOnAction((action)->{ - getParams(); - //change the colour of the colour range slider. - notifySettingsListeners(); - }); - PamHBox classHolder = new PamHBox(); classHolder.setSpacing(5); classHolder.setAlignment(Pos.CENTER_LEFT); - classHolder.getChildren().addAll(new Label("Show Class"), classNameBox); + classHolder.getChildren().addAll(new Label("Show class"), classNameBox); PamHBox colorHolder = new PamHBox(); colorHolder.setSpacing(5); @@ -112,7 +236,7 @@ public class DLSymbolOptionPane extends StandardSymbolModifierPane { holder.getChildren().addAll(classHolder, new Label("Probability"), colorHolder); - holder.getChildren().add(showOnlyBinary); +// holder.getChildren().add(showOnlyBinary); holder.setPadding(new Insets(5,0,5,0)); setParams = false; @@ -120,35 +244,68 @@ public class DLSymbolOptionPane extends StandardSymbolModifierPane { return holder; } - @Override - public StandardSymbolOptions getParams(){ - StandardSymbolOptions standardSymbolOptions = super.getParams(); + + /** + * get parameters for colouring by class. + * @param symbolOptions - the symbol options. + * @return + */ + public DLSymbolModifierParams getClassColParams(DLSymbolModifierParams symbolOptions ) { + + int index = classNameBox2.getSelectionModel().getSelectedIndex()>=0 ? classNameBox2.getSelectionModel().getSelectedIndex():0; - - //bit messy but works / - DLSymbolModifierParams symbolOptions = dlSymbolModifier.getSymbolModifierParams(); + symbolOptions.classColors[index] = PamUtilsFX.colorToInt(colourPicker.getValue()); + + symbolOptions.classIndex2 = classNameBox2.getSelectionModel().getSelectedIndex(); + + return symbolOptions; + } + + /** + * + * @param symbolOptions + * @return + */ + public DLSymbolModifierParams getPredictionColParams(DLSymbolModifierParams symbolOptions ) { - //need to chekc this here. - //checkClassNamesBox(symbolOptions); - - symbolOptions.clims=new double[] {colourRangeSlider.getLowValue(), colourRangeSlider.getHighValue()}; symbolOptions.colArray = ColourArray.getColorArrayType(this.colourBox.getSelectionModel().getSelectedItem()); symbolOptions.classIndex = classNameBox.getSelectionModel().getSelectedIndex(); + return symbolOptions; + } + + + @Override + public StandardSymbolOptions getParams(){ + StandardSymbolOptions standardSymbolOptions = super.getParams(); + + //bit messy but works + DLSymbolModifierParams symbolOptions = dlSymbolModifier.getSymbolModifierParams(); + + //need to check this here. + //checkClassNamesBox(symbolOptions); + + if (b1.isSelected()) symbolOptions.colTypeSelection = DLSymbolModifierParams.PREDICITON_COL; + if (b2.isSelected()) symbolOptions.colTypeSelection = DLSymbolModifierParams.CLASS_COL; + + //get parameters for colouring + symbolOptions = getClassColParams(symbolOptions); + + //get parameters for colouring by prediction value + symbolOptions = getPredictionColParams(symbolOptions) ; + symbolOptions.showOnlyBinary = showOnlyBinary.isSelected(); dlSymbolModifier.checkColourArray(); - //System.out.println("Get params: " ); - return standardSymbolOptions; - } - private void checkClassNamesBox(DLSymbolModifierParams symbolOptions) { + + private int checkClassNamesBox(DLSymbolModifierParams symbolOptions, ComboBox classNameBox) { DLClassName[] classNames = dlSymbolModifier.getDLAnnotType().getDlControl().getDLModel().getClassNames(); @@ -167,8 +324,51 @@ public class DLSymbolOptionPane extends StandardSymbolModifierPane { classNameBox.getItems().add("Class: " + i); } } + + return nClass; + + } + + /** + * Set parameters for controls to change the colour gradient based on prediction. + * @param symbolOptions - the symbol options + */ + private void setPredictionColParams(DLSymbolModifierParams symbolOptions) { + + //now set frequency parameters + colourRangeSlider.setLowValue(symbolOptions.clims[0]); + colourRangeSlider.setHighValue(symbolOptions.clims[1]); + colourRangeSlider.setColourArrayType( symbolOptions.colArray); + + + int nClass = checkClassNamesBox( symbolOptions, classNameBox); symbolOptions.classIndex = Math.min(symbolOptions.classIndex, nClass-1); classNameBox.getSelectionModel().select(Math.max(symbolOptions.classIndex, 0)); + + //color box. + colourBox.setValue(symbolOptions.colArray); + } + + /** + * Set parameters for controls to change the colour gradient based on prediction. + * @param symbolOptions - the symbol options + */ + private void setClassColParams(DLSymbolModifierParams symbolOptions) { + + int nClass = checkClassNamesBox( symbolOptions, classNameBox2); + + symbolOptions.classIndex = Math.min(symbolOptions.classIndex, nClass-1); + classNameBox2.getSelectionModel().select(Math.max(symbolOptions.classIndex2, 0)); + + + int index = symbolOptions.classIndex2>=0? symbolOptions.classIndex2 : 0; + + if (symbolOptions.classColors==null) { + symbolOptions.setDefaultClassColors(nClass); + } + + //set the correct colour + colourPicker.setValue(PamUtilsFX.intToColor(symbolOptions.classColors[index])); } @Override @@ -180,24 +380,27 @@ public class DLSymbolOptionPane extends StandardSymbolModifierPane { setParams = true; -// StandardSymbolOptions standardSymbolOptions = (StandardSymbolOptions) getSymbolModifier().getSymbolChooser().getSymbolOptions(); - + //get the symbool options DLSymbolModifierParams symbolOptions = dlSymbolModifier.getSymbolModifierParams(); - //now set frequency parameters - colourRangeSlider.setLowValue(symbolOptions.clims[0]); - colourRangeSlider.setHighValue(symbolOptions.clims[1]); - colourRangeSlider.setColourArrayType( symbolOptions.colArray); - - //set the combo box class types. - checkClassNamesBox( symbolOptions); +// b1.setSelected(false); +// b2.setSelected(false); + if (symbolOptions.colTypeSelection == DLSymbolModifierParams.PREDICITON_COL) b1.setSelected(true); + if (symbolOptions.colTypeSelection == DLSymbolModifierParams.PREDICITON_COL) b2.setSelected(true); + setSettingsPane(); - //color box. - colourBox.setValue(symbolOptions.colArray); + symbolOptions.colTypeSelection = b1.isSelected() ? DLSymbolModifierParams.PREDICITON_COL : DLSymbolModifierParams.CLASS_COL; + + //set the parameters for colouring by prediction + setPredictionColParams(symbolOptions); + + //set the class colour parameters + setClassColParams(symbolOptions); //set the selected. showOnlyBinary.setSelected(symbolOptions.showOnlyBinary); + setParams = false; } From 6ee03579d04a4af064778be29906f3f223da4140 Mon Sep 17 00:00:00 2001 From: Jamie Mac Date: Tue, 9 Jul 2024 13:14:43 +0100 Subject: [PATCH 04/10] Add zero pad option for sud files. --- src/Acquisition/FolderInputSystem.java | 16 +++- .../pamAudio/PamAudioFileLoader.java | 1 + .../pamAudio/PamAudioFileManager.java | 2 + .../pamAudio/PamAudioSettingsPane.java | 13 +++ src/Acquisition/pamAudio/PamAudioSystem.java | 2 + src/Acquisition/sud/PamSudParams.java | 33 ++++++++ .../{pamAudio => sud}/SudAudioFile.java | 65 +++++++++++++-- .../{pamAudio => sud}/SudAudioFileReader.java | 23 +++++- src/Acquisition/sud/SudAudioSettingsPane.java | 79 +++++++++++++++++-- 9 files changed, 219 insertions(+), 15 deletions(-) create mode 100644 src/Acquisition/sud/PamSudParams.java rename src/Acquisition/{pamAudio => sud}/SudAudioFile.java (78%) rename src/Acquisition/{pamAudio => sud}/SudAudioFileReader.java (76%) diff --git a/src/Acquisition/FolderInputSystem.java b/src/Acquisition/FolderInputSystem.java index fab8e961..5357638a 100644 --- a/src/Acquisition/FolderInputSystem.java +++ b/src/Acquisition/FolderInputSystem.java @@ -256,11 +256,11 @@ public class FolderInputSystem extends FileInputSystem implements PamSettings, D addComponent(p, skipSecondsField = new JTextField(4), constraints); constraints.gridx++; addComponent(p, new JLabel("seconds"), constraints); - constraints.anchor = GridBagConstraints.EAST; // } //panel to show bespoke settings for certain audio loaders. + constraints.anchor = GridBagConstraints.WEST; constraints.gridx = 0; constraints.gridy++; constraints.gridwidth = 3; @@ -618,6 +618,7 @@ public class FolderInputSystem extends FileInputSystem implements PamSettings, D // System.out.println("ADD AUDIO PANEL: " +loader.getSettingsPane().getAudioLoaderPanel()); //gridbag layout addComponent(audioLoaderHolder, loader.getSettingsPane().getAudioLoaderPanel(), constraints); + loader.getSettingsPane().setParams(); constraints.gridy++; } } @@ -823,6 +824,17 @@ public class FolderInputSystem extends FileInputSystem implements PamSettings, D return false; } } + + //get bespoke paramters from selected audio loaders. + ArrayList loaders = PamAudioFileManager.getInstance().getAudioFileLoaders(allFiles); + + + for (PamAudioFileLoader loader : loaders) { + if (loader.getSettingsPane()!=null) { + loader.getSettingsPane().getParams(); + } + } + return super.dialogGetParams(); } @@ -1005,6 +1017,8 @@ public class FolderInputSystem extends FileInputSystem implements PamSettings, D String bs = String.format("%d,%d,%d,%s", nFiles,currentFile,generalStatus,currFile); return bs; } + + } diff --git a/src/Acquisition/pamAudio/PamAudioFileLoader.java b/src/Acquisition/pamAudio/PamAudioFileLoader.java index 077e1f6e..797ab56d 100644 --- a/src/Acquisition/pamAudio/PamAudioFileLoader.java +++ b/src/Acquisition/pamAudio/PamAudioFileLoader.java @@ -53,6 +53,7 @@ public interface PamAudioFileLoader { * @return settings pane for audio loader - can be null. */ public PamAudioSettingsPane getSettingsPane(); + } diff --git a/src/Acquisition/pamAudio/PamAudioFileManager.java b/src/Acquisition/pamAudio/PamAudioFileManager.java index 3805e738..cf57787e 100644 --- a/src/Acquisition/pamAudio/PamAudioFileManager.java +++ b/src/Acquisition/pamAudio/PamAudioFileManager.java @@ -9,6 +9,8 @@ import javax.sound.sampled.UnsupportedAudioFileException; import org.codehaus.plexus.util.FileUtils; +import Acquisition.sud.SudAudioFile; + /** * Central class for opening sound files. *

diff --git a/src/Acquisition/pamAudio/PamAudioSettingsPane.java b/src/Acquisition/pamAudio/PamAudioSettingsPane.java index 50e33f4d..0db5beab 100644 --- a/src/Acquisition/pamAudio/PamAudioSettingsPane.java +++ b/src/Acquisition/pamAudio/PamAudioSettingsPane.java @@ -3,6 +3,9 @@ package Acquisition.pamAudio; import PamView.panel.PamPanel; import javafx.scene.layout.Pane; +/** + * User controls to change bespoke settings for audio loaders. + */ public interface PamAudioSettingsPane { /** @@ -17,5 +20,15 @@ public interface PamAudioSettingsPane { */ public PamPanel getAudioLoaderPanel(); + /** + * Get the parameters. This called whenever the settings dialog or pane is closed. + */ + public void getParams(); + + /** + * Set parameters. This is called when the dialog or pane is opened. + */ + public void setParams(); + } diff --git a/src/Acquisition/pamAudio/PamAudioSystem.java b/src/Acquisition/pamAudio/PamAudioSystem.java index ae50726f..6c012e21 100644 --- a/src/Acquisition/pamAudio/PamAudioSystem.java +++ b/src/Acquisition/pamAudio/PamAudioSystem.java @@ -10,6 +10,8 @@ import javax.sound.sampled.UnsupportedAudioFileException; //import org.kc7bfi.jflac.sound.spi.FlacAudioFileReader; import org.jflac.sound.spi.FlacAudioFileReader; +import Acquisition.sud.SudAudioFileReader; + /** * Now replaced with PamAudioFileManager. * diff --git a/src/Acquisition/sud/PamSudParams.java b/src/Acquisition/sud/PamSudParams.java new file mode 100644 index 00000000..85ce3ea2 --- /dev/null +++ b/src/Acquisition/sud/PamSudParams.java @@ -0,0 +1,33 @@ +package Acquisition.sud; + +import java.io.Serializable; + + +/** + * Parameters for sud file extraction. + */ +public class PamSudParams implements Serializable, Cloneable { + + public static final long serialVersionUID = 1L; + + /** + * Zero padding fills gaps in sud files with zeros - these gaps are usually due + * to errors in the recording hardware.Without zero pad then time drift within a + * file can be difficult to predict, however zero padding means the sample + * numbers in other files e.g. csv sensor files will not align. + */ + public boolean zeroPad = true; + + @Override + public PamSudParams clone() { + try { + PamSudParams ap = (PamSudParams) super.clone(); + + return ap; + } + catch (CloneNotSupportedException Ex) { + Ex.printStackTrace(); + } + return null; + } +} diff --git a/src/Acquisition/pamAudio/SudAudioFile.java b/src/Acquisition/sud/SudAudioFile.java similarity index 78% rename from src/Acquisition/pamAudio/SudAudioFile.java rename to src/Acquisition/sud/SudAudioFile.java index efd86f67..840edec1 100644 --- a/src/Acquisition/pamAudio/SudAudioFile.java +++ b/src/Acquisition/sud/SudAudioFile.java @@ -1,7 +1,8 @@ -package Acquisition.pamAudio; +package Acquisition.sud; import java.io.File; import java.io.IOException; +import java.io.Serializable; import java.util.ArrayList; import java.util.Arrays; @@ -12,8 +13,13 @@ import javax.swing.SwingUtilities; import org.pamguard.x3.sud.ChunkHeader; import org.pamguard.x3.sud.SudMapListener; -import Acquisition.sud.SudAudioSettingsPane; +import Acquisition.AcquisitionParameters; +import Acquisition.pamAudio.PamAudioSettingsPane; +import Acquisition.pamAudio.WavAudioFile; +import PamController.PamControlledUnitSettings; import PamController.PamController; +import PamController.PamSettingManager; +import PamController.PamSettings; import PamUtils.worker.PamWorkProgressMessage; import PamUtils.worker.PamWorkWrapper; import PamUtils.worker.PamWorker; @@ -31,21 +37,32 @@ import PamUtils.worker.PamWorker; * @author Jamie Macaulay * */ -public class SudAudioFile extends WavAudioFile { +public class SudAudioFile extends WavAudioFile implements PamSettings { private Object conditionSync = new Object(); private volatile PamWorker worker; + private volatile SudMapWorker sudMapWorker; /** * Settings pane to allow users to set some additional options. */ private SudAudioSettingsPane sudAudioSettingsPane; + + /** + * Parameters for the sud file. TODO Note: PamAudioManager is always a single + * instance referenced globally from PAMGuard. Having parameters is therefore + * slightly problematic because they will apply across SoundAcquisition modules. + * So in the case that someone is using two or more Sound Acquisition modules + * then selecting zero and non -zero pad would be impossible + */ + private PamSudParams sudParams = new PamSudParams(); public SudAudioFile() { super(); fileExtensions = new ArrayList(Arrays.asList(new String[] { ".sud" })); + PamSettingManager.getInstance().registerSettings(this); } @Override @@ -69,7 +86,7 @@ public class SudAudioFile extends WavAudioFile { if (new File(soundFile.getAbsolutePath() + "x").exists()) { // System.out.println("----NO NEED TO MAP SUD FILE-----" + soundFile); try { - return new SudAudioFileReader().getAudioInputStream(soundFile); + return new SudAudioFileReader(sudParams.zeroPad).getAudioInputStream(soundFile); } catch (UnsupportedAudioFileException | IOException e) { // TODO Auto-generated catch block e.printStackTrace(); @@ -187,7 +204,7 @@ public class SudAudioFile extends WavAudioFile { // System.out.println("START OPEN SUD FILE:"); this.sudMapListener = new SudMapProgress(pamWorker); - stream = new SudAudioFileReader().getAudioInputStream(soundFile, sudMapListener); + stream = new SudAudioFileReader(sudParams.zeroPad).getAudioInputStream(soundFile, sudMapListener); // System.out.println("END SUD FILE:"); @@ -231,4 +248,42 @@ public class SudAudioFile extends WavAudioFile { return sudAudioSettingsPane; } + @Override + public String getUnitName() { + return "PamAudioManager"; + } + + @Override + public String getUnitType() { + return "sud_files"; + } + + @Override + public Serializable getSettingsReference() { + return sudParams; + } + + @Override + public long getSettingsVersion() { + return PamSudParams.serialVersionUID; + } + + + @Override + public boolean restoreSettings(PamControlledUnitSettings pamControlledUnitSettings) { + try { + sudParams = ((PamSudParams) pamControlledUnitSettings.getSettings()).clone(); + + return true; + } + catch (ClassCastException e) { + e.printStackTrace(); + } + return false; + } + + public PamSudParams getSudParams() { + return this.sudParams; + } + } \ No newline at end of file diff --git a/src/Acquisition/pamAudio/SudAudioFileReader.java b/src/Acquisition/sud/SudAudioFileReader.java similarity index 76% rename from src/Acquisition/pamAudio/SudAudioFileReader.java rename to src/Acquisition/sud/SudAudioFileReader.java index b38ea34e..f69467f6 100644 --- a/src/Acquisition/pamAudio/SudAudioFileReader.java +++ b/src/Acquisition/sud/SudAudioFileReader.java @@ -1,4 +1,4 @@ -package Acquisition.pamAudio; +package Acquisition.sud; import java.io.File; import java.io.IOException; @@ -25,15 +25,32 @@ public class SudAudioFileReader { */ SudParams sudParams; - + /** + * Constructor to create an Sud Audio reader with a + * default true to zeropad sud files. + * */ public SudAudioFileReader() { + this(true); + } + + + /** + * Constructor to create an Sud Audio reader. Allows the option of zero padding. + * Zero padding fills gaps in sud files with zeros - these gaps are usually due + * to errors in the recording hardware.Without zero pad then time drift within a + * file can be difficult to predict, however zero padding means the sample + * numbers in other files e.g. csv sensor files will not align. + * + * @param zeroPad - true to zero pad sud files. + */ + public SudAudioFileReader(boolean zeroPad) { sudParams = new SudParams(); //set up the sud params for default. i.e. just read files and //don't save any decompressed or meta data. // sudParams.saveWav = false; // sudParams.saveMeta = false; sudParams.setFileSave(false, false, false, false); - sudParams.zeroPad = true; + sudParams.zeroPad = zeroPad; } /** diff --git a/src/Acquisition/sud/SudAudioSettingsPane.java b/src/Acquisition/sud/SudAudioSettingsPane.java index 5d3e1e87..71d91e3b 100644 --- a/src/Acquisition/sud/SudAudioSettingsPane.java +++ b/src/Acquisition/sud/SudAudioSettingsPane.java @@ -1,17 +1,25 @@ package Acquisition.sud; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; + +import javax.swing.JCheckBox; import javax.swing.JLabel; +import javax.swing.JPanel; import Acquisition.pamAudio.PamAudioSettingsPane; -import Acquisition.pamAudio.SudAudioFile; +import PamView.dialog.PamGridBagContraints; import PamView.panel.PamPanel; import javafx.scene.layout.Pane; +/** + * Some Swing and FX controls to allow a user to chnage sud parameters. + */ public class SudAudioSettingsPane implements PamAudioSettingsPane { - + private SudAudioFile sudAudioFile; - - private PamPanel sudAudioPanel; + + private SudSettingsPanel sudAudioPanel; public SudAudioSettingsPane(SudAudioFile sudAudioFile) { this.sudAudioFile=sudAudioFile; @@ -32,10 +40,69 @@ public class SudAudioSettingsPane implements PamAudioSettingsPane { } private void createSudAudioPanel() { - sudAudioPanel = new PamPanel(); + sudAudioPanel = new SudSettingsPanel(); + } + + public void setParams(PamSudParams sudParams) { + //System.out.println("Set SUD PARAMS: " + sudParams + " " + sudParams.zeroPad); + if (sudAudioPanel!=null) sudAudioPanel.setParams(sudParams);; + + } + + public PamSudParams getParams(PamSudParams sudParams) { + //System.out.println("Get SUD PARAMS: " + sudParams + " " + sudParams.zeroPad); + + if (sudAudioPanel!=null) return sudAudioPanel.getParams(sudParams);; + return null; + } + + + /** + * The sud settings panel. + */ + public class SudSettingsPanel extends PamPanel { - sudAudioPanel.add(new JLabel("Hello")); + private static final long serialVersionUID = 1L; + private JCheckBox zeroPadSud; + + public SudSettingsPanel() { + + this.setLayout(new GridBagLayout()); +// soundTrapDate.setPreferredSize(tzPanel.getPreferredSize()); + GridBagConstraints c = new PamGridBagContraints(); + c.gridy = 0; + c.gridx = 0; + c.gridwidth = 1; + + zeroPadSud = new JCheckBox("Zero pad sud files"); + zeroPadSud.setToolTipText("Zero pad sud files. Zero padding replaces sections of sud files \n" + + "with corrupt or no data with zeros. This can improve time drift. "); + + this.add(zeroPadSud,c); + } + + public void setParams(PamSudParams sudParams) { + this.zeroPadSud.setSelected(sudParams.zeroPad); + } + + + public PamSudParams getParams(PamSudParams sudParams) { + sudParams.zeroPad = zeroPadSud.isSelected(); + return sudParams; + } + + } + + + @Override + public void getParams() { + getParams(sudAudioFile.getSudParams()); + } + + @Override + public void setParams() { + setParams(sudAudioFile.getSudParams()); } } From 38dc7e60764f29e2f4354c7ddb34a2c707f5f684 Mon Sep 17 00:00:00 2001 From: Jamie Mac Date: Tue, 9 Jul 2024 16:36:04 +0100 Subject: [PATCH 05/10] Updates to sud zero pad options on FX GUI --- src/Acquisition/FolderInputSystem.java | 1 + .../layoutFX/AcquisitionPaneFX.java | 91 +++++++++---------- src/Acquisition/layoutFX/FolderInputPane.java | 56 +++++++++++- .../pamAudio/PamAudioFileManager.java | 3 +- src/Acquisition/sud/SudAudioSettingsPane.java | 64 +++++++++++-- .../fxNodes/flipPane/PamFlipPane.java | 2 +- 6 files changed, 160 insertions(+), 57 deletions(-) diff --git a/src/Acquisition/FolderInputSystem.java b/src/Acquisition/FolderInputSystem.java index 5357638a..7f777aa1 100644 --- a/src/Acquisition/FolderInputSystem.java +++ b/src/Acquisition/FolderInputSystem.java @@ -853,6 +853,7 @@ public class FolderInputSystem extends FileInputSystem implements PamSettings, D if (skipSecondsField!=null) { skipSecondsField.setText(String.format("%.1f", fileInputParameters.skipStartFileTime/1000.)); } + } @Override diff --git a/src/Acquisition/layoutFX/AcquisitionPaneFX.java b/src/Acquisition/layoutFX/AcquisitionPaneFX.java index 59400599..14fc557e 100644 --- a/src/Acquisition/layoutFX/AcquisitionPaneFX.java +++ b/src/Acquisition/layoutFX/AcquisitionPaneFX.java @@ -6,23 +6,18 @@ import javafx.collections.FXCollections; import javafx.collections.ObservableList; import javafx.geometry.Insets; import javafx.geometry.Orientation; -import javafx.geometry.Pos; import javafx.scene.Node; import javafx.scene.control.Label; import javafx.scene.control.TextField; import javafx.scene.layout.HBox; import javafx.scene.layout.Pane; import javafx.scene.layout.Priority; -import javafx.scene.paint.Color; import javafx.stage.Stage; import javafx.util.StringConverter; import pamViewFX.PamGuiManagerFX; -import pamViewFX.fxGlyphs.PamGlyphDude; import pamViewFX.fxNodes.PamBorderPane; -import pamViewFX.fxNodes.PamButton; import pamViewFX.fxNodes.PamComboBox; import pamViewFX.fxNodes.PamGridPane; -import pamViewFX.fxNodes.PamHBox; import pamViewFX.fxNodes.PamTextField; import pamViewFX.fxNodes.PamVBox; import pamViewFX.fxNodes.flipPane.FlipPane; @@ -43,7 +38,7 @@ import dataMap.filemaps.OfflineFileParameters; */ public class AcquisitionPaneFX extends SettingsPane{ - private final static int TEXT_FIELD_WIDTH = 60; + private final static int TEXT_FIELD_WIDTH = 80; /** * Reference to acquisition control. @@ -120,13 +115,13 @@ public class AcquisitionPaneFX extends SettingsPane{ /** * */ - private FlipPane flipPane; + private PamFlipPane flipPane; /** * Pane which can be used for advanced settings. */ - private PamBorderPane advancedSettingPane; +// private PamBorderPane advancedSettingPane; /** * Title label for the advanced pane. @@ -147,12 +142,13 @@ public class AcquisitionPaneFX extends SettingsPane{ super(null); mainPane = new PamBorderPane(); mainPane.setPrefWidth(400); + mainPane.setPadding(new Insets(5,5,5,5)); this.acquisitionControl=aquisitionControl; this.acquisitionParameters=acquisitionControl.getAcquisitionParameters(); //create the flip pane. - flipPane=new FlipPane(); + flipPane=new PamFlipPane(); flipPane.setFlipDirection(Orientation.HORIZONTAL); flipPane.setFlipTime(PamFlipPane.FLIP_TIME); //default is 700ms- way too high //flipPane.prefWidthProperty().bind(mainPane.widthProperty()); @@ -166,45 +162,45 @@ public class AcquisitionPaneFX extends SettingsPane{ flipPane.getFront().getChildren().add(mainPane); //create the advanced flip pane. - advancedSettingPane = createAdvSettingsPane(); - flipPane.getBack().getChildren().add(advancedSettingPane); +// advancedSettingPane = createAdvSettingsPane(); +// flipPane.getBack().getChildren().add(advancedSettingPane); //System.out.println("MAKE PANE: " + acquisitionParameters.getDaqSystemType()); } - /** - * Create the advanced settings pane which can be accessed by DAQ panes if needed. - */ - private PamBorderPane createAdvSettingsPane() { - - PamButton back = new PamButton(); - back.setGraphic(PamGlyphDude.createPamIcon("mdi2c-chevron-left", Color.WHITE, PamGuiManagerFX.iconSize)); - - back.setOnAction((action)->{ - flipPane.flipToFront(); - }); - - PamBorderPane advPane = new PamBorderPane(); - advPane.setPadding(new Insets(5,5,5,5)); - - PamHBox buttonHolder = new PamHBox(); - - buttonHolder.setBackground(null); - //buttonHolder.setStyle("-fx-background-color: red;"); - buttonHolder.setAlignment(Pos.CENTER_LEFT); - buttonHolder.getChildren().addAll(back, advLabel = new Label("Adv. Settings")); - advLabel.setAlignment(Pos.CENTER); - advLabel.setMaxWidth(Double.MAX_VALUE); //need to make sure label is in center. - PamGuiManagerFX.titleFont2style(advLabel); - - advLabel.setAlignment(Pos.CENTER); - HBox.setHgrow(advLabel, Priority.ALWAYS); - - advPane.setTop(buttonHolder); - - return advPane; - - } +// /** +// * Create the advanced settings pane which can be accessed by DAQ panes if needed. +// */ +// private PamBorderPane createAdvSettingsPane() { +// +// PamButton back = new PamButton(); +// back.setGraphic(PamGlyphDude.createPamIcon("mdi2c-chevron-left", Color.WHITE, PamGuiManagerFX.iconSize)); +// +// back.setOnAction((action)->{ +// flipPane.flipToFront(); +// }); +// +// PamBorderPane advPane = new PamBorderPane(); +// advPane.setPadding(new Insets(5,5,5,5)); +// +// PamHBox buttonHolder = new PamHBox(); +// +// buttonHolder.setBackground(null); +// //buttonHolder.setStyle("-fx-background-color: red;"); +// buttonHolder.setAlignment(Pos.CENTER_LEFT); +// buttonHolder.getChildren().addAll(back, advLabel = new Label("Adv. Settings")); +// advLabel.setAlignment(Pos.CENTER); +// advLabel.setMaxWidth(Double.MAX_VALUE); //need to make sure label is in center. +// PamGuiManagerFX.titleFont2style(advLabel); +// +// advLabel.setAlignment(Pos.CENTER); +// HBox.setHgrow(advLabel, Priority.ALWAYS); +// +// advPane.setTop(buttonHolder); +// +// return advPane; +// +// } /** * Create the Sound Aquisition pane for real time monitoring. @@ -606,11 +602,12 @@ public class AcquisitionPaneFX extends SettingsPane{ } public PamBorderPane getAdvancedPane() { - return this.advancedSettingPane; + return this.flipPane.getAdvContentPane() +; } - public Label getAdvancedLabel() { - return this.advLabel; + public TextField getAdvancedLabel() { + return this.flipPane.getAdvLabel(); } } diff --git a/src/Acquisition/layoutFX/FolderInputPane.java b/src/Acquisition/layoutFX/FolderInputPane.java index 48e7cb16..5a6d2366 100644 --- a/src/Acquisition/layoutFX/FolderInputPane.java +++ b/src/Acquisition/layoutFX/FolderInputPane.java @@ -1,5 +1,6 @@ package Acquisition.layoutFX; +import java.awt.GridBagConstraints; import java.io.File; import java.util.ArrayList; import java.util.List; @@ -12,6 +13,8 @@ import Acquisition.FileInputParameters; import Acquisition.FolderInputParameters; import Acquisition.FolderInputSystem; import Acquisition.pamAudio.PamAudioFileFilter; +import Acquisition.pamAudio.PamAudioFileLoader; +import Acquisition.pamAudio.PamAudioFileManager; import PamController.PamController; import PamController.PamFolders; import PamUtils.PamCalendar; @@ -147,6 +150,8 @@ public class FolderInputPane extends DAQSettingsPane{ */ private ToggleButton mergeContigious; + private PamBorderPane audioHolderloader; + // /** // * The folder input system. // * @param folderInputSystem - the folder system. @@ -245,8 +250,11 @@ public class FolderInputPane extends DAQSettingsPane{ Label utilsLabel=new Label("Sound file utilities"); PamGuiManagerFX.titleFont2style(utilsLabel); + // + audioHolderloader = new PamBorderPane(); + pamVBox.getChildren().addAll(fileSelectBox, subFolderPane, progressBar, createTablePane(), - fileDateText=new Label(), utilsLabel, createUtilsPane()); + fileDateText=new Label(), audioHolderloader, utilsLabel, createUtilsPane()); //allow users to check file headers in viewer mode. // if (PamController.getInstance().getRunMode() == PamController.RUN_PAMVIEW) { @@ -361,6 +369,7 @@ public class FolderInputPane extends DAQSettingsPane{ return new PamBorderPane(table); } + /** * Open a dialog to select either a folder or a list of files. * @param folderDir - true to use directory chooser, false to use multiple file chooser. @@ -594,6 +603,9 @@ public class FolderInputPane extends DAQSettingsPane{ this.table.getItems().addAll(fileList); fileDateStrip.setFileList(fileList); + + //set any bespoke options for the files to be laoded. + setFileOptionPane(fileList); //need to set the sample rate and channels in the main pane. if (fileList!=null && fileList.size()>0) { @@ -609,6 +621,34 @@ public class FolderInputPane extends DAQSettingsPane{ // fileDateStrip.setDate(folderInputSystem.getFileStartTime(selFiles[0])); // } } + + + /** + * Set bespoke options for certain file types. + */ + public void setFileOptionPane(ObservableList fileList) { + + audioHolderloader.setCenter(null); + + if (fileList.size() > 0) { + //Get all the audio file laoders that will be used for this list of files. Usually + //just one but possible that there can be mixed files. + ArrayList loaders = PamAudioFileManager.getInstance().getAudioFileLoaders(fileList); + + PamVBox vBox = new PamVBox(); + vBox.setSpacing(5); + for (PamAudioFileLoader loader : loaders) { + if (loader.getSettingsPane()!=null) { + //add the settings pane to the vbox + vBox.getChildren().add(loader.getSettingsPane().getAudioLoaderPane()); + loader.getSettingsPane().setParams(); + } + } + audioHolderloader.setCenter(vBox); + + } + + } /** * Called whenever a new file worker is initialised to search a folder for wav files. @@ -637,16 +677,30 @@ public class FolderInputPane extends DAQSettingsPane{ public void setParams() { //set the parameters for the dialog. this.setParams(folderInputSystem.getFolderInputParameters()); + + } @Override public boolean getParams() { FolderInputParameters params = this.getParams(folderInputSystem.getFolderInputParameters()); + + //get bespoke paramters from selected audio loaders. Note these are global and so are not part + //of the folder input system + ArrayList loaders = PamAudioFileManager.getInstance().getAudioFileLoaders(); + + for (PamAudioFileLoader loader : loaders) { + if (loader.getSettingsPane()!=null) { + loader.getSettingsPane().getParams(); + } + } + if (params == null) return false; else { this.folderInputSystem.setFolderInputParameters(params); return true; } + } diff --git a/src/Acquisition/pamAudio/PamAudioFileManager.java b/src/Acquisition/pamAudio/PamAudioFileManager.java index cf57787e..aa321e30 100644 --- a/src/Acquisition/pamAudio/PamAudioFileManager.java +++ b/src/Acquisition/pamAudio/PamAudioFileManager.java @@ -3,6 +3,7 @@ package Acquisition.pamAudio; import java.io.File; import java.io.IOException; import java.util.ArrayList; +import java.util.List; import javax.sound.sampled.AudioInputStream; import javax.sound.sampled.UnsupportedAudioFileException; @@ -177,7 +178,7 @@ public class PamAudioFileManager { * @param files - the files to find audio loaders for. * @return a list of the audio loaders required for the file list */ - public ArrayList getAudioFileLoaders(ArrayList files) { + public ArrayList getAudioFileLoaders(List files) { ArrayList audioLoaders = new ArrayList(); PamAudioFileLoader loader; for (int i=0; i Date: Wed, 10 Jul 2024 21:40:53 +0100 Subject: [PATCH 06/10] Implementing a data selector for the deep learning module --- src/export/swing/ExportProcessDialog.java | 4 +- src/rawDeepLearningClassifier/DLControl.java | 5 +- .../dataPlotFX/DLSymbolOptionPane.java | 2 +- .../dataSelector/DLDataFilter.java | 35 +++++ .../dataSelector/DLDataSelectCreator.java | 34 +++++ .../dataSelector/DLDataSelector.java | 120 ++++++++++++++++++ .../dataSelector/DLDataSelectorParams.java | 22 ++++ .../dataSelector/DLPredictionFilter.java | 107 ++++++++++++++++ .../DLPredictionFilterParams.java | 35 +++++ .../dataSelector/DLSelectPaneFX.java | 47 +++++++ .../dataSelector/DLSelectPanel.java | 34 +++++ .../dlClassification/DLClassiferModel.java | 2 +- .../dlClassification/DLClassifyProcess.java | 2 +- .../DLDetectionDataBlock.java | 36 ++++++ .../DLPredictionDecision.java | 26 ++++ .../dlClassification/SimpleDLDecision.java | 49 +++++++ .../StandardClassifierModel.java | 45 ++++--- 17 files changed, 578 insertions(+), 27 deletions(-) create mode 100644 src/rawDeepLearningClassifier/dataSelector/DLDataFilter.java create mode 100644 src/rawDeepLearningClassifier/dataSelector/DLDataSelectCreator.java create mode 100644 src/rawDeepLearningClassifier/dataSelector/DLDataSelector.java create mode 100644 src/rawDeepLearningClassifier/dataSelector/DLDataSelectorParams.java create mode 100644 src/rawDeepLearningClassifier/dataSelector/DLPredictionFilter.java create mode 100644 src/rawDeepLearningClassifier/dataSelector/DLPredictionFilterParams.java create mode 100644 src/rawDeepLearningClassifier/dataSelector/DLSelectPaneFX.java create mode 100644 src/rawDeepLearningClassifier/dataSelector/DLSelectPanel.java create mode 100644 src/rawDeepLearningClassifier/dlClassification/DLPredictionDecision.java create mode 100644 src/rawDeepLearningClassifier/dlClassification/SimpleDLDecision.java diff --git a/src/export/swing/ExportProcessDialog.java b/src/export/swing/ExportProcessDialog.java index 7008b7f8..93184f13 100644 --- a/src/export/swing/ExportProcessDialog.java +++ b/src/export/swing/ExportProcessDialog.java @@ -76,7 +76,6 @@ public class ExportProcessDialog { public void createExportGroup() { - //clear current tasks. dlOfflineGroup.clearTasks(); @@ -88,7 +87,6 @@ public class ExportProcessDialog { dlOfflineGroup.addTask(new ExportTask(dataBlocks.get(i), exportManager)); } } - } ////---Swing stuff----/// should not be here but this is how PG works. @@ -388,7 +386,7 @@ public class ExportProcessDialog { /** - * Export taks group. + * Export task */ class ExportTaskGroup extends OfflineTaskGroup { diff --git a/src/rawDeepLearningClassifier/DLControl.java b/src/rawDeepLearningClassifier/DLControl.java index 4b7d3713..67126845 100644 --- a/src/rawDeepLearningClassifier/DLControl.java +++ b/src/rawDeepLearningClassifier/DLControl.java @@ -622,8 +622,9 @@ public class DLControl extends PamControlledUnit implements PamSettings { } /** - * Get the classifier chooser. - * @return the classifier chooser.Take it + * Get the classifier chooser. The classifier chooser chooses which classifier use + * based on a selected file or URL. + * @return the classifier chooser. */ public DLClassifierChooser getDlClassifierChooser() { return dlClassifierChooser; diff --git a/src/rawDeepLearningClassifier/dataPlotFX/DLSymbolOptionPane.java b/src/rawDeepLearningClassifier/dataPlotFX/DLSymbolOptionPane.java index 7023de9d..7e6370dc 100644 --- a/src/rawDeepLearningClassifier/dataPlotFX/DLSymbolOptionPane.java +++ b/src/rawDeepLearningClassifier/dataPlotFX/DLSymbolOptionPane.java @@ -361,7 +361,7 @@ public class DLSymbolOptionPane extends StandardSymbolModifierPane { classNameBox2.getSelectionModel().select(Math.max(symbolOptions.classIndex2, 0)); - int index = symbolOptions.classIndex2>=0? symbolOptions.classIndex2 : 0; + int index = symbolOptions.classIndex2>=0 ? symbolOptions.classIndex2 : 0; if (symbolOptions.classColors==null) { symbolOptions.setDefaultClassColors(nClass); diff --git a/src/rawDeepLearningClassifier/dataSelector/DLDataFilter.java b/src/rawDeepLearningClassifier/dataSelector/DLDataFilter.java new file mode 100644 index 00000000..f4527b5d --- /dev/null +++ b/src/rawDeepLearningClassifier/dataSelector/DLDataFilter.java @@ -0,0 +1,35 @@ +package rawDeepLearningClassifier.dataSelector; + +import PamguardMVC.PamDataUnit; +import PamguardMVC.dataSelector.DataSelectParams; + +/** + * Score a data unit with a deep learning annotation. Note that this could be an + * external data unit with an annotation e.g. a click, or deep learning + * detections generated by the deep learning module. + * + * @author Jamie Macaulay + */ +public interface DLDataFilter { + + /** + * Score a data unit with deep learning annotations + * @param dataUnit - the data unit to score + * @return 0 to reject and >0 to accept. + */ + public int scoreDLData(PamDataUnit dataUnit); + + /** + * Get parameters for the data filter. + * @return parameters for the data selectors. + */ + public DataSelectParams getParams(); + + /** + * Set the parameters for the data filter. + * @param params - the parameters to set. + */ + public void setParams(DataSelectParams params); + + +} diff --git a/src/rawDeepLearningClassifier/dataSelector/DLDataSelectCreator.java b/src/rawDeepLearningClassifier/dataSelector/DLDataSelectCreator.java new file mode 100644 index 00000000..a4cf341c --- /dev/null +++ b/src/rawDeepLearningClassifier/dataSelector/DLDataSelectCreator.java @@ -0,0 +1,34 @@ +package rawDeepLearningClassifier.dataSelector; + +import PamguardMVC.PamDataBlock; +import PamguardMVC.dataSelector.DataSelectParams; +import PamguardMVC.dataSelector.DataSelector; +import PamguardMVC.dataSelector.DataSelectorCreator; +import clickDetector.dataSelector.ClickDataSelector; +import rawDeepLearningClassifier.DLControl; + +/** + * Creates a data selector for the deep learning module. + * + * @author Jamie Macaulay + */ +public class DLDataSelectCreator extends DataSelectorCreator { + + private DLControl dlcontrol; + + public DLDataSelectCreator(DLControl dlcontrol, PamDataBlock pamDataBlock) { + super(pamDataBlock); + this.dlcontrol = dlcontrol; + } + + @Override + public DataSelectParams createNewParams(String name) { + return new DLDataSelectorParams(); + } + + @Override + public DataSelector createDataSelector(String selectorName, boolean allowScores, String selectorType) { + return new DLDataSelector(dlcontrol, this.getPamDataBlock(), selectorName, allowScores, selectorType); + } + +} diff --git a/src/rawDeepLearningClassifier/dataSelector/DLDataSelector.java b/src/rawDeepLearningClassifier/dataSelector/DLDataSelector.java new file mode 100644 index 00000000..4ca9913f --- /dev/null +++ b/src/rawDeepLearningClassifier/dataSelector/DLDataSelector.java @@ -0,0 +1,120 @@ +package rawDeepLearningClassifier.dataSelector; + +import java.util.ArrayList; +import java.util.List; + +import PamView.dialog.PamDialogPanel; +import PamguardMVC.PamDataBlock; +import PamguardMVC.PamDataUnit; +import PamguardMVC.dataSelector.DataSelectParams; +import PamguardMVC.dataSelector.DataSelector; +import pamViewFX.fxSettingsPanes.DynamicSettingsPane; +import rawDeepLearningClassifier.DLControl; + +/** + * Data selector of DL data units. Note that data selectors are really data + * unit specific and not annotation specific. + *

+ * The data selector can have different types of data selectors which can + * depend on the classifer used and user choice. + *

+ * Note that this is slightly different from DLPredicitoDecision + * as it deals with data units that may have a more than one prediction. + * i.e. + */ +public class DLDataSelector extends DataSelector { + + + /** + * Swing panel for the data selector. + */ + private DLSelectPanel dlSelectPanel; + + + /** + * FX panel for the data selector. + */ + private DLSelectPaneFX dlSelectPaneFX; + + /** + * Data filter for filtering. + */ + private List dataFilters = new ArrayList (); + + + private DLDataSelectorParams dlDataSelectParams; + + /** + * Constructor for the data selector. + * @param dlcontrol - reference to the DLControl. + * @param pamDataBlock - the data block. + * @param selectorName - the selector name. + * @param allowScores - allow all the scores. + * @param selectorType - the selector type. + */ + public DLDataSelector(DLControl dlcontrol, PamDataBlock pamDataBlock, String selectorName, boolean allowScores, String selectorType) { + super(pamDataBlock, selectorName, allowScores); + /****New data filters go here****/ + dataFilters.add(new DLPredictionFilter(dlcontrol)); + } + + @Override + public PamDialogPanel getDialogPanel() { + if (dlSelectPanel == null) { + dlSelectPanel = new DLSelectPanel(this); + } + return dlSelectPanel; + } + + @Override + public DynamicSettingsPane getDialogPaneFX() { + if (dlSelectPaneFX == null) { + dlSelectPaneFX = new DLSelectPaneFX(this); + } + return dlSelectPaneFX; + } + + @Override + public void setParams(DataSelectParams dataSelectParams) { + + if (dataSelectParams instanceof DLDataSelectorParams) { + dlDataSelectParams = (DLDataSelectorParams) dataSelectParams; + + checkDataFilterParamsSize(dlDataSelectParams); + + //set paramters for all data filters. + for (int i=0; i=0 ? 1 : 0; + } + +} diff --git a/src/rawDeepLearningClassifier/dataSelector/DLDataSelectorParams.java b/src/rawDeepLearningClassifier/dataSelector/DLDataSelectorParams.java new file mode 100644 index 00000000..ebbc48c1 --- /dev/null +++ b/src/rawDeepLearningClassifier/dataSelector/DLDataSelectorParams.java @@ -0,0 +1,22 @@ +package rawDeepLearningClassifier.dataSelector; + +import java.io.Serializable; + +import PamguardMVC.dataSelector.DataSelectParams; + +/** + * Paramters for the DL data seelctor + */ +public class DLDataSelectorParams extends DataSelectParams implements Serializable, Cloneable{ + + private static final long serialVersionUID = 1L; + + public int dataSelectorIndex = 0; + + /** + * List of data selector parameters for different deep learning selectors. + */ + public DataSelectParams[] dataSelectorParams; + + +} diff --git a/src/rawDeepLearningClassifier/dataSelector/DLPredictionFilter.java b/src/rawDeepLearningClassifier/dataSelector/DLPredictionFilter.java new file mode 100644 index 00000000..54d193b1 --- /dev/null +++ b/src/rawDeepLearningClassifier/dataSelector/DLPredictionFilter.java @@ -0,0 +1,107 @@ +package rawDeepLearningClassifier.dataSelector; + + +import PamguardMVC.PamDataUnit; +import PamguardMVC.dataSelector.DataSelectParams; +import rawDeepLearningClassifier.DLControl; +import rawDeepLearningClassifier.dlClassification.DLDetection; +import rawDeepLearningClassifier.dlClassification.PredictionResult; + +/** + * A data filter which filters data by the maximum prediction value + * for different classes. + * + * @author Jamie Macaulay + */ +public class DLPredictionFilter implements DLDataFilter { + + /** + * Reference to the DLControl + */ + private DLControl dlcontrol; + + /** + * The filter parameters + */ + private DLPredictionFilterParams filterParams = new DLPredictionFilterParams(); + + public DLPredictionFilter(DLControl dlcontrol) { + this.dlcontrol = dlcontrol; + checkParamsClass() ; + } + + + @Override + public int scoreDLData(PamDataUnit dataUnit) { + + DLDetection dlDetection = (DLDetection) dataUnit; + + //iterate through all results and check that at least one class passes data selection. + float[] results; + int maxClassIndex = -1; + int maxPred = -1; + + //get the maximum prediction index which passes the minimum threshold + for (PredictionResult modelResult: dlDetection.getModelResults()) { + results = modelResult.getPrediction(); + for (int j=0; jfilterParams.minClassPredicton[j]) { + if (results[j]> maxPred) maxClassIndex = j; + } + } + } + + return maxClassIndex; + } + + @Override + public DataSelectParams getParams() { + checkParamsClass() ; + return filterParams; + } + + @Override + public void setParams(DataSelectParams params) { + this.filterParams = ((DLPredictionFilterParams) params).clone(); + checkParamsClass(); + + } + + private void checkParamsClass() { + if (filterParams.classSelect==null || dlcontrol.getNumClasses()!=filterParams.classSelect.length) { + filterParams.classSelect = new boolean[dlcontrol.getNumClasses()]; + filterParams.minClassPredicton = new double[dlcontrol.getNumClasses()]; + + //set default so data selector does not always prevent all classes from showing. + for (int i=0; i predictions) { +// +// float[][] results = new float[predictions.size()][]; +// +// //A detection might have multiple prediction results, i.e. predictions are a matrix. Need +// //to iterate through all the predictions and then work out whihc is the maximum. That index is then then] +// //class colour. +// int i=0; +// for (PredictionResult modelResult: predictions) { +// results[i] = modelResult.getPrediction(); +// i++; +// } +// +// int[] indexBest = PamArrayUtils.maxPos(results); +// +// return indexBest; +// } + +} diff --git a/src/rawDeepLearningClassifier/dataSelector/DLPredictionFilterParams.java b/src/rawDeepLearningClassifier/dataSelector/DLPredictionFilterParams.java new file mode 100644 index 00000000..b4dda76c --- /dev/null +++ b/src/rawDeepLearningClassifier/dataSelector/DLPredictionFilterParams.java @@ -0,0 +1,35 @@ +package rawDeepLearningClassifier.dataSelector; + +import PamguardMVC.dataSelector.DataSelectParams; + +/** + * Parameters for filtering by the minimum prediciton value. + */ +public class DLPredictionFilterParams extends DataSelectParams { + + private static final long serialVersionUID = 1L; + + /** + * An array indicating which classes are to be used in data selection + */ + public boolean[] classSelect; + + /** + * The minimum class prediction. + */ + public double[] minClassPredicton; + + /** + * Clone the parameters. + */ + public DLPredictionFilterParams clone() { + try { + return (DLPredictionFilterParams) super.clone(); + } catch (CloneNotSupportedException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + return null; + } + +} diff --git a/src/rawDeepLearningClassifier/dataSelector/DLSelectPaneFX.java b/src/rawDeepLearningClassifier/dataSelector/DLSelectPaneFX.java new file mode 100644 index 00000000..b49f9f47 --- /dev/null +++ b/src/rawDeepLearningClassifier/dataSelector/DLSelectPaneFX.java @@ -0,0 +1,47 @@ +package rawDeepLearningClassifier.dataSelector; + +import javafx.scene.Node; +import javafx.scene.control.Label; +import pamViewFX.fxSettingsPanes.DynamicSettingsPane; + +/** + * JavaFX pane for the deep learning data selector. + * + * @author Jamie Macaulay + */ +public class DLSelectPaneFX extends DynamicSettingsPane{ + + public DLSelectPaneFX(Object ownerWindow) { + super(ownerWindow); + // TODO Auto-generated constructor stub + } + + @Override + public Boolean getParams(Boolean currParams) { + // TODO Auto-generated method stub + return currParams; + } + + @Override + public void setParams(Boolean input) { + // TODO Auto-generated method stub + + } + + @Override + public String getName() { + return "Deep Learning Data Selector:"; + } + + @Override + public Node getContentNode() { + return new Label("Hello data selector"); + } + + @Override + public void paneInitialized() { + // TODO Auto-generated method stub + + } + +} diff --git a/src/rawDeepLearningClassifier/dataSelector/DLSelectPanel.java b/src/rawDeepLearningClassifier/dataSelector/DLSelectPanel.java new file mode 100644 index 00000000..bf7f054c --- /dev/null +++ b/src/rawDeepLearningClassifier/dataSelector/DLSelectPanel.java @@ -0,0 +1,34 @@ +package rawDeepLearningClassifier.dataSelector; + +import javax.swing.JComponent; + +import PamView.dialog.PamDialogPanel; + +/** + * Swing panel for the deep learning data selector. + */ +public class DLSelectPanel implements PamDialogPanel { + + public DLSelectPanel(DLDataSelector dlDataSelector) { + // TODO Auto-generated constructor stub + } + + @Override + public JComponent getDialogComponent() { + // TODO Auto-generated method stub + return null; + } + + @Override + public void setParams() { + // TODO Auto-generated method stub + + } + + @Override + public boolean getParams() { + // TODO Auto-generated method stub + return false; + } + +} diff --git a/src/rawDeepLearningClassifier/dlClassification/DLClassiferModel.java b/src/rawDeepLearningClassifier/dlClassification/DLClassiferModel.java index dd0e85b7..7575d80b 100644 --- a/src/rawDeepLearningClassifier/dlClassification/DLClassiferModel.java +++ b/src/rawDeepLearningClassifier/dlClassification/DLClassiferModel.java @@ -115,7 +115,7 @@ public interface DLClassiferModel { /** * Get the parameters which can be serialized from transforms. - * @param dlTransfroms- the dl transforms. + * @param dlTransfroms- the DL transforms. */ public static ArrayList getDLTransformParams(ArrayList dlTransfroms) { ArrayList dlTransformParams = new ArrayList(); diff --git a/src/rawDeepLearningClassifier/dlClassification/DLClassifyProcess.java b/src/rawDeepLearningClassifier/dlClassification/DLClassifyProcess.java index 4f487b5c..5efdee3e 100644 --- a/src/rawDeepLearningClassifier/dlClassification/DLClassifyProcess.java +++ b/src/rawDeepLearningClassifier/dlClassification/DLClassifyProcess.java @@ -415,7 +415,7 @@ public class DLClassifyProcess extends PamInstantProcess { else { /****Add annotation to existing data unit (e.g. click, clip or other RawDataHolder)****/ //Need to go by the parent data unit for merging data not the segments. Note that we may still add multiple - //predicitions to a single data unit depending on how many segments it contains. + //predictions to a single data unit depending on how many segments it contains. //System.out.println("New model data " + pamRawData.getParentDataUnit().getUID() + " " + groupDataBuffer[i].size() + " " + modelResultDataBuffer[i].size()); diff --git a/src/rawDeepLearningClassifier/dlClassification/DLDetectionDataBlock.java b/src/rawDeepLearningClassifier/dlClassification/DLDetectionDataBlock.java index 490925cf..97bfe6d5 100644 --- a/src/rawDeepLearningClassifier/dlClassification/DLDetectionDataBlock.java +++ b/src/rawDeepLearningClassifier/dlClassification/DLDetectionDataBlock.java @@ -3,7 +3,10 @@ package rawDeepLearningClassifier.dlClassification; import PamView.GroupedDataSource; import PamView.GroupedSourceParameters; import PamguardMVC.AcousticDataBlock; +import PamguardMVC.dataSelector.DataSelectorCreator; +import clickTrainDetector.dataselector.CTDataSelectCreator; import rawDeepLearningClassifier.DLControl; +import rawDeepLearningClassifier.dataSelector.DLDataSelectCreator; import rawDeepLearningClassifier.tethys.DLSpeciesManager; import rawDeepLearningClassifier.tethys.DLTethysDataProvider; import tethys.TethysControl; @@ -18,10 +21,30 @@ import tethys.species.DataBlockSpeciesManager; */ public class DLDetectionDataBlock extends AcousticDataBlock implements GroupedDataSource { + /** + * Reference to the deep learning classifier process. + */ private DLClassifyProcess dlClassifyProcess; + + /** + * Reference to the Tethys data provider for the deep learning module (provides standardised metadata). + */ private DLTethysDataProvider dlTethysDataProvider; + + /** + * A species manager for metadata. + */ private DLSpeciesManager dlSpeciesManager; + + /** + * Reference to the deep learning control. + */ private DLControl dlControl; + + /** + * Data selector for the deep learning detections. + */ + private DLDataSelectCreator dlDataSelectCreator; public DLDetectionDataBlock(String dataName, DLClassifyProcess parentProcess, int channelMap) { super(DLDetection.class, dataName, parentProcess, channelMap); @@ -49,6 +72,19 @@ public class DLDetectionDataBlock extends AcousticDataBlock impleme } return dlSpeciesManager; } + + + /* (non-Javadoc) + * @see PamguardMVC.PamDataBlock#getDataSelectCreator() + */ + @Override + public synchronized DataSelectorCreator getDataSelectCreator() { + if (dlDataSelectCreator == null) { + dlDataSelectCreator = new DLDataSelectCreator(dlControl, this); + } + return dlDataSelectCreator; + + } } diff --git a/src/rawDeepLearningClassifier/dlClassification/DLPredictionDecision.java b/src/rawDeepLearningClassifier/dlClassification/DLPredictionDecision.java new file mode 100644 index 00000000..1ba8555e --- /dev/null +++ b/src/rawDeepLearningClassifier/dlClassification/DLPredictionDecision.java @@ -0,0 +1,26 @@ +package rawDeepLearningClassifier.dlClassification; + +/** + * Decides whether a prediction has passed a threshold to be used to create a new + * data unit. + *

+ * Note that the majority of the time this will be a simple test of the value of + * predictions of a model but there will be cases when a classifier implements a + * more complex system. For example, a implementation could save a buffer of predictions + * so that previous predictions inform the latest prediction. Or results may include + * some sort of object detection components and frequency bounds etc could be used for + * classification. + * + * + */ +public interface DLPredictionDecision { + + /** + * Check whether a single prediction passes a binary classifier. Prediction which pass decision will be + * passed on to create new data units. + * @param result - the prediciton result to test. + * @return true if the result is passed. + */ + public boolean isBinaryResult(PredictionResult result); + +} diff --git a/src/rawDeepLearningClassifier/dlClassification/SimpleDLDecision.java b/src/rawDeepLearningClassifier/dlClassification/SimpleDLDecision.java new file mode 100644 index 00000000..248faf4f --- /dev/null +++ b/src/rawDeepLearningClassifier/dlClassification/SimpleDLDecision.java @@ -0,0 +1,49 @@ +package rawDeepLearningClassifier.dlClassification; + +import rawDeepLearningClassifier.dlClassification.animalSpot.StandardModelParams; + +/** + * Make a decision based on a simple binary threshold for a prediction. + */ +public class SimpleDLDecision implements DLPredictionDecision { + + /** + * Reference to the parameters. + */ + private StandardModelParams params; + + + @Override + public boolean isBinaryResult(PredictionResult modelResult) { + return isBinaryResult(modelResult, getParams()) ; + } + + + /** + * Check whether a model passes a binary test... + * @param modelResult - the model results + * @return the model results. + */ + private static boolean isBinaryResult(PredictionResult modelResult, StandardModelParams genericModelParams) { + for (int i=0; igenericModelParams.threshold && genericModelParams.binaryClassification[i]) { + // System.out.println("SoundSpotClassifier: prediciton: " + i + " passed threshold with val: " + modelResult.getPrediction()[i]); + return true; + } + } + return false; + } + + + public StandardModelParams getParams() { + return params; + } + + + public void setParams( StandardModelParams genericModelParams) { + this.params = genericModelParams; + } + +} diff --git a/src/rawDeepLearningClassifier/dlClassification/StandardClassifierModel.java b/src/rawDeepLearningClassifier/dlClassification/StandardClassifierModel.java index bc11e883..7d363263 100644 --- a/src/rawDeepLearningClassifier/dlClassification/StandardClassifierModel.java +++ b/src/rawDeepLearningClassifier/dlClassification/StandardClassifierModel.java @@ -44,6 +44,12 @@ public abstract class StandardClassifierModel implements DLClassiferModel, PamSe * in real time without slowing down the rest of PAMGaurd. */ private TaskThread workerThread; + + /** + * Makes a binary decision on whether a prediction result should go on + * to be part of a data unit. + */ + private SimpleDLDecision simpleDLDecision = new SimpleDLDecision(); public StandardClassifierModel(DLControl dlControl) { @@ -84,7 +90,7 @@ public abstract class StandardClassifierModel implements DLClassiferModel, PamSe for (int i =0; igenericModelParams.threshold && genericModelParams.binaryClassification[i]) { - // System.out.println("SoundSpotClassifier: prediciton: " + i + " passed threshold with val: " + modelResult.getPrediction()[i]); - return true; - } - } - return false; - } +// /** +// * Check whether a model passes a binary test... +// * @param modelResult - the model results +// * @return the model results. +// */ +// public static boolean isBinaryResult(StandardPrediction modelResult, StandardModelParams genericModelParams) { +// for (int i=0; igenericModelParams.threshold && genericModelParams.binaryClassification[i]) { +// // System.out.println("SoundSpotClassifier: prediciton: " + i + " passed threshold with val: " + modelResult.getPrediction()[i]); +// return true; +// } +// } +// return false; +// } @Override From 19393586394881ba55235709517e675a299838f2 Mon Sep 17 00:00:00 2001 From: Jamie Mac Date: Thu, 11 Jul 2024 11:27:25 +0100 Subject: [PATCH 07/10] Bug fix to data transforms if the wave data and sample length are different --- src/PamguardMVC/RawDataTransforms.java | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/PamguardMVC/RawDataTransforms.java b/src/PamguardMVC/RawDataTransforms.java index 44690ed1..2c7380b3 100644 --- a/src/PamguardMVC/RawDataTransforms.java +++ b/src/PamguardMVC/RawDataTransforms.java @@ -411,7 +411,15 @@ public class RawDataTransforms { paddedRawData = new double[fftLength]; rawData = getWaveData(channel); //double[] rotData = getRotationCorrection(channel); - mn = Math.min(fftLength, getSampleDuration().intValue()); + + /** + *FIXME + * 11/07 Changed from getSampleDuration because an error sometimes occurs where the sample duration + * is not the same as the wavefom length...not sure why. + */ + //mn = Math.min(fftLength, getSampleDuration().intValue()); + mn = Math.min(fftLength, rawData.length); +// System.out.println("fftLength: " + rawData.length + " " + getSampleDuration().intValue() + " mn " +mn); for (i = 0; i < mn; i++) { paddedRawData[i] = rawData[i];//-rotData[i]; } From f9e87e92ab87086598967f8fec8862a60dcc986d Mon Sep 17 00:00:00 2001 From: Jamie Mac Date: Fri, 12 Jul 2024 08:33:03 +0100 Subject: [PATCH 08/10] Updated POm to ignore test resources and updates to DL data selector --- .classpath | 2 +- .settings/org.eclipse.core.resources.prefs | 1 + .settings/org.eclipse.jdt.core.prefs | 6 +- dependency-reduced-pom.xml | 1 + pom.xml | 3 +- .../dataSelector/CompoundDataSelector.java | 8 +- .../dataSelector/CompoundDialogPaneFX.java | 81 ++++++++++++++++++ .../dataSelector/CompoundDialogPanel.java | 2 - .../DataSelectorDialogPaneFX.java | 42 +++++++++ .../clickPlotFX/ClickControlPane2.java | 1 + .../dataSelector/DLDataSelectCreator.java | 22 +++-- .../dataSelector/DLDataSelector.java | 34 ++++++-- .../dataSelector/DLSelectPanel.java | 5 +- .../dlClassification/DLDataUnit.java | 2 +- .../DLDetectionDataBlock.java | 22 ++--- .../layoutFX/.DS_Store | Bin 6148 -> 6148 bytes .../logging/DLAnnotationType.java | 14 ++- 17 files changed, 208 insertions(+), 38 deletions(-) create mode 100644 src/PamguardMVC/dataSelector/CompoundDialogPaneFX.java create mode 100644 src/PamguardMVC/dataSelector/DataSelectorDialogPaneFX.java diff --git a/.classpath b/.classpath index 49faf461..924124d8 100644 --- a/.classpath +++ b/.classpath @@ -6,7 +6,7 @@ - + diff --git a/.settings/org.eclipse.core.resources.prefs b/.settings/org.eclipse.core.resources.prefs index 51bb81c3..a474f512 100644 --- a/.settings/org.eclipse.core.resources.prefs +++ b/.settings/org.eclipse.core.resources.prefs @@ -1,5 +1,6 @@ eclipse.preferences.version=1 encoding//src/rawDeepLearningClassifer/segmenter/SegmenterProcess.java=UTF-8 +encoding//src/test=UTF-8 encoding//src/test/resources=UTF-8 encoding/=UTF-8 encoding/src=UTF-8 diff --git a/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs index ecb498c8..43939db6 100644 --- a/.settings/org.eclipse.jdt.core.prefs +++ b/.settings/org.eclipse.jdt.core.prefs @@ -1,9 +1,9 @@ eclipse.preferences.version=1 org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate -org.eclipse.jdt.core.compiler.codegen.targetPlatform=17 +org.eclipse.jdt.core.compiler.codegen.targetPlatform=21 org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve -org.eclipse.jdt.core.compiler.compliance=17 +org.eclipse.jdt.core.compiler.compliance=21 org.eclipse.jdt.core.compiler.debug.lineNumber=generate org.eclipse.jdt.core.compiler.debug.localVariable=generate org.eclipse.jdt.core.compiler.debug.sourceFile=generate @@ -13,4 +13,4 @@ org.eclipse.jdt.core.compiler.problem.enumIdentifier=error org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=warning org.eclipse.jdt.core.compiler.release=enabled -org.eclipse.jdt.core.compiler.source=17 +org.eclipse.jdt.core.compiler.source=21 diff --git a/dependency-reduced-pom.xml b/dependency-reduced-pom.xml index e135ce29..1775ad8d 100644 --- a/dependency-reduced-pom.xml +++ b/dependency-reduced-pom.xml @@ -107,6 +107,7 @@ META-INF/*.SF META-INF/*.DSA META-INF/*.RSA + test/resources/** diff --git a/pom.xml b/pom.xml index dd3572a4..0295d525 100644 --- a/pom.xml +++ b/pom.xml @@ -101,6 +101,7 @@ META-INF/*.SF META-INF/*.DSA META-INF/*.RSA + test/resources/** @@ -179,7 +180,7 @@ --> - + diff --git a/src/PamguardMVC/dataSelector/CompoundDataSelector.java b/src/PamguardMVC/dataSelector/CompoundDataSelector.java index 923baf6c..78effad5 100644 --- a/src/PamguardMVC/dataSelector/CompoundDataSelector.java +++ b/src/PamguardMVC/dataSelector/CompoundDataSelector.java @@ -13,6 +13,8 @@ public class CompoundDataSelector extends DataSelector { private CompoundParams compoundParams = new CompoundParams(); + private CompoundDialogPaneFX compoundPaneFX; + public CompoundDataSelector(PamDataBlock pamDataBlock, ArrayList allSelectors, String selectorName, boolean allowScores, String selectorType) { super(pamDataBlock, selectorName, allowScores); @@ -57,8 +59,10 @@ public class CompoundDataSelector extends DataSelector { @Override public DynamicSettingsPane getDialogPaneFX() { - // TODO Auto-generated method stub - return null; + if (compoundPaneFX==null) { + compoundPaneFX = new CompoundDialogPaneFX(this); + } + return compoundPaneFX; } @Override diff --git a/src/PamguardMVC/dataSelector/CompoundDialogPaneFX.java b/src/PamguardMVC/dataSelector/CompoundDialogPaneFX.java new file mode 100644 index 00000000..23ca4b9d --- /dev/null +++ b/src/PamguardMVC/dataSelector/CompoundDialogPaneFX.java @@ -0,0 +1,81 @@ +package PamguardMVC.dataSelector; + +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.util.ArrayList; + +import javax.swing.JComponent; +import javax.swing.JPanel; + +import PamView.dialog.PamDialogPanel; +import PamView.dialog.PamGridBagContraints; +import javafx.scene.Node; +import pamViewFX.fxNodes.PamBorderPane; +import pamViewFX.fxNodes.PamVBox; +import pamViewFX.fxSettingsPanes.DynamicSettingsPane; + +public class CompoundDialogPaneFX extends DynamicSettingsPane { + + + private CompoundDataSelector compoundDataSelector; + + private ArrayList selectorList; + private ArrayList selectorPanels; + + private PamVBox mainPanel; + + public CompoundDialogPaneFX(CompoundDataSelector compoundDataSelector) { + this.compoundDataSelector = compoundDataSelector; + this.selectorList = compoundDataSelector.getSelectorList(); + + mainPanel = new PamVBox(); + mainPanel.setSpacing(5); + selectorPanels = new ArrayList(selectorList.size()); + int ind = 0; + for (DataSelector ds : selectorList) { + DynamicSettingsPane panel = ds.getDialogPaneFX(); + // turn all these panels into the compound ones with the extra enable options. + +// DataSelectorDialogPanel dsp = new DataSelectorDialogPanel(ds, panel, ind++); +// selectorPanels.add(dsp); + + mainPanel.getChildren().add(panel.getContentNode()); + } + } + + + @Override + public Boolean getParams(Boolean currParams) { + boolean ok = true; + for (int i = 0; i < selectorPanels.size(); i++) { + PamDialogPanel panel = selectorPanels.get(i); + ok |= panel.getParams(); + } + return ok; + } + + @Override + public void setParams(Boolean input) { + for (int i = 0; i < selectorPanels.size(); i++) { + PamDialogPanel panel = selectorPanels.get(i); + panel.setParams(); + } + } + + @Override + public String getName() { + return "Compound data selector pane"; + } + + @Override + public Node getContentNode() { + return mainPanel; + } + + @Override + public void paneInitialized() { + // TODO Auto-generated method stub + + } + +} diff --git a/src/PamguardMVC/dataSelector/CompoundDialogPanel.java b/src/PamguardMVC/dataSelector/CompoundDialogPanel.java index 6ce8cc0f..8f648caa 100644 --- a/src/PamguardMVC/dataSelector/CompoundDialogPanel.java +++ b/src/PamguardMVC/dataSelector/CompoundDialogPanel.java @@ -1,11 +1,9 @@ package PamguardMVC.dataSelector; -import java.awt.Color; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.util.ArrayList; -import javax.swing.BoxLayout; import javax.swing.JComponent; import javax.swing.JPanel; diff --git a/src/PamguardMVC/dataSelector/DataSelectorDialogPaneFX.java b/src/PamguardMVC/dataSelector/DataSelectorDialogPaneFX.java new file mode 100644 index 00000000..97894ae9 --- /dev/null +++ b/src/PamguardMVC/dataSelector/DataSelectorDialogPaneFX.java @@ -0,0 +1,42 @@ +package PamguardMVC.dataSelector; + +import javafx.scene.Node; +import pamViewFX.fxSettingsPanes.DynamicSettingsPane; + +public class DataSelectorDialogPaneFX extends DynamicSettingsPane { + + public DataSelectorDialogPaneFX(Object ownerWindow) { + super(ownerWindow); + // TODO Auto-generated constructor stub + } + + @Override + public Boolean getParams(Boolean currParams) { + // TODO Auto-generated method stub + return null; + } + + @Override + public void setParams(Boolean input) { + // TODO Auto-generated method stub + + } + + @Override + public String getName() { + return "Data selector wrapper pane"; + } + + @Override + public Node getContentNode() { + // TODO Auto-generated method stub + return null; + } + + @Override + public void paneInitialized() { + // TODO Auto-generated method stub + + } + +} diff --git a/src/dataPlotsFX/clickPlotFX/ClickControlPane2.java b/src/dataPlotsFX/clickPlotFX/ClickControlPane2.java index ad31f10c..655ee46a 100644 --- a/src/dataPlotsFX/clickPlotFX/ClickControlPane2.java +++ b/src/dataPlotsFX/clickPlotFX/ClickControlPane2.java @@ -242,6 +242,7 @@ public class ClickControlPane2 extends PamBorderPane implements TDSettingsPane { * @return the data select pane. */ private DynamicSettingsPane createDataSelectPane(){ + System.out.println("Data selector: " + clickPlotInfo.getClickDataSelector()); return clickPlotInfo.getClickDataSelector().getDialogPaneFX(); } diff --git a/src/rawDeepLearningClassifier/dataSelector/DLDataSelectCreator.java b/src/rawDeepLearningClassifier/dataSelector/DLDataSelectCreator.java index a4cf341c..e484aa86 100644 --- a/src/rawDeepLearningClassifier/dataSelector/DLDataSelectCreator.java +++ b/src/rawDeepLearningClassifier/dataSelector/DLDataSelectCreator.java @@ -1,23 +1,24 @@ package rawDeepLearningClassifier.dataSelector; -import PamguardMVC.PamDataBlock; import PamguardMVC.dataSelector.DataSelectParams; import PamguardMVC.dataSelector.DataSelector; -import PamguardMVC.dataSelector.DataSelectorCreator; -import clickDetector.dataSelector.ClickDataSelector; +import annotation.DataAnnotationType; +import annotation.dataselect.AnnotationDataSelCreator; import rawDeepLearningClassifier.DLControl; +import rawDeepLearningClassifier.logging.DLAnnotation; +import rawDeepLearningClassifier.logging.DLAnnotationType; /** * Creates a data selector for the deep learning module. * * @author Jamie Macaulay */ -public class DLDataSelectCreator extends DataSelectorCreator { +public class DLDataSelectCreator extends AnnotationDataSelCreator { private DLControl dlcontrol; - public DLDataSelectCreator(DLControl dlcontrol, PamDataBlock pamDataBlock) { - super(pamDataBlock); + public DLDataSelectCreator(DLControl dlcontrol, DLAnnotationType type) { + super(type); this.dlcontrol = dlcontrol; } @@ -27,8 +28,13 @@ public class DLDataSelectCreator extends DataSelectorCreator { } @Override - public DataSelector createDataSelector(String selectorName, boolean allowScores, String selectorType) { - return new DLDataSelector(dlcontrol, this.getPamDataBlock(), selectorName, allowScores, selectorType); + public DataSelector createDataSelector(DataAnnotationType dataAnnotationType, String selectorName, + boolean allowScores, String selectorType) { + // TODO Auto-generated method stub + return new DLDataSelector(dlcontrol, dataAnnotationType, null, + selectorName, allowScores); } + + } diff --git a/src/rawDeepLearningClassifier/dataSelector/DLDataSelector.java b/src/rawDeepLearningClassifier/dataSelector/DLDataSelector.java index 4ca9913f..9fe55e04 100644 --- a/src/rawDeepLearningClassifier/dataSelector/DLDataSelector.java +++ b/src/rawDeepLearningClassifier/dataSelector/DLDataSelector.java @@ -8,21 +8,24 @@ import PamguardMVC.PamDataBlock; import PamguardMVC.PamDataUnit; import PamguardMVC.dataSelector.DataSelectParams; import PamguardMVC.dataSelector.DataSelector; +import annotation.DataAnnotationType; +import annotation.dataselect.AnnotationDataSelector; import pamViewFX.fxSettingsPanes.DynamicSettingsPane; import rawDeepLearningClassifier.DLControl; +import rawDeepLearningClassifier.logging.DLAnnotation; /** - * Data selector of DL data units. Note that data selectors are really data - * unit specific and not annotation specific. + * Data selector of DL data units. Note that data selectors are for deep leanring annotations + * rather than deep learning data units. *

* The data selector can have different types of data selectors which can - * depend on the classifer used and user choice. + * depend on the classifier used and user choice. *

* Note that this is slightly different from DLPredicitoDecision * as it deals with data units that may have a more than one prediction. * i.e. */ -public class DLDataSelector extends DataSelector { +public class DLDataSelector extends AnnotationDataSelector { /** @@ -52,10 +55,19 @@ public class DLDataSelector extends DataSelector { * @param allowScores - allow all the scores. * @param selectorType - the selector type. */ - public DLDataSelector(DLControl dlcontrol, PamDataBlock pamDataBlock, String selectorName, boolean allowScores, String selectorType) { - super(pamDataBlock, selectorName, allowScores); + public DLDataSelector(DLControl dlcontrol, DataAnnotationType annotationType, PamDataBlock pamDataBlock, + String selectorName, boolean allowScores) { + super(annotationType, pamDataBlock, selectorName, allowScores); /****New data filters go here****/ dataFilters.add(new DLPredictionFilter(dlcontrol)); + + //create default params + dlDataSelectParams = new DLDataSelectorParams(); + dlDataSelectParams.dataSelectorParams = new DataSelectParams[dataFilters.size()]; + for (int i=0; i=0 ? 1 : 0; } + @Override + protected double scoreData(PamDataUnit pamDataUnit, DLAnnotation annotation) { + int score = dataFilters.get(dlDataSelectParams.dataSelectorIndex).scoreDLData(pamDataUnit); + + //the score is the index of the class that scores highest or -1 if it does not pass threshold prediciton. + //Need to make more simple here as scores in PG are 0 for not passed rather than negative. + + return score>=0 ? 1 : 0; + } + } diff --git a/src/rawDeepLearningClassifier/dataSelector/DLSelectPanel.java b/src/rawDeepLearningClassifier/dataSelector/DLSelectPanel.java index bf7f054c..c56e4d7e 100644 --- a/src/rawDeepLearningClassifier/dataSelector/DLSelectPanel.java +++ b/src/rawDeepLearningClassifier/dataSelector/DLSelectPanel.java @@ -1,6 +1,7 @@ package rawDeepLearningClassifier.dataSelector; import javax.swing.JComponent; +import javax.swing.JLabel; import PamView.dialog.PamDialogPanel; @@ -16,7 +17,7 @@ public class DLSelectPanel implements PamDialogPanel { @Override public JComponent getDialogComponent() { // TODO Auto-generated method stub - return null; + return new JLabel("Hello Annotation DL"); } @Override @@ -28,7 +29,7 @@ public class DLSelectPanel implements PamDialogPanel { @Override public boolean getParams() { // TODO Auto-generated method stub - return false; + return true; } } diff --git a/src/rawDeepLearningClassifier/dlClassification/DLDataUnit.java b/src/rawDeepLearningClassifier/dlClassification/DLDataUnit.java index 4fa03e38..ffd4e002 100644 --- a/src/rawDeepLearningClassifier/dlClassification/DLDataUnit.java +++ b/src/rawDeepLearningClassifier/dlClassification/DLDataUnit.java @@ -5,7 +5,7 @@ import PamguardMVC.PamDataUnit; import rawDeepLearningClassifier.dlClassification.genericModel.StandardPrediction; /** - * A data unit created from classification results of DL model. this data unit holds one model results, i.e. + * A data unit created from classification results of DL model. This data unit holds one model results, i.e. * corresponds to one segment of raw data. * * @author Jamie Macaulay diff --git a/src/rawDeepLearningClassifier/dlClassification/DLDetectionDataBlock.java b/src/rawDeepLearningClassifier/dlClassification/DLDetectionDataBlock.java index 97bfe6d5..f0f4c43e 100644 --- a/src/rawDeepLearningClassifier/dlClassification/DLDetectionDataBlock.java +++ b/src/rawDeepLearningClassifier/dlClassification/DLDetectionDataBlock.java @@ -74,17 +74,17 @@ public class DLDetectionDataBlock extends AcousticDataBlock impleme } - /* (non-Javadoc) - * @see PamguardMVC.PamDataBlock#getDataSelectCreator() - */ - @Override - public synchronized DataSelectorCreator getDataSelectCreator() { - if (dlDataSelectCreator == null) { - dlDataSelectCreator = new DLDataSelectCreator(dlControl, this); - } - return dlDataSelectCreator; - - } +// /* (non-Javadoc) +// * @see PamguardMVC.PamDataBlock#getDataSelectCreator() +// */ +// @Override +// public synchronized DataSelectorCreator getDataSelectCreator() { +// if (dlDataSelectCreator == null) { +// dlDataSelectCreator = new DLDataSelectCreator(dlControl, this); +// } +// return dlDataSelectCreator; +// +// } } diff --git a/src/rawDeepLearningClassifier/layoutFX/.DS_Store b/src/rawDeepLearningClassifier/layoutFX/.DS_Store index d9c2dd0a3d5a22ccdf3788f0a560bfde534aec5f..854e4d03182745e66c5d4bd06b5b0533eb97ae01 100644 GIT binary patch literal 6148 zcmeHKy-ve05I#ePingc{3u7iWhQ0s_RghRZg^8)8DP>6Fs%a5p-h+XMV1k8-5s7Eu zK@i{BmPjc5VW|M!N%oi6clLK*WIHAz)t$v1qBarLQ5b7GsJ=1o=Q3wou4Naf%osCD zXi7cO6wi4}gD4;h{5A!6?Y6L5hjd97R9?SBtmQlzb;5y8$Cmcqbh&q`gCI(D1h0DU z{NN>fe?DFn5&tk^d{t)TVR&kSe?n(8K?WJ+$Yg>YDE-1-H6EAwmbs^=&FqV$9N8XhlNG-z?2OI+EC@L7|MpD-E)4v!@{BsCuL^FIBsU;ZYavk zj&{$ulkzP}EeePNRRy-~Yn#vilg0P{YLL8%0;0gbQb5&$Fz6yBcec(Y$7ijF(nn!q ozrvykLFJBP^T0>(K8i8)dHewM9TpbR0+SyBBZE|;z(y7L0N1~qZvX%Q delta 94 zcmZoMXfc=|#>CJ*u~2NHo}wrd0|Nsi1A_nqLjgk$Ln=csLq0?4#6tDS1|lqz9oVEN yFJe{S+{sqQxLJXNon>RgM#jzT9Q+(W12zkCd}p4_FJj2ZFxiGjdUK4(3T6Nl5f { private DLAnnotationSymbolChooser dlSymbolChooser; + private DLDataSelectCreator dlDataSelectorCreator; + public DLAnnotationType(DLControl mtControl) { this.dlControl=mtControl; dlAnnotationSQL = new DLAnnotationSQL(this); @@ -92,6 +95,15 @@ public class DLAnnotationType extends DataAnnotationType { public DLControl getDlControl() { return dlControl; } + + + @Override + public AnnotationDataSelCreator getDataSelectCreator(String selectorName, boolean allowScores) { + if (dlDataSelectorCreator == null) { + dlDataSelectorCreator = new DLDataSelectCreator(dlControl, this); + } + return dlDataSelectorCreator; + } From 3f06d8faf0c1a6e920e40d9e0226bb6374d0ec95 Mon Sep 17 00:00:00 2001 From: Jamie Mac Date: Sun, 14 Jul 2024 22:15:55 +0100 Subject: [PATCH 09/10] Updates to DL data selector and POM --- pom.xml | 2 +- .../dataSelector/CompoundDialogPaneFX.java | 36 ++-- .../DataSelectorDialogPaneFX.java | 184 ++++++++++++++++-- src/Resources/css/pamSettingsCSS.css | 24 +++ .../dataSelector/ClickSelectPaneFX.java | 2 +- .../TemplateSpectrumPane.java | 5 +- .../clickPlotFX/ClickControlPane2.java | 3 +- src/detectionPlotFX/plots/SpectrumPlot.java | 8 +- .../dataPlotFX/DLSymbolOptionPane.java | 12 +- .../dataSelector/DLDataFilter.java | 7 + .../dataSelector/DLDataSelector.java | 9 + .../dataSelector/DLPredictionFilter.java | 8 + .../dataSelector/DLPredictonPane.java | 77 ++++++++ .../dataSelector/DLSelectPaneFX.java | 47 ++++- 14 files changed, 371 insertions(+), 53 deletions(-) create mode 100644 src/rawDeepLearningClassifier/dataSelector/DLPredictonPane.java diff --git a/pom.xml b/pom.xml index 0295d525..d9100e53 100644 --- a/pom.xml +++ b/pom.xml @@ -309,7 +309,7 @@ io.github.macster110 jdl4pam - 0.0.99a + 0.0.99b diff --git a/src/PamguardMVC/dataSelector/CompoundDialogPaneFX.java b/src/PamguardMVC/dataSelector/CompoundDialogPaneFX.java index 23ca4b9d..decc0be6 100644 --- a/src/PamguardMVC/dataSelector/CompoundDialogPaneFX.java +++ b/src/PamguardMVC/dataSelector/CompoundDialogPaneFX.java @@ -1,45 +1,45 @@ package PamguardMVC.dataSelector; -import java.awt.GridBagConstraints; -import java.awt.GridBagLayout; import java.util.ArrayList; -import javax.swing.JComponent; -import javax.swing.JPanel; - -import PamView.dialog.PamDialogPanel; -import PamView.dialog.PamGridBagContraints; import javafx.scene.Node; -import pamViewFX.fxNodes.PamBorderPane; import pamViewFX.fxNodes.PamVBox; import pamViewFX.fxSettingsPanes.DynamicSettingsPane; + +/** + * shows multiple data selectors in a pane. This would be used, for example, in a situation where + * a data unit has multiple associated data selectors, for example if is annotated with an annotation + * that has a data selector. + */ public class CompoundDialogPaneFX extends DynamicSettingsPane { private CompoundDataSelector compoundDataSelector; private ArrayList selectorList; - private ArrayList selectorPanels; + private ArrayList selectorPanels; private PamVBox mainPanel; public CompoundDialogPaneFX(CompoundDataSelector compoundDataSelector) { + super(null); + this.compoundDataSelector = compoundDataSelector; this.selectorList = compoundDataSelector.getSelectorList(); mainPanel = new PamVBox(); - mainPanel.setSpacing(5); - selectorPanels = new ArrayList(selectorList.size()); + mainPanel.setSpacing(10); + selectorPanels = new ArrayList(selectorList.size()); int ind = 0; for (DataSelector ds : selectorList) { DynamicSettingsPane panel = ds.getDialogPaneFX(); // turn all these panels into the compound ones with the extra enable options. -// DataSelectorDialogPanel dsp = new DataSelectorDialogPanel(ds, panel, ind++); -// selectorPanels.add(dsp); + DataSelectorDialogPaneFX dsp = new DataSelectorDialogPaneFX(ds, panel, ind++); + selectorPanels.add(dsp); - mainPanel.getChildren().add(panel.getContentNode()); + mainPanel.getChildren().add(dsp.getContentNode()); } } @@ -48,8 +48,8 @@ public class CompoundDialogPaneFX extends DynamicSettingsPane { public Boolean getParams(Boolean currParams) { boolean ok = true; for (int i = 0; i < selectorPanels.size(); i++) { - PamDialogPanel panel = selectorPanels.get(i); - ok |= panel.getParams(); + DataSelectorDialogPaneFX panel = selectorPanels.get(i); + ok |= panel.getParams(currParams); } return ok; } @@ -57,8 +57,8 @@ public class CompoundDialogPaneFX extends DynamicSettingsPane { @Override public void setParams(Boolean input) { for (int i = 0; i < selectorPanels.size(); i++) { - PamDialogPanel panel = selectorPanels.get(i); - panel.setParams(); + DataSelectorDialogPaneFX panel = selectorPanels.get(i); + panel.setParams(input); } } diff --git a/src/PamguardMVC/dataSelector/DataSelectorDialogPaneFX.java b/src/PamguardMVC/dataSelector/DataSelectorDialogPaneFX.java index 97894ae9..79b06257 100644 --- a/src/PamguardMVC/dataSelector/DataSelectorDialogPaneFX.java +++ b/src/PamguardMVC/dataSelector/DataSelectorDialogPaneFX.java @@ -1,42 +1,198 @@ package PamguardMVC.dataSelector; +import org.controlsfx.control.SegmentedButton; + +import javafx.geometry.Pos; import javafx.scene.Node; +import javafx.scene.control.Label; +import javafx.scene.control.Separator; +import javafx.scene.control.ToggleButton; +import javafx.scene.control.ToggleGroup; +import javafx.scene.control.Tooltip; +import javafx.scene.layout.HBox; +import pamViewFX.PamGuiManagerFX; +import pamViewFX.fxNodes.PamVBox; import pamViewFX.fxSettingsPanes.DynamicSettingsPane; + +/** + * Dialog panel to wrap around a standard dialog panel from a data selector. + * This adds a wrapper the data selector which enables or disables it based on + * whether it has been selected or not. + * @author Jamie Macaulay + * + */ public class DataSelectorDialogPaneFX extends DynamicSettingsPane { - public DataSelectorDialogPaneFX(Object ownerWindow) { - super(ownerWindow); - // TODO Auto-generated constructor stub + private static final double PREF_TOGGLE_WIDTH = 60; + + private DataSelector dataSelector; + private DynamicSettingsPane innerPanel; + private int setIndex; + + private PamVBox dsPane; + private ToggleGroup buttonGroup; + private ToggleButton andButton, orButton, disableButton; + private HBox buttonPane; + private Node contentPane; + + /** + * Create the DataSelectorDialogPaneFX + * @param dataSelector + * @param innerPanel + * @param setIndex + */ + public DataSelectorDialogPaneFX(DataSelector dataSelector, DynamicSettingsPane innerPanel, int setIndex) { + super(null); + this.dataSelector = dataSelector; + this.innerPanel = innerPanel; + this.setIndex = setIndex; + + dsPane = new PamVBox(); // Use VBox for vertical layout + dsPane.setSpacing(5); + + contentPane = innerPanel.getContentNode(); + + // Border exBorder = innerComponent.getBorder(); + // + // if (exBorder instanceof TitledBorder) { + // innerComponent.setBorder(null); + // // Set a lower bevel border if desired: + // // innerComponent.setBorder(new BevelBorder(BevelBorder.LOWERED)); + // dsPane.setBorder(exBorder); + // } else { + // dsPane.setBorder(new TitledBorder(dataSelector.getSelectorTitle())); + // } + + Label title = new Label(innerPanel.getName()); + title.setTooltip(new Tooltip("Data selector: " + dataSelector.getLongSelectorName())); + PamGuiManagerFX.titleFont2style(title); + + + buttonGroup = new ToggleGroup(); + andButton = new ToggleButton(setIndex == 0 ? "Enable" : "AND"); + andButton.setPrefWidth(PREF_TOGGLE_WIDTH); + + disableButton = new ToggleButton("Skip"); + disableButton.setPrefWidth(PREF_TOGGLE_WIDTH); + + orButton = new ToggleButton("OR"); + orButton.setPrefWidth(PREF_TOGGLE_WIDTH); + + buttonGroup.getToggles().addAll(andButton, orButton, disableButton); + + SegmentedButton segmentedButton ; + if (setIndex > 0) { + segmentedButton = new SegmentedButton(andButton, orButton, disableButton); + } + else { + segmentedButton = new SegmentedButton(andButton, disableButton); + + } + + + andButton.setOnAction(event -> enableComponent()); + orButton.setOnAction(event -> enableComponent()); + disableButton.setOnAction(event -> enableComponent()); + + buttonPane = new HBox(); // Use HBox for horizontal button layout + buttonPane.setSpacing(5); + buttonPane.setAlignment(Pos.CENTER); + buttonPane.getChildren().addAll(segmentedButton); + + //add everything to the main pane + if (setIndex > 0) { + dsPane.getChildren().add(new Separator()); + } + dsPane.getChildren().add(title); + dsPane.getChildren().add(buttonPane); // Add button pane at top + dsPane.getChildren().add(contentPane); + + + orButton.setVisible(setIndex > 0); + if (dataSelector instanceof CompoundDataSelector || setIndex < 0) { + buttonPane.setVisible(false); + dsPane.setBorder(null); + } + enableComponent(); + //buttonPane.setTooltip(new Tooltip("Options for " + dataSelector.getLongSelectorName())); } - @Override - public Boolean getParams(Boolean currParams) { - // TODO Auto-generated method stub - return null; + + private void enableComponent() { + contentPane.setDisable(disableButton.isSelected()); } @Override public void setParams(Boolean input) { - // TODO Auto-generated method stub - + DataSelectParams params = dataSelector.getParams(); + if (params == null) { + return; + } + if (buttonPane.isVisible()) { + andButton.setSelected(params.getCombinationFlag() == DataSelectParams.DATA_SELECT_AND); + orButton.setSelected(params.getCombinationFlag() == DataSelectParams.DATA_SELECT_OR); + disableButton.setSelected(params.getCombinationFlag() == DataSelectParams.DATA_SELECT_DISABLE); + } else { + andButton.setSelected(true); + orButton.setSelected(false); + disableButton.setSelected(false); + } + + innerPanel.setParams(input); + enableComponent(); } + @Override + public Boolean getParams(Boolean a) { + DataSelectParams params = dataSelector.getParams(); + + if (disableButton.isSelected()) { + if (params != null) { + params.setCombinationFlag(DataSelectParams.DATA_SELECT_DISABLE); + } + return true; + } + + boolean innerOk = innerPanel.getParams(a); + if (!innerOk) { + return false; + } + + params = dataSelector.getParams(); // Might have been recreated + + if (params != null) { + if (andButton.isSelected()) { + params.setCombinationFlag(DataSelectParams.DATA_SELECT_AND); + } else if (orButton.isSelected()) { + params.setCombinationFlag(DataSelectParams.DATA_SELECT_OR); + } else if (disableButton.isSelected()) { + params.setCombinationFlag(DataSelectParams.DATA_SELECT_DISABLE); + } + } + + return innerOk; + } + + // Utility method for disabling/enabling a Node (doesn't work recursively) + public static void setEnabled(Node node, boolean enabled) { + + } + + @Override public String getName() { - return "Data selector wrapper pane"; + return "Data selector"; } @Override public Node getContentNode() { - // TODO Auto-generated method stub - return null; + return dsPane; } @Override public void paneInitialized() { // TODO Auto-generated method stub - - } + } } diff --git a/src/Resources/css/pamSettingsCSS.css b/src/Resources/css/pamSettingsCSS.css index af9f9cf9..9bec582c 100644 --- a/src/Resources/css/pamSettingsCSS.css +++ b/src/Resources/css/pamSettingsCSS.css @@ -987,3 +987,27 @@ .spinner { -fx-pref-width: 100px; } + + +/******************************************** +* * +* Segmented button * +* * +*********************************************/ + +.segmented-button .toggle-button.left-pill { + -fx-border-radius: 5 0 0 5; + -fx-background-radius: 5 0 0 5; +} + +.segmented-button .toggle-button.center-pill { + -fx-border-radius: 0 0 0 0; + -fx-background-radius: 0 0 0 0; +} + +.segmented-button .toggle-button.right-pill { + -fx-border-radius: 0 5 5 0; + -fx-background-radius: 0 5 5 0; +} + + diff --git a/src/clickDetector/dataSelector/ClickSelectPaneFX.java b/src/clickDetector/dataSelector/ClickSelectPaneFX.java index 0c1d661d..04e911ff 100644 --- a/src/clickDetector/dataSelector/ClickSelectPaneFX.java +++ b/src/clickDetector/dataSelector/ClickSelectPaneFX.java @@ -95,7 +95,7 @@ public class ClickSelectPaneFX extends DynamicSettingsPane { @Override public String getName() { - return "Click Type Selection"; + return "Filter by click type"; } @Override diff --git a/src/clickTrainDetector/layout/classification/templateClassifier/TemplateSpectrumPane.java b/src/clickTrainDetector/layout/classification/templateClassifier/TemplateSpectrumPane.java index ab4af7c6..b7d2d5d3 100644 --- a/src/clickTrainDetector/layout/classification/templateClassifier/TemplateSpectrumPane.java +++ b/src/clickTrainDetector/layout/classification/templateClassifier/TemplateSpectrumPane.java @@ -185,6 +185,7 @@ public class TemplateSpectrumPane extends PamBorderPane { this.currentSpectrum = new SpectrumTemplateDataUnit(spectrumTemplate); templateDisplay.setDataUnit(currentSpectrum); + } /** @@ -226,7 +227,6 @@ public class TemplateSpectrumPane extends PamBorderPane { if (templateImporters.get(i).getExtension()[j].equals(extension)) { // System.out.println("Import using the extensions: " + extension); template=templateImporters.get(i).importTemplate(file); - } } } @@ -288,7 +288,7 @@ public class TemplateSpectrumPane extends PamBorderPane { } @Override - public double[][] getPowerSpectrum(SpectrumTemplateDataUnit data, int strat, int end) { + public double[][] getPowerSpectrum(SpectrumTemplateDataUnit data, int start, int end) { return new double[][]{data.spectrumTemplate.waveform}; } @@ -296,6 +296,7 @@ public class TemplateSpectrumPane extends PamBorderPane { @Override public double getSampleRate(SpectrumTemplateDataUnit data) { //bit of a hack to get sample rate but works. +// System.out.println("Set sample rate: " + data.spectrumTemplate.sR); return data.spectrumTemplate.sR; } diff --git a/src/dataPlotsFX/clickPlotFX/ClickControlPane2.java b/src/dataPlotsFX/clickPlotFX/ClickControlPane2.java index 655ee46a..b0579368 100644 --- a/src/dataPlotsFX/clickPlotFX/ClickControlPane2.java +++ b/src/dataPlotsFX/clickPlotFX/ClickControlPane2.java @@ -215,6 +215,7 @@ public class ClickControlPane2 extends PamBorderPane implements TDSettingsPane { scrollPane2.setHbarPolicy(ScrollBarPolicy.NEVER); scrollPane2.getStyleClass().clear(); symbolTab.setContent(scrollPane2); + Tab dataView=new Tab("Data"); dataView.setContent(dataSelectHolder); dataView.getStyleClass().add("tab-square"); @@ -242,7 +243,7 @@ public class ClickControlPane2 extends PamBorderPane implements TDSettingsPane { * @return the data select pane. */ private DynamicSettingsPane createDataSelectPane(){ - System.out.println("Data selector: " + clickPlotInfo.getClickDataSelector()); + System.out.println("DATA SELECTOR: " + clickPlotInfo.getClickDataSelector()); return clickPlotInfo.getClickDataSelector().getDialogPaneFX(); } diff --git a/src/detectionPlotFX/plots/SpectrumPlot.java b/src/detectionPlotFX/plots/SpectrumPlot.java index 752130d7..a8a4971b 100644 --- a/src/detectionPlotFX/plots/SpectrumPlot.java +++ b/src/detectionPlotFX/plots/SpectrumPlot.java @@ -74,7 +74,7 @@ public abstract class SpectrumPlot implements Detection private SpectrumSettingsPane spectrumSettingsPane; - private double sR; + private double storedsR; // //TODO @@ -132,7 +132,7 @@ public abstract class SpectrumPlot implements Detection if (data ==null) return; - this.sR=sR; + this.storedsR=sR; int[] minmax = getAxisMinMaxSamples(plotProjector); @@ -172,7 +172,7 @@ public abstract class SpectrumPlot implements Detection } private double getSampleRate() { - return sR; + return storedsR; } @Override @@ -234,6 +234,8 @@ public abstract class SpectrumPlot implements Detection storedSpectrum=this.getPowerSpectrum(data, minmax[0], minmax[1]); storedCepstrum=this.getCepstrum(data, minmax[0], minmax[1]); + + storedsR = getSampleRate(data); if (spectrumPlotParams.logScale) { paintLogSpectrum(gc, clipRect,projector); diff --git a/src/rawDeepLearningClassifier/dataPlotFX/DLSymbolOptionPane.java b/src/rawDeepLearningClassifier/dataPlotFX/DLSymbolOptionPane.java index 7e6370dc..13eb8aab 100644 --- a/src/rawDeepLearningClassifier/dataPlotFX/DLSymbolOptionPane.java +++ b/src/rawDeepLearningClassifier/dataPlotFX/DLSymbolOptionPane.java @@ -95,7 +95,7 @@ public class DLSymbolOptionPane extends StandardSymbolModifierPane { b1 = new ToggleButton("Prediction"); b1.setPrefWidth(80); - b1.setStyle("-fx-border-radius: 5 0 0 5; -fx-background-radius: 5 0 0 5;"); +// b1.setStyle("-fx-border-radius: 5 0 0 5; -fx-background-radius: 5 0 0 5;"); b2 = new ToggleButton("Class"); b2.setPrefWidth(80); @@ -105,7 +105,7 @@ public class DLSymbolOptionPane extends StandardSymbolModifierPane { //change the colour of the colour range slider. notifySettingsListeners(); }); - b2.setStyle("-fx-border-radius: 0 5 5 0; -fx-background-radius: 0 5 5 0;"); +// b2.setStyle("-fx-border-radius: 0 5 5 0; -fx-background-radius: 0 5 5 0;"); b2.setOnAction((a)->{ @@ -116,7 +116,9 @@ public class DLSymbolOptionPane extends StandardSymbolModifierPane { SegmentedButton segmentedButton = new SegmentedButton(); segmentedButton.getButtons().addAll(b1, b2); - segmentedButton.setPadding(new Insets(5,0,5,0)); + segmentedButton.setPadding(new Insets(5,0,5,0)); + segmentedButton.getStyleClass().add(SegmentedButton.STYLE_CLASS_DARK); + BorderPane.setAlignment(segmentedButton, Pos.CENTER); b1.setSelected(true); @@ -161,8 +163,10 @@ public class DLSymbolOptionPane extends StandardSymbolModifierPane { }); classNameBox2.setMaxWidth(Double.MAX_VALUE); - classNameBox2.setOnAction((action)->{ + classNameBox2.setOnAction((action)->{ + if (classNameBox2.getSelectionModel().getSelectedIndex()>=0){ colourPicker.setValue(PamUtilsFX.intToColor(dlSymbolModifier.getSymbolModifierParams().classColors[classNameBox2.getSelectionModel().getSelectedIndex()])); + } }); classNameBox2.setPrefWidth(CLASS_NAME_BOX_WIDTH); diff --git a/src/rawDeepLearningClassifier/dataSelector/DLDataFilter.java b/src/rawDeepLearningClassifier/dataSelector/DLDataFilter.java index f4527b5d..0a5b2da8 100644 --- a/src/rawDeepLearningClassifier/dataSelector/DLDataFilter.java +++ b/src/rawDeepLearningClassifier/dataSelector/DLDataFilter.java @@ -2,6 +2,7 @@ package rawDeepLearningClassifier.dataSelector; import PamguardMVC.PamDataUnit; import PamguardMVC.dataSelector.DataSelectParams; +import pamViewFX.fxSettingsPanes.DynamicSettingsPane; /** * Score a data unit with a deep learning annotation. Note that this could be an @@ -30,6 +31,12 @@ public interface DLDataFilter { * @param params - the parameters to set. */ public void setParams(DataSelectParams params); + + /** + * Settings controls for this filter. + * @return the cotnrols for this filter. + */ + public DynamicSettingsPane getSettingsPane(); } diff --git a/src/rawDeepLearningClassifier/dataSelector/DLDataSelector.java b/src/rawDeepLearningClassifier/dataSelector/DLDataSelector.java index 9fe55e04..f68ac41d 100644 --- a/src/rawDeepLearningClassifier/dataSelector/DLDataSelector.java +++ b/src/rawDeepLearningClassifier/dataSelector/DLDataSelector.java @@ -10,6 +10,7 @@ import PamguardMVC.dataSelector.DataSelectParams; import PamguardMVC.dataSelector.DataSelector; import annotation.DataAnnotationType; import annotation.dataselect.AnnotationDataSelector; +import javafx.scene.Node; import pamViewFX.fxSettingsPanes.DynamicSettingsPane; import rawDeepLearningClassifier.DLControl; import rawDeepLearningClassifier.logging.DLAnnotation; @@ -139,4 +140,12 @@ public class DLDataSelector extends AnnotationDataSelector { return score>=0 ? 1 : 0; } + public DLDataFilter getCurrentDataSelector() { + return dataFilters.get(dlDataSelectParams.dataSelectorIndex); + } + + public List getDataSelectors() { + return dataFilters; + } + } diff --git a/src/rawDeepLearningClassifier/dataSelector/DLPredictionFilter.java b/src/rawDeepLearningClassifier/dataSelector/DLPredictionFilter.java index 54d193b1..cda9bc7d 100644 --- a/src/rawDeepLearningClassifier/dataSelector/DLPredictionFilter.java +++ b/src/rawDeepLearningClassifier/dataSelector/DLPredictionFilter.java @@ -3,6 +3,7 @@ package rawDeepLearningClassifier.dataSelector; import PamguardMVC.PamDataUnit; import PamguardMVC.dataSelector.DataSelectParams; +import pamViewFX.fxSettingsPanes.DynamicSettingsPane; import rawDeepLearningClassifier.DLControl; import rawDeepLearningClassifier.dlClassification.DLDetection; import rawDeepLearningClassifier.dlClassification.PredictionResult; @@ -80,6 +81,13 @@ public class DLPredictionFilter implements DLDataFilter { } } + + + @Override + public DynamicSettingsPane getSettingsPane() { + // TODO Auto-generated method stub + return null; + } // /** // * Get the index of the highest prediciton value a list of results. diff --git a/src/rawDeepLearningClassifier/dataSelector/DLPredictonPane.java b/src/rawDeepLearningClassifier/dataSelector/DLPredictonPane.java new file mode 100644 index 00000000..4b715b47 --- /dev/null +++ b/src/rawDeepLearningClassifier/dataSelector/DLPredictonPane.java @@ -0,0 +1,77 @@ +package rawDeepLearningClassifier.dataSelector; + +import javafx.scene.Node; +import javafx.scene.control.CheckBox; +import pamViewFX.fxNodes.PamHBox; +import pamViewFX.fxNodes.PamSpinner; +import pamViewFX.fxNodes.PamVBox; +import pamViewFX.fxSettingsPanes.DynamicSettingsPane; + +/** + * Settings pane for filtering deep learning results by class prediciton. + */ +public class DLPredictonPane extends DynamicSettingsPane{ + + private DLPredictionFilter predicitonFilter; + + private PamVBox mainPane; + + public DLPredictonPane(DLPredictionFilter predicitonFilter) { + super(null); + this.predicitonFilter=predicitonFilter; + + createPane(); + } + + + private void createPane() { + mainPane = new PamVBox(); + mainPane.setSpacing(5); + } + + class ClassDataSelector extends PamHBox { + + PamSpinner spinner; + + CheckBox enable; + + ClassDataSelector(String classType, int index) { + + enable = new CheckBox(classType); + spinner = new PamSpinner(0., 1., 0.7, 0.1); + spinner.setEditable(true); + + this.getChildren().addAll(getChildrenUnmodifiable()); + } + } + + + @Override + public DLPredictionFilterParams getParams(DLPredictionFilterParams currParams) { + // TODO Auto-generated method stub + return currParams; + } + + @Override + public void setParams(DLPredictionFilterParams input) { + + + } + + @Override + public String getName() { + return "Deep learning prediciton filter"; + } + + @Override + public Node getContentNode() { + return mainPane; + } + + @Override + public void paneInitialized() { + // TODO Auto-generated method stub + + } + +} diff --git a/src/rawDeepLearningClassifier/dataSelector/DLSelectPaneFX.java b/src/rawDeepLearningClassifier/dataSelector/DLSelectPaneFX.java index b49f9f47..e18416aa 100644 --- a/src/rawDeepLearningClassifier/dataSelector/DLSelectPaneFX.java +++ b/src/rawDeepLearningClassifier/dataSelector/DLSelectPaneFX.java @@ -1,41 +1,70 @@ package rawDeepLearningClassifier.dataSelector; import javafx.scene.Node; +import javafx.scene.control.CheckBox; import javafx.scene.control.Label; +import pamViewFX.fxNodes.PamHBox; +import pamViewFX.fxNodes.PamSpinner; +import pamViewFX.fxNodes.PamVBox; import pamViewFX.fxSettingsPanes.DynamicSettingsPane; /** - * JavaFX pane for the deep learning data selector. + * JavaFX pane for the deep learning data selector. This simply selects the rype + * of filter to use and sets that as the controls. * * @author Jamie Macaulay */ public class DLSelectPaneFX extends DynamicSettingsPane{ + + private PamVBox mainPane; + + /** + * Refrence to the deep learning data selector. + */ + private DLDataSelector dlDataSelector; + + private int currentIndex = 0; - public DLSelectPaneFX(Object ownerWindow) { - super(ownerWindow); - // TODO Auto-generated constructor stub + public DLSelectPaneFX(DLDataSelector dlDataSelector) { + super(null); + this.dlDataSelector=dlDataSelector; + + //there is currently one implemented fitler so no + //need for a comboBox etc. to select. + createPane(); + +// mainPane.getChildren().add(dlDataSelector.getDataSelectors().getSettingsPane().getContentNode()); + + } + + private void createPane() { + mainPane = new PamVBox(); + mainPane.setSpacing(5); } + + @Override public Boolean getParams(Boolean currParams) { - // TODO Auto-generated method stub + dlDataSelector.getDataSelectors().get(currentIndex).getSettingsPane().getParams(null); + + return currParams; } @Override public void setParams(Boolean input) { - // TODO Auto-generated method stub - + dlDataSelector.getDataSelectors().get(currentIndex).getSettingsPane().getParams(null); } @Override public String getName() { - return "Deep Learning Data Selector:"; + return "Filter by deep learning result"; } @Override public Node getContentNode() { - return new Label("Hello data selector"); + return new Label("Hello DL data selector"); } @Override From 53d6b7bd88ffdb7860b207f98ed69dfeaac132fe Mon Sep 17 00:00:00 2001 From: Jamie Mac Date: Mon, 15 Jul 2024 15:25:16 +0100 Subject: [PATCH 10/10] Work on the data selector for the deep learning module. --- pom.xml | 2 +- .../dataSelector/CompoundDialogPaneFX.java | 8 + .../DataSelectorDialogPaneFX.java | 6 + .../clickPlotFX/ClickControlPane2.java | 11 +- .../dataSelector/DLDataSelector.java | 6 +- .../dataSelector/DLPredictionFilter.java | 30 +++- .../DLPredictionFilterParams.java | 3 +- .../dataSelector/DLPredictonPane.java | 168 +++++++++++++++--- .../dataSelector/DLSelectPaneFX.java | 56 ++++-- 9 files changed, 238 insertions(+), 52 deletions(-) diff --git a/pom.xml b/pom.xml index d9100e53..7f5eb4e8 100644 --- a/pom.xml +++ b/pom.xml @@ -101,7 +101,7 @@ META-INF/*.SF META-INF/*.DSA META-INF/*.RSA - test/resources/** + test/resources/** diff --git a/src/PamguardMVC/dataSelector/CompoundDialogPaneFX.java b/src/PamguardMVC/dataSelector/CompoundDialogPaneFX.java index decc0be6..bb5ce81e 100644 --- a/src/PamguardMVC/dataSelector/CompoundDialogPaneFX.java +++ b/src/PamguardMVC/dataSelector/CompoundDialogPaneFX.java @@ -39,8 +39,16 @@ public class CompoundDialogPaneFX extends DynamicSettingsPane { DataSelectorDialogPaneFX dsp = new DataSelectorDialogPaneFX(ds, panel, ind++); selectorPanels.add(dsp); + //need to add a listener for settings changes to these panes so it can be passed to this pane + dsp.addSettingsListener(()->{ + //notify any listeners to this pane that the sub pane has changed. + notifySettingsListeners(); + }); + mainPanel.getChildren().add(dsp.getContentNode()); } + + } diff --git a/src/PamguardMVC/dataSelector/DataSelectorDialogPaneFX.java b/src/PamguardMVC/dataSelector/DataSelectorDialogPaneFX.java index 79b06257..bcfa7aeb 100644 --- a/src/PamguardMVC/dataSelector/DataSelectorDialogPaneFX.java +++ b/src/PamguardMVC/dataSelector/DataSelectorDialogPaneFX.java @@ -109,6 +109,12 @@ public class DataSelectorDialogPaneFX extends DynamicSettingsPane { dsPane.getChildren().add(contentPane); + //need to add a listener for settings changes to these panes so it can be passed to this pane + innerPanel.addSettingsListener(()->{ + notifySettingsListeners(); + }); + + orButton.setVisible(setIndex > 0); if (dataSelector instanceof CompoundDataSelector || setIndex < 0) { buttonPane.setVisible(false); diff --git a/src/dataPlotsFX/clickPlotFX/ClickControlPane2.java b/src/dataPlotsFX/clickPlotFX/ClickControlPane2.java index b0579368..3f84e405 100644 --- a/src/dataPlotsFX/clickPlotFX/ClickControlPane2.java +++ b/src/dataPlotsFX/clickPlotFX/ClickControlPane2.java @@ -189,11 +189,6 @@ public class ClickControlPane2 extends PamBorderPane implements TDSettingsPane { dataSelectHolder.setPadding(new Insets(5,5,5,5)); dataSelectPane = createDataSelectPane(); - dataSelectPane.addSettingsListener(()->{ - //dynamic settings pane so have to repaint whenever a control is selected. - getParams(); - clickPlotInfo.getTDGraph().repaint(0); - }); dataSelectPane.setParams(true); @@ -231,6 +226,12 @@ public class ClickControlPane2 extends PamBorderPane implements TDSettingsPane { enablePane(); setFreqType(); setParams(); + + dataSelectPane.addSettingsListener(()->{ + //dynamic settings pane so have to repaint whenever a control is selected. + getParams(); + clickPlotInfo.getTDGraph().repaint(0); + }); this.setCenter(tabPane); diff --git a/src/rawDeepLearningClassifier/dataSelector/DLDataSelector.java b/src/rawDeepLearningClassifier/dataSelector/DLDataSelector.java index f68ac41d..e4858e5f 100644 --- a/src/rawDeepLearningClassifier/dataSelector/DLDataSelector.java +++ b/src/rawDeepLearningClassifier/dataSelector/DLDataSelector.java @@ -97,7 +97,7 @@ public class DLDataSelector extends AnnotationDataSelector { //set paramters for all data filters. for (int i=0; i { } @Override - public DataSelectParams getParams() { + public DLDataSelectorParams getParams() { + //get the paramters from the pane. + for (int i=0; ifilterParams.minClassPredicton[j]) { @@ -84,11 +90,19 @@ public class DLPredictionFilter implements DLDataFilter { @Override - public DynamicSettingsPane getSettingsPane() { - // TODO Auto-generated method stub - return null; + public DynamicSettingsPane getSettingsPane() { + if (dlPredictonPane ==null) { + dlPredictonPane = new DLPredictonPane(this); + } + return dlPredictonPane; } - + + + public DLControl getDLControl() { + return this.dlcontrol; + } + + // /** // * Get the index of the highest prediciton value a list of results. // * @param predictions - index of the highest prediction within a matrix of predicitons. diff --git a/src/rawDeepLearningClassifier/dataSelector/DLPredictionFilterParams.java b/src/rawDeepLearningClassifier/dataSelector/DLPredictionFilterParams.java index b4dda76c..853849b6 100644 --- a/src/rawDeepLearningClassifier/dataSelector/DLPredictionFilterParams.java +++ b/src/rawDeepLearningClassifier/dataSelector/DLPredictionFilterParams.java @@ -5,7 +5,7 @@ import PamguardMVC.dataSelector.DataSelectParams; /** * Parameters for filtering by the minimum prediciton value. */ -public class DLPredictionFilterParams extends DataSelectParams { +public class DLPredictionFilterParams extends DataSelectParams implements Cloneable{ private static final long serialVersionUID = 1L; @@ -22,6 +22,7 @@ public class DLPredictionFilterParams extends DataSelectParams { /** * Clone the parameters. */ + @Override public DLPredictionFilterParams clone() { try { return (DLPredictionFilterParams) super.clone(); diff --git a/src/rawDeepLearningClassifier/dataSelector/DLPredictonPane.java b/src/rawDeepLearningClassifier/dataSelector/DLPredictonPane.java index 4b715b47..219fd25e 100644 --- a/src/rawDeepLearningClassifier/dataSelector/DLPredictonPane.java +++ b/src/rawDeepLearningClassifier/dataSelector/DLPredictonPane.java @@ -1,63 +1,191 @@ package rawDeepLearningClassifier.dataSelector; +import javafx.geometry.Insets; +import javafx.geometry.Pos; import javafx.scene.Node; import javafx.scene.control.CheckBox; +import javafx.scene.control.Label; +import javafx.scene.control.Slider; +import javafx.scene.control.ToggleButton; +import javafx.scene.control.Tooltip; +import javafx.scene.layout.Priority; +import javafx.scene.text.TextAlignment; +import pamViewFX.PamGuiManagerFX; +import pamViewFX.fxGlyphs.PamGlyphDude; +import pamViewFX.fxNodes.PamBorderPane; import pamViewFX.fxNodes.PamHBox; import pamViewFX.fxNodes.PamSpinner; import pamViewFX.fxNodes.PamVBox; +import pamViewFX.fxNodes.sliders.PamSlider; import pamViewFX.fxSettingsPanes.DynamicSettingsPane; +import rawDeepLearningClassifier.dlClassification.DLClassName; /** * Settings pane for filtering deep learning results by class prediciton. */ public class DLPredictonPane extends DynamicSettingsPane{ - + private DLPredictionFilter predicitonFilter; - - private PamVBox mainPane; + + private PamBorderPane mainPane; + + private PamVBox contentPane; + + private ClassDataSelector[] classPanes; + + private ToggleButton toggelButton; public DLPredictonPane(DLPredictionFilter predicitonFilter) { super(null); this.predicitonFilter=predicitonFilter; - createPane(); } - + private void createPane() { - mainPane = new PamVBox(); - mainPane.setSpacing(5); + mainPane = new PamBorderPane(); + + PamBorderPane topPane = new PamBorderPane(); + + Label label = new Label("Select classes to show"); + PamBorderPane.setAlignment(label, Pos.BOTTOM_LEFT); +// label.setTextAlignment(TextAlignment.LEFT); + label.setAlignment(Pos.BOTTOM_LEFT); + + topPane.setLeft(label); + + toggelButton = new ToggleButton(); + toggelButton.setTooltip(new Tooltip("Lock sliders together so one slider changes all values")); + + toggelButton.setGraphic(PamGlyphDude.createPamIcon("mdi2l-lock-open", PamGuiManagerFX.iconSize)); + toggelButton.selectedProperty().addListener((onsVal, oldVal,newVal)->{ + if (newVal) { + toggelButton.setGraphic(PamGlyphDude.createPamIcon("mdi2l-lock", PamGuiManagerFX.iconSize)); + } + else { + toggelButton.setGraphic(PamGlyphDude.createPamIcon("mdi2l-lock-open", PamGuiManagerFX.iconSize)); + } + }); + toggelButton.setSelected(false); + + topPane.setRight(toggelButton); +// topPane.setAlignment(Pos.CENTER_RIGHT); + + contentPane = new PamVBox(); + contentPane.setSpacing(5); + + mainPane.setTop(topPane); + mainPane.setCenter(contentPane); + PamBorderPane.setMargin(topPane, new Insets(0,0,5.,0)); } - + class ClassDataSelector extends PamHBox { - - PamSpinner spinner; - + + Slider slider; + CheckBox enable; + Label valueLabel; + ClassDataSelector(String classType, int index) { - + enable = new CheckBox(classType); - spinner = new PamSpinner(0., 1., 0.7, 0.1); - spinner.setEditable(true); + enable.setPrefWidth(80); + enable.setTooltip(new Tooltip(classType)); + enable.setOnAction((a)->{ + //disable if the class is unticked. + slider.setDisable(!enable.isSelected()); + valueLabel.setDisable(!enable.isSelected()); + notifySettingsListeners(); + }); - this.getChildren().addAll(getChildrenUnmodifiable()); + slider = new Slider(); + slider.setShowTickMarks(true); + slider.setMinorTickCount(10); + slider.setShowTickLabels(false); + slider.setMajorTickUnit(0.5); + + slider.valueProperty().addListener((obsval, oldval, newval)->{ + valueLabel.setText(String.format("%.2f", newval)); + + //if the lock button has been sleected then change all the sliders + //so that they are the same value as this slide (unless the class is disabled) + if (toggelButton.isSelected()) { + for (int i=0; i + * Note that at the moment this only implements one type of data filter and so + * essentially all controls etc. for changing filters are invisible to the user. * * @author Jamie Macaulay */ public class DLSelectPaneFX extends DynamicSettingsPane{ - private PamVBox mainPane; + private PamBorderPane mainPane; /** - * Refrence to the deep learning data selector. + * Reference to the deep learning data selector. */ private DLDataSelector dlDataSelector; + /** + * The current index selected by the user - not in the params. + */ private int currentIndex = 0; public DLSelectPaneFX(DLDataSelector dlDataSelector) { @@ -38,23 +40,47 @@ public class DLSelectPaneFX extends DynamicSettingsPane{ } private void createPane() { - mainPane = new PamVBox(); - mainPane.setSpacing(5); + mainPane = new PamBorderPane(); + + //need to add a settings listener to the filter panes to pass on any notification this settings listener. + for (int i=0; i{ + //notify any listeners to this pane that a filter pane has changed. + notifySettingsListeners(); + }); + } + } - @Override - public Boolean getParams(Boolean currParams) { - dlDataSelector.getDataSelectors().get(currentIndex).getSettingsPane().getParams(null); + public Boolean getParams(Boolean input) { +// System.out.println("Get params DL data selector!"); + DLDataSelectorParams currParams = dlDataSelector.getParams(); + //TODO - maybe should grab settings from all filters or just the selected one? + currParams.dataSelectorParams[currentIndex] = dlDataSelector.getDataSelectors().get(currentIndex).getSettingsPane().getParams(currParams.dataSelectorParams[currentIndex]); - return currParams; + dlDataSelector.setParams(currParams); + + return true; } @Override public void setParams(Boolean input) { - dlDataSelector.getDataSelectors().get(currentIndex).getSettingsPane().getParams(null); + DLDataSelectorParams currParams = dlDataSelector.getParams(); + + this.currentIndex = currParams.dataSelectorIndex; + + dlDataSelector.getDataSelectors().get(currentIndex).getSettingsPane().setParams(currParams.dataSelectorParams[currentIndex]); + + setDataFilterPane(currentIndex); + + } + + private void setDataFilterPane(int index) { + DLDataFilter dlFilter = dlDataSelector.getDataSelectors().get(index); + mainPane.setCenter(dlFilter.getSettingsPane().getContentNode()); } @Override @@ -64,7 +90,7 @@ public class DLSelectPaneFX extends DynamicSettingsPane{ @Override public Node getContentNode() { - return new Label("Hello DL data selector"); + return mainPane; } @Override