Merge pull request #7 from macster110/main

Matched click classifier bug fixes and TDDisplayFX real time changes
This commit is contained in:
Douglas Gillespie 2022-02-01 15:02:52 +00:00 committed by GitHub
commit 59e5735d4d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 157 additions and 41 deletions

View File

@ -184,15 +184,21 @@ public class TDAcousticScroller extends AcousticScrollerFX implements PamSetting
//add a listener so the visible amount changes of the spinner changes value.
spinner.valueProperty().addListener((obsVal, oldVal, newVal)->{
if (spinnerCall) return ; //prevent overflow.
if (newVal<=this.getRangeMillis()) {
// Debug.out.println("TDAcousticScroller: TimeRangeSpinner: " + newVal);
Platform.runLater(()->{ //why? But seems necessary
super.setVisibleMillis(newVal);
});
}
else spinner.getValueFactory().decrement(1); //need to use decrement here instead of set time because otherwise arrow buttons
//don't work.
if (spinnerCall) return ; //prevent overflow
/**
* There are two slightly different modes here- in viewer mode we want the spinner to set
* only the visible range. However in real time mode we want it to set the visible time and
* the data keep time.
*/
if (newVal<=this.getRangeMillis() || !isViewer) {
// Debug.out.println("TDAcousticScroller: TimeRangeSpinner: " + newVal);
Platform.runLater(()->{ //why? But seems necessary
super.setVisibleMillis(newVal);
super.setRangeMillis(0, newVal, false);
});
}
else spinner.getValueFactory().decrement(1); //need to use decrement here instead of set time because otherwise arrow buttons
});
}

View File

