Merge from Jamie (#143)

* Working on the exporter

* Working out on exporter

* Working on new symbol options for deep learning and sud file options in SoundAcquisition GUI

* Add zero pad option for sud files.

* Updates to sud zero pad options on FX GUI

* Implementing a data selector for the deep learning module

* Bug fix to data transforms if the wave data and sample length are different

* Updated POm to ignore test resources and updates to DL data selector

* Updates to DL data selector and POM

* Work on the data selector for the deep learning module.

* Working on the exporter

* Working out on exporter

* Working on new symbol options for deep learning and sud file options in SoundAcquisition GUI

* Add zero pad option for sud files.

* Updates to sud zero pad options on FX GUI

* Implementing a data selector for the deep learning module

* Updated POm to ignore test resources and updates to DL data selector

* Bug fix to data transforms if the wave data and sample length are different

* Updates to DL data selector and POM

* Work on the data selector for the deep learning module.

* Data selectors for deep learning models now working with FX

Still have to be implemented in Swing

* Update deep_learning_help.md

Updated help values to have some info on data selectors and symbol managers.

* Updates to deep learning symbol options for swing and data selector panel for swing

Made tooltips last longer on TD display  FX

* Update deep_learning_help.md

* Swing symbol modifiers for DL and peak freq

* Squashed commit of the following:

commit bad2255710
Author: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com>
Date:   Thu Jul 18 09:47:47 2024 +0100

    Better symbol options

    Add clearer options button to multi option symbol manager panel.

* Fix bugs in detection display

* Bug fix to hiding panes

* Squashed commit of the following:

commit 8b5b5b2f18
Author: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com>
Date:   Fri Jul 19 17:14:28 2024 +0100

    Updated data selectors for BT display

    Merging old status bar and new DataSelector options so that they work more consistently on the BT display.

commit bad2255710
Author: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com>
Date:   Thu Jul 18 09:47:47 2024 +0100

    Better symbol options

    Add clearer options button to multi option symbol manager panel.

* Deep learning bug fix

* Ignore the class path

* Squashed commit of the following:

commit 577670ccd0
Merge: 6510d226 8b5b5b2f
Author: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com>
Date:   Mon Jul 22 09:35:01 2024 +0100

    Merge branch 'main' of https://github.com/PAMGuard/PAMGuard

commit 6510d2260e
Author: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com>
Date:   Mon Jul 22 09:34:58 2024 +0100

    Remove unnecessary text output

commit 3da8401756
Author: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com>
Date:   Mon Jul 22 09:29:38 2024 +0100

    updates to Daq

    New DLL, and some LED control, but hasn't fixed stalling problem.

commit 8b5b5b2f18
Author: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com>
Date:   Fri Jul 19 17:14:28 2024 +0100

    Updated data selectors for BT display

    Merging old status bar and new DataSelector options so that they work more consistently on the BT display.

commit bad2255710
Author: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com>
Date:   Thu Jul 18 09:47:47 2024 +0100

    Better symbol options

    Add clearer options button to multi option symbol manager panel.

* Squashed commit of the following:

commit 687220dad5
Author: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com>
Date:   Mon Jul 22 11:42:19 2024 +0100

    Change click data selector

    Change Click BT display so it can handle CompoundDataSelectors

commit 577670ccd0
Merge: 6510d226 8b5b5b2f
Author: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com>
Date:   Mon Jul 22 09:35:01 2024 +0100

    Merge branch 'main' of https://github.com/PAMGuard/PAMGuard

commit 6510d2260e
Author: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com>
Date:   Mon Jul 22 09:34:58 2024 +0100

    Remove unnecessary text output

commit 3da8401756
Author: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com>
Date:   Mon Jul 22 09:29:38 2024 +0100

    updates to Daq

    New DLL, and some LED control, but hasn't fixed stalling problem.

commit 8b5b5b2f18
Author: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com>
Date:   Fri Jul 19 17:14:28 2024 +0100

    Updated data selectors for BT display

    Merging old status bar and new DataSelector options so that they work more consistently on the BT display.

commit bad2255710
Author: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com>
Date:   Thu Jul 18 09:47:47 2024 +0100

    Better symbol options

    Add clearer options button to multi option symbol manager panel.

---------

Co-authored-by: Jamie Mac <macster110@gmail.com>
This commit is contained in:
Douglas Gillespie 2024-07-22 11:55:06 +01:00 committed by GitHub
parent 687220dad5
commit 32c7c9b263
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
91 changed files with 3861 additions and 394 deletions

View File

@ -6,7 +6,7 @@
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-17">
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-21">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>

1
.gitignore vendored
View File

@ -111,3 +111,4 @@ settings.xml
.classpath
.classpath
.settings/org.eclipse.jdt.core.prefs
.classpath

View File

@ -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/<project>=UTF-8
encoding/src=UTF-8

View File

@ -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

24
pom.xml
View File

@ -4,7 +4,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>org.pamguard</groupId>
<artifactId>Pamguard</artifactId>
<version>2.02.12</version>
<version>2.02.11d</version>
<name>Pamguard</name>
<description>Pamguard using Maven to control dependencies</description>
<url>www.pamguard.org</url>
@ -101,7 +101,7 @@
<exclude>META-INF/*.SF</exclude> <!-- get rid of manifests from library jars - also done in orig ant build file -->
<exclude>META-INF/*.DSA</exclude>
<exclude>META-INF/*.RSA</exclude>
<exclude>test/resources/**</exclude>
<exclude>test/resources/**</exclude><!-- get rid of deep learning moduels and wav files used for unit tests -->
</excludes>
</filter>
</filters>
@ -181,24 +181,6 @@
</executions>
</plugin>
<!-- Exclude the resource folder from build
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<version>3.2.0</version>
<configuration>
<resources>
<resource>
<directory>src/Resources</directory>
<excludes>
<exclude>src/test/resources/**</exclude>
</excludes>
</resource>
</resources>
</configuration>
</plugin>-->
</plugins>
@ -598,7 +580,7 @@
<dependency>
<groupId>com.fazecast</groupId>
<artifactId>jSerialComm</artifactId>
<version>2.11.0</version>
<version>2.5.3</version>
</dependency>
<!-- https://mvnrepository.com/artifact/edu.emory.mathcs/JTransforms -->

View File

@ -1,26 +1,28 @@
# 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.
2) **Processing and visuslisation of large datasets** -
1) **Real time operation**: Almost all PAMGuard features and modules work in real time - this allows scientists and industry to detect, classify and localise the position of animals in real time on a standard consumer laptop, enabling mitigation and research surveys without expensive bespoke software solutions and the transparncy of open source software.
2) **Processing and visuslisation of large datasets**: Off-the-shelf autonomous PAM devices, large scale surveys involving drifters, towed arrays, glidders, 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 crucially provides comprehensive 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.
PAMGuard is available on Windows and can be downloaded from the [PAMGuard website](www.pamguard.org). Note that we are considering MacOS and Linux installers but they are not available at this time.
## Tutorial
## Quick start tutorial
PAMGuard is a modular program with two modes; real-time and viewer. Typically a user will start with real-time model, either in the field collecting data or post processing sound files from a recorder. Once data are processed, users move on to viewer mode where data can be explored and further processed.
Upon opening PAMGuard for the first time you are greeted with a blank screen. You must add a series of modules to create the desired acosutic workflow. For example if processing sound files then first add the Sound Acquisition module **_File->Add Modules->Sound Processing->Sound Acquisition_**. Then add the desired detection algorothms e.g. **_File->Add Modules->Detector->Click Detectors_**. Some modules (such as the click detector) have their own displays, others are added to more generalised displays. For example, the whislte and moan detector module shows detections on a spectrgram display. First add a new tab using **_File->Add Modules->Displays->User Display**. Click on the user display tab and then from the top menu select **_User display-> New Spectrgram_**. Right click on the added spectrgram and select whistle and moan contours to show whistle detections overlaid on the raw spectrgram.
Upon opening PAMGuard for the first time you are greeted with a blank screen. You must add a series of modules to create the desired acosutic workflow - this is referred to as PAMGuard *data model*. For example if processing sound files then first add the Sound Acquisition module **_File->Add Modules->Sound Processing->Sound Acquisition_**. Then add the desired detection algorothms e.g. **_File->Add Modules->Detector->Click Detectors_**. Some modules (such as the click detector) have their own bespoke displays, others are added to more generalised displays. For example, the whistle and moan detector module shows detections on a spectrgram display, time base display, map etc.. First add a new tab using **_File->Add Modules->Displays->User Display**. Click on the user display tab and then from the top menu select **_User display-> New Spectrogram_**. Right click on the added spectrgram and select whistle and moan contours to show whistle detections overlaid on the raw spectrgram.
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.
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 running might take hours or even days. Progress is shown on the bottom of the screen.
Once the data has run, open PAMGuard viewer mode. Select the database you used to process the data along and thebianry file storage path and PAMGuard will open, showing an overview of the dataset in a new _Data map_ tab. Right click anywhere on the data map and select "Center data here" - PAMGuard will load the data for the selected period which can be explored using whichever displays have been added to the data model.
## 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,31 +38,35 @@ 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 annotation tools. Users can add data to annotations in multiple ways, from simple text annotations to complex forms created by users.
### 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 more efficient analysis of data.
### Meatadata standard and Tethys compatibility
### Metadata standard and Tethys compatibility
PAMGuard Integrates with Tethys database. Users can export processed PAMGuard data to a Tethys database seamlessly; this ifeature is great for large scale projects or organisatiosn with long term datasets.
## 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;
* Support for decidecade noise bands (base 10 filter bank) in noise band monitor to meet Euopean standards
* Capabaility to export data directly from PAMGaurd e.g. as MAT files.
* Capabaility to export data directly from PAMGaurd e.g. as MAT files (in progress).
* Automated test suite to make releases more stable. Note that unit and integration tests are also being slowly incorporated.
* Support for ARM based computers (in progress).
* A graphical user interface and Python library for training PAMGuard compatible deep learning models.
## Development
This is the main code repository for the PAMGuard software and was created on 7 January 2022 from a [sourceforge SVN repository](https://sourceforge.net/p/pamguard/svn/HEAD/tree/) revision r6278.

View File

@ -181,7 +181,6 @@ public class FileInputSystem extends DaqSystem implements ActionListener, PamSe
this.acquisitionDialog = acquisitionDialog;
return getDialogPanel();
}
@ -198,7 +197,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};
@ -253,6 +252,7 @@ public class FileInputSystem extends DaqSystem implements ActionListener, PamSe
// constraints.gridwidth = 2;
// addComponent(p, fileDateText = new JTextField(), constraints);
// fileDateText.setEnabled(false);
return p;
}

View File

@ -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;
@ -93,6 +94,12 @@ public class FolderInputSystem extends FileInputSystem implements PamSettings, D
*/
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() {
currentFileStart = System.currentTimeMillis();
@ -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);
// }
//panel to show bespoke settings for certain audio loaders.
constraints.anchor = GridBagConstraints.WEST;
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<WavFileType> 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);
});
}
}
@ -573,6 +597,38 @@ public class FolderInputSystem extends FileInputSystem implements PamSettings, D
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<PamAudioFileLoader> 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);
loader.getSettingsPane().setParams();
constraints.gridy++;
}
}
}
}
/**
* Show the date of the first file in the dialog.
*/
public void setFileDateText() {
if (allFiles.size() > 0) {
long fileTime = getFileStartTime(getCurrentFile());
@ -768,6 +824,17 @@ public class FolderInputSystem extends FileInputSystem implements PamSettings, D
return false;
}
}
//get bespoke paramters from selected audio loaders.
ArrayList<PamAudioFileLoader> loaders = PamAudioFileManager.getInstance().getAudioFileLoaders(allFiles);
for (PamAudioFileLoader loader : loaders) {
if (loader.getSettingsPane()!=null) {
loader.getSettingsPane().getParams();
}
}
return super.dialogGetParams();
}
@ -786,6 +853,7 @@ public class FolderInputSystem extends FileInputSystem implements PamSettings, D
if (skipSecondsField!=null) {
skipSecondsField.setText(String.format("%.1f", fileInputParameters.skipStartFileTime/1000.));
}
}
@Override
@ -952,4 +1020,6 @@ public class FolderInputSystem extends FileInputSystem implements PamSettings, D
}
}

View File

