Getting DelphinID working

This commit is contained in:
Jamie Mac 2024-04-25 17:00:29 +01:00
parent 9b77a97a17
commit 7ee8562c0c
10 changed files with 331 additions and 30 deletions

View File

@ -4,7 +4,7 @@
<groupId>org.pamguard</groupId>
<artifactId>Pamguard</artifactId>
<name>Pamguard</name>
<version>2.02.11b</version>
<version>2.02.11c</version>
<description>Pamguard using Maven to control dependencies</description>
<url>www.pamguard.org</url>
<organization>
@ -18,6 +18,7 @@
<resource>
<directory>src</directory>
<excludes>
<exclude>META-INF/*.SF,META-INF/*.DSA,META-INF/*.RSA</exclude>
<exclude>**/*.java</exclude>
<exclude>jars/*.*</exclude>
</excludes>
@ -92,16 +93,6 @@
</transformer>
<transformer />
</transformers>
<filters>
<filter>
<artifact>*:*</artifact>
<excludes>
<exclude>META-INF/*.SF</exclude>
<exclude>META-INF/*.DSA</exclude>
<exclude>META-INF/*.RSA</exclude>
</excludes>
</filter>
</filters>
</configuration>
</execution>
</executions>
@ -109,6 +100,16 @@
<transformers>
<transformer />
</transformers>
<filters>
<filter>
<artifact>*:*</artifact>
<excludes>
<exclude>META-INF/*.SF</exclude>
<exclude>META-INF/*.DSA</exclude>
<exclude>META-INF/*.RSA</exclude>
</excludes>
</filter>
</filters>
</configuration>
</plugin>
<plugin>
@ -191,5 +192,8 @@
<maven.compiler.source>11</maven.compiler.source>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<javafx.version>21</javafx.version>
<jaxb.runtime.version>2.4.0-b180830.0438</jaxb.runtime.version>
<jaxb.xjc.version>2.4.0-b180830.0438</jaxb.xjc.version>
<jaxb.api.version>2.4.0-b180830.0359</jaxb.api.version>
</properties>
</project>

View File

@ -214,7 +214,7 @@ public class PairBearingLocaliser implements BearingLocaliser {
private boolean resetArray(long timeMillis){
if (this.timeMillis!=timeMillis && currentArray.getHydrophoneLocator().isChangeable()){
System.out.println("Reset PairBearingLocaliser");
// System.out.println("Reset PairBearingLocaliser");
prepare(this.arrayElements, timeMillis, this.timingError);
return true;
}

View File

@ -178,7 +178,9 @@ public abstract class StandardModelPane extends SettingsPane<StandardModelParams
/**Classification thresholds etc to set.**/
Label classiferInfoLabel2 = new Label("Binary Classification Threshold");
Label classiferInfoLabel2 = new Label("Decision Threshold");
classiferInfoLabel2.setTooltip(new Tooltip("Set the minimum prediciton value for selected classes. If a prediction exceeds this value "
+ "a detection will be saved."));
classiferInfoLabel2.setFont(font);
//PamGuiManagerFX.titleFont2style(classiferInfoLabel2);

View File

@ -1,5 +1,8 @@
package rawDeepLearningClassifier.dlClassification.delphinID;
import java.util.ArrayList;
import PamguardMVC.PamDataUnit;
import rawDeepLearningClassifier.DLControl;
import rawDeepLearningClassifier.dlClassification.animalSpot.StandardModelParams;
import rawDeepLearningClassifier.dlClassification.genericModel.DLModelWorker;
@ -34,6 +37,17 @@ public class DelphinIDWorker extends DLModelWorker<DelphinIDPrediction>{
// TODO Auto-generated method stub
}
@Override
public float[][][] dataUnits2ModelInput(ArrayList<? extends PamDataUnit> dataUnits, float sampleRate, int iChan){
//Our data units are groups of whistles.
return null;
}

View File