@ -4,12 +4,14 @@ import java.io.Serializable;
import java.lang.reflect.Field;
import org.apache.commons.lang3.ArrayUtils;
import org.jamdev.jpamutils.wavFiles.WavInterpolator;
import com.jmatio.types.MLArray;
import com.jmatio.types.MLDouble;
import com.jmatio.types.MLStructure;
import Filters.SmoothingFilter;
import Localiser.DelayMeasurementParams;
import PamModel.parametermanager.ManagedParameters;
import PamModel.parametermanager.PamParameterSet;
import PamModel.parametermanager.PrivatePamParameterData;
@ -87,6 +89,21 @@ public class MTClassifier implements Serializable, Cloneable, ManagedParameters
public final static int TEST_FFT_LENGTH=300;
/**
* Decimates waveforms.
*/
transient private WavInterpolator wavInterpolator = new WavInterpolator();
// /**
// * The delay measurment parameters.
// */
// private transient DelayMeasurementParams delayMeasurementParams = defualtDelayParams();
//
// /**
// * Runs the cross correlation algorithm.
// */
// private transient Correlations correlations = new Correlations();
/**
* Default MT classifier
*/
@ -94,6 +111,14 @@ public class MTClassifier implements Serializable, Cloneable, ManagedParameters
fft= new FastFFT();
}
private DelayMeasurementParams defualtDelayParams() {
DelayMeasurementParams delayMeasurementParams = new DelayMeasurementParams();
//delayMeasurementParams.setUpSample(4);
delayMeasurementParams.setFftFilterParams(null);
return delayMeasurementParams;
}
/**
* Get the match waveform FFT for the sampleRate.
* @param fftLength - the FFTlength to use. Bins around waveform peak are used.
@ -103,9 +128,14 @@ public class MTClassifier implements Serializable, Cloneable, ManagedParameters
if (waveformMatchFFT==null || currentSr!=sR) {
if (fft==null) fft=new FastFFT();
//System.out.println("interpWaveform: " + waveformMatch.waveform.length + " sR " + waveformMatch.sR);
//re-sample the waveform if the sample rate is different
this.interpWaveformMatch=interpWaveform(this.waveformMatch, sR);
//System.out.println("interpWaveformMatch: " + interpWaveformMatch.length + " sR " + sR);
//normalise
//this.interpWaveformMatch=PamArrayUtils.normalise(interpWaveformMatch);
@ -115,9 +145,11 @@ public class MTClassifier implements Serializable, Cloneable, ManagedParameters
// System.out.println("MatchNorm: MATCH");
// MTClassifierTest.normalizeTest(interpWaveformMatch);
//here use the FFT length with the maximum template size.
waveformMatchFFT = fft.rfft(PamArrayUtils.flip(interpWaveformMatch),
length);
waveformMatchFFT = fft.rfft(interpWaveformMatch, length);
//need to calculate the complex conjugate - note that originally I was flipping the array but this means
//the max value does not equal one with identical waveforms...doh.
waveformMatchFFT = waveformMatchFFT.conj();
// System.out.println("waveformMatch: " + waveformMatch.waveform.length +
// " interpWaveformMatch: " + interpWaveformMatch.length + " for " +sR + " sr ");
@ -155,9 +187,11 @@ public class MTClassifier implements Serializable, Cloneable, ManagedParameters
// MTClassifierTest.printWaveform(inteprWaveformReject);
//System.out.println("waveformReject: " +inteprWaveformReject.length + " fftLength: " + getFFTLength(sR));
waveformRejectFFT = fft.rfft(inteprWaveformReject, length);
//must flip the waveform for cross correlation - might as well do this here.
waveformRejectFFT = fft.rfft(PamArrayUtils.flip(inteprWaveformReject), length);
//need to calculate the complex conjugate - note that originally I was flipping the array but this means
//the max value does not equal one with identical waveforms...doh.
waveformRejectFFT = waveformRejectFFT.conj();
}
return waveformRejectFFT;
}
@ -342,11 +376,9 @@ public class MTClassifier implements Serializable, Cloneable, ManagedParameters
//set the stored sR
currentSr=sR;
// System.out.println("Matched click classifier: Waveform click: " + click.length());
// System.out.println("Matched click classifier: Waveform click: " + click.length());
//System.out.println("Waveform click: len: " + click.length());
// System.out.println("Waveform Reject max: " + PamArrayUtils.max(this.inteprWaveformReject));
// System.out.println("Waveform Reject max: " + PamArrayUtils.max(this.inteprWaveformReject));
//System.out.println("Waveform Reject max: " + PamArrayUtils.max(this.inteprWaveformReject)+ " len " + interpWaveformMatch.length);
//int fftLength = getFFTLength(sR);
//int fftLength = this.getFFTLength(sR);
@ -362,14 +394,17 @@ public class MTClassifier implements Serializable, Cloneable, ManagedParameters
int fftLength = click.length()*2;
ComplexArray matchResult= new ComplexArray(fftLength);
ComplexArray matchTemplate = getWaveformMatchFFT(sR,fftLength);
//System.out.println("matchTemplate: Waveform click: " + matchTemplate.length());
ComplexArray matchTemplate = getWaveformMatchFFT(sR,fftLength); //remember this is the complex conjugate
//System.out.println("matchTemplate interp: len: " + interpWaveformMatch.length+ " max: " + PamArrayUtils.max(interpWaveformMatch));
//System.out.println("matchTemplate: len: " + waveformMatch.waveform.length+ " max: " + PamArrayUtils.max(waveformMatch.waveform));
for (int i=0; i<Math.min(matchTemplate.length(), click.length()); i++) {
matchResult.set(i, click.get(i).times(matchTemplate.get(i)));
}
ComplexArray rejectResult= new ComplexArray(fftLength);
ComplexArray rejectTemplate = getWaveformRejectFFT(sR, fftLength);
ComplexArray rejectTemplate = getWaveformRejectFFT(sR, fftLength); //remember this is the complex conjugate
for (int i=0; i<Math.min(rejectTemplate.length(), click.length()); i++) {
rejectResult.set(i, click.get(i).times(rejectTemplate.get(i)));
}
@ -378,6 +413,9 @@ public class MTClassifier implements Serializable, Cloneable, ManagedParameters
// System.out.println("Waveform Reject max: " + PamArrayUtils.max(this.inteprWaveformReject));
//System.out.println("Click input: " + click.length() + " click template: " + matchTemplate.length());
//must use scaling to get the same result as MATLAB
if (fft==null) fft= new FastFFT();
@ -387,13 +425,13 @@ public class MTClassifier implements Serializable, Cloneable, ManagedParameters
//System.out.println("Inverse MATCH RESULTS");
//need to take the real part of the result and multiply by 2 to get same as
//ifft function in MATLAB
//ifft function in MATLAB - dunno why this is...
double[] matchReal = new double[matchResult.length()];
double[] rejectReal = new double[rejectResult.length()];
for (int i=0; i<matchResult.length(); i++) {
matchReal[i]=matchResult.getReal(i);
rejectReal[i]=rejectResult.getReal(i);
matchReal[i]=2*matchResult.getReal(i);
rejectReal[i]=2*rejectResult.getReal(i);
//System.out.println("iFFt match result: " + matchResult.get(i) + " iFFT rejectResult: " + rejectResult.get(i) );
}
@ -407,6 +445,14 @@ public class MTClassifier implements Serializable, Cloneable, ManagedParameters
double maxmatch=PamArrayUtils.max(matchReal);
double maxreject=PamArrayUtils.max(rejectReal);
// //TEST
// if (correlations==null) correlations=new Correlations();
// TimeDelayData matchResultTD = correlations.getDelay(click, matchTemplate.conj(), null, sR, fftLength);
// System.out.println("Old xcorr method: " + maxmatch + " new PG method: " + matchResultTD.getDelayScore());
// //TEST
//if set to "none" then reject template will return a NaN - TODO bit messy and inefficient.
double result;
double maxReject = PamArrayUtils.max(rejectReal);
@ -434,9 +480,25 @@ public class MTClassifier implements Serializable, Cloneable, ManagedParameters
*/
private double[] interpWaveform(MatchTemplate waveformMatch, double sR) {
//System.out.println("Interp waveform: " + " old: " + waveformMatch.sR + " new: " + sR);
if ( waveformMatch.sR>sR) {
//up sample
double[] interpWaveformMatch=reSampleWaveform(waveformMatch.waveform, waveformMatch.sR, sR);
//System.out.println("RESULT: old len: " + waveformMatch.waveform.length + " new len: " +interpWaveformMatch.length);
return interpWaveformMatch;
}
else if (waveformMatch.sR<sR){
// //TODO - make a better decimator?
// double[] interpWaveformMatch=reSampleWaveform(waveformMatch.waveform, waveformMatch.sR, sR);
// return interpWaveformMatch;
if (wavInterpolator == null) wavInterpolator = new WavInterpolator();
return wavInterpolator.decimate(waveformMatch.waveform, waveformMatch.sR, (float) sR);
}
else {
//nothing needed/
return waveformMatch.waveform;
}
}