@ -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<AcquisitionParameters>{
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<AcquisitionParameters>{
/**
*
*/
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<AcquisitionParameters>{
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<AcquisitionParameters>{
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<AcquisitionParameters>{
}
public PamBorderPane getAdvancedPane() {
return this.advancedSettingPane;
return this.flipPane.getAdvContentPane()
;
}
public Label getAdvancedLabel() {
return this.advLabel;
public TextField getAdvancedLabel() {
return this.flipPane.getAdvLabel();
}
}

View File

@ -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;
@ -48,7 +51,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
*
@ -147,6 +150,8 @@ public class FolderInputPane extends DAQSettingsPane<FolderInputParameters>{
*/
private ToggleButton mergeContigious;
private PamBorderPane audioHolderloader;
// /**
// * The folder input system.
// * @param folderInputSystem - the folder system.
@ -245,8 +250,11 @@ public class FolderInputPane extends DAQSettingsPane<FolderInputParameters>{
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<FolderInputParameters>{
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.
@ -595,6 +604,9 @@ public class FolderInputPane extends DAQSettingsPane<FolderInputParameters>{
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) {
this.acquisitionPaneFX.setSampleRate(fileList.get(0).getAudioInfo().getSampleRate());
@ -610,6 +622,34 @@ public class FolderInputPane extends DAQSettingsPane<FolderInputParameters>{
// }
}
/**
* Set bespoke options for certain file types.
*/
public void setFileOptionPane(ObservableList<WavFileType> 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<PamAudioFileLoader> 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.
* @param worker - the new file worker being used.
@ -637,16 +677,30 @@ public class FolderInputPane extends DAQSettingsPane<FolderInputParameters>{
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<PamAudioFileLoader> 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;
}
}

View File

@ -178,7 +178,7 @@ public class OfflineWavFileServer extends OfflineFileServer<FileDataMapPoint> {
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());

View File

@ -233,4 +233,10 @@ public class FlacAudioFile implements PamAudioFileLoader {
return (end.equals(".flac"));
}
@Override
public PamAudioSettingsPane getSettingsPane() {
// TODO Auto-generated method stub
return null;
}
}

View File

@ -48,5 +48,12 @@ 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();
}

View File

@ -3,12 +3,15 @@ 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;
import org.codehaus.plexus.util.FileUtils;
import Acquisition.sud.SudAudioFile;
/**
* Central class for opening sound files.
* <p>
@ -56,7 +59,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);
@ -170,6 +173,23 @@ public class PamAudioFileManager {
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<PamAudioFileLoader> getAudioFileLoaders(List<? extends File> files) {
ArrayList<PamAudioFileLoader> audioLoaders = new ArrayList<PamAudioFileLoader>();
PamAudioFileLoader loader;
for (int i=0; i<files.size(); i++) {
loader = getAudioFileLoader(files.get(i));
if (!audioLoaders.contains(loader)) {
audioLoaders.add(loader);
}
}
return audioLoaders;
}
/**
* Get the default file loader for raw files.
* @return the default file loader for raw files.

View File

@ -0,0 +1,34 @@
package Acquisition.pamAudio;
import PamView.panel.PamPanel;
import javafx.scene.layout.Pane;
/**
* User controls to change bespoke settings for audio loaders.
*/
public interface PamAudioSettingsPane {
/**
* Get the JavaFX pane for loading audio.
* @return
*/
public Pane getAudioLoaderPane();
/**
* Get the swing audio loader pane.
* @return
*/
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();
}

View File

@ -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.
*

View File

@ -296,5 +296,11 @@ public class WavAudioFile implements PamAudioFileLoader {
}
}
@Override
public PamAudioSettingsPane getSettingsPane() {
// TODO Auto-generated method stub
return null;
}
}

View File

@ -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;
}
}

View File

@ -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,7 +13,13 @@ import javax.swing.SwingUtilities;
import org.pamguard.x3.sud.ChunkHeader;
import org.pamguard.x3.sud.SudMapListener;
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;
@ -30,16 +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<AudioInputStream> 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<String>(Arrays.asList(new String[] { ".sud" }));
PamSettingManager.getInstance().registerSettings(this);
}
@Override
@ -63,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();
@ -181,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:");
@ -217,4 +240,50 @@ public class SudAudioFile extends WavAudioFile {
}
@Override
public PamAudioSettingsPane getSettingsPane() {
if (sudAudioSettingsPane==null) {
sudAudioSettingsPane = new SudAudioSettingsPane(this);
}
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;
}
}

View File

@ -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;
}
/**

View File

@ -0,0 +1,158 @@
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 PamView.dialog.PamGridBagContraints;
import PamView.panel.PamPanel;
import javafx.scene.control.Tooltip;
import javafx.scene.layout.Pane;
import pamViewFX.fxNodes.PamHBox;
import pamViewFX.fxNodes.utilityPanes.PamToggleSwitch;
/**
* Some Swing and FX controls to allow a user to chnage sud parameters.
*/
public class SudAudioSettingsPane implements PamAudioSettingsPane {
private SudAudioFile sudAudioFile;
private SudSettingsPanel sudAudioPanel;
private SudSettingsPane sudAudioPaneFX;
private String sudTooltip = "Zero pad sud files. Zero padding replaces sections of sud files \n"
+ "with corrupt or no data with zeros. This can improve time drift. ";
public SudAudioSettingsPane(SudAudioFile sudAudioFile) {
this.sudAudioFile=sudAudioFile;
}
@Override
public Pane getAudioLoaderPane() {
if (sudAudioPaneFX==null) {
createSudAudioPaneFX();
}
return sudAudioPaneFX;
}
@Override
public PamPanel getAudioLoaderPanel() {
if (sudAudioPanel==null) {
createSudAudioPanel();
}
return sudAudioPanel;
}
private void createSudAudioPanel() {
sudAudioPanel = new SudSettingsPanel();
}
private void createSudAudioPaneFX() {
sudAudioPaneFX = new SudSettingsPane();
}
public void setParams(PamSudParams sudParams) {
//System.out.println("Set SUD PARAMS: " + sudParams + " " + sudParams.zeroPad);
if (sudAudioPanel!=null) sudAudioPanel.setParams(sudParams);;
if (sudAudioPaneFX!=null) sudAudioPaneFX.setParams(sudParams);;
}
public PamSudParams getParams(PamSudParams sudParams) {
//System.out.println("Get SUD PARAMS: " + sudParams + " " + sudParams.zeroPad);
if (sudAudioPanel!=null) return sudAudioPanel.getParams(sudParams);
if (sudAudioPaneFX!=null) return sudAudioPaneFX.getParams(sudParams);;
return null;
}
/**
* The sud settings panel.
*/
public class SudSettingsPanel extends PamPanel {
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(sudTooltip);
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;
}
}
/**
* The sud settings panel for JavaFX
*/
public class SudSettingsPane extends PamHBox {
private PamToggleSwitch zeroPadSud;
public SudSettingsPane() {
// soundTrapDate.setPreferredSize(tzPanel.getPreferredSize());
GridBagConstraints c = new PamGridBagContraints();
c.gridy = 0;
c.gridx = 0;
c.gridwidth = 1;
zeroPadSud = new PamToggleSwitch("Zero pad sud files");
zeroPadSud.setTooltip(new Tooltip(sudTooltip));
this.getChildren().add(zeroPadSud);
}
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());
}
}

View File