@ -0,0 +1,121 @@
package rawDeepLearningClassifier.dlClassification.delphinID;
import org.jamdev.jdl4pam.transforms.FreqTransform;
import org.jamdev.jpamutils.spectrogram.SpecTransform;
import javafx.scene.canvas.Canvas;
import javafx.scene.image.WritableImage;
import javafx.scene.paint.Color;
import rawDeepLearningClassifier.segmenter.SegmenterDetectionGroup;
/**
* Transform whistles to an image.
*
* Here we extend the FreqTransform class because it contains lots of image
* transforms that are useful once the whistles have been converted into an
* image.
*
* @author Jamie Macaulay
*
*/
public class Whistles2Image extends FreqTransform {
/**
* Create an image transform from a whistleGroup.
* @param whistleGroup
* @param params
*/
public Whistles2Image(SegmenterDetectionGroup whistleGroup, Number[] params) {
super(null, params);
double[] freqLimits = new double[] {params[0].doubleValue(), params[1].doubleValue()};
double[] size = new double[] {params[2].doubleValue(), params[3].doubleValue()};
SpecTransform specTransform = whistleGroupToImage( whistleGroup, freqLimits, size);
this.setSpecTransfrom(specTransform);
this.setFreqlims(freqLimits);
}
/**
* Convert a group of whistles
* @param whistleGroup - the whistle groups
* @param freqLimits - the frequency limits
* @return the spectrogram transform.
*/
private SpecTransform whistleGroupToImage(SegmenterDetectionGroup whistleGroup, double[] freqLimits, double[] size) {
SpecTransform specTransform = new SpecTransform();
/*
* All time-frequency points are saved as a scatterplot with x-axis spanning 0-4
* seconds in time and y-axis spanning 0-20 kHz in frequency. - Matplotlib was
* used to produce plot (matplotlib.pyplot.scatter) (Point size set at 5 and all
* other values kept at default, including figure size which is saved as 6.4 x
* 4.8 inches as default, axes removed before saving using plt.axes(off))
**/
double[][] points = whistContours2Points(whistleGroup);
Canvas canvas = makeScatterImage(points, size, new double[]{0, whistleGroup.getDurationInMilliseconds()}, freqLimits, 5.);
WritableImage image = canvas.getGraphicsContext2D().getCanvas().snapshot(null, null);
double[][] imaged = new double[(int) size[0]][(int) size[1]];
Color color;
for (int i=0; i<imaged.length; i++) {
for (int j=0; j<imaged[0].length; j++) {
color = image.getPixelReader().getColor(i, j);
imaged[i][j] = color.getRed();
}
}
specTransform.setSpecData(imaged);
specTransform.setSampleRate((float) (freqLimits[1]*2));
return specTransform;
}
/**
* Convert a list of whistle contours to a list of time and frequency points.
* @param whistleGroup - list of whistle contours within a detection group.
* @return an array with time (milliseconds from start of group) and frequency (Hz)
*/
private double[][] whistContours2Points(SegmenterDetectionGroup whistleGroup) {
// TODO Auto-generated method stub
return null;
}
/**
* Create a scatter image from points
* @param points - the points are time (milliseconds from 0) and frequency
* @param size - the width and height of the image in pixels
* @param xlims - the minimum and maximum time in milliseconds from 0;
* @param ylims - the minimum and maximum frequency in Hz
* @param markerSize - the marker size in pixels
* @return an image with y axis as frequency and x axis as time.
*/
private Canvas makeScatterImage(double[][] points, double[] size, double[] xlims, double[] ylims, double markerSize) {
Canvas canvas = new Canvas(size[0], size[1]);
double x, y;
for (int i=0; i<points.length; i++) {
canvas.getGraphicsContext2D().setFill(Color.BLACK);
//Calculate x and y in pixels.
x = ((points[i][0]-xlims[0])/(xlims[1]-xlims[0]))*size[0];
y = ((points[i][0]-xlims[0])/(xlims[1]-xlims[0]))*size[0];
canvas.getGraphicsContext2D().fillOval(x+markerSize/2, y-markerSize/2, markerSize/2, markerSize/2);
}
return canvas;
}
}

View File