View File

@ -72,19 +72,32 @@ public class MTClassifierTest {
return struct;
}
/**
* Test the correlation of several templates
* @param testWaveform - the waveform to correlate against.
* @param sR - the sample rate of the waveform.
* @param templates - the match templates to test.
*/
private static void testCorrelation(double[] testWaveform, float sR, ArrayList<MatchTemplate> templates) {
testCorrelation(testWaveform, sR, templates, MatchedTemplateParams.NORMALIZATION_RMS);
}
/**
* Test the correlation of several templates
* @param testWaveform
* @param sR
* @param templates
* @return
* @param testWaveform - the waveform to correlate against.
* @param sR - the sample rate of the waveform.
* @param templates - the match templates to test.
* @param normalisation - the normalisation type to use e.g. MatchedTemplateParams.NORMALIZATION_RMS
*/
private static void testCorrelation(double[] testWaveform, float sR, ArrayList<MatchTemplate> templates) {
private static void testCorrelation(double[] testWaveform, float sR, ArrayList<MatchTemplate> templates, int normalisation) {
//create the classifier object
for (int i=0; i<templates.size(); i++){
MTClassifier mtclassifier = new MTClassifier();
mtclassifier.normalisation = normalisation;
//System.out.println("Template " + i + " " + templates.get(i));
//add templates if inpout
@ -97,8 +110,14 @@ public class MTClassifierTest {
//System.out.println("Waveform len: " +testWaveform.length + " min: " + PamArrayUtils.min(testWaveform) + " max: " + PamArrayUtils.max(testWaveform));
testWaveform=PamArrayUtils.divide(testWaveform, PamUtils.PamArrayUtils.max(testWaveform));
testWaveform = MTClassifier.normaliseWaveform(testWaveform, MatchedTemplateParams.NORMALIZATION_RMS);
//System.out.println("Waveform max: " + PamArrayUtils.max(testWaveform) + " len: " + testWaveform.length);
//calculate the click FFT.
fft = new FastFFT();
//int fftSize = FastFFT.nextBinaryExp(testWaveform.length/2);
@ -115,14 +134,25 @@ public class MTClassifierTest {
MatchedTemplateResult matchResult = mtclassifier.calcCorrelationMatch(matchClick, sR);
System.out.println(String.format("The match correlation for %d is %.5f", i, matchResult.matchCorr));
System.out.println(String.format("The match correlation for %d is %.5f", i, matchResult.matchCorr));
//
// printFFt(matchClick);
//
// System.out.println("-----------------------");
//
// ComplexArray matchTemplate = mtclassifier.getWaveformMatchFFT(sR, matchClick.length()*2);
//
// printFFt(matchTemplate);
}
}
public static void printFFt(ComplexArray complexArray) {
for (int i=0; i<complexArray.length(); i++ ) {
System.out.println(complexArray.get(i).toString(6));
//System.out.println(complexArray.get(i).toString(6));
System.out.println(complexArray.get(i).real + "," + complexArray.get(i).imag);
}
}
@ -177,7 +207,7 @@ public class MTClassifierTest {
MLDouble clickUID=(MLDouble) mlArrayRetrived.getField("UID", i);
clicks.add(new MatchTemplate(Integer.toString((int) clickUID.get(0).doubleValue()), waveform, 288000));
clicks.add(new MatchTemplate(Long.toString(clickUID.get(0).longValue()), waveform, 288000));
}
return clicks;
}
@ -303,16 +333,19 @@ public class MTClassifierTest {
}
public static void testMatchCorr() {
/**
* Test how the length of the waveform affects the match correlation values
*/
public static void testMatchCorrLen() {
String testClicksPath = "/Users/au671271/MATLAB-Drive/MATLAB/PAMGUARD/matchedclickclassifer/DS2clks_test.mat";
String templteFilePath= "/Users/au671271/MATLAB-Drive/MATLAB/PAMGUARD/matchedclickclassifer/DS2templates_test.mat";
String testClicksPath = "/Users/au671271/MATLAB-Drive/MATLAB/PAMGUARD/matchedclickclassifer/DS3clks_test.mat";
String templteFilePath= "/Users/au671271/MATLAB-Drive/MATLAB/PAMGUARD/matchedclickclassifer/DS3templates_test.mat";
float sR = 288000; //sample rate in samples per second.
ArrayList<MatchTemplate> clicks = importClicks(testClicksPath, sR);
ArrayList<MatchTemplate> templates = importTemplates(templteFilePath);
int index = 0;
int index = 24;
//values in MATLAB are9.73577287114938 8.82782814105430 3.51936216182390
System.out.println("Number of clicks: " + clicks.size() + " UID " + clicks.get(index).name);
@ -321,7 +354,7 @@ public class MTClassifierTest {
System.out.println("------Restricted Length--------");
int restrictedBins= 1024;
int restrictedBins= 2048;
ClickLength clickLength = new ClickLength();
int[][] lengthPoints = clickLength.createLengthData(clicks.get(index), sR, 5.5, 3, false, null);
@ -333,9 +366,23 @@ public class MTClassifierTest {
}
/**
* Test the match corr algorithm by cross correlating a waveform with itself.
*/
public static void testMatchCorr() {
String templteFilePath= "/Users/au671271/MATLAB-Drive/MATLAB/PAMGUARD/matchedclickclassifer/DS2templates_test.mat";
//float sR = 288000; //sample rate in samples per second.
ArrayList<MatchTemplate> templates = importTemplates(templteFilePath);
testCorrelation(templates.get(0).waveform, templates.get(0).sR, templates);
}
public static void main(String args[]) {
testMatchCorr();
testMatchCorrLen();
}

View File

@ -257,7 +257,7 @@ public class MTSettingsPane extends SettingsPane<MatchedTemplateParams> {
//click normalisation
normBox = new ComboBox<String>();
normBox.getItems().addAll("peak to peak", "RMS", "none");
normBox.getItems().addAll("peak to peak", "norm", "none");
PamHBox clickNormPane= new PamHBox();
clickNormPane.setSpacing(5);

View File

@ -65,6 +65,8 @@ public class MTOfflineProcess {
//System.out.println("Click train offline data block " + clickTrainControl.getParentDataBlock());
mtOfflineGroup.setPrimaryDataBlock(mtContorl.getParentDataBlock());
mtOfflineTask.setParentDataBlock(mtContorl.getParentDataBlock());
//need this to make sure annotations trigger saving.
mtOfflineTask.addAffectedDataBlock(mtContorl.getParentDataBlock());
//if null open the dialog- also create a new offlineTask group if the datablock has changed.
if (mtOfflineDialog == null) {

View File

@ -75,7 +75,6 @@ public class GenericModelTest {
//long time1 = System.currentTimeMillis();
data = new float[][][] {DLUtils.toFloatArray(((FreqTransform) transform).getSpecTransfrom().getTransformedData())};
//data = new float[][][] { DLUtils.makeDummySpectrogram(40, 40)};
//System.out.println("data len: " + data.length + " " + data[0].length + " " + data[0][0].length);