@ -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; i<arr.length; i++) {
for(int j=0; j<arr.length; j++) {
cur = arr[i][j];
if (cur>max) {
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.

View File

@ -159,4 +159,29 @@ public class PamAWTUtils {
return bestPoint;
}
/**
* Convert a colour to an int.
* @param c - the colour to change.
* @return the int representation of the colour
*/
public static int colorToInt(java.awt.Color c) {
int r = (int) Math.round(c.getRed());
int g = (int) Math.round(c.getGreen());
int b = (int) Math.round(c.getBlue());
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 java.awt.Color intToColor(int value) {
int r = (value >>> 16) & 0xFF;
int g = (value >>> 8) & 0xFF;
int b = value & 0xFF;
return new java.awt.Color(r,g,b);
}
}

View File

@ -5,6 +5,7 @@ import java.awt.Color;
import PamUtils.PamUtils;
import PamView.GeneralProjector;
import PamView.PamSymbolType;
import PamView.dialog.PamDialogPanel;
import PamView.symbol.PamSymbolChooser;
import PamView.symbol.SymbolData;
import PamguardMVC.PamDataUnit;
@ -45,7 +46,15 @@ public class PeakFreqModifier extends SymbolModifier {
*/
private ColourArrayType colourArrayType;
private PeakFreqOptionsPane peakFreqOptions;
/**
* JavaFX pane for frequency symbol options.
*/
private PeakFreqOptionsPane peakFreqOptionsPaneFX;
/**
* Swing panel for frequency symbol options
*/
private PamDialogPanel peakFreqOptionsPanel;
public PeakFreqModifier(PamSymbolChooser symbolChooser) {
super(PEAK_FREQ_MODIFIER_NAME, symbolChooser, SymbolModType.FILLCOLOUR | SymbolModType.LINECOLOUR );
@ -130,11 +139,20 @@ public class PeakFreqModifier extends SymbolModifier {
@Override
public SymbolModifierPane getOptionsPane() {
//System.out.println("PEAK FREQ COLOUR ARRAY2: " + peakFreqSymbolOptions.freqColourArray);
if (this.peakFreqOptions==null) {
peakFreqOptions = new PeakFreqOptionsPane(this);
peakFreqOptions.setParams();
if (this.peakFreqOptionsPaneFX==null) {
peakFreqOptionsPaneFX = new PeakFreqOptionsPane(this);
peakFreqOptionsPaneFX.setParams();
}
return peakFreqOptions;
return peakFreqOptionsPaneFX;
}
public PamDialogPanel getDialogPanel() {
//System.out.println("PEAK FREQ COLOUR ARRAY2: " + peakFreqSymbolOptions.freqColourArray);
if (this.peakFreqOptionsPanel==null) {
peakFreqOptionsPanel = new PeakFreqOptionsPanel(this);
peakFreqOptionsPanel.setParams();
}
return peakFreqOptionsPanel;
}
@Override

View File

@ -0,0 +1,203 @@
package PamView.symbol.modifier;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JSpinner;
import javax.swing.SpinnerNumberModel;
import javax.swing.border.TitledBorder;
import PamView.ColourComboBox;
import PamView.dialog.PamDialogPanel;
import PamView.panel.PamPanel;
import pamViewFX.fxNodes.utilsFX.PamUtilsFX;
/**
* Swing panel for changing parameters for the peak frequency symbol chooser.
*
* @author Jamie Macaulay
*/
public class PeakFreqOptionsPanel implements PamDialogPanel {
private PeakFreqModifier freqSymbolModifer;
private PamPanel mainPanel;
private JSpinner minFreq;
private JSpinner maxFreq;
private ColourComboBox colourBox;
public PeakFreqOptionsPanel(PeakFreqModifier symbolModifer) {
this.freqSymbolModifer=symbolModifer;
mainPanel = createFreqPaneL();
}
/**
* Pane which changes the frequency limits.
* @return pane with controls to change freq. limits.
*/
private PamPanel createFreqPaneL(){
PamPanel holder = new PamPanel();
holder.setLayout(new GridBagLayout());
holder.setBorder(new TitledBorder("Peak frequency colour map"));
GridBagConstraints c = new GridBagConstraints();
c.gridy=0;
c.gridx=0;
//doesn't seem to work so added spaces in labels instead
c.ipadx = 5;
c.anchor =GridBagConstraints.EAST;
holder.add(new JLabel("Min. freq"), c);
c.gridx++;
c.anchor =GridBagConstraints.WEST;
minFreq = new JSpinner(new SpinnerNumberModel(0., 0., 10000000., 1000.));
//make the lock button the same height as the spinner
Dimension prefSize = minFreq.getPreferredSize();
minFreq.setPreferredSize(new Dimension(90, prefSize.height));
holder.add(minFreq, c);
c.gridx++;
c.ipadx = 5;
c.anchor =GridBagConstraints.EAST;
holder.add(new JLabel(" Max. freq"), c);
c.gridx++;
c.ipadx = 5;
c.anchor =GridBagConstraints.WEST;
maxFreq =new JSpinner(new SpinnerNumberModel(1000., 1., Math.max(1000.,freqSymbolModifer.getSymbolChooser().getPamDataBlock().getSampleRate()/2.), 1000.));
maxFreq.setPreferredSize(new Dimension(90, prefSize.height));
holder.add(maxFreq, c);
c.gridx++;
c.ipadx = 5;
c.anchor =GridBagConstraints.WEST;
holder.add(new JLabel(" Hz"), c);
c.gridy++;
c.ipadx = 5;
c.gridwidth=2;
c.gridx=0;
c.anchor =GridBagConstraints.EAST;
holder.add(new JLabel("Freq. colour map"), c);
c.gridx=2;
c.gridwidth=3;
colourBox = new ColourComboBox();
c.anchor =GridBagConstraints.WEST;
prefSize = colourBox.getPreferredSize();
colourBox.setPreferredSize(new Dimension(200, prefSize.height));
holder.add(colourBox, c);
return holder;
}
@Override
public JComponent getDialogComponent() {
return mainPanel;
}
@Override
public boolean getParams(){
//bit messy but works /
// PeakFreqSymbolOptions symbolOptions = (PeakFreqSymbolOptions) standardSymbolOptions.getModifierParams(this.getSymbolModifier().getName());
PeakFreqSymbolOptions symbolOptions = (PeakFreqSymbolOptions)freqSymbolModifer.getSymbolModifierParams().clone();
//must make sure we do not call get parameters during a set parameters - the listeners on the controls call getParams so all goes
//haywire if the setParams is not set properly.
//System.out.println("GETPARAMS: " + ColourArray.getColorArrayType(this.colourBox.getSelectionModel().getSelectedItem()) + " " + setParams);
symbolOptions.freqLimts=new double[] {(double) minFreq.getValue(), (double) maxFreq.getValue()};
symbolOptions.freqColourArray = PamUtilsFX.swingColArray2FX(this.colourBox.getSelectedColourMap());
//System.out.println("StandardSymbolModifierPane : getParams(): new mod: " +mod);
freqSymbolModifer.setSymbolModifierParams(symbolOptions);
System.out.println("Get freq limits: 1: " + symbolOptions.freqLimts[0] + " " + symbolOptions.freqLimts[1] + " " + (double) maxFreq.getValue());
return true;
}
@Override
public void setParams() {
// StandardSymbolOptions standardSymbolOptions = (StandardSymbolOptions) getSymbolModifier().getSymbolChooser().getSymbolOptions();
// PeakFreqSymbolOptions symbolOptions = (PeakFreqSymbolOptions) standardSymbolOptions.getModifierParams(this.getSymbolModifier().getName());
PeakFreqSymbolOptions symbolOptions = (PeakFreqSymbolOptions)freqSymbolModifer.getSymbolModifierParams();
//now set frequency parameters;
checkFreqLimits( symbolOptions ) ;
minFreq.setValue(symbolOptions.freqLimts[0]);
maxFreq.setValue(symbolOptions.freqLimts[1]);
colourBox.setSelectedColourMap((PamUtilsFX.fxColArray2Swing(symbolOptions.freqColourArray)));
}
/**
* Check the frequency limits make sense for the given datablock
* @param symbolOptions - the peak frequency options.
*/
private void checkFreqLimits(PeakFreqSymbolOptions symbolOptions ) {
System.out.println("Check freq limits: 1: " + symbolOptions.freqLimts[0] + " " + symbolOptions.freqLimts[1]);
SpinnerNumberModel spinnerValFact = (SpinnerNumberModel) maxFreq.getModel();
spinnerValFact.setMaximum(getSampleRate() /2.);
//for some reason also need to set this
spinnerValFact.setMinimum(1.);
//set reasonable step sizes
if (getSampleRate()>=10000) {
spinnerValFact.setStepSize(1000.);
}
else if (getSampleRate()>=2000){
spinnerValFact.setStepSize(200.);
}
else {
spinnerValFact.setStepSize(50.);
}
if (symbolOptions.freqLimts==null) {
symbolOptions.freqLimts= new double[] {0, getSampleRate() /2};
}
System.out.println("Check freq limits: 2: " + symbolOptions.freqLimts[0] + " " + symbolOptions.freqLimts[1]);
//check nyquist for upper limit
if (symbolOptions.freqLimts[1]>getSampleRate() /2) {
symbolOptions.freqLimts[1]=getSampleRate() /2;
}
//check nyquist for lower limit
if (symbolOptions.freqLimts[0]>getSampleRate() /2) {
symbolOptions.freqLimts[0]=0;
}
}
private float getSampleRate() {
return freqSymbolModifer.getSymbolChooser().getPamDataBlock().getSampleRate();
}
}

View File

@ -18,12 +18,10 @@ import javax.swing.tree.TreePath;
import PamView.dialog.DialogScrollPane;
import PamView.dialog.PamDialogPanel;
import PamView.symbol.PamSymbolChooser;
import PamView.symbol.StandardSymbolChooser;
import PamView.symbol.StandardSymbolOptions;
import PamView.symbol.modifier.SymbolModifier;
import PamView.symbol.modifier.SymbolModifierParams;
import PamguardMVC.debug.Debug;
/**
* Panel to include in a dialog with options to select and activate a variety of symbol modifiers.

View File

@ -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];
}

View File

@ -13,6 +13,8 @@ public class CompoundDataSelector extends DataSelector {
private CompoundParams compoundParams = new CompoundParams();
private CompoundDialogPaneFX compoundPaneFX;
public CompoundDataSelector(PamDataBlock pamDataBlock, ArrayList<DataSelector> allSelectors,
String selectorName, boolean allowScores, String selectorType) {
super(pamDataBlock, selectorName, allowScores);
@ -57,8 +59,10 @@ public class CompoundDataSelector extends DataSelector {
@Override
public DynamicSettingsPane<Boolean> getDialogPaneFX() {
// TODO Auto-generated method stub
return null;
if (compoundPaneFX==null) {
compoundPaneFX = new CompoundDialogPaneFX(this);
}
return compoundPaneFX;
}
@Override
@ -91,6 +95,9 @@ public class CompoundDataSelector extends DataSelector {
score = Math.max(score, score2); // take the largest
}
}
// System.out.println("Hello Compound Data selector: " + score);
return score;
}

View File

@ -0,0 +1,89 @@
package PamguardMVC.dataSelector;
import java.util.ArrayList;
import javafx.scene.Node;
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<Boolean> {
private CompoundDataSelector compoundDataSelector;
private ArrayList<DataSelector> selectorList;
private ArrayList<DataSelectorDialogPaneFX> selectorPanels;
private PamVBox mainPanel;
public CompoundDialogPaneFX(CompoundDataSelector compoundDataSelector) {
super(null);
this.compoundDataSelector = compoundDataSelector;
this.selectorList = compoundDataSelector.getSelectorList();
mainPanel = new PamVBox();
mainPanel.setSpacing(10);
selectorPanels = new ArrayList<DataSelectorDialogPaneFX>(selectorList.size());
int ind = 0;
for (DataSelector ds : selectorList) {
DynamicSettingsPane<Boolean> panel = ds.getDialogPaneFX();
// turn all these panels into the compound ones with the extra enable options.
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());
}
}
@Override
public Boolean getParams(Boolean currParams) {
boolean ok = true;
for (int i = 0; i < selectorPanels.size(); i++) {
DataSelectorDialogPaneFX panel = selectorPanels.get(i);
ok |= panel.getParams(currParams);
}
return ok;
}
@Override
public void setParams(Boolean input) {
for (int i = 0; i < selectorPanels.size(); i++) {
DataSelectorDialogPaneFX panel = selectorPanels.get(i);
panel.setParams(input);
}
}
@Override
public String getName() {
return "Compound data selector pane";
}
@Override
public Node getContentNode() {
return mainPanel;
}
@Override
public void paneInitialized() {
// TODO Auto-generated method stub
}
}

View File

@ -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;

View File

@ -0,0 +1,213 @@
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<Boolean> {
private static final double PREF_TOGGLE_WIDTH = 60;
private DataSelector dataSelector;
private DynamicSettingsPane<Boolean> 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<Boolean> 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();
notifySettingsListeners();
});
orButton.setOnAction(event ->{
enableComponent();
notifySettingsListeners();
});
disableButton.setOnAction(event ->{
enableComponent();
notifySettingsListeners();
});
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);
//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);
dsPane.setBorder(null);
}
enableComponent();
//buttonPane.setTooltip(new Tooltip("Options for " + dataSelector.getLongSelectorName()));
}
private void enableComponent() {
contentPane.setDisable(disableButton.isSelected());
}
@Override
public void setParams(Boolean input) {
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";
}
@Override
public Node getContentNode() {
return dsPane;
}
@Override
public void paneInitialized() {
// TODO Auto-generated method stub
}
}

View File

@ -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;
}

View File

@ -6,6 +6,11 @@ import PamView.ColourArray;
import PamView.ColourArray.ColourArrayType;
import PamView.sliders.PamRangeSlider;
/**
* A range slider which shows a colour gradient between two thumbs.
*
* @author Jamie Macaulay
*/
public class ColourRangeSlider extends PamRangeSlider {
/**
@ -18,13 +23,29 @@ public class ColourRangeSlider extends PamRangeSlider {
}
public ColourRangeSlider(int min, int max){
super(min,max, JSlider.VERTICAL);
super(min, max,JSlider.VERTICAL);
}
public ColourRangeSlider(int min, int max, int orientation){
super(min, max,orientation);
}
public ColourRangeSlider(int orientation){
super(orientation);
}
/**
* Set the colour map type to show between the two thumbs.
* @param colourMap - the colour map to show.
*/
public void setColourMap(ColourArrayType colourMap){
((ColourRangeSliderUI) getUI()).setColourMap(colourMap);
}
/**
* Set a custom colour map type to show between the two thumbs.
* @param colourMap - the colour map to show.
*/
public void setColourMap(ColourArray colourMap){
((ColourRangeSliderUI) getUI()).setColourMap(colourMap);
}

View File

@ -8,8 +8,7 @@ import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.awt.image.WritableRaster;
import javax.swing.JSlider;
import PamView.ColourArray;
import PamView.ColourArray.ColourArrayType;
@ -78,6 +77,8 @@ public class ColourRangeSliderUI extends PamRangeSliderUI {
private void createColourMapImage(){
if (b.getOrientation() == JSlider.VERTICAL) {
// now make a standard amplitude image
if (colourArray != null && colourArray.length > 0) {
amplitudeImage = new BufferedImage(1, colourArray.length,
@ -87,6 +88,15 @@ public class ColourRangeSliderUI extends PamRangeSliderUI {
raster.setPixel(0, colourArray.length - i - 1, colourArray[i]);
}
}
}
else {
amplitudeImage = new BufferedImage(colourArray.length, 1,
BufferedImage.TYPE_INT_RGB);
WritableRaster raster = amplitudeImage.getRaster();
for (int i = 0; i < colourArray.length; i++) {
raster.setPixel(i, 0,colourArray[i]);
}
}
}
@ -119,26 +129,98 @@ public class ColourRangeSliderUI extends PamRangeSliderUI {
Rectangle trackBounds = trackRect;
// Determine position of selected range by moving from the middle
// of one thumb to the other.
int lowerY = thumbRect.y + (thumbRect.width / 2);
int upperY = getUpperThumbRect().y + (getUpperThumbRect().width / 2);
// Determine track position.
int cx = (trackBounds.width / 2) - 2;
// Save colour and shift position.
Color oldColor = g.getColor();
g.translate(trackBounds.x + cx, trackBounds.y);
drawColourMapVert( g, lowerY - trackBounds.y, upperY - trackBounds.y, -(getUpperThumbRect().width /4)-trackBounds.x,(getUpperThumbRect().width / 2)-trackBounds.x+(trackBounds.width / 4)+2);
int cx;
if (b.getOrientation() == JSlider.VERTICAL) {
// Determine position of selected range by moving from the middle
// of one thumb to the other.
int lowerY = thumbRect.y + (thumbRect.width / 2);
int upperY = getUpperThumbRect().y + (getUpperThumbRect().width / 2);
// Determine track position.
cx = (trackBounds.width / 2) - 2;
g.translate(trackBounds.x + cx, trackBounds.y);
drawColourMapVert( g, lowerY - trackBounds.y, upperY - trackBounds.y, -(getUpperThumbRect().width /4)-trackBounds.x,(getUpperThumbRect().width / 2)-trackBounds.x+(trackBounds.width / 4)+2);
cx = (trackBounds.width / 2) - 2;
g.translate(-(trackBounds.x + cx), -trackBounds.y);
}
else {
// Determine position of selected range by moving from the middle
// of one thumb to the other.
int lowerX = thumbRect.x;
int upperX = getUpperThumbRect().x;
// Determine track position.
cx = (trackBounds.width / 2) - 2;
// -(getUpperThumbRect().height /4)-trackBounds.y, (getUpperThumbRect().height / 2)-trackBounds.y+(trackBounds.height / 4)+2
drawColourMapHorz(g, trackBounds.height/2-getUpperThumbRect().height/2, trackBounds.height/2+getUpperThumbRect().height/2, lowerX + getUpperThumbRect().width/2, upperX+ getUpperThumbRect().width/2);
}
// Restore position and colour.
g.translate(-(trackBounds.x + cx), -trackBounds.y);
g.setColor(oldColor);
}
/**
* Draw the colour map between the two thumbs in the slider bar. Colour the section above the top most thumb
* and the section below the lower most thumb with the color map extremes.
* @param g- graphics
* @param y1
* @param y2
* @param x1
* @param x2
*/
private void drawColourMapHorz(Graphics g, int y1, int y2, int x1, int x2){
if (amplitudeImage == null) return;
Graphics2D g2d = (Graphics2D) g;
int width=Math.abs(x2-x1);
int height=Math.abs(y2-y1);
// System.out.println("Width: " + width + " " + height + " x1 " + x1);
//calculate the distance between thumbs
double ascaleX = width
/ (double) amplitudeImage.getWidth(null);
double ascaleY = height
/ (double) amplitudeImage.getHeight(null);
AffineTransform xform = new AffineTransform();
// xform.translate(1, amplitudeImage.getWidth(null));
xform.scale(ascaleX, ascaleY);
//translate to the correct location;
g2d.translate(x1, y1);
//now translate back for the rest of the operations;
g2d.drawImage(amplitudeImage, xform, b);
//translate back to our original position.
g2d.translate(-x1, -y1);
//go to the left of the lower thumb;
// g2d.translate(0, height);
g2d.setColor(new Color((int) colourArray[0][0],(int)colourArray[0][1],(int) colourArray[0][2]));
for (int i=y1; i<y2; i++){
g2d.drawLine(0,i,x1, i);
}
//color left of the thumb
g2d.setColor(new Color((int) colourArray[colourArray.length-1][0],(int)colourArray[colourArray.length-1][1],(int) colourArray[colourArray.length-1][2]));
for (int i=y1; i<y2; i++){
g2d.drawLine(x2,i, trackRect.width + thumbRect.width/2, i);
}
}
/**
* Draw the colour map between the two thumbs in the slider bar. Colour the section above the top most thumb
* and the section below the lower most thumb with the color map extremes.

View File

@ -95,7 +95,7 @@ public class ClickSelectPaneFX extends DynamicSettingsPane<Boolean> {
@Override
public String getName() {
return "Click Type Selection";
return "Filter by click type";
}
@Override

View File

@ -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;
}

View File

@ -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);
@ -215,6 +210,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");
@ -231,6 +227,21 @@ public class ClickControlPane2 extends PamBorderPane implements TDSettingsPane {
setFreqType();
setParams();
dataSelectPane.addSettingsListener(()->{
//dynamic settings pane so have to repaint whenever a control is selected.
getParams();
/**
* If there are raw amplitude or frequency panes that have a buffer of painted units then
* these have to be cleared for the data selector
*/
clickPlotInfo.getClickRawPlotManager().clear();
clickPlotInfo.getClickFFTPlotManager().clear();
clickPlotInfo.getTDGraph().repaint(50);
});
this.setCenter(tabPane);
this.setPrefHeight(PREF_HEIGHT);
@ -242,6 +253,7 @@ public class ClickControlPane2 extends PamBorderPane implements TDSettingsPane {
* @return the data select pane.
*/
private DynamicSettingsPane<Boolean> createDataSelectPane(){
// System.out.println("DATA SELECTOR: " + clickPlotInfo.getClickDataSelector());
return clickPlotInfo.getClickDataSelector().getDialogPaneFX();
}
@ -446,7 +458,7 @@ public class ClickControlPane2 extends PamBorderPane implements TDSettingsPane {
PamVBox vBox = new PamVBox();
vBox.setSpacing(5);
minMaxWidthPane = new DualControlField<Double>("Min", "Max" , "", 2, 100, 1);
minMaxWidthPane = new DualControlField<Double>("", "" , "", 2, 100, 1);
minMaxWidthPane.addChangeListener((obsval, oldval, newval)->{
newSettings();
//do not allow the min ti be larger than the max.
@ -469,7 +481,7 @@ public class ClickControlPane2 extends PamBorderPane implements TDSettingsPane {
//height pane
minMaxHeightPane = new DualControlField<Double>("Min", "Max" , "", 2, 100, 1);
minMaxHeightPane = new DualControlField<Double>("", "" , "", 2, 100, 1);
minMaxHeightPane.addChangeListener((obsval, oldval, newval)->{
newSettings();
//do not allow the min ti be larger than the max.
@ -516,7 +528,7 @@ public class ClickControlPane2 extends PamBorderPane implements TDSettingsPane {
getParams();
//on a parameter change must clear the FFT plot.
clickPlotInfo.getClickFFTplotManager().clear();
clickPlotInfo.getClickFFTPlotManager().clear();
clickPlotInfo.getClickRawPlotManager().clear();
clickPlotInfo.getTDGraph().repaint(milliswait);
@ -770,7 +782,7 @@ public class ClickControlPane2 extends PamBorderPane implements TDSettingsPane {
clickPlotInfo.getClickDisplayParams().fftHop = this.fftSpinnerHop.getValue().intValue();
clickPlotInfo.getClickDisplayParams().fftLength = this.fftSpinnerLength.getValue().intValue();
clickPlotInfo.getClickDisplayParams().colourMap = this.spectroControlPane.getColourArrayType();
clickPlotInfo.getClickFFTplotManager().update();
clickPlotInfo.getClickFFTPlotManager().update();
/*** Data select pane****/
//dynamic settings pane so have to repaint whenever a control is selected.

View File

@ -238,7 +238,7 @@ public class ClickPlotInfoFX extends TDDataInfoFX {
ClickDetection click = (ClickDetection) pamDataUnit;
//first check we can generally plot the click
if (!shouldPlot(click)) return null;
// if (!shouldPlot(click)) return null;
//click has passed the first test! Now get the correct data value;
Double val = null;
switch (getCurrentScaleInfo().getDataType()) {
@ -599,21 +599,21 @@ public class ClickPlotInfoFX extends TDDataInfoFX {
// }
/***
* Used to determine which clicks should be plotted on the display by using the click data selector.
* @param click - the click to check.
* @return true to plot the click.
*/
private synchronized boolean shouldPlot(ClickDetection click) {
if (click == null) return false;
dataSelector.scoreData(click);
if (clickDisplayParams.displayChannels > 0 && (clickDisplayParams.displayChannels & click.getChannelBitmap()) == 0) return false;
return true;
}
// /***
// * Used to determine which clicks should be plotted on the display by using the click data selector.
// * @param click - the click to check.
// * @return true to plot the click.
// */
// private synchronized boolean shouldPlot(ClickDetection click) {
//
// if (click == null) return false;
//
// if (dataSelector.scoreData(click)<=0) return false;
//
// if (clickDisplayParams.displayChannels > 0 && (clickDisplayParams.displayChannels & click.getChannelBitmap()) == 0) return false;
//
// return true;
// }
@Override
public TDSymbolChooserFX getSymbolChooser() {
@ -931,7 +931,7 @@ public class ClickPlotInfoFX extends TDDataInfoFX {
* time/frequency displays...
* @return The FFT plot manager.
*/
public ClickFFTPlotManager2 getClickFFTplotManager() {
public ClickFFTPlotManager2 getClickFFTPlotManager() {
return clickFFTplotManager;
}

View File

@ -318,7 +318,6 @@ public abstract class RawWavePlotManager {
//check if we can plot click on this plot pane.
if (!rawClipInfo.shouldDraw(plotNumber, pamDataUnit)) {
//System.out.println("Shoudl not draw!");
return null;
}

View File

@ -901,13 +901,14 @@ public class TDGraphFX extends PamBorderPane {
}
/**
* A tgool tip for detections.
* A Tooltip for showing information about detections.
*
* @author Jamie Macaulay
*
*/
private class TDTooltip extends Tooltip {
private static final double TOOL_TIP_DELAY = 60; //seconds
private TDPlotPane plotPanel;
/**
@ -917,6 +918,8 @@ public class TDGraphFX extends PamBorderPane {
public TDTooltip(TDPlotPane aPlot) {
super("Test tooltip");
this.plotPanel = aPlot;
//make the Tooltip last a little longer
this.setShowDelay(Duration.seconds(TOOL_TIP_DELAY));
/*
* See https://bugs.openjdk.java.net/browse/JDK-8090477 fr info about tool tip
* timing on these displys.

View File

@ -44,7 +44,7 @@ public class RExportOverlayMenu extends ExportOverlayMenu {
* Create the export overlay
*/
public RExportOverlayMenu(){
rExportManger= new RExportManager();
rExportManger= new RExportManager(null);
buttonNode = createButton();

View File

@ -74,7 +74,7 @@ public abstract class SpectrumPlot <D extends PamDataUnit> implements Detection
private SpectrumSettingsPane spectrumSettingsPane;
private double sR;
private double storedsR;
// //TODO
@ -132,7 +132,7 @@ public abstract class SpectrumPlot <D extends PamDataUnit> implements Detection
if (data ==null) return;
this.sR=sR;
this.storedsR=sR;
int[] minmax = getAxisMinMaxSamples(plotProjector);
@ -172,7 +172,7 @@ public abstract class SpectrumPlot <D extends PamDataUnit> implements Detection
}
private double getSampleRate() {
return sR;
return storedsR;
}
@Override
@ -235,6 +235,8 @@ public abstract class SpectrumPlot <D extends PamDataUnit> 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);
}

View File

@ -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;
}
}

View File

@ -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();
@ -269,6 +266,12 @@ public class MLDetectionsManager implements PamDataUnitExporter {
}
@Override
public boolean isNeedsNewFile() {
// TODO Auto-generated method stub
return false;
}

View File

@ -82,7 +82,7 @@ public class MLWhistleMoanExport extends MLDataUnitExport<ConnectedRegionDataUni
// Struct mlStructure= new MLStructure("sliceData", new int[]{dataUnit.getConnectedRegion().getSliceData().size(), 1});
Struct mlStructure= Mat5.newStruct();
Struct mlStructure= Mat5.newStruct(dataUnit.getConnectedRegion().getSliceData().size(), 1);
//the start sample.
Matrix sliceNumber;

View File

@ -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();
}

View File

@ -56,11 +56,12 @@ public class PamExporterManager {
"yyyy_MM_dd_HHmmss");
public PamExporterManager() {
pamExporters = new ArrayList<PamDataUnitExporter>();
//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());
}
@ -72,6 +73,8 @@ public class PamExporterManager {
public boolean exportDataUnit(PamDataUnit<?, ?> 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() );
@ -83,10 +86,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
@ -101,7 +104,7 @@ public class PamExporterManager {
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 +121,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();
}
/**

View File

@ -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<RDataUnitExport> rDataExport = new ArrayList<RDataUnitExport>();
@ -39,8 +39,11 @@ public class RExportManager implements PamDataUnitExporter {
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;
}
}

View File

@ -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,6 +37,8 @@ import export.PamExporterManager;
import export.layoutFX.ExportParams;
import offlineProcessing.OLProcessDialog;
import offlineProcessing.OfflineTaskGroup;
import offlineProcessing.TaskMonitor;
import offlineProcessing.TaskMonitorData;
import offlineProcessing.TaskStatus;
/**
@ -75,7 +76,6 @@ public class ExportProcessDialog {
public void createExportGroup() {
//clear current tasks.
dlOfflineGroup.clearTasks();
@ -87,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.
@ -129,6 +128,9 @@ public class ExportProcessDialog {
*/
private JFileChooser fc;
/**
* S hows the folder stuff is going to export to.
*/
private JTextField exportTo;
/**
@ -268,7 +270,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) {
@ -352,19 +354,82 @@ public class ExportProcessDialog {
}
class ExportTaskMonitor implements TaskMonitor {
private int taskIndex;
private ExportTaskGroup exportTaskGroup;
private boolean started = false;
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<exportTaskGroup.getNTasks()) {
exportTaskGroup.runTaskFrom(taskIndex+1);
started = true;
}
}
}
}
/**
* Export task
*/
class ExportTaskGroup extends OfflineTaskGroup {
class ExportTaskGroup extends OfflineTaskGroup{
public ExportTaskGroup(String settingsName) {
super(null, settingsName);
// TODO Auto-generated constructor stub
}
@Override
public String getUnitType() {
return "Export Data";
}
/**
* Runs tasks from a specific task number.
* @param i - the index
*/
public void runTaskFrom(int i) {
System.out.println("RUN TASK FROM :" + i);
this.setPrimaryDataBlock(getTask(i).getDataBlock());
if (i<getNTasks()-1) {
//will start a new thread after this one has finished
this.setTaskMonitor(new ExportTaskMonitor(i, this));
}
super.runTasks();
}
/**
* 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() {
runTaskFrom(0) ;
return true;
}
}

View File

@ -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;
@ -32,7 +33,7 @@ public class ExportTask extends OfflineTask<PamDataUnit<?,?>>{
public ExportTask(PamDataBlock<PamDataUnit<?, ?>> 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 +45,20 @@ public class ExportTask extends OfflineTask<PamDataUnit<?,?>>{
@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 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);
}
}
return false; //we don't need to indicate that anything has changed - we are just exporting.
}
@ -54,13 +66,13 @@ public class ExportTask extends OfflineTask<PamDataUnit<?,?>>{
@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");
//force the exporter so save any renaming data units in the buffer
exporter.exportDataUnit(null, true);
exporter.close();

View File

@ -465,6 +465,13 @@ public class WavFileExportManager implements PamDataUnitExporter {
@Override
public boolean isNeedsNewFile() {
return false;
}
// hello(){

View File

@ -142,7 +142,7 @@ public class OfflineTaskGroup implements PamSettings {
private ArrayList<OfflineTask> offlineTasks = new ArrayList<OfflineTask>();
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<Integer, TaskMonitorData> implements ViewLoadObserver {
public class TaskGroupWorker extends SwingWorker<Integer, TaskMonitorData> 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<long[]> timeChunks){
@ -976,4 +977,6 @@ public class OfflineTaskGroup implements PamSettings {
offlineTasks.clear();
}
}

View File

@ -35,6 +35,7 @@ import dataModelFX.DataModelPaneFX;
import org.controlsfx.control.ToggleSwitch;
/**
* A pane which holds a set of tabs.
*
@ -125,7 +126,7 @@ public class PamGuiFX extends StackPane implements PamViewInterface {
this.pamGuiManagerFX=pamGuiManagerFX;
this.mainTabPane = mainTabPane;
Pane layout=createMainPane(mainTabPane, stage);
Node layout=createMainPane(mainTabPane, stage);
//add main pane to PamGui
this.getChildren().add(layout);
@ -140,7 +141,7 @@ public class PamGuiFX extends StackPane implements PamViewInterface {
//create the main tab pane.
this.mainTabPane = new PamTabPane();
Pane layout=createMainPane(mainTabPane, stage);
Node layout=createMainPane(mainTabPane, stage);
//add main pane to PamGui
this.getChildren().add(layout);
@ -154,7 +155,7 @@ public class PamGuiFX extends StackPane implements PamViewInterface {
* @param stage - the stage holding this GUI.
* @return a pane which sits in the stage.
*/
private Pane createMainPane(PamTabPane mainTabPane, Stage stage){
private Node createMainPane(PamTabPane mainTabPane, Stage stage){
//create the pane which holds tab pane
final PamBorderPane layout = new PamBorderPane();
@ -262,7 +263,6 @@ public class PamGuiFX extends StackPane implements PamViewInterface {
PamBorderPane layoutHolder=new PamBorderPane(layout);
layoutHolder.setTop(hidingLoadPane);
return layoutHolder;
}

View File

@ -254,6 +254,10 @@ public class PamGuiTabFX extends PamTabFX {
int smallWindows = 0;
//the padding between windows
double padding = 10;
ArrayList<PamGuiInternalPane> dw = internalPanes;
if (dw.size() == 0) return;
@ -268,7 +272,9 @@ public class PamGuiTabFX extends PamTabFX {
//now place windows in correct position
//large windows
double x, y, w, h = 0;
double x, y, w, h = 0.;
double pad =0.;
if (largeWindows > 0) {
x = 0;
y = 0;
@ -284,7 +290,10 @@ public class PamGuiTabFX extends PamTabFX {
if (dw.get(i).getUserDisplayNode().isMinorDisplay()== true) continue;
dw.get(i).setPaneLayout(x, y);
dw.get(i).setPaneSize(w, h);
//set the padding if the pane is not the last pane.
pad = (i== dw.size()-1) ? 0 : padding;
dw.get(i).setPaneSize(w - (horz? pad:0), h - (horz? 0:pad));
if (horz) x += w;
else y += h;
}
@ -307,7 +316,10 @@ public class PamGuiTabFX extends PamTabFX {
for (int i = 0; i < dw.size(); i++) {
if (dw.get(i).getUserDisplayNode().isMinorDisplay() == false) continue;
dw.get(i).setPaneLayout(x, y);
dw.get(i).setPaneSize(w, h);
//set the padding if the pane is not the last pane.
pad = (i== dw.size()-1) ? 0 : padding;
dw.get(i).setPaneSize(w- (horz? pad:0), h - (horz? 0:pad));
if (horz) x += w;
else y +=h;
}

View File

@ -128,7 +128,7 @@ public class PamFlipPane extends FlipPane {
* Convenience duplicate of getBackPane().
* @return the back pane.
*/
public PamBorderPane getAdvPane() {
public PamBorderPane getAdvContentPane() {
return advPane;
}

View File

@ -20,7 +20,6 @@ import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
/**
* Hiding pane which can be added to any node.
@ -156,7 +155,7 @@ public class HidingPane extends StackPane {
//create a button which hides the side panel
hideButton=createShowButton(false);
styleHideButton(hideButton);
hideButton.setOnAction(new HideButtonPressed());
// hideButton.setOnAction(new HideButtonPressed());
hideButton.setVisible(false);
// hideButton.setStyle("close-button-right");
@ -333,8 +332,6 @@ public class HidingPane extends StackPane {
double dragX=0;
double dragY=0;
double distance=0;
private PamButton createShowButton(final boolean show){
final PamButton pamButton=new PamButton();
@ -343,6 +340,7 @@ public class HidingPane extends StackPane {
@Override public void handle(MouseEvent mouseEvent) {
// record a delta distance for the drag and drop operation.
// System.out.println("HidingPane.showButton - setOnMousePressed");
dragX =mouseEvent.getSceneX();
dragY =mouseEvent.getSceneY();
}
@ -350,10 +348,24 @@ public class HidingPane extends StackPane {
pamButton.setOnMouseReleased(new EventHandler<MouseEvent>() {
@Override public void handle(MouseEvent mouseEvent) {
if (distance==0) return;
// System.out.println("HidingPane.showButton - setOnMouseReleased");
//the mouse has been pressed
if (distance==0) {
if (isShowing()) {
showHidePane(false);
}
else {
showHidePane(true);
}
return;
}
//the mouse has been dragged dragged
distance=sideIndex*distance;
// System.out.println("Mouse released: HidePanel distance: "+distance);
if (!overlay) distance=Math.abs(distance);
//need to see where the drag has ended-if greater than 50% then open but if less then close.
if (!isHorizontal()){
if (distance<expandedSize/2) showHidePane(overlay? true : false);
@ -363,6 +375,7 @@ public class HidingPane extends StackPane {
if (distance<expandedSize/2) showHidePane(overlay? true : false);
if (distance>=expandedSize/2) showHidePane(overlay? false : true);
}
//reset the distance
distance=0;
}
@ -370,12 +383,14 @@ public class HidingPane extends StackPane {
pamButton.setOnMouseDragged(new EventHandler<MouseEvent>() {
@Override public void handle(MouseEvent mouseEvent) {
// System.out.println("HidingPane.showButton - setOnMouseDragged");
if (visibleImmediatly) hidePane.setVisible(true);
else hidePane.setVisible(false);
// hideButton.setVisible(true);
/**
* Work out the distance the panel is to be dragged;
*/
double distance = 0;
if (!isHorizontal()){
if (!show) distance=(mouseEvent.getSceneX()-dragX);
else distance=(mouseEvent.getSceneX()-dragX)+sideIndex*expandedSize;
@ -389,24 +404,26 @@ public class HidingPane extends StackPane {
// if (show && Math.abs(distance)>expandedSize) return;
translatePanel(distance);
HidingPane.this.distance = distance ;
}
});
pamButton.setOnMouseEntered(new EventHandler<MouseEvent>() {
@Override public void handle(MouseEvent mouseEvent) {
// System.out.println("HidingPane.showButton - mouse entered");
pamButton.setOpacity(1.0);
pamButton.setPadding(new Insets(2.,2.,2.,2.));
}
});
pamButton.setOnMouseExited(new EventHandler<MouseEvent>() {
@Override public void handle(MouseEvent mouseEvent) {
// System.out.println("HidingPane.showButton - mouse exited");
if (show) pamButton.setOpacity(showButtonOpacity);
pamButton.setPadding(new Insets(0.,0.,0.,0.));
}
});
// pamButton.setOnMouseEntered(new EventHandler<MouseEvent>() {
// @Override public void handle(MouseEvent mouseEvent) {
//// System.out.println("HidingPane.showButton - mouse entered");
// pamButton.setOpacity(1.0);
// pamButton.setPadding(new Insets(2.,2.,2.,2.));
// }
// });
//
// pamButton.setOnMouseExited(new EventHandler<MouseEvent>() {
// @Override public void handle(MouseEvent mouseEvent) {
//// System.out.println("HidingPane.showButton - mouse exited");
// if (show) pamButton.setOpacity(showButtonOpacity);
// pamButton.setPadding(new Insets(0.,0.,0.,0.));
// }
// });
// pamButton.setOnMouseClicked(new EventHandler<MouseEvent>() {
// @Override public void handle(MouseEvent mouseEvent) {
@ -415,15 +432,15 @@ public class HidingPane extends StackPane {
// }
// });
pamButton.addEventHandler(ActionEvent.ACTION,new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent e) {
System.out.println("HidingPane.showButton - action event clicked");
showHidePane(show);
}
});
// pamButton.addEventHandler(ActionEvent.ACTION,new EventHandler<ActionEvent>() {
// @Override
// public void handle(ActionEvent e) {
// System.out.println("HidingPane.showButton - action event clicked");
// showHidePane(show);
// }
// });
if (show) pamButton.setOpacity(showButtonOpacity);
// if (show) pamButton.setOpacity(showButtonOpacity);
return pamButton;
}
@ -601,7 +618,7 @@ public class HidingPane extends StackPane {
showing.setValue(true);
if (visibleImmediatly) hidePane.setVisible(true);
//hideButton.setVisible(true);
//System.out.println("HidingPane: Open Hide Pane");
// System.out.println("HidingPane: Open Hide Pane");
//open the panel
if (timeLineShow.getStatus()==Status.RUNNING) {
//stops the issue with the hiding pane freezing.
@ -613,7 +630,7 @@ public class HidingPane extends StackPane {
else{
showing.setValue(false);
if (!visibleImmediatly) hidePane.setVisible(false);
//System.out.println("HidingPane: Close Hide Pane");
// System.out.println("HidingPane: Close Hide Pane");
//close the panel
timeLineHide.play();
}
@ -638,17 +655,17 @@ public class HidingPane extends StackPane {
}
}
/**
* Called whenever the pin button is pressed.
* @author Jamie Macaulay
*/
class HideButtonPressed implements EventHandler<ActionEvent>{
@Override
public void handle(ActionEvent arg0) {
showHidePane(false);
}
}
// /**
// * Called whenever the pin button is pressed.
// * @author Jamie Macaulay
// */
// class HideButtonPressed implements EventHandler<ActionEvent>{
//
// @Override
// public void handle(ActionEvent arg0) {
// showHidePane(false);
// }
// }
/**
* Get the button which hides the pane.
@ -732,6 +749,15 @@ public class HidingPane extends StackPane {
return this.showing;
}
/**
* Check whether the hiding pane is showing.
* @return true if showing.
*/
public boolean isShowing(){
return this.showing.get();
}
/**
* Get the opacity of the show button when the mouse is outside the button.
* @return the opacity of the show button.

View File

@ -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();

View File

@ -16,9 +16,9 @@ import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.JPopupMenu.Separator;
import javax.swing.JRadioButtonMenuItem;
import javax.swing.JSeparator;
import javax.swing.SwingUtilities;
import PamView.ColourArray.ColourArrayType;
import PamView.PamSymbol;
import javafx.animation.KeyFrame;
import javafx.animation.KeyValue;
@ -54,6 +54,26 @@ import pamViewFX.fxNodes.PamSymbolFX;
*/
public class PamUtilsFX {
/**
* Convert an FX based ColourArrayType to Swing. Note that swing options that do not exist in FX and vice versa will return null.
* @param arrayFX - the FX ColourArrayType.
* @return the Swing ColourArrayType;
*/
public static ColourArrayType fxColArray2Swing(pamViewFX.fxNodes.utilsFX.ColourArray.ColourArrayType arrayFX) {
ColourArrayType type = ColourArrayType.valueOf(arrayFX.toString());
return type;
}
/**
* Convert an Swing based ColourArrayType to FX. Note that swing options that do not exist in FX and vice versa will return null.
* @param arraySwing - the Swing ColourArrayType.
* @return the FX ColourArrayType.
*/
public static pamViewFX.fxNodes.utilsFX.ColourArray.ColourArrayType swingColArray2FX(ColourArrayType arraySwing) {
pamViewFX.fxNodes.utilsFX.ColourArray.ColourArrayType type = pamViewFX.fxNodes.utilsFX.ColourArray.ColourArrayType.valueOf(arraySwing.toString());
return type;
}
/**
*
* @param awtColor
@ -632,13 +652,36 @@ 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);
}
}

View File

@ -1,6 +1,9 @@
package pamViewFX.fxPlotPanes;
import Layout.PamAxis;
import javafx.beans.binding.Bindings;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.beans.value.ObservableValue;
import javafx.geometry.Side;
import javafx.scene.Node;
import javafx.scene.canvas.Canvas;
@ -256,13 +259,31 @@ public class PlotPane extends PamBorderPane {
PamHBox horzHolder=new PamHBox();
Pane leftPane=new Pane();
//create an observable which is the size of the axis pane if the pane is visible and otherwise
//is zero.
ObservableValue<Number> valLeft = Bindings
.when(yAxisLeftPane.visibleProperty())
.then(yAxisLeftPane.widthProperty())
.otherwise(
new SimpleDoubleProperty(0.)
);
//need both min and pref to make binding work properly;
leftPane.prefWidthProperty().bind(yAxisLeftPane.widthProperty());
leftPane.minWidthProperty().bind(yAxisLeftPane.widthProperty());
leftPane.prefWidthProperty().bind(valLeft);
leftPane.minWidthProperty().bind(valLeft);
Pane rightPane=new Pane();
rightPane.prefWidthProperty().bind(yAxisRightPane.widthProperty());
rightPane.minWidthProperty().bind(yAxisRightPane.widthProperty());
ObservableValue<Number> valRight = Bindings
.when(yAxisRightPane.visibleProperty())
.then(yAxisRightPane.widthProperty())
.otherwise(
new SimpleDoubleProperty(0.)
);
rightPane.prefWidthProperty().bind(valRight);
rightPane.minWidthProperty().bind(valRight);
horzHolder.getChildren().addAll(leftPane, axisPane, rightPane);
//axisPane.toFront(); this changes the order of children in a PamHBox.
@ -369,8 +390,8 @@ public class PlotPane extends PamBorderPane {
//holderPane.getChildren().clear();
//HACK- 05/08/2016 have to do this because there is a bug in switching children postions in a border pane.
//casues a duplicate childrne error.
//HACK- 05/08/2016 have to do this because there is a bug in switching children positions in a border pane.
//causes duplicate children error
holderPane.setRight(null);
holderPane.setLeft(null);
holderPane.setTop(null);
@ -385,24 +406,30 @@ public class PlotPane extends PamBorderPane {
else if (topBorder > 0) {
// holderPane.setTopSpace(topBorder);
}
if (bottom) {
holderPane.setBottom(bottomHolder);
}
else if (bottomBorder > 0) {
// holderPane.setBottomSpace(bottomBorder);
}
if (right) {
holderPane.setRight(yAxisRightPane) ;
yAxisRightPane.setVisible(true);
}
else if (rightBorder > 0){
// holderPane.setRightSpace(rightBorder);
else {
yAxisRightPane.setVisible(false);
}
if (left) {
holderPane.setLeft(yAxisLeftPane) ;
yAxisLeftPane.setVisible(true);
}
else if (leftBorder > 0) {
// holderPane.setLeftSpace(leftBorder);
else {
yAxisLeftPane.setVisible(false);
}
holderPane.setCenter(canvasHolder);
//bottomHolder.toBack();

View File

@ -180,6 +180,17 @@ public class PeakFreqOptionsPane extends StandardSymbolModifierPane {
DoubleSpinnerValueFactory spinnerValFact = (DoubleSpinnerValueFactory) maxFreq.getValueFactory();
spinnerValFact.maxProperty().set(getSampleRate() /2);
//set reasonable step sizes
if (getSampleRate()>=10000) {
spinnerValFact.amountToStepByProperty().set(1000.);
}
else if (getSampleRate()>=2000){
spinnerValFact.amountToStepByProperty().set(200.);
}
else {
spinnerValFact.amountToStepByProperty().set(50.);
}
if (symbolOptions.freqLimts==null) {
symbolOptions.freqLimts= new double[] {0, getSampleRate() /2};

View File

@ -561,6 +561,7 @@ public class DLControl extends PamControlledUnit implements PamSettings {
* @return the number of classes.
*/
public int getNumClasses() {
if (getDLModel()==null) return 0;
return getDLModel().getNumClasses();
}
@ -622,8 +623,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;

View File

@ -19,7 +19,9 @@ import pamViewFX.fxNodes.utilityPanes.PamToggleSwitch;
import rawDeepLearningClassifier.dlClassification.DLClassName;
/*
* Symbol Options for the annotation pane
* Symbol Options for the annotation pane.
*
* @author Jamie Macaulay
*/
public class DLPredictionPane extends PamBorderPane implements TDSettingsPane {

View File

@ -1,9 +1,14 @@
package rawDeepLearningClassifier.dataPlotFX;
import java.awt.Color;
import java.awt.event.ActionEvent;
import PamController.PamController;
import PamUtils.PamArrayUtils;
import PamView.GeneralProjector;
import PamView.PamSymbolType;
import PamView.dialog.GenericSwingDialog;
import PamView.dialog.PamDialogPanel;
import PamView.symbol.PamSymbolChooser;
import PamView.symbol.SymbolData;
import PamView.symbol.modifier.SymbolModType;
@ -20,7 +25,8 @@ import rawDeepLearningClassifier.logging.DLAnnotationType;
/**
* The DL symbol modifier. Colours symbols by prediction.
* The DL symbol modifier. Colours symbols by either the value of the prediction
* by a user selected class or by the class with the highest prediction value.
*
* @author Jamie Macaulay.
*
@ -36,12 +42,12 @@ public class DLSymbolModifier extends SymbolModifier {
/**
* The symbol options pane.
* JavaFX symbol options pane.
*/
private DLSymbolOptionPane optionsPane;
/**
* Rge DL annotation type.
* The DL annotation type.
*/
private DLAnnotationType dlAnnotType;
@ -55,6 +61,11 @@ public class DLSymbolModifier extends SymbolModifier {
private ColourArray colourArray;
/**
* Swing option panel for the symbol chooser.
*/
private DLSymbolOptionPanel optionsPanel;
public DLSymbolModifier(PamSymbolChooser symbolChooser, DLAnnotationType dlAnnotType) {
@ -94,7 +105,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;
@ -161,6 +235,7 @@ public class DLSymbolModifier extends SymbolModifier {
* Get the JavaFX symbol options pane that has options for the symbol pane.
* @return the symbol options pane.
*/
@Override
public SymbolModifierPane getOptionsPane() {
if (optionsPane == null) {
optionsPane = new DLSymbolOptionPane(this);
@ -168,6 +243,23 @@ public class DLSymbolModifier extends SymbolModifier {
return optionsPane;
}
@Override
public PamDialogPanel getDialogPanel() {
if (optionsPanel == null) {
optionsPanel = new DLSymbolOptionPanel(this);
}
return optionsPanel;
}
/**
* Default behaviour to show the dialog panel.
* @param e
* @param dialogPanel
*/
protected void showOptionsDialog(ActionEvent e, PamDialogPanel dialogPanel) {
GenericSwingDialog.showDialog(PamController.getMainFrame(), getName() + " options", dialogPanel);
}
public DLAnnotationType getDLAnnotType() {
return dlAnnotType;

View File

@ -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.
@ -11,10 +14,27 @@ 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<num) {
classColors = new int[32];
}
//run through default colours
for (int i=0; i<classColors.length; i++) {
switch (i%8) {
case 0:
classColors[i]=PamUtilsFX.colorToInt(Color.RED);
break;
case 1:
classColors[i]=PamUtilsFX.colorToInt(Color.GREEN);
break;
case 2:
classColors[i]=PamUtilsFX.colorToInt(Color.BLUE);
break;
case 3:
classColors[i]=PamUtilsFX.colorToInt(Color.CYAN);
break;
case 4:
classColors[i]=PamUtilsFX.colorToInt(Color.MAGENTA);
break;
case 5:
classColors[i]=PamUtilsFX.colorToInt(Color.YELLOW);
break;
case 6:
classColors[i]=PamUtilsFX.colorToInt(Color.ORANGE);
break;
case 7:
classColors[i]=PamUtilsFX.colorToInt(Color.PURPLE);
break;
}
}
}
@Override
protected DLSymbolModifierParams clone() {

View File

@ -1,30 +1,43 @@
package rawDeepLearningClassifier.dataPlotFX;
import org.controlsfx.control.SegmentedButton;
import PamView.symbol.StandardSymbolOptions;
import PamView.symbol.modifier.SymbolModifier;
import PamView.symbol.modifier.SymbolModifierParams;
import javafx.geometry.Insets;
import javafx.geometry.Orientation;
import javafx.geometry.Pos;
import javafx.scene.control.CheckBox;
import javafx.scene.control.ColorPicker;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Label;
import javafx.scene.control.ToggleButton;
import javafx.scene.control.Tooltip;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.layout.Priority;
import pamViewFX.fxNodes.PamBorderPane;
import pamViewFX.fxNodes.PamHBox;
import pamViewFX.fxNodes.PamVBox;
import pamViewFX.fxNodes.comboBox.ColorComboBox;
import pamViewFX.fxNodes.sliders.ColourRangeSlider;
import pamViewFX.fxNodes.utilsFX.ColourArray;
import pamViewFX.fxNodes.utilsFX.PamUtilsFX;
import pamViewFX.symbol.StandardSymbolModifierPane;
import rawDeepLearningClassifier.dlClassification.DLClassName;
/*
* Symbol Options for the annotation pane
* Symbol Options for the annotation pane.
*
* @author Jamie Macaulay
*/
public class DLSymbolOptionPane extends StandardSymbolModifierPane {
private static final double CLASS_NAME_BOX_WIDTH = 130;
/**
* The colour range slider for colouring probabilities.
*/
@ -42,21 +55,143 @@ public class DLSymbolOptionPane extends StandardSymbolModifierPane {
/**
* Combo box which allows a user to select colour
* Color picker which allows a user to select the gradient for colouring predictions
*/
private ColorComboBox colourBox;
/**
* Color picker which allows a user to select colour for each class.
*/
private ColorPicker colourPicker;
private boolean initialised=true;
private DLSymbolModifier dlSymbolModifier;
private ComboBox<String> 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));
segmentedButton.getStyleClass().add(SegmentedButton.STYLE_CLASS_DARK);
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<String>();
classNameBox2.setOnAction((action)->{
getParams();
//change the colour of the colour range slider.
notifySettingsListeners();
});
classNameBox2.setMaxWidth(Double.MAX_VALUE);
classNameBox2.setOnAction((action)->{
if (classNameBox2.getSelectionModel().getSelectedIndex()>=0){
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() {
PamVBox holder = new PamVBox();
@ -82,6 +217,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 +229,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 +242,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,17 +250,29 @@ 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 ) {
//bit messy but works /
DLSymbolModifierParams symbolOptions = dlSymbolModifier.getSymbolModifierParams();
int index = classNameBox2.getSelectionModel().getSelectedIndex()>=0 ? classNameBox2.getSelectionModel().getSelectedIndex():0;
//need to chekc this here.
//checkClassNamesBox(symbolOptions);
symbolOptions.classColors[index] = PamUtilsFX.colorToInt(colourPicker.getValue());
symbolOptions.classIndex2 = classNameBox2.getSelectionModel().getSelectedIndex();
return symbolOptions;
}
/**
*
* @param symbolOptions
* @return
*/
public DLSymbolModifierParams getPredictionColParams(DLSymbolModifierParams symbolOptions ) {
symbolOptions.clims=new double[] {colourRangeSlider.getLowValue(), colourRangeSlider.getHighValue()};
@ -138,17 +280,38 @@ public class DLSymbolOptionPane extends StandardSymbolModifierPane {
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<String> classNameBox) {
DLClassName[] classNames = dlSymbolModifier.getDLAnnotType().getDlControl().getDLModel().getClassNames();
@ -167,8 +330,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,25 +386,28 @@ 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);
// b1.setSelected(false);
// b2.setSelected(false);
if (symbolOptions.colTypeSelection == DLSymbolModifierParams.PREDICITON_COL) b1.setSelected(true);
if (symbolOptions.colTypeSelection == DLSymbolModifierParams.CLASS_COL) b2.setSelected(true);
//set the combo box class types.
checkClassNamesBox( symbolOptions);
setSettingsPane();
// symbolOptions.colTypeSelection = b1.isSelected() ? DLSymbolModifierParams.PREDICITON_COL : DLSymbolModifierParams.CLASS_COL;
//color box.
colourBox.setValue(symbolOptions.colArray);
//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;
}

View File

@ -0,0 +1,424 @@
package rawDeepLearningClassifier.dataPlotFX;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.AbstractButton;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.ButtonGroup;
import javax.swing.JCheckBox;
import javax.swing.JColorChooser;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.JToggleButton;
import javax.swing.border.TitledBorder;
import PamView.ColourComboBox;
import PamView.PamAWTUtils;
import PamView.dialog.GenericSwingDialog;
import PamView.dialog.PamButton;
import PamView.dialog.PamDialogPanel;
import PamView.panel.PamPanel;
import PamView.symbol.StandardSymbolOptions;
import PamView.symbol.modifier.SymbolModifier;
import Spectrogram.ColourRangeSlider;
import pamViewFX.fxNodes.utilsFX.ColourArray;
import pamViewFX.fxNodes.utilsFX.PamUtilsFX;
import rawDeepLearningClassifier.dlClassification.DLClassName;
/**
*
* Swing symbol options for the annotation pane.
*
* @author Jamie Macaulay
*/
public class DLSymbolOptionPanel implements PamDialogPanel, ActionListener {
private static final long serialVersionUID = 1L;
private static final double CLASS_NAME_BOX_WIDTH = 130;
private DLSymbolModifier dlSymbolModifier;
/**
* The color range slider for coloring probabilities.
*/
private ColourRangeSlider colorRangeSlider;
/**
* The combo box allowing users to select which class to show.
*/
private JComboBox<String> classNameBox;
private JComboBox<String> classNameBox2;
/**
* Check box allowing users only to show only those detections which have passed binary classification.
*/
private JCheckBox showOnlyBinary;
/**
* Color picker which allows a user to select the gradient for coloring predictions
*/
private ColourComboBox colorComboBox;
/**
* Color picker which allows a user to select color for each class.
*/
private JColorChooser colorPicker;
private boolean initialized = true;
/**
* Pane which holds controls for changing the colour based on prediciton value
*/
private JPanel probPane;
/**
* Pane which holds controls for changing the colour based on the highest prediction value
*/
private JPanel classPane;
private PamPanel holder;
/**
* Button to select how to colour.
*/
private JToggleButton b1, b2;
private PamPanel mainPanel;
private int[] classColours;
private AbstractButton colourButton;
public DLSymbolOptionPanel(SymbolModifier symbolModifer) {
this.dlSymbolModifier = (DLSymbolModifier) symbolModifer;
probPane = createProbPane();
probPane.setBorder(new TitledBorder("Colour by prediction value"));
classPane = createClassPane();
classPane.setBorder(new TitledBorder("Colour by class"));
b1 = new JToggleButton("Prediction");
b1.setPreferredSize(new Dimension(100, 25)); // Set preferred size
b2 = new JToggleButton("Class");
b2.setPreferredSize(new Dimension(100, 25));
ButtonGroup buttonGroup = new ButtonGroup(); // Group toggle buttons
buttonGroup.add(b1);
buttonGroup.add(b2);
b1.addActionListener(this);
b2.addActionListener(this);
JPanel segmentedButtonPanel = new JPanel(new FlowLayout(FlowLayout.CENTER));
segmentedButtonPanel.add(b1);
segmentedButtonPanel.add(b2);
holder = new PamPanel();
holder.setLayout(new BorderLayout());
holder.add(new JLabel("Hello"), BorderLayout.NORTH);
mainPanel = new PamPanel();
mainPanel.setLayout(new BorderLayout());
mainPanel.add(segmentedButtonPanel, BorderLayout.NORTH);
mainPanel.add(holder, BorderLayout.CENTER);
mainPanel.add(showOnlyBinary = new JCheckBox("Show only binary classification"), BorderLayout.SOUTH);
setSettingsPane();
initialized = true;
}
@Override
public JComponent getDialogComponent() {
return mainPanel;
}
@Override
public void setParams() {
//get the symbool options
DLSymbolModifierParams symbolOptions = dlSymbolModifier.getSymbolModifierParams();
// b1.setSelected(false);
// b2.setSelected(false);
if (symbolOptions.colTypeSelection == DLSymbolModifierParams.PREDICITON_COL) b1.setSelected(true);
if (symbolOptions.colTypeSelection == DLSymbolModifierParams.CLASS_COL) b2.setSelected(true);
setSettingsPane();
//set the parameters for colouring by prediction
setPredictionColParams(symbolOptions);
//set the class colour parameters
setClassColParams(symbolOptions);
//set the selected.
showOnlyBinary.setSelected(symbolOptions.showOnlyBinary);
setSettingsPane();
}
private int checkClassNamesBox(DLSymbolModifierParams symbolOptions, JComboBox<String> classNameBox) {
DLClassName[] classNames = dlSymbolModifier.getDLAnnotType().getDlControl().getDLModel().getClassNames();
// for (int i =0; i<classNames.length; i++) {
// System.out.println("DLSymbolOptionsPane: classNames: " + i + " " + classNames[i].className);
// }
int nClass = dlSymbolModifier.getDLAnnotType().getDlControl().getDLModel().getNumClasses();
classNameBox.removeAllItems();
for (int i=0; i<nClass; i++) {
if (classNames!=null && classNames.length>i) {
classNameBox.addItem(classNames[i].className);
}
else {
classNameBox.addItem("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
colorRangeSlider.setValue((int) symbolOptions.clims[0]*100);
colorRangeSlider.setUpperValue((int) symbolOptions.clims[1]*100);
// colorRangeSlider.setColourArrayType( symbolOptions.colArray);
colorRangeSlider.setColourMap(PamUtilsFX.fxColArray2Swing(symbolOptions.colArray));
int nClass = checkClassNamesBox( symbolOptions, classNameBox);
symbolOptions.classIndex = Math.min(symbolOptions.classIndex, nClass-1);
classNameBox.setSelectedIndex(Math.max(symbolOptions.classIndex, 0));
//color box.
colorComboBox.setSelectedColourMap(PamUtilsFX.fxColArray2Swing(symbolOptions.colArray));
}
/**
* Set parameters for controls to change the colour gradient based on prediction.
* @param symbolOptions - the symbol options
*/
private void setClassColParams(DLSymbolModifierParams symbolOptions) {
//create a temporary array to save different class colours in - this needs to be
//before other params are set.
this.classColours = symbolOptions.classColors;
int nClass = checkClassNamesBox( symbolOptions, classNameBox2);
symbolOptions.classIndex = Math.min(symbolOptions.classIndex, nClass-1);
classNameBox2.setSelectedIndex(Math.max(symbolOptions.classIndex2, 0));
// int index = symbolOptions.classIndex2>=0 ? symbolOptions.classIndex2 : 0;
if (symbolOptions.classColors==null) {
symbolOptions.setDefaultClassColors(nClass);
}
if (classNameBox2.getSelectedIndex()>=0) {
colourButton.setBackground(PamAWTUtils.intToColor(classColours[classNameBox2.getSelectedIndex()]));
colourButton.repaint();
}
// //set the correct colour
// colorPicker.setColor(symbolOptions.classColors[index]);
}
/**
* get parameters for colouring by class.
* @param symbolOptions - the symbol options.
* @return
*/
public DLSymbolModifierParams getClassColParams(DLSymbolModifierParams symbolOptions ) {
// int index = classNameBox2.getSelectedIndex()>=0 ? classNameBox2.getSelectedIndex():0;
symbolOptions.classColors = classColours;
symbolOptions.classIndex2 = classNameBox2.getSelectedIndex();
return symbolOptions;
}
/**
*
* @param symbolOptions
* @return
*/
public DLSymbolModifierParams getPredictionColParams(DLSymbolModifierParams symbolOptions ) {
symbolOptions.clims=new double[] {((double) colorRangeSlider.getValue())/100., ((double) colorRangeSlider.getUpperValue())/100.};
symbolOptions.colArray = PamUtilsFX.swingColArray2FX(this.colorComboBox.getSelectedColourMap());
symbolOptions.classIndex = classNameBox.getSelectedIndex();
return symbolOptions;
}
@Override
public boolean getParams() {
//bit messy but works
DLSymbolModifierParams symbolOptions = dlSymbolModifier.getSymbolModifierParams().clone();
//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();
//set the paratmers.
dlSymbolModifier.setSymbolModifierParams(symbolOptions);
return true;
}
private void setSettingsPane() {
holder.removeAll();
//holder.setLayout(new BorderLayout());
holder.validate();
if (b1.isSelected()) {
holder.add(probPane, BorderLayout.CENTER);
} else if (b2.isSelected()) {
holder.add(classPane, BorderLayout.CENTER);
}
holder.validate();
mainPanel.validate();
if (mainPanel.getRootPane()!=null) {
//pack the dialog because it is a different size
((GenericSwingDialog) mainPanel.getRootPane().getParent()).pack();
}
}
private JPanel createClassPane() {
classNameBox2 = new JComboBox<>();
classNameBox2.addActionListener((action)->{
//make sure the setting button shows the colour
if (classNameBox2.getSelectedIndex()>=0) {
colourButton.setBackground(PamAWTUtils.intToColor(classColours[classNameBox2.getSelectedIndex()]));
}
setSettingsPane();
});
classNameBox2.setPreferredSize(new Dimension((int) CLASS_NAME_BOX_WIDTH, 25));
// colorPicker.setPreferredSize(new Dimension(60, 25));
colourButton = new PamButton("Color");
colourButton.addActionListener((action)->{
Color color = JColorChooser.showDialog(colourButton, "Pick colour for class", PamAWTUtils.intToColor(classColours[classNameBox2.getSelectedIndex()]));
if (color!=null) {
colourButton.setBackground(color);
// colourButton.setForeground(color);
classColours[classNameBox2.getSelectedIndex()]=PamAWTUtils.colorToInt(color);
}
});
JPanel classHolder = new JPanel();
FlowLayout flowLayout = new FlowLayout();
classHolder.setLayout(flowLayout);
classHolder.add(classNameBox2);
classHolder.add(colourButton);
return classHolder;
}
private PamPanel createProbPane() {
PamPanel holder = new PamPanel();
holder.setLayout(new GridBagLayout());
GridBagConstraints c = new GridBagConstraints();
c.gridx = 0;
c.gridy = 0;
holder.add(new JLabel("Select class"), c);
c.gridx++;
classNameBox = new JComboBox<String>();
holder.add(classNameBox, c);
c.gridx = 0;
c.gridwidth = 1;
c.gridy++;
holder.add(new JLabel("Color map"), c);
c.gridx++;
colorComboBox = new ColourComboBox();
colorComboBox.addActionListener((action)->{
colorRangeSlider.setColourMap(colorComboBox.getSelectedColourMap());
colorRangeSlider.repaint();
});
holder.add(colorComboBox, c);
c.gridx = 0;
c.gridy++;
c.gridwidth =2;
colorRangeSlider = new ColourRangeSlider(0, 100, JSlider.HORIZONTAL); // Min 0, Max 1 for probabilities
colorRangeSlider.setPaintTicks(true);
colorRangeSlider.setMinorTickSpacing(20);
holder.add(colorRangeSlider, c);
return holder;
}
@Override
public void actionPerformed(ActionEvent e) {
setSettingsPane();
}
}

View File

@ -0,0 +1,49 @@
package rawDeepLearningClassifier.dataSelector;
import PamView.dialog.PamDialogPanel;
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
* 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);
/**
* Settings controls for the deep learning filter.
* @return the controls for this filter.
*/
public DynamicSettingsPane<DataSelectParams> getSettingsPane();
/**
* Swing settings controls for the data deep learning filter.
* @return Swing dialog for the settings
*/
public PamDialogPanel getSettingsPanel();
}

View File

@ -0,0 +1,40 @@
package rawDeepLearningClassifier.dataSelector;
import PamguardMVC.dataSelector.DataSelectParams;
import PamguardMVC.dataSelector.DataSelector;
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 AnnotationDataSelCreator<DLAnnotation> {
private DLControl dlcontrol;
public DLDataSelectCreator(DLControl dlcontrol, DLAnnotationType type) {
super(type);
this.dlcontrol = dlcontrol;
}
@Override
public DataSelectParams createNewParams(String name) {
return new DLDataSelectorParams();
}
@Override
public DataSelector createDataSelector(DataAnnotationType<DLAnnotation> dataAnnotationType, String selectorName,
boolean allowScores, String selectorType) {
// TODO Auto-generated method stub
return new DLDataSelector(dlcontrol, dataAnnotationType, null,
selectorName, allowScores);
}
}

View File

@ -0,0 +1,153 @@
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 annotation.DataAnnotationType;
import annotation.dataselect.AnnotationDataSelector;
import javafx.scene.Node;
import pamViewFX.fxSettingsPanes.DynamicSettingsPane;
import rawDeepLearningClassifier.DLControl;
import rawDeepLearningClassifier.logging.DLAnnotation;
/**
* Data selector of DL data units. Note that data selectors are for deep leanring annotations
* rather than deep learning data units.
* <p>
* The data selector can have different types of data selectors which can
* depend on the classifier used and user choice.
* <p>
* 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 AnnotationDataSelector<DLAnnotation> {
/**
* Swing panel for the data selector.
*/
private DLSelectPanel dlSelectPanel;
/**
* FX panel for the data selector.
*/
private DLSelectPaneFX dlSelectPaneFX;
/**
* Data filter for filtering.
*/
private List<DLDataFilter> dataFilters = new ArrayList<DLDataFilter> ();
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, DataAnnotationType<DLAnnotation> 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<dataFilters.size() ; i++) {
dlDataSelectParams.dataSelectorParams[i] = dataFilters.get(i).getParams();
}
}
@Override
public PamDialogPanel getDialogPanel() {
if (dlSelectPanel == null) {
dlSelectPanel = new DLSelectPanel(this);
}
return dlSelectPanel;
}
@Override
public DynamicSettingsPane<Boolean> 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<dlDataSelectParams.dataSelectorParams.length; i++) {
dataFilters.get(i).setParams((DataSelectParams) dlDataSelectParams.dataSelectorParams[i]);
}
}
}
/**
* Check that the data filters are the corret size.
* @param dataSelectParams - the apramters to set.
*/
private void checkDataFilterParamsSize(DLDataSelectorParams dataSelectParams) {
if (dataSelectParams.dataSelectorParams==null || dataSelectParams.dataSelectorParams.length!=dataFilters.size()) {
dataSelectParams.dataSelectorParams = new DataSelectParams[dataFilters.size()];
for (int i=0; i<dataSelectParams.dataSelectorParams.length; i++) {
dataSelectParams.dataSelectorParams[i] = dataFilters.get(i).getParams();
}
}
}
@Override
public DLDataSelectorParams getParams() {
//get the paramters from the pane.
for (int i=0; i<dlDataSelectParams.dataSelectorParams.length; i++) {
dlDataSelectParams.dataSelectorParams[i] = dataFilters.get(i).getParams();
}
return dlDataSelectParams;
}
@Override
public double scoreData(PamDataUnit pamDataUnit) {
int score = dataFilters.get(dlDataSelectParams.dataSelectorIndex).scoreDLData(pamDataUnit);
return score>=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;
}
public DLDataFilter getCurrentDataSelector() {
return dataFilters.get(dlDataSelectParams.dataSelectorIndex);
}
public List<DLDataFilter> getDataSelectors() {
return dataFilters;
}
}

View File

@ -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;
}

View File

@ -0,0 +1,140 @@
package rawDeepLearningClassifier.dataSelector;
import PamView.dialog.PamDialogPanel;
import PamguardMVC.PamDataUnit;
import PamguardMVC.dataSelector.DataSelectParams;
import pamViewFX.fxSettingsPanes.DynamicSettingsPane;
import rawDeepLearningClassifier.DLControl;
import rawDeepLearningClassifier.dlClassification.DLDetection;
import rawDeepLearningClassifier.dlClassification.PredictionResult;
import rawDeepLearningClassifier.logging.DLAnnotation;
/**
* 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();
private DLPredictonPane dlPredictonPane;
private DLPredictionPanel dlPredictonPanel;
public DLPredictionFilter(DLControl dlcontrol) {
this.dlcontrol = dlcontrol;
checkParamsClass() ;
}
@Override
public int scoreDLData(PamDataUnit dataUnit) {
DLAnnotation annotation = (DLAnnotation) dataUnit. findDataAnnotation(DLAnnotation.class) ;
if (annotation==null) return -1;
//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: annotation.getModelResults()) {
results = modelResult.getPrediction();
for (int j=0; j<results.length; j++) {
if (filterParams.classSelect[j] && results[j]>filterParams.minClassPredicton[j]) {
if (results[j]> maxPred) maxClassIndex = j;
}
}
}
return maxClassIndex;
}
@Override
public DLPredictionFilterParams 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<filterParams.classSelect.length; i++) {
filterParams.classSelect[i]=true;
filterParams.minClassPredicton[i]=0.4;
}
}
}
@Override
public DynamicSettingsPane getSettingsPane() {
if (dlPredictonPane ==null) {
dlPredictonPane = new DLPredictonPane(this);
}
return dlPredictonPane;
}
@Override
public PamDialogPanel getSettingsPanel() {
if (dlPredictonPanel ==null) {
dlPredictonPanel = new DLPredictionPanel(this);
}
return dlPredictonPanel;
}
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.
// * @return an index of the hghest predictions.
// */
// public static int[] getBestClass(List<PredictionResult> 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;
// }
}

View File

@ -0,0 +1,36 @@
package rawDeepLearningClassifier.dataSelector;
import PamguardMVC.dataSelector.DataSelectParams;
/**
* Parameters for filtering by the minimum prediciton value.
*/
public class DLPredictionFilterParams extends DataSelectParams implements Cloneable{
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.
*/
@Override
public DLPredictionFilterParams clone() {
try {
return (DLPredictionFilterParams) super.clone();
} catch (CloneNotSupportedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
}

View File

@ -0,0 +1,183 @@
package rawDeepLearningClassifier.dataSelector;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Label;
import javax.swing.JCheckBox;
import javax.swing.JComponent;
import javax.swing.JSpinner;
import javax.swing.JToggleButton;
import javax.swing.SpinnerNumberModel;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import org.kordamp.ikonli.materialdesign2.MaterialDesignL;
import org.kordamp.ikonli.swing.FontIcon;
import PamView.dialog.PamDialogPanel;
import PamView.dialog.PamGridBagContraints;
import PamView.panel.PamPanel;
import rawDeepLearningClassifier.dlClassification.DLClassName;
/**
* Swing panel for Deep learning predicitons.
*/
public class DLPredictionPanel implements PamDialogPanel {
private DLPredictionFilter predicitonFilter;
private PamPanel contentPanel;
private JCheckBox[] enableClass;
private JSpinner[] spinnerClass;
private JToggleButton lockButton;
public DLPredictionPanel(DLPredictionFilter dlPredictionFilter) {
super();
this.predicitonFilter = dlPredictionFilter;
contentPanel = new PamPanel();
contentPanel.setLayout(new GridBagLayout());
lockButton = new JToggleButton();
FontIcon iconlock = FontIcon.of(MaterialDesignL.LOCK);
iconlock.setIconSize(20);
iconlock.setIconColor(Color.DARK_GRAY);
FontIcon iconlockopen = FontIcon.of(MaterialDesignL.LOCK_OPEN);
iconlockopen.setIconSize(20);
iconlockopen.setIconColor(Color.DARK_GRAY);
lockButton.setIcon(iconlockopen);
lockButton.addActionListener((action)->{
if (lockButton.isSelected()) {
lockButton.setIcon(iconlock);
}
else {
lockButton.setIcon(iconlockopen);
}
lockButton.validate();
});
}
@Override
public JComponent getDialogComponent() {
return contentPanel;
}
@Override
public void setParams() {
DLPredictionFilterParams params = predicitonFilter.getParams();
// TODO Auto-generated method stub
setClassPane(params);
for (int i=0; i<params.classSelect.length ; i++) {
//set the correct params
enableClass[i].setSelected(params.classSelect[i]);
spinnerClass[i].setValue(params.minClassPredicton[i]);
}
}
private void setClassPane(DLPredictionFilterParams input) {
DLClassName[] classNames = predicitonFilter.getDLControl().getDLModel().getClassNames();
contentPanel.removeAll();
enableClass = new JCheckBox[input.classSelect.length];
spinnerClass = new JSpinner[input.classSelect.length];
GridBagConstraints c = new PamGridBagContraints();
c.ipadx =5;
c.gridwidth=2;
contentPanel.add(new Label("Min. prediciton for each class"), c);
c.gridwidth=1;
c.gridy++;
for (int i=0; i<input.classSelect.length ; i++) {
//create the row
c.gridx = 0;
enableClass[i] = new JCheckBox(classNames[i].className);
final int ii = i;
enableClass[i].addActionListener((action)->{
spinnerClass[ii].setEnabled(enableClass[ii].isSelected());
});
enableClass[i].setToolTipText(classNames[i].className);
contentPanel.add(enableClass[i], c);
c.gridx = 1;
spinnerClass[i] = new JSpinner(new SpinnerNumberModel(0., 0., 1., 0.05));
Dimension prefSize = spinnerClass[i].getPreferredSize();
prefSize = new Dimension(60, prefSize.height);
spinnerClass[i] .setPreferredSize(prefSize);
spinnerClass[i].addChangeListener(new ChangeListener() {
@Override
public void stateChanged(ChangeEvent e) {
if (lockButton.isSelected()) {
for (int j=0; j<spinnerClass.length ; j++) {
if (j!=ii) {
spinnerClass[j].setValue(spinnerClass[ii].getValue());
}
}
}
}
});
spinnerClass[i].setToolTipText(classNames[i].className);
contentPanel.add(spinnerClass[i], c);
if (i==0 && input.classSelect.length>1) {
//set a lock button to
c.gridx=2;
contentPanel.add(lockButton, c);
//make the lock button the same height as the spinner
Dimension prefSizeB = lockButton.getPreferredSize();
lockButton.setPreferredSize(new Dimension(prefSizeB.width, prefSize.height));
}
c.gridy++;
}
contentPanel.validate();
}
@Override
public boolean getParams() {
DLPredictionFilterParams currParams = predicitonFilter.getParams();
for (int i=0; i<spinnerClass.length ; i++) {
currParams.classSelect[i] = enableClass[i].isSelected();
currParams.minClassPredicton[i] = (double) spinnerClass[i].getValue();
}
predicitonFilter.setParams(currParams);
return true;
}
}

View File

@ -0,0 +1,207 @@
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 pamViewFX.PamGuiManagerFX;
import pamViewFX.fxGlyphs.PamGlyphDude;
import pamViewFX.fxNodes.PamBorderPane;
import pamViewFX.fxNodes.PamHBox;
import pamViewFX.fxNodes.PamVBox;
import pamViewFX.fxSettingsPanes.DynamicSettingsPane;
import rawDeepLearningClassifier.dlClassification.DLClassName;
/**
* Settings pane for filtering deep learning results by class prediciton.
*/
public class DLPredictonPane extends DynamicSettingsPane<DLPredictionFilterParams>{
private DLPredictionFilter predicitonFilter;
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 PamBorderPane();
PamBorderPane topPane = new PamBorderPane();
Label label = new Label("Show classes above min. prediction");
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 {
Slider slider;
CheckBox enable;
Label valueLabel;
private boolean enableListener = true;
ClassDataSelector(String classType, int index) {
enable = new CheckBox(classType);
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();
});
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() && enableListener) {
for (int i=0; i<classPanes.length; i++) {
if (classPanes[i].enable.isSelected() && i!=index) {
classPanes[i].enableListener = false; //prevent needless calls to notify settings
classPanes[i].slider.setValue(newval.doubleValue());
classPanes[i].enableListener = true;
}
};
}
notifySettingsListeners();
});
slider.setMin(0.);
slider.setMax(1.);
PamHBox.setHgrow(slider, Priority.ALWAYS);
valueLabel = new Label();
valueLabel.setMinWidth(30);
valueLabel.setText(String.format("%.2f", slider.getValue()));
this.getChildren().addAll(enable, slider, valueLabel);
}
}
@Override
public DLPredictionFilterParams getParams(DLPredictionFilterParams currParams) {
for (int i=0; i<classPanes.length ; i++) {
currParams.classSelect[i] = classPanes[i].enable.isSelected();
currParams.minClassPredicton[i] = classPanes[i].slider.getValue();
}
return currParams;
}
@Override
public void setParams(DLPredictionFilterParams input) {
//set the parameters. Note that class numbers should have been checked already.
setClassPane(input);
}
private void setClassPane(DLPredictionFilterParams input) {
DLClassName[] classNames = predicitonFilter.getDLControl().getDLModel().getClassNames();
contentPane.getChildren().clear();
classPanes = new ClassDataSelector[input.classSelect.length];
ClassDataSelector classPane;
for (int i=0; i<input.classSelect.length ; i++) {
classPane = new ClassDataSelector(classNames[i].className, i);
classPanes[i] = classPane;
contentPane.getChildren().add(classPane);
classPane.enable.setSelected(input.classSelect[i]);
classPane.slider.setValue(input.minClassPredicton[i]);
if (i==input.classSelect.length-1) {
classPane.slider.setShowTickLabels(true);
}
}
}
// /**
// * Check the class numbers are correct
// * @param input
// */
// private void checkSpeciesClassNumbers(DLPredictionFilterParams input) {
// this.predicitonFilter.getSpeciesClassList();
//
// }
@Override
public String getName() {
return "Deep learning prediciton filter";
}
@Override
public Node getContentNode() {
return mainPane;
}
@Override
public void paneInitialized() {
// TODO Auto-generated method stub
}
}

View File

@ -0,0 +1,102 @@
package rawDeepLearningClassifier.dataSelector;
import javafx.scene.Node;
import pamViewFX.fxNodes.PamBorderPane;
import pamViewFX.fxSettingsPanes.DynamicSettingsPane;
/**
* JavaFX pane for the deep learning data selector. This simply selects the rype
* of filter to use and sets that as the controls.
* <p>
* 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<Boolean>{
private PamBorderPane mainPane;
/**
* 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) {
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 PamBorderPane();
//need to add a settings listener to the filter panes to pass on any notification this settings listener.
for (int i=0; i<dlDataSelector.getDataSelectors().size(); i++) {
dlDataSelector.getDataSelectors().get(currentIndex).getSettingsPane().addSettingsListener(()->{
//notify any listeners to this pane that a filter pane has changed.
notifySettingsListeners();
});
}
}
@Override
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]);
dlDataSelector.setParams(currParams);
return true;
}
@Override
public void setParams(Boolean input) {
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
public String getName() {
return "Filter by deep learning result";
}
@Override
public Node getContentNode() {
return mainPane;
}
@Override
public void paneInitialized() {
// TODO Auto-generated method stub
}
}

View File

@ -0,0 +1,83 @@
package rawDeepLearningClassifier.dataSelector;
import java.awt.BorderLayout;
import javax.swing.JComponent;
import javax.swing.JLabel;
import PamView.dialog.PamDialogPanel;
import PamView.panel.PamPanel;
import javafx.scene.Node;
import pamViewFX.fxNodes.PamBorderPane;
/**
* Swing panel for the deep learning data selector.
*/
public class DLSelectPanel implements PamDialogPanel {
private PamPanel mainPanel;
private DLDataSelector dlDataSelector;
private int currentIndex = 0;
public DLSelectPanel(DLDataSelector dlDataSelector) {
super();
this.dlDataSelector=dlDataSelector;
mainPanel = new PamPanel();
mainPanel.setLayout(new BorderLayout());
}
@Override
public JComponent getDialogComponent() {
return mainPanel;
}
private void setDataFilterPane(int index) {
DLDataFilter dlFilter = dlDataSelector.getDataSelectors().get(index);
mainPanel.add(dlFilter.getSettingsPanel().getDialogComponent(), BorderLayout.CENTER);
}
@Override
public void setParams() {
DLDataSelectorParams currParams = dlDataSelector.getParams();
this.currentIndex = currParams.dataSelectorIndex;
//set the stored paramters for the deep learning filter
dlDataSelector.getDataSelectors().get(currentIndex).setParams(currParams.dataSelectorParams[currentIndex]);
//set the paramters in the dialog - note the dialog will have areference ot the filter and so can access the r
//set params above.
dlDataSelector.getDataSelectors().get(currentIndex).getSettingsPanel().setParams();
//set the
setDataFilterPane(currentIndex);
}
@Override
public boolean getParams() {
DLDataSelectorParams currParams = dlDataSelector.getParams();
//dialog has a reference to the data filter and will change params.
boolean dataFilterOK = dlDataSelector.getDataSelectors().get(currentIndex).getSettingsPanel().getParams();
if (dataFilterOK) {
//TODO - maybe should grab settings from all filters or just the selected one?
currParams.dataSelectorParams[currentIndex] = dlDataSelector.getDataSelectors().get(currentIndex).getParams();
dlDataSelector.setParams(currParams);
return true;
}
else {
return false;
}
}
}

View File

@ -106,15 +106,15 @@ If using a deep learning model from a supported framework then all transforms ar
_An AnimalSpot, Ketos or other supported deep learning model will automatically create a list of transforms with the appropriate settings. These is no need to use the advanced pane but it is there in case users wish to change transform settings for some reason_
### Default Models
### Exanmple Models
Default models are selectable from the menu button in the Deep Learning Pane. Default models are deep learning classifiers which are open source, known to be effective and have have been published in open access academic litrature; they are downloaded directly from a GitHub repository and then all associated settings are automtically applied. The default model selection pane also contains hyperlinks to the papers descirbing each model which will take users directly to the relvent website.
Example models are selectable from the menu button in the Deep Learning Pane. Default models are deep learning classifiers which are open source, known to be effective and have have been published in open access academic litrature; they are downloaded directly from a GitHub repository and then all associated settings are automtically applied. The default model selection pane also contains hyperlinks to the papers descirbing each model which will take users directly to the relvent website.
<p align="center">
<img width="550" height="700" src = "resources/default_settings_humpback_1.png">
</p>
_Default models can be downloaded. Default models are models which are published, open and have been known to work well amongst the bioacoustics community. More will be added to PAMGaurd over time. If you you would like your model to become a defualt model then drop PAMGuard support an email._
_Example models can be downloaded. Example models are models which are published, open and have been known to work well amongst the bioacoustics community. More will be added to PAMGaurd over time. If you you would like your model to become a defualt model then drop PAMGuard support an email._
## Running
@ -144,6 +144,26 @@ _An example click detection module output coloured by deep learning annotations.
Other displays also show outputs from the deep learning module. Hovering over data units in the click display will, for example, show deep learning prediction values. The spectrogram will also show deep learning detections as translucent blue boxes (these must be selected in the right click menu).
### Symbol Manager
The deep learning classifier has a symbol manager that is used throughtout different PAMGuard displays. The symbol manager can either display deep learning detections by the prediction values of a particular class or colour the output class with the highest prediciton class. Colouring by a prediction value requires the user to select which output class they wish to display - all detections are then coloured by the output prediction values for that particular class via a selectable colour map. If colour by class is selected then each detection is coloured by the class with the maximum prediction value. The colours for each class are selectable.
<p align="center">
<img width="429" height="150" src = "resources/deep_learning_symbol_manager.png">
</p>
_The options for the deep learning symbol manager_
### Data selector
The deep learning classifier has a data selector which is utilsied thorughout PAMGuard (for example for exporting detections, generating clips, showing on displays). The data selector allows users to filter detections based on a minimum prediciton values for different output classes. Classifiers with multiple classes can have different minimum predicitons values assigned to each class. The user interface allows user to use a slider to dynimically change the minimum prediciton value. The lock button clamps the minimum prediction values of all enabled output classes together.
<p align="center">
<img width="190" height="170" src = "resources/deep_learning_data_selector.png">
</p>
_The options for the deep learning data selector_
### MATLAB
The easiest way to export to MATLAB is to select the desired units in the time base display, right click and select the MATLAB icon. Data units will be exported to a .mat file as list of structures which is then saved to the clipboard. This file can be saved and then dragged into MATLAB to open.

View File

@ -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<DLTransfromParams> getDLTransformParams(ArrayList<DLTransform> dlTransfroms) {
ArrayList<DLTransfromParams> dlTransformParams = new ArrayList<DLTransfromParams>();

View File

@ -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());

View File

@ -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

View File

@ -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,11 +21,31 @@ import tethys.species.DataBlockSpeciesManager;
*/
public class DLDetectionDataBlock extends AcousticDataBlock<DLDetection> 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);
this.dlClassifyProcess = parentProcess;
@ -51,4 +74,17 @@ public class DLDetectionDataBlock extends AcousticDataBlock<DLDetection> impleme
}
// /* (non-Javadoc)
// * @see PamguardMVC.PamDataBlock#getDataSelectCreator()
// */
// @Override
// public synchronized DataSelectorCreator getDataSelectCreator() {
// if (dlDataSelectCreator == null) {
// dlDataSelectCreator = new DLDataSelectCreator(dlControl, this);
// }
// return dlDataSelectCreator;
//
// }
}

View File

@ -0,0 +1,26 @@
package rawDeepLearningClassifier.dlClassification;
/**
* Decides whether a prediction has passed a threshold to be used to create a new
* data unit.
* <p>
* 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);
}

View File

@ -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; i<modelResult.getPrediction().length; i++) {
//System.out.println("Binary Classification: " + genericModelParams.binaryClassification.length);
if (modelResult.getPrediction()[i]>genericModelParams.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;
}
}

View File

@ -45,6 +45,12 @@ public abstract class StandardClassifierModel implements DLClassiferModel, PamSe
*/
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) {
this.dlControl=dlControl;
@ -84,7 +90,7 @@ public abstract class StandardClassifierModel implements DLClassiferModel, PamSe
for (int i =0; i<modelResult.size(); i++) {
modelResult.get(i).setClassNameID(GenericDLClassifier.getClassNameIDs(getDLParams()));
modelResult.get(i).setBinaryClassification(GenericDLClassifier.isBinaryResult(modelResult.get(i), getDLParams()));
modelResult.get(i).setBinaryClassification(isDecision(modelResult.get(i), getDLParams()));
modelResult.get(i).setTimeMillis(groupedRawData.get(i).getTimeMilliseconds());
}
@ -106,7 +112,7 @@ public abstract class StandardClassifierModel implements DLClassiferModel, PamSe
@Override
public void prepModel() {
System.out.println("STANDARD CLASSIFIER MODEL PREP MODEL! !!!: " + getDLParams().modelPath);
// System.out.println("STANDARD CLASSIFIER MODEL PREP MODEL! !!!: " + getDLParams().modelPath);
// StandardModelParams oldParams = getDLParams().clone();
getDLWorker().prepModel(getDLParams(), dlControl);
@ -221,7 +227,8 @@ public abstract class StandardClassifierModel implements DLClassiferModel, PamSe
* @return true if a threshold has been met.
*/
public boolean isDecision(StandardPrediction modelResult, StandardModelParams modelParmas) {
return isBinaryResult(modelResult, modelParmas);
simpleDLDecision.setParams(modelParmas);
return simpleDLDecision.isBinaryResult(modelResult);
}
@ -241,22 +248,22 @@ public abstract class StandardClassifierModel implements DLClassiferModel, PamSe
/**
* 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; i<modelResult.getPrediction().length; i++) {
//System.out.println("Binary Classification: " + genericModelParams.binaryClassification.length);
if (modelResult.getPrediction()[i]>genericModelParams.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; i<modelResult.getPrediction().length; i++) {
// //System.out.println("Binary Classification: " + genericModelParams.binaryClassification.length);
//
// if (modelResult.getPrediction()[i]>genericModelParams.threshold && genericModelParams.binaryClassification[i]) {
// // System.out.println("SoundSpotClassifier: prediciton: " + i + " passed threshold with val: " + modelResult.getPrediction()[i]);
// return true;
// }
// }
// return false;
// }
@Override

Binary file not shown.

View File

@ -1,13 +1,14 @@
package rawDeepLearningClassifier.logging;
import PamView.symbol.PamSymbolChooser;
import PamView.symbol.modifier.SymbolModifier;
import annotation.CentralAnnotationsList;
import annotation.DataAnnotationType;
import annotation.binary.AnnotationBinaryHandler;
import annotation.dataselect.AnnotationDataSelCreator;
import generalDatabase.SQLLoggingAddon;
import rawDeepLearningClassifier.DLControl;
import rawDeepLearningClassifier.dataPlotFX.DLSymbolModifier;
import rawDeepLearningClassifier.dataSelector.DLDataSelectCreator;
/**
* Annotation type for data from the matched click classifier.
@ -26,6 +27,8 @@ public class DLAnnotationType extends DataAnnotationType<DLAnnotation> {
private DLAnnotationSymbolChooser dlSymbolChooser;
private DLDataSelectCreator dlDataSelectorCreator;
public DLAnnotationType(DLControl mtControl) {
this.dlControl=mtControl;
dlAnnotationSQL = new DLAnnotationSQL(this);
@ -94,6 +97,15 @@ public class DLAnnotationType extends DataAnnotationType<DLAnnotation> {
}
@Override
public AnnotationDataSelCreator getDataSelectCreator(String selectorName, boolean allowScores) {
if (dlDataSelectorCreator == null) {
dlDataSelectorCreator = new DLDataSelectCreator(dlControl, this);
}
return dlDataSelectorCreator;
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 111 KiB

View File

@ -0,0 +1,33 @@
package test.export;
import static org.junit.jupiter.api.Assertions.assertEquals;
import org.junit.jupiter.api.Test;
import clickDetector.ClickDetection;
/**
* Tests for export functionality.
*/
public class ExportTest {
/**
* Test exporting detections to mat files.
*/
@Test
public void matFileTest() {
System.out.println("Matched template classifier test: match corr");
//create a list of click detections.
ClickDetection clickDetection = new ClickDetection(0, 0, 0, null, null, 0);
//now open the mat file and check that we have all the data from these click detections.
}
}

View File

@ -83,8 +83,9 @@ public class MatchedTemplateClassifierTest {
}
/**
* Test the match correlation algorithm combined with click length algorithm. Here we awant to test that
* a long waveform
* Test the match correlation algorithm combined with click length algorithm. Here we want to test that
* a long waveform can be processed properly - i.e the peak of the click can be found and cross correlation performed
* on a shortened section.
*/
@Test
public void testMatchCorrLen() {

View File

@ -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) {