@ -348,7 +348,7 @@ public class DLSettingsPane extends SettingsPane<RawDLParams>{
//only show the data selector box for detec tion data.
if (sourcePane.getSource() == null) this.dataSelectorPane.setVisible(false);
if (sourcePane.getSource().getDataSelectCreator() instanceof NullDataSelectorCreator) {
else if (sourcePane.getSource().getDataSelectCreator() instanceof NullDataSelectorCreator) {
//^bit messy but cannot think of a better way to do it.
this.dataSelectorPane.setVisible(false);
}

View File

@ -4,14 +4,21 @@ import Localiser.detectionGroupLocaliser.GroupDetection;
import PamguardMVC.PamDataUnit;
/**
* A group of detection which are within a particular segment. This is used to pass detection groups straight to
*
*
* * @author Jamie Macaulay
* A group of detection which are within a particular segment. This is used to pass detection groups straight to a classifier.
* @author Jamie Macaulay
*
*/
public class SegmenterDetectionGroup extends GroupDetection<PamDataUnit> {
/**
* Constructor for a group of detections within a detection. Note that some
* longer detections (e.g. whistles) may have sections outside the segment.
*
* @param timeMilliseconds - this is the start of the SEGMENT - Note that the
* @param channelBitmap - channels of all detections
* @param startSample - the stratSample of the SEGMENT.
* @param duration - the duration of the SEGMENT.
*/
public SegmenterDetectionGroup(long timeMilliseconds, int channelBitmap, long startSample, long duration) {
super(timeMilliseconds, channelBitmap, startSample, duration);
// TODO Auto-generated constructor stub

View File

@ -11,10 +11,11 @@ public class WMAlarmParameters extends DataSelectParams implements Cloneable, Se
public static final long serialVersionUID = 1L;
public double minFreq, maxFreq;
public double minAmplitude;
public double minLengthMillis;
public boolean superDetOnly;
public double minFreq = 0.;
public double maxFreq = 30000.;
public double minAmplitude = 90.;
public double minLengthMillis = 0.0;
public boolean superDetOnly = false;
@Override
public WMAlarmParameters clone() {

View File

@ -1,15 +1,9 @@
package whistlesAndMoans.dataSelector;
import java.io.Serializable;
import alarm.AlarmParameters;
import pamViewFX.fxSettingsPanes.DynamicSettingsPane;
import whistlesAndMoans.ConnectedRegionDataUnit;
import whistlesAndMoans.WhistleMoanControl;
import whistlesAndMoans.alarm.WMAlarmParameters;
import PamController.PamControlledUnitSettings;
import PamController.PamSettingManager;
import PamController.PamSettings;
import PamView.dialog.PamDialogPanel;
import PamguardMVC.PamDataBlock;
import PamguardMVC.PamDataUnit;
@ -24,6 +18,11 @@ public class WMDDataSelector extends DataSelector {
private WMAlarmParameters wmAlarmParameters = new WMAlarmParameters();
/**
* JavaFX pane.
*/
private WMDSelectPaneFX wmdSelectPaneFX;
public WMDDataSelector(WhistleMoanControl wmControl, PamDataBlock pamDataBlock, String selectorName,
boolean allowScores) {
super(pamDataBlock, selectorName, allowScores);
@ -94,8 +93,10 @@ public class WMDDataSelector extends DataSelector {
@Override
public DynamicSettingsPane<Boolean> getDialogPaneFX() {
// TODO Auto-generated method stub
return null;
if (wmdSelectPaneFX == null) {
wmdSelectPaneFX = new WMDSelectPaneFX(this);
}
return wmdSelectPaneFX;
}
}

View File

@ -0,0 +1,151 @@
package whistlesAndMoans.dataSelector;
import javafx.scene.Node;
import javafx.scene.control.Label;
import javafx.scene.control.Spinner;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.Pane;
import pamViewFX.fxNodes.PamGridPane;
import pamViewFX.fxNodes.PamVBox;
import pamViewFX.fxNodes.utilityPanes.PamToggleSwitch;
import pamViewFX.fxSettingsPanes.DynamicSettingsPane;
import whistlesAndMoans.alarm.WMAlarmParameters;
/**
* JavaFX settings pane for the whsitle and moan detector data selector.
*
* @author Jamie Macaulay
*
*/
public class WMDSelectPaneFX extends DynamicSettingsPane<Boolean> {
private Pane mainPane;
private WMDDataSelector wmdDataSelector;
private Spinner<Double> minFreq;
private Spinner<Double> maxFreq;
private Spinner<Double> minAmplitude;
private Spinner<Double> minLength;
private PamToggleSwitch superDetOnly;
public WMDSelectPaneFX(WMDDataSelector wmdDataSelector) {
super(wmdDataSelector);
this.wmdDataSelector = wmdDataSelector;
// TODO Auto-generated constructor stub
mainPane = createPane();
}
private Pane createPane() {
PamVBox mainPane = new PamVBox();
mainPane.setSpacing(5.);
PamGridPane gridPane = new PamGridPane();
gridPane.setHgap(5.);
gridPane.setVgap(5.);
int row = 0;
int column = 0;
minFreq = new Spinner<Double>(0.,Double.MAX_VALUE, 100., 100.);
minFreq.setEditable(true);
gridPane.add(new Label("Min. frequency"), column, row);
column++;
gridPane.add(minFreq, column, row);
column++;
gridPane.add(new Label("Hz"), column, row);
maxFreq = new Spinner<Double>(0.,Double.MAX_VALUE, 30000., 100.);
maxFreq.setEditable(true);
row++;
column=0;
gridPane.add(new Label("Max. frequency"), column, row);
gridPane.add(maxFreq, ++column, row);
gridPane.add(new Label("Hz"), ++column, row);
minAmplitude = new Spinner<Double>(0.,1000., 90., 1.);
minAmplitude.setEditable(true);
row++;
column=0;
gridPane.add(new Label("Min. amplitude"), column, row);
gridPane.add(minAmplitude, ++column, row);
gridPane.add(new Label("dB"), ++column, row);
minLength = new Spinner<Double>(0.,Double.MAX_VALUE, 0., 1.);
minLength.setEditable(true);
row++;
column=0;
gridPane.add(new Label("Min. length"), column, row);
gridPane.add(minLength, ++column, row);
gridPane.add(new Label("milliseconds"), ++column, row);
row++;
column=0;
superDetOnly = new PamToggleSwitch("Only whistles with super-detections");
gridPane.add(superDetOnly, column, row);
GridPane.setColumnSpan(superDetOnly, 3);
mainPane.getChildren().add(gridPane);
return mainPane;
}
@Override
public Boolean getParams(Boolean currParams) {
WMAlarmParameters wmAlarmParameters = wmdDataSelector.getWmAlarmParameters().clone();
try {
wmAlarmParameters.minFreq = minFreq.getValue();
wmAlarmParameters.maxFreq = maxFreq.getValue();
wmAlarmParameters.minAmplitude = minAmplitude.getValue();
wmAlarmParameters.minLengthMillis = minLength.getValue();
wmAlarmParameters.superDetOnly = superDetOnly.isSelected();
}
catch (NumberFormatException e) {
return false;
}
wmdDataSelector.setWmAlarmParameters(wmAlarmParameters);
return true;
}
@Override
public void setParams(Boolean input) {
WMAlarmParameters wmAlarmParameters = wmdDataSelector.getWmAlarmParameters();
minFreq.getValueFactory().setValue(wmAlarmParameters.minFreq);
maxFreq.getValueFactory().setValue(wmAlarmParameters.maxFreq);
minAmplitude.getValueFactory().setValue(wmAlarmParameters.minAmplitude);
minLength.getValueFactory().setValue(wmAlarmParameters.minLengthMillis);
superDetOnly.setSelected(wmAlarmParameters.superDetOnly);
}
@Override
public String getName() {
return "WMD Data Selector";
}
@Override
public Node getContentNode() {
return mainPane;
}
@Override
public void paneInitialized() {
// TODO Auto-generated method stub
}
}