mirror of
https://github.com/PAMGuard/PAMGuard.git
synced 2024-11-21 22:52:22 +00:00
More updates to get DelphinID working
This commit is contained in:
parent
fa6991eb80
commit
ee694146c3
@ -32,6 +32,7 @@ import pamViewFX.fxNodes.PamSpinner;
|
||||
import pamViewFX.fxNodes.PamVBox;
|
||||
import pamViewFX.validator.PamValidator;
|
||||
import rawDeepLearningClassifier.dlClassification.DLClassiferModel;
|
||||
import rawDeepLearningClassifier.dlClassification.StandardClassifierModel;
|
||||
|
||||
/**
|
||||
* Settings pane for SoundSpot
|
||||
@ -163,7 +164,7 @@ public abstract class StandardModelPane extends SettingsPane<StandardModelParams
|
||||
defaultSegmentLenChanged();
|
||||
//only set the hop if the user physically changes the toggle switch. This is not included in defaultSegmentLenChanged
|
||||
//becuase defaultSegmentLenChanged can be called from elsewhere
|
||||
int defaultsamples = getDefaultSamples();
|
||||
int defaultsamples = getDefaultSamples(dlClassifierModel, paramsClone);
|
||||
dlClassifierModel.getDLControl().getSettingsPane().getHopLenSpinner().getValueFactory().setValue((int) defaultsamples/2);
|
||||
});
|
||||
usedefaultSeg.setPadding(new Insets(0,0,0,0));
|
||||
@ -269,7 +270,7 @@ public abstract class StandardModelPane extends SettingsPane<StandardModelParams
|
||||
|
||||
// float sR = dlClassifierModel.getDLControl().getSettingsPane().getSelectedParentDataBlock().getSampleRate();
|
||||
|
||||
int defaultsamples = getDefaultSamples();
|
||||
int defaultsamples = getDefaultSamples(dlClassifierModel, paramsClone);
|
||||
|
||||
//work out the window length in samples
|
||||
dlClassifierModel.getDLControl().getSettingsPane().getSegmentLenSpinner().getValueFactory().setValue(defaultsamples);
|
||||
@ -282,7 +283,7 @@ public abstract class StandardModelPane extends SettingsPane<StandardModelParams
|
||||
}
|
||||
}
|
||||
|
||||
private int getDefaultSamples() {
|
||||
public static int getDefaultSamples(DLClassiferModel dlClassifierModel, StandardModelParams paramsClone) {
|
||||
float sR = dlClassifierModel.getDLControl().getSettingsPane().getSelectedParentDataBlock().getSampleRate();
|
||||
int defaultsamples = (int) (paramsClone.defaultSegmentLen.doubleValue()*sR/1000.0);
|
||||
return defaultsamples;
|
||||
|
@ -58,10 +58,11 @@ public class ArchiveModelWorker extends GenericModelWorker {
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare the model
|
||||
* Prepare the model.
|
||||
* Note it is important to put a synchonized here or the model loading can fail.
|
||||
*/
|
||||
@Override
|
||||
public void prepModel(StandardModelParams dlParams, DLControl dlControl) {
|
||||
public synchronized void prepModel(StandardModelParams dlParams, DLControl dlControl) {
|
||||
//ClassLoader origCL = Thread.currentThread().getContextClassLoader();
|
||||
try {
|
||||
|
||||
@ -198,6 +199,8 @@ public class ArchiveModelWorker extends GenericModelWorker {
|
||||
* @throws IOException
|
||||
*/
|
||||
public ArchiveModel loadModel(String currentPath2) throws MalformedModelException, IOException {
|
||||
|
||||
System.out.println("HELLO MODEL: " +currentPath2 );
|
||||
return new SimpleArchiveModel(new File(currentPath2));
|
||||
}
|
||||
|
||||
|
@ -1,13 +1,26 @@
|
||||
package rawDeepLearningClassifier.dlClassification.delphinID;
|
||||
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import PamController.SettingsPane;
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.control.ContentDisplay;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.control.Slider;
|
||||
import javafx.scene.control.Spinner;
|
||||
import javafx.scene.control.Tooltip;
|
||||
import javafx.scene.layout.Pane;
|
||||
import javafx.scene.paint.Color;
|
||||
import javafx.scene.text.Font;
|
||||
import javafx.scene.text.FontWeight;
|
||||
import pamViewFX.PamGuiManagerFX;
|
||||
import pamViewFX.fxGlyphs.PamGlyphDude;
|
||||
import pamViewFX.fxNodes.PamHBox;
|
||||
import pamViewFX.fxNodes.PamSpinner;
|
||||
import pamViewFX.fxNodes.PamVBox;
|
||||
import rawDeepLearningClassifier.dlClassification.animalSpot.StandardModelPane;
|
||||
|
||||
/**
|
||||
* Settings pane for delphin ID.
|
||||
@ -15,60 +28,141 @@ import pamViewFX.fxNodes.PamVBox;
|
||||
* @author Jamie Macaulay
|
||||
*
|
||||
*/
|
||||
public class DelphinIDPane extends SettingsPane<DelphinIDParams> {
|
||||
|
||||
public class DelphinIDPane extends SettingsPane<DelphinIDParams> {
|
||||
|
||||
/**
|
||||
* The main pane.
|
||||
*/
|
||||
private Pane mainPane;
|
||||
|
||||
|
||||
/**
|
||||
* Reference to the delphinID classifier
|
||||
*/
|
||||
private DelphinIDClassifier delphinUIClassifier;
|
||||
|
||||
private PamSpinner<Double> detectionDensitySpinner;
|
||||
|
||||
private Slider decisionSlider;
|
||||
|
||||
private DelphinIDParams currentParams;
|
||||
|
||||
private File currentSelectedFile;
|
||||
|
||||
public DelphinIDPane(DelphinIDClassifier delphinUIClassifier) {
|
||||
super(null);
|
||||
this.delphinUIClassifier = delphinUIClassifier;
|
||||
mainPane = createPane();
|
||||
}
|
||||
|
||||
|
||||
private Pane createPane() {
|
||||
|
||||
|
||||
//font to use for title labels.
|
||||
Font font= Font.font(null, FontWeight.BOLD, 11);
|
||||
|
||||
Node classifierIcon;
|
||||
classifierIcon = delphinUIClassifier.getModelUI().getIcon();
|
||||
|
||||
|
||||
Label classifierIcon;
|
||||
classifierIcon = new Label("DelphinID");
|
||||
PamGuiManagerFX.titleFont2style(classifierIcon);
|
||||
//todo - will need to figure out colour of icon using CSS.
|
||||
Node icon = PamGlyphDude.createPamIcon("mdi2r-rss", Color.BLACK, PamGuiManagerFX.iconSize);
|
||||
icon.getStyleClass().add(getName());
|
||||
icon.setRotate(45);
|
||||
classifierIcon.setGraphic(icon);
|
||||
classifierIcon.setContentDisplay(ContentDisplay.RIGHT);
|
||||
|
||||
|
||||
// String settings = currentParams.toString();
|
||||
// classifierIcon.setTooltip(new Tooltip(settings));
|
||||
|
||||
PamVBox vBox = new PamVBox();
|
||||
vBox.setSpacing(5.);
|
||||
|
||||
|
||||
/**Classification thresholds etc to set.**/
|
||||
Label detectionDensity = new Label("Detection Density");
|
||||
detectionDensity.setFont(font);
|
||||
String tooltip = "Set the minimum detection density to attempt to classify.";
|
||||
detectionDensity.setTooltip(new Tooltip(tooltip));
|
||||
detectionDensitySpinner = new PamSpinner<Double>(0.0, 1.0, 0.3, 0.1);
|
||||
detectionDensitySpinner.setPrefWidth(70);
|
||||
detectionDensitySpinner.setEditable(true);
|
||||
detectionDensitySpinner.getStyleClass().add(Spinner.STYLE_CLASS_SPLIT_ARROWS_HORIZONTAL);
|
||||
|
||||
PamHBox minDensityHolder = new PamHBox();
|
||||
minDensityHolder.setAlignment(Pos.CENTER_RIGHT);
|
||||
minDensityHolder.setSpacing(5);
|
||||
Label minDensity = new Label("Min. density");
|
||||
minDensityHolder.getChildren().addAll(minDensity, detectionDensitySpinner);
|
||||
|
||||
/**Classification thresholds etc to set.**/
|
||||
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);
|
||||
|
||||
|
||||
vBox.getChildren().addAll(classifierIcon, classiferInfoLabel2);
|
||||
|
||||
|
||||
decisionSlider = new Slider();
|
||||
decisionSlider.setMin(0);
|
||||
decisionSlider.setMax(1);
|
||||
decisionSlider.setMajorTickUnit(0.2);
|
||||
decisionSlider.setMinorTickCount(10);
|
||||
decisionSlider.valueProperty().addListener((obsVal, oldVal, newVal)->{
|
||||
classiferInfoLabel2.setText(String.format("Decision Threshold %.2f", newVal));
|
||||
});
|
||||
decisionSlider.setShowTickMarks(true);
|
||||
decisionSlider.setShowTickLabels(true);
|
||||
|
||||
vBox.getChildren().addAll(classifierIcon, detectionDensity, minDensityHolder, classiferInfoLabel2, decisionSlider);
|
||||
|
||||
return vBox;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DelphinIDParams getParams(DelphinIDParams currParams) {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
currParams.threshold = decisionSlider.getValue();
|
||||
currParams.minDetectionDensity = detectionDensitySpinner.getValue();
|
||||
return currParams;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setParams(DelphinIDParams input) {
|
||||
// TODO Auto-generated method stub
|
||||
this.currentParams = input;
|
||||
decisionSlider.setValue(input.threshold);
|
||||
detectionDensitySpinner.getValueFactory().setValue(input.minDetectionDensity);
|
||||
|
||||
if (input.modelPath!=null) {
|
||||
//this might
|
||||
currentSelectedFile = new File(currentParams.modelPath);
|
||||
|
||||
//this might change the paramsClone values if the model contains pamguard compatible metadata
|
||||
newModelSelected(currentSelectedFile);
|
||||
}
|
||||
}
|
||||
|
||||
private void newModelSelected(File currentSelectedFile2) {
|
||||
if (currentParams!=null && currentParams.defaultSegmentLen != null) {
|
||||
|
||||
//System.out.println("Defualt segment length: " + paramsClone.defaultSegmentLen);
|
||||
|
||||
//cannot use because, if the parent datablock has changed, samplerate will be out of date.
|
||||
// int defaultsamples = (int) this.soundSpotClassifier.millis2Samples(paramsClone.defaultSegmentLen);
|
||||
|
||||
|
||||
// float sR = dlClassifierModel.getDLControl().getSettingsPane().getSelectedParentDataBlock().getSampleRate();
|
||||
|
||||
int defaultsamples = StandardModelPane.getDefaultSamples(delphinUIClassifier, currentParams);
|
||||
|
||||
//work out the window length in samples
|
||||
delphinUIClassifier.getDLControl().getSettingsPane().getSegmentLenSpinner().getValueFactory().setValue(defaultsamples);
|
||||
// dlClassifierModel.getDLControl().getSettingsPane().getHopLenSpinner().getValueFactory().setValue((int) defaultsamples/2);
|
||||
|
||||
delphinUIClassifier.getDLControl().getSettingsPane().getSegmentLenSpinner().setDisable(true);
|
||||
}
|
||||
else {
|
||||
delphinUIClassifier.getDLControl().getSettingsPane().getSegmentLenSpinner().setDisable(false);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "delphinIDParams";
|
||||
@ -82,7 +176,7 @@ public class DelphinIDPane extends SettingsPane<DelphinIDParams> {
|
||||
@Override
|
||||
public void paneInitialized() {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -8,5 +8,11 @@ public class DelphinIDParams extends StandardModelParams {
|
||||
*
|
||||
*/
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* The minimum detection density.
|
||||
*/
|
||||
public double minDetectionDensity = 0.3;
|
||||
|
||||
|
||||
}
|
||||
|
@ -48,6 +48,11 @@ public class DelphinIDWorker extends ArchiveModelWorker {
|
||||
System.err.println("Error: could not find whistle2image transform in DelphinID JSON file. Model will not work.");
|
||||
this.setModel(null); // set model to null to make sure nothing works and errors are thrown
|
||||
}
|
||||
|
||||
dlParams.binaryClassification = new boolean[dlParams.classNames.length];
|
||||
for (int i=0; i<dlParams.classNames.length; i++) {
|
||||
dlParams.binaryClassification[i]=true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -1,6 +1,8 @@
|
||||
package rawDeepLearningClassifier.dlClassification.delphinID;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.RenderingHints;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.awt.image.Raster;
|
||||
import java.util.ArrayList;
|
||||
@ -72,7 +74,7 @@ public class Whistles2Image extends FreqTransform {
|
||||
for (int i=0; i<imaged.length; i++) {
|
||||
for (int j=0; j<imaged[0].length; j++) {
|
||||
color = raster.getPixel(i, j, color);
|
||||
imaged[i][j] = color[0]/255.; //normalize
|
||||
imaged[i][j] = (255-color[0])/255.; //normalize
|
||||
}
|
||||
}
|
||||
//
|
||||
@ -173,7 +175,7 @@ public class Whistles2Image extends FreqTransform {
|
||||
* @param markerSize - the marker size in pixels
|
||||
* @return an image with y axis as frequency and x axis as time.
|
||||
*/
|
||||
private BufferedImage makeScatterImage(ArrayList<double[][]> points, double[] size, double[] xlims, double[] ylims, double markerSize) {
|
||||
public static BufferedImage makeScatterImage(ArrayList<double[][]> points, double[] size, double[] xlims, double[] ylims, double markerSize) {
|
||||
|
||||
BufferedImage canvas = new BufferedImage((int) size[0], (int) size[1], BufferedImage.TYPE_INT_RGB);
|
||||
|
||||
@ -187,9 +189,13 @@ public class Whistles2Image extends FreqTransform {
|
||||
x = ((points.get(j)[i][0]-xlims[0])/(xlims[1]-xlims[0]))*size[0];
|
||||
y = ((points.get(j)[i][1]-ylims[0])/(ylims[1]-ylims[0]))*size[1];
|
||||
|
||||
// System.out.println("Fill oval: x" + x + " y: " + y + " time: " + points.get(j)[i][0]);
|
||||
//System.out.println("Fill oval: x" + x + " y: " + y + " time: " + points.get(j)[i][0]);
|
||||
|
||||
Graphics2D g2 = (Graphics2D) canvas.getGraphics();
|
||||
|
||||
canvas.getGraphics().fillOval((int) (x+markerSize/2),(int) (y-markerSize/2), (int) markerSize,(int) markerSize);
|
||||
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
||||
|
||||
g2.fillOval((int) (x+markerSize/2),(int) (y-markerSize/2), (int) markerSize,(int) markerSize);
|
||||
}
|
||||
}
|
||||
|
||||
@ -206,6 +212,7 @@ public class Whistles2Image extends FreqTransform {
|
||||
public double[] size;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -60,7 +60,17 @@ public class SegmenterProcess extends PamProcess {
|
||||
/**
|
||||
* Holds groups of data units which are within a defined segment.
|
||||
*/
|
||||
private SegmenterGroupDataBlock segmenterGroupDataBlock;
|
||||
private SegmenterGroupDataBlock segmenterGroupDataBlock;
|
||||
|
||||
/**
|
||||
* The first clock update - segments for detection groups (not raw sound data) are referenced from this.
|
||||
*/
|
||||
private long firstClockUpdate;
|
||||
|
||||
/**
|
||||
* The current segmenter detection group.
|
||||
*/
|
||||
private SegmenterDetectionGroup segmenterDetectionGroup = null;
|
||||
|
||||
|
||||
public SegmenterProcess(DLControl pamControlledUnit, PamDataBlock parentDataBlock) {
|
||||
@ -111,7 +121,7 @@ public class SegmenterProcess extends PamProcess {
|
||||
*/
|
||||
@Override
|
||||
public ArrayList getCompatibleDataUnits(){
|
||||
return new ArrayList<Class<? extends PamDataUnit>>(Arrays.asList(RawDataUnit.class, ClickDetection.class, ClipDataUnit.class));
|
||||
return new ArrayList<Class<? extends PamDataUnit>>(Arrays.asList(RawDataUnit.class, ClickDetection.class, ClipDataUnit.class, ConnectedRegionDataUnit.class));
|
||||
}
|
||||
|
||||
|
||||
@ -177,6 +187,8 @@ public class SegmenterProcess extends PamProcess {
|
||||
if (rawDataBlock==null) return;
|
||||
|
||||
setParentDataBlock(rawDataBlock);
|
||||
|
||||
this.firstClockUpdate = -1;
|
||||
|
||||
}
|
||||
|
||||
@ -232,10 +244,27 @@ public class SegmenterProcess extends PamProcess {
|
||||
|
||||
//TODO
|
||||
//this contains no raw data so we are branching off on a completely different processing path here.
|
||||
//Whislte data units are saved to a buffer and then fed to the deep learning algorohtm
|
||||
//Whislte data units are saved to a buffer and then fed to the deep learning algorithms
|
||||
|
||||
if (segmenterDetectionGroup==null) {
|
||||
//iterate until we find the correct time
|
||||
long segmentStart = firstClockUpdate;
|
||||
while() {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
public void masterClockUpdate(long milliSeconds, long sampleNumber) {
|
||||
super.masterClockUpdate(milliSeconds, sampleNumber);
|
||||
if (firstClockUpdate<0) {
|
||||
firstClockUpdate = milliSeconds;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
|
91
src/test/rawDeepLearningClassifier/DelphinIDTest.java
Normal file
91
src/test/rawDeepLearningClassifier/DelphinIDTest.java
Normal file
@ -0,0 +1,91 @@
|
||||
package test.rawDeepLearningClassifier;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.awt.image.Raster;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import org.jamdev.jdl4pam.utils.DLMatFile;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import rawDeepLearningClassifier.dlClassification.delphinID.Whistles2Image;
|
||||
import us.hebi.matlab.mat.format.Mat5;
|
||||
import us.hebi.matlab.mat.types.MatFile;
|
||||
import us.hebi.matlab.mat.types.Matrix;
|
||||
|
||||
public class DelphinIDTest {
|
||||
|
||||
|
||||
@Test
|
||||
public void whistle2ImageTest() {
|
||||
|
||||
System.out.println("Whislte2Image test started");
|
||||
|
||||
/**
|
||||
* Test whether the Whistles2Image transform works properly
|
||||
*/
|
||||
String relMatPath = "./src/test/resources/rawDeepLearningClassifier/DelphinID/whistle_image_example.mat";
|
||||
|
||||
Path path = Paths.get(relMatPath);
|
||||
|
||||
// Create MAT file with a scalar in a nested struct
|
||||
try {
|
||||
MatFile matFile = Mat5.readFromFile(path.toString());
|
||||
Matrix array = matFile.getArray("tfvalues");
|
||||
|
||||
//the values for the whistle detector.
|
||||
double[][] whistleValues = DLMatFile.matrix2array(array);
|
||||
|
||||
//the image after compression
|
||||
array = matFile.getArray("image1compressedgrayscale");
|
||||
double[][] compressedWhistleImage = DLMatFile.matrix2array(array);
|
||||
|
||||
//the whistle2Image transform image
|
||||
array = matFile.getArray("image1originalgrayscalenorm");
|
||||
double[][] whislteImage = DLMatFile.matrix2array(array);
|
||||
|
||||
//now perform the image transform in Java
|
||||
double[] freqLimits = new double[] {0., 20000.};
|
||||
double[] size = new double[] {680., 480.};
|
||||
|
||||
ArrayList<double[][]> whistleImageArr = new ArrayList<double[][]>();
|
||||
whistleImageArr.add(whistleValues);
|
||||
|
||||
BufferedImage canvas = Whistles2Image.makeScatterImage(whistleImageArr, size, new double[]{48, 48. + 4.}, freqLimits, 5.);
|
||||
|
||||
double[][] imaged = new double[(int) size[0]][(int) size[1]];
|
||||
|
||||
float[] color = new float[3];
|
||||
Raster raster = canvas.getData();
|
||||
for (int i=0; i<imaged.length; i++) {
|
||||
for (int j=0; j<imaged[0].length; j++) {
|
||||
color = raster.getPixel(i, j, color);
|
||||
imaged[i][j] = (255-color[0])/255.; //normalize
|
||||
}
|
||||
}
|
||||
|
||||
//now save this image to a MATFILE
|
||||
// Create MAT file with a scalar in a nested struct
|
||||
MatFile matFileWrite = Mat5.newMatFile()
|
||||
.addArray("image1originalgrayscalenorm",DLMatFile.array2Matrix(imaged));
|
||||
// Serialize to disk using default configurations
|
||||
Mat5.writeToFile(matFileWrite, "C:\\Users\\Jamie Macaulay\\MATLAB Drive\\MATLAB\\PAMGUARD\\deep_learning\\delphinID\\whistle_image_example_java.mat");
|
||||
|
||||
System.out.println("Whislte2Image test finished");
|
||||
|
||||
} catch (IOException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
assertEquals(false, false);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
Binary file not shown.
Loading…
Reference in New Issue
Block a user