mirror of
https://github.com/PAMGuard/PAMGuard.git
synced 2025-04-15 21:46:33 +00:00
Deep learning and group localiser bug fixes (#205)
* Getting dolphin deep learning click detector working * Thomas click deep learning unit test * Add deb installer to Maven build * Updates to deep learning click classifier Fixed bug for classifying clicks in viewer mode where the classification buffer was not being filled. Implemented Risso's classifier and checked it's working. Required changes to jpam Fixed bug in source data blocks in deep learning classifier Updated deb build * Getting dolphin deep learning click detector working * Thomas click deep learning unit test * Add deb installer to Maven build * Updates to deep learning click classifier Fixed bug for classifying clicks in viewer mode where the classification buffer was not being filled. Implemented Risso's classifier and checked it's working. Required changes to jpam Fixed bug in source data blocks in deep learning classifier Updated deb build * Fixes to group localiser Group localiser was failing with linear arrays - now sorted. Also minor bug fixes * Chnage version number and rename segmenter group data * Change version number to 16b * Squashed commit of the following: commitc3b58ab3e2
Author: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com> Date: Wed Apr 2 09:34:45 2025 +0100 Update D3Control.java don't actually enable the FX for d3 since it doesn't work commit7665c1257f
Merge:56375bd4
2dda6791
Author: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com> Date: Wed Apr 2 09:29:43 2025 +0100 Merge branch 'main' of https://github.com/PAMGuard/PAMGuard commit56375bd4c0
Author: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com> Date: Wed Apr 2 09:29:38 2025 +0100 null traps in TD display commit2dda67918c
Author: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com> Date: Wed Apr 2 09:05:06 2025 +0100 Updates to Raven data plotting Also reading HARP header Also adding a second to data keep in Click Detector --------- Co-authored-by: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com>
This commit is contained in:
parent
c3b58ab3e2
commit
05dcc2d445
@ -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/ojdk-21.0.1">
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/java-21-openjdk-amd64">
|
||||
<attributes>
|
||||
<attribute name="module" value="true"/>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
|
@ -11,9 +11,9 @@ org.eclipse.jdt.core.codeComplete.staticFinalFieldPrefixes=
|
||||
org.eclipse.jdt.core.codeComplete.staticFinalFieldSuffixes=
|
||||
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
|
||||
@ -23,4 +23,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
|
||||
|
BIN
build/deb/PAMGuardIcon2.png
Normal file
BIN
build/deb/PAMGuardIcon2.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 99 KiB |
13
build/deb/control
Normal file
13
build/deb/control
Normal file
@ -0,0 +1,13 @@
|
||||
Package: PAMGuard
|
||||
Version: 2.0.1
|
||||
Section: java
|
||||
Priority: optional
|
||||
Architecture: all
|
||||
Maintainer: Jamie Macaulay <jdjm@st-andrews.ac.uk>
|
||||
Description: Process passive acoustic data for whales, dolphins and other species.
|
||||
Icon: pamguard_icon.png
|
||||
Depends: openjdk-21-jre
|
||||
postinst script:
|
||||
#!/bin/bash
|
||||
# Set JVM options for the application
|
||||
export JAVA_OPTS="-Xmx4g -Xms512m -Dsun.java2d.uiScale=2 -Dawt.useSystemAAFontSettings=on -Dswing.aatext=true"
|
8
build/deb/pamguard.desktop
Executable file
8
build/deb/pamguard.desktop
Executable file
@ -0,0 +1,8 @@
|
||||
[Desktop Entry]
|
||||
Name=PAMGuard
|
||||
Comment=Application for passive acoustic monitoring v1
|
||||
Exec=java -jar /usr/share/pamguard/Pamguard -c
|
||||
Icon=/usr/share/pamguard/PAMGuardIcon2.png
|
||||
Terminal=true
|
||||
Type=Application
|
||||
Categories=Application;
|
8
build/deb/set-java-propery.sh
Normal file
8
build/deb/set-java-propery.sh
Normal file
@ -0,0 +1,8 @@
|
||||
[Desktop Entry]
|
||||
Name=PAMGuard
|
||||
Comment=Application for passive acoustic monitoring
|
||||
Exec=java -jar /usr/share/pamguard/Pamguard-2.02.14a.jar -c
|
||||
Icon=/path/to/your/icon.png
|
||||
Terminal=true
|
||||
Type=Application
|
||||
Categories=Application;
|
@ -26,6 +26,71 @@
|
||||
</resources>
|
||||
</build>
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>linux-profile</id>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.vafer</groupId>
|
||||
<artifactId>jdeb</artifactId>
|
||||
<version>1.11</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>jdeb</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<controlDir>${basedir}/build/deb</controlDir>
|
||||
<dataSet>
|
||||
<data>
|
||||
<src>${project.build.directory}/${project.build.finalName}.jar</src>
|
||||
<type>file</type>
|
||||
<mapper>
|
||||
<type>perm</type>
|
||||
<prefix>/usr/share/pamguard</prefix>
|
||||
</mapper>
|
||||
</data>
|
||||
<data>
|
||||
<src>${basedir}/liblinux</src>
|
||||
<type>directory</type>
|
||||
<includes>*.txt</includes>
|
||||
<includes>*.so</includes>
|
||||
<mapper>
|
||||
<type>perm</type>
|
||||
<prefix>/usr/share/pamguard/liblinux</prefix>
|
||||
</mapper>
|
||||
</data>
|
||||
<data>
|
||||
<src>${basedir}/build/deb/PAMGuardIcon2.png</src>
|
||||
<type>file</type>
|
||||
<mapper>
|
||||
<type>perm</type>
|
||||
<prefix>/usr/share/pamguard</prefix>
|
||||
</mapper>
|
||||
</data>
|
||||
<data>
|
||||
<src>${basedir}/build/deb/pamguard.desktop</src>
|
||||
<type>file</type>
|
||||
<mapper>
|
||||
<type>perm</type>
|
||||
<prefix>/usr/share/applications</prefix>
|
||||
</mapper>
|
||||
</data>
|
||||
<data>
|
||||
<type>link</type>
|
||||
<linkName>/usr/share/pamguard/Pamguard</linkName>
|
||||
<linkTarget>/usr/share/pamguard/${project.build.finalName}.jar</linkTarget>
|
||||
<symlink>true</symlink>
|
||||
</data>
|
||||
</dataSet>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
<profile>
|
||||
<id>macos-profile</id>
|
||||
<build>
|
||||
@ -145,36 +210,11 @@
|
||||
<plugin>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.12.1</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>default-compile</id>
|
||||
<phase>compile</phase>
|
||||
<goals>
|
||||
<goal>compile</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<release>21</release>
|
||||
<compilerId>jdt</compilerId>
|
||||
</configuration>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>default-testCompile</id>
|
||||
<phase>test-compile</phase>
|
||||
<goals>
|
||||
<goal>testCompile</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<release>21</release>
|
||||
<compilerId>jdt</compilerId>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.tycho</groupId>
|
||||
<artifactId>tycho-compiler-jdt</artifactId>
|
||||
<version>1.5.1</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<configuration>
|
||||
@ -207,17 +247,6 @@
|
||||
</transformer>
|
||||
<transformer />
|
||||
</transformers>
|
||||
<filters>
|
||||
<filter>
|
||||
<artifact>*:*</artifact>
|
||||
<excludes>
|
||||
<exclude>META-INF/*.SF</exclude>
|
||||
<exclude>META-INF/*.DSA</exclude>
|
||||
<exclude>META-INF/*.RSA</exclude>
|
||||
<exclude>test/resources/**</exclude>
|
||||
</excludes>
|
||||
</filter>
|
||||
</filters>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
@ -254,7 +283,7 @@
|
||||
<goal>copy-dependencies</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<outputDirectory>C:\Users\dg50\source\repos\PAMGuardPAMGuard\target/tempDependencies</outputDirectory>
|
||||
<outputDirectory>${project.build.directory}/tempDependencies</outputDirectory>
|
||||
<overWriteReleases>false</overWriteReleases>
|
||||
<overWriteSnapshots>false</overWriteSnapshots>
|
||||
<overWriteIfNewer>true</overWriteIfNewer>
|
||||
|
162
pom.xml
162
pom.xml
@ -51,7 +51,106 @@
|
||||
earlier. Most OS specific profiles are for generating executables and code signing.
|
||||
Note use help:active-profiles to see which profiles are active -->
|
||||
<profiles>
|
||||
|
||||
|
||||
<!-- The Linux profile creates a DMG package for installing PAMGuard on
|
||||
Linux Ubuntu based systems-->
|
||||
<!--Note: There is a Maven bug/feature when using profiles when the plugin order is messed up and seemingly quite random.
|
||||
In PAMGuard it essential that the shade plugin is called before the linux plugin. Therefore explicately call
|
||||
the package phase using mvn package shade:shade jdeb:jdeb. Note that although a deb file can be created in Windows it
|
||||
will not have the correct JavaFX libraries and therefore will not work when riun -->
|
||||
<profile>
|
||||
<id>linux-profile</id>
|
||||
<activation>
|
||||
<os>
|
||||
<family>linux</family>
|
||||
</os>
|
||||
</activation>
|
||||
<build>
|
||||
<plugins>
|
||||
<!--Creates a deb file for Linux-->
|
||||
<plugin>
|
||||
<artifactId>jdeb</artifactId>
|
||||
<groupId>org.vafer</groupId>
|
||||
<version>1.11</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>jdeb</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<controlDir>${basedir}/build/deb</controlDir>
|
||||
<dataSet>
|
||||
<data>
|
||||
<!--TODO-really the jar file should be named properly but thos would mean we would
|
||||
have to change the name in the desktop file too. For now create a link to the jar file
|
||||
with correct version number-->
|
||||
<src>
|
||||
${project.build.directory}/${project.build.finalName}.jar</src>
|
||||
<type>file</type>
|
||||
<mapper>
|
||||
<type>perm</type>
|
||||
<prefix>/usr/share/pamguard</prefix>
|
||||
</mapper>
|
||||
</data>
|
||||
<data>
|
||||
<src>${basedir}/liblinux</src>
|
||||
<type>directory</type>
|
||||
<includes>*.txt</includes>
|
||||
<includes>*.so</includes>
|
||||
<mapper>
|
||||
<type>perm</type>
|
||||
<prefix>
|
||||
/usr/share/pamguard/liblinux</prefix>
|
||||
</mapper>
|
||||
</data>
|
||||
<data>
|
||||
<src>
|
||||
${basedir}/build/deb/PAMGuardIcon2.png</src>
|
||||
<type>file</type>
|
||||
<mapper>
|
||||
<type>perm</type>
|
||||
<prefix>/usr/share/pamguard</prefix>
|
||||
</mapper>
|
||||
</data>
|
||||
<!---THe desktop file will create a PAMGuard icon in the applications tray-->
|
||||
<data>
|
||||
<src>
|
||||
${basedir}/build/deb/pamguard.desktop</src>
|
||||
<type>file</type>
|
||||
<mapper>
|
||||
<type>perm</type>
|
||||
<prefix>/usr/share/applications</prefix>
|
||||
</mapper>
|
||||
</data>
|
||||
<!---Create a link which is just called Pamguard. This means the .desktop file does not need
|
||||
to be altered depending on the build name-->
|
||||
<data>
|
||||
<type>link</type>
|
||||
<linkName>
|
||||
/usr/share/pamguard/Pamguard</linkName>
|
||||
<linkTarget>
|
||||
/usr/share/pamguard/${project.build.finalName}.jar</linkTarget>
|
||||
<symlink>true</symlink>
|
||||
</data>
|
||||
<!--<data>
|
||||
<src>${project.basedir}/build/deb/set-java-property.sh</src>
|
||||
<type>file</type>
|
||||
<mapper>
|
||||
<type>perm</type>
|
||||
<prefix>/usr/bin</prefix>
|
||||
<filemode>755</filemode>
|
||||
</mapper>
|
||||
</data>-->
|
||||
</dataSet>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
|
||||
<!-- The MacOS profile creates a DMG package for installing PAMGuard on MacOS-->
|
||||
<!--Note: There is a Maven bug when using profiles when the plugin order is messed up and seemingly quite random.
|
||||
In PAMGuard it essential that the shade plugin is called before the macos plugin and so the phase of the
|
||||
@ -311,11 +410,10 @@
|
||||
</execution>
|
||||
-->
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
</plugin>
|
||||
|
||||
</plugins>
|
||||
|
||||
|
||||
<pluginManagement>
|
||||
<plugins>
|
||||
<!--This plugin's configuration is used to store Eclipse m2e settings only. It has no influence on the Maven build itself.-->
|
||||
@ -358,7 +456,52 @@
|
||||
</profile>
|
||||
|
||||
</profiles>
|
||||
|
||||
|
||||
<!-- Plugin which creates a .dmg file for MacOS.
|
||||
<plugin>
|
||||
<groupId>de.perdian.maven.plugins</groupId>
|
||||
<artifactId>macosappbundler-maven-plugin</artifactId>
|
||||
<version>1.21.1</version>
|
||||
<configuration>
|
||||
<plist>
|
||||
<JVMMainClassName>pamguard.Pamguard</JVMMainClassName>
|
||||
<CFBundleIconFile>src/Resources/PamguardIcon2.icns</CFBundleIconFile>
|
||||
<CFBundleDisplayName>PAMGuard</CFBundleDisplayName>
|
||||
<CFBundleDevelopmentRegion>English</CFBundleDevelopmentRegion>
|
||||
<CFBundleURLTypes>
|
||||
<string>msa</string>
|
||||
</CFBundleURLTypes>
|
||||
<JVMVersion>21+</JVMVersion>
|
||||
<JVMArguments>
|
||||
<string>-c</string>
|
||||
</JVMArguments>
|
||||
</plist>
|
||||
<dmg>
|
||||
<generate>true</generate>
|
||||
<additionalResources>
|
||||
<additionalResource>
|
||||
<directory>src/target/bundle/</directory>
|
||||
</additionalResource>
|
||||
</additionalResources>
|
||||
</dmg>
|
||||
<jdk>
|
||||
<include>false</include>
|
||||
<location>/Library/Java/JavaVirtualMachines/amazon-corretto-21.jdk</location>
|
||||
</jdk>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>bundle</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>-->
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<reporting>
|
||||
@ -442,7 +585,7 @@
|
||||
<groupId>io.github.macster110</groupId>
|
||||
<artifactId>jpamutils</artifactId>
|
||||
<version>0.0.59f</version>
|
||||
<!-- com.github.psambit9791:wavfile:jar:0.1 pulls in various junit dependencies which breaks our own testing -->
|
||||
<!-- com.github.psambit9791:wavfile:jar:0.1 pulls in various junit dependencies which breaks our own testing -->
|
||||
<!--<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.junit.platform</groupId>
|
||||
@ -610,7 +753,7 @@
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<!-- html to markdown conversion https://github.com/furstenheim/copy-down -->
|
||||
<!-- html to markdown conversion https://github.com/furstenheim/copy-down -->
|
||||
<dependency>
|
||||
<groupId>io.github.furstenheim</groupId>
|
||||
<artifactId>copy_down</artifactId>
|
||||
@ -997,7 +1140,7 @@
|
||||
<version>2.1.1</version>
|
||||
</dependency>
|
||||
|
||||
<!--
|
||||
<!--
|
||||
<dependency>
|
||||
<groupId>javax.xml.bind</groupId>
|
||||
<artifactId>jaxb-api</artifactId>
|
||||
@ -1065,7 +1208,7 @@
|
||||
-->
|
||||
<dependency>
|
||||
<groupId>org.pamguard</groupId>
|
||||
<artifactId>x3</artifactId>
|
||||
<artifactId>X3</artifactId>
|
||||
<version>2.2.8</version>
|
||||
</dependency>
|
||||
|
||||
@ -1154,7 +1297,6 @@
|
||||
</dependency>
|
||||
|
||||
|
||||
|
||||
<!-- Test dependencies -->
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
|
@ -106,10 +106,6 @@ public class GroupLocResult implements Comparable<GroupLocResult>, LocalisationC
|
||||
this.chi2 = chi2;
|
||||
}
|
||||
|
||||
public GroupLocResult(double[] result, double[] resultErrors, int side, double chi2) {
|
||||
// TODO Auto-generated constructor stub
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the latLong
|
||||
*/
|
||||
|
@ -1381,7 +1381,25 @@ public class PamArrayUtils {
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Convert a matrix to a
|
||||
* @param matrix - the MAT file matrix
|
||||
* @return double[][] array of results
|
||||
*/
|
||||
public static float[][] matrix2arrayF(Matrix matrix) {
|
||||
if (matrix==null) return null;
|
||||
|
||||
float[][] arrayOut = new float[matrix.getNumRows()][];
|
||||
float[] arrayRow;
|
||||
for (int i=0; i<matrix.getNumRows(); i++) {
|
||||
arrayRow=new float[matrix.getNumCols()];
|
||||
for (int j=0; j<matrix.getNumCols(); j++) {
|
||||
arrayRow[j] = matrix.getFloat(i, j);
|
||||
}
|
||||
arrayOut[i]=arrayRow;
|
||||
}
|
||||
return arrayOut;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
@ -54,13 +54,18 @@ public class DataModelConnectPane extends ConnectionPane {
|
||||
@Override
|
||||
public void handle(DragEvent event)
|
||||
{
|
||||
|
||||
// System.out.println("START drag over node drag dropped" + dataModelPaneFX.getDraggingStructure());
|
||||
|
||||
final Dragboard dragboard = event.getDragboard();
|
||||
if (dragboard.hasString()
|
||||
&& dataModelPaneFX.getModuleDragKey().equals(dragboard.getString())
|
||||
&& dataModelPaneFX.getDraggingModule().get() != null || dataModelPaneFX.getDraggingStructure()!=null)
|
||||
{
|
||||
event.acceptTransferModes(TransferMode.MOVE);
|
||||
event.consume();
|
||||
// System.out.println("ACCEPT drag over node drag dropped" + dataModelPaneFX.getDraggingStructure());
|
||||
|
||||
event.acceptTransferModes(TransferMode.ANY);
|
||||
//event.consume(); //causesd issues with dropping nodes not being detected
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -70,6 +75,9 @@ public class DataModelConnectPane extends ConnectionPane {
|
||||
@Override
|
||||
public void handle(DragEvent event)
|
||||
{
|
||||
|
||||
System.out.println("Add Some Node drag dropped: " + dataModelPaneFX.getDraggingStructure());
|
||||
|
||||
final Dragboard dragboard = event.getDragboard();
|
||||
if (dragboard.hasString()
|
||||
&& dataModelPaneFX.getModuleDragKey().equals(dragboard.getString())
|
||||
@ -80,6 +88,7 @@ public class DataModelConnectPane extends ConnectionPane {
|
||||
|
||||
ModuleRectangle moduleRect = dataModelPaneFX.getDraggingModule().get();
|
||||
|
||||
// System.out.println("Add Connection Node drag dropped");
|
||||
dataModelPaneFX.getDraggingModule().set(null);
|
||||
dataModelPaneFX.getConnectionNodeFactory().addNewModule(moduleRect.getPamModuleInfo(), event.getX(), event.getY());
|
||||
|
||||
@ -88,6 +97,7 @@ public class DataModelConnectPane extends ConnectionPane {
|
||||
if (dragboard.hasString()
|
||||
&& dataModelPaneFX.getModuleDragKey().equals(dragboard.getString())
|
||||
&& dataModelPaneFX.getDraggingStructure().get() != null){
|
||||
// System.out.println("Add Structure Node drag dropped");
|
||||
|
||||
dataModelPaneFX.getConnectionNodeFactory().addNewStructure(dataModelPaneFX.getDraggingStructure().get(), event.getX(), event.getY());
|
||||
dataModelPaneFX.getDraggingStructure().set(null);
|
||||
|
@ -121,8 +121,7 @@ public class ConnectionNodeFactory {
|
||||
* @param y - the pixel y co-ordinate
|
||||
*/
|
||||
public StandardConnectionNode addNewStructure(StructureRectangle structureRectangle, double x, double y) {
|
||||
// System.out.println("DataModelConnectPane: add new structure " + structureRectangle.getStructureType()
|
||||
// + " no. nodes: " + this.getConnectionNodes().size());
|
||||
System.out.println("DataModelConnectPane: add new structure " + structureRectangle.getStructureType() );
|
||||
StandardConnectionNode connectionNode = createConnectionNode(structureRectangle.getStructureType(), null);
|
||||
|
||||
if (connectionNode!=null) {
|
||||
|
@ -136,6 +136,8 @@ public class GenericDataPlotInfo extends TDDataInfoFX {
|
||||
System.out.println("GenericDataPlotInfo: Single frequency measure in data unit " + pamDataUnit.toString());
|
||||
}
|
||||
|
||||
//System.out.println("Frequency: " + f[0] + " " + f[1] + " " + pamDataUnit);
|
||||
|
||||
// draw a frequency box.
|
||||
double y0 = tdProjector.getYPix(f[0]);
|
||||
double y1 = tdProjector.getYPix(f[1]);
|
||||
|
@ -153,6 +153,9 @@ public class MLDetectionsManager implements PamDataUnitExporter {
|
||||
public boolean hasCompatibleUnits(List<PamDataUnit> dataUnits) {
|
||||
//first need to figure out how many data units there are.
|
||||
for (int j=0; j<dataUnits.size(); j++){
|
||||
|
||||
|
||||
|
||||
if (hasCompatibleUnits(dataUnits.get(j).getClass())) return true;
|
||||
}
|
||||
return false;
|
||||
|
@ -232,7 +232,7 @@ public class Group3DProcess extends PamProcess implements DetectionGroupMonitor
|
||||
}
|
||||
}
|
||||
|
||||
// System.out.println("Ran localisation " + i + " " + localiserAlgorithm3D.getName() + " got: " + abstractLocalisation.getLatLong(0) + " " + abstractLocalisation.getHeight(0) + " Error: " + abstractLocalisation.getLocError(0));
|
||||
System.out.println("Ran localisation " + i + " " + localiserAlgorithm3D.getName() + " got: " + abstractLocalisation.getLatLong(0) + " " + abstractLocalisation.getHeight(0) + " Error: " + abstractLocalisation.getLocError(0));
|
||||
|
||||
if (abstractLocalisation instanceof GroupLocalisation) {
|
||||
groupLocalisation = (GroupLocalisation) abstractLocalisation;
|
||||
|
@ -388,7 +388,8 @@ public class HyperbolicLocaliser extends TOADBaseAlgorithm {
|
||||
PamVector centre = geometry.getGeometricCentre();
|
||||
int[] hydrophones = toadInformation.getHydrophoneList();
|
||||
double xArray[] = new double[geom.length];
|
||||
for (int i = 0; i < geom.length; i++) {
|
||||
//System.out.println("Hyperbolic loc: Geom length: " + hydrophones.length + " " + geom.length);
|
||||
for (int i = 0; i < hydrophones.length; i++) {
|
||||
xArray[i] = geom[hydrophones[i]].sub(centre).dotProd(arrayAxes);
|
||||
}
|
||||
|
||||
@ -581,6 +582,8 @@ public class HyperbolicLocaliser extends TOADBaseAlgorithm {
|
||||
//// glr.setModel(model);
|
||||
// return groupLocalisation;
|
||||
LinearLocalisation linLoc = new LinearLocalisation(groupDataUnit, toadInformation.getHydrophoneMap(), geometry.getArrayAxes(), angle, r);
|
||||
linLoc.setChi2(chiData);
|
||||
|
||||
GpsData refPos = new GpsData(geometry.getReferenceGPS().addDistanceMeters(geometry.getGeometricCentre()));
|
||||
linLoc.setReferencePosition(refPos);
|
||||
return linLoc;
|
||||
|
@ -132,11 +132,14 @@ abstract public class TOADBaseAlgorithm extends LocaliserAlgorithm3D {
|
||||
// if (superDetection.getSubDetection(0).getUID() == 9035004477L) {
|
||||
// System.out.println("Found it");
|
||||
// }
|
||||
|
||||
|
||||
TOADInformation toadInformation = toadCalculator.getTOADInformation(superDetection.getSubDetections(), sampleRate, allChannels, geometry);
|
||||
|
||||
|
||||
boolean toadOK = checkTOADInformation(toadInformation);
|
||||
|
||||
// System.out.println("ToadOK: " + toadOK + " toadInformation: " + toadInformation + " allChannels: " + allChannels + " geometry: " + geometry);
|
||||
//System.out.println("ToadOK: " + toadOK + " toadInformation: " + toadInformation + " allChannels: " + allChannels + " geometry: " + geometry.getGeometry() + "n sub: " + superDetection.getSubDetectionsCount() );
|
||||
|
||||
if (!toadOK) {
|
||||
return null;
|
||||
|
@ -0,0 +1,17 @@
|
||||
package group3dlocaliser.algorithm.toadmimplex;
|
||||
|
||||
import Localiser.detectionGroupLocaliser.GroupLocResult;
|
||||
import group3dlocaliser.localisation.LinearLocalisation;
|
||||
|
||||
|
||||
/**
|
||||
* Wrapper to make a linear localisation into a group localisation
|
||||
*/
|
||||
public class LinearGroupLocResult extends GroupLocResult {
|
||||
|
||||
public LinearGroupLocResult(LinearLocalisation linearLocalisation) {
|
||||
super(linearLocalisation.getReferencePosition(), 0, linearLocalisation.getChi2());
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -17,6 +17,7 @@ import group3dlocaliser.algorithm.toadmcmc.MCMCLocaliserPane;
|
||||
import group3dlocaliser.algorithm.toadmcmc.ToadMCMCLocaliser;
|
||||
import group3dlocaliser.algorithm.toadsimplex.ToadSimplexLocaliser;
|
||||
import group3dlocaliser.grouper.DetectionGroupedSet;
|
||||
import group3dlocaliser.localisation.LinearLocalisation;
|
||||
|
||||
|
||||
/**
|
||||
@ -94,7 +95,7 @@ public class ToadMimplexLocaliser extends ToadMCMCLocaliser {
|
||||
*/
|
||||
public DetectionGroupedSet preFilterLoc(DetectionGroupedSet preGroups) {
|
||||
|
||||
System.out.println("Pre filter groups: " + preGroups.getNumGroups());
|
||||
//System.out.println("Pre filter groups: " + preGroups.getNumGroups());
|
||||
|
||||
MimplexParams params = (MimplexParams) group3dLocaliser.getLocaliserAlgorithmParams(this).getAlgorithmParameters();
|
||||
|
||||
@ -105,12 +106,12 @@ public class ToadMimplexLocaliser extends ToadMCMCLocaliser {
|
||||
return preGroups;
|
||||
}
|
||||
|
||||
//no need to do a y more processing.
|
||||
//no need to do a any more processing.
|
||||
if (preGroups.getNumGroups()<=2 && params.useFirstCombination) {
|
||||
return preGroups;
|
||||
}
|
||||
|
||||
//localiser using both hyperbolic and the
|
||||
//localiser using both hyperbolic and simplex the
|
||||
// will have to make a data unit for each group now...
|
||||
Group3DDataUnit[] group3dDataUnits = new Group3DDataUnit[preGroups.getNumGroups()];
|
||||
|
||||
@ -121,18 +122,22 @@ public class ToadMimplexLocaliser extends ToadMCMCLocaliser {
|
||||
|
||||
group3dDataUnits[i] = new Group3DDataUnit(preGroups.getGroup(i));
|
||||
|
||||
//System.out.println("ToadMImplex. group3dDataUnits[i]: " + group3dDataUnits[i] );
|
||||
|
||||
GroupLocalisation preAbstractLocalisation = null;
|
||||
AbstractLocalisation preAbstractLocalisation = null;
|
||||
double minChi2 = Double.MAX_VALUE;
|
||||
|
||||
GroupLocResult locResult = null;
|
||||
for (LocaliserModel model: preLocaliserModels) {
|
||||
|
||||
//System.out.println("ToadMImplex. model: " + model );
|
||||
|
||||
|
||||
locResult = null;
|
||||
minChi2=Double.MAX_VALUE;
|
||||
preAbstractLocalisation = null; //must reset this.
|
||||
try {
|
||||
preAbstractLocalisation = (GroupLocalisation) model.runModel(group3dDataUnits[i], null, false);
|
||||
preAbstractLocalisation = model.runModel(group3dDataUnits[i], null, false);
|
||||
}
|
||||
catch (Exception e) {
|
||||
System.out.println("Mimplex pre filter loclaisation failed");
|
||||
@ -142,12 +147,23 @@ public class ToadMimplexLocaliser extends ToadMCMCLocaliser {
|
||||
// System.out.println("Pre-localisation result: " + locResult + " " + model.getName() + "N units: " + preGroups.getGroup(i).size());
|
||||
|
||||
if (preAbstractLocalisation!=null) {
|
||||
|
||||
if (preAbstractLocalisation instanceof GroupLocalisation) {
|
||||
//now iterate through the potential ambiguities (this is a bit redunadant with Simplex and Hyperbolic)
|
||||
for (GroupLocResult groupLocResult: preAbstractLocalisation.getGroupLocResults()) {
|
||||
for (GroupLocResult groupLocResult: ((GroupLocalisation) preAbstractLocalisation).getGroupLocResults()) {
|
||||
if (groupLocResult.getChi2()<minChi2) {
|
||||
locResult = groupLocResult;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (preAbstractLocalisation instanceof LinearLocalisation) {
|
||||
//if a linear vertical array (exactly) then will return a linear localisation. Need to make this into
|
||||
//a group localisation to satisfy the mimplex localiser.
|
||||
if (((LinearLocalisation) preAbstractLocalisation).getChi2()<minChi2) {
|
||||
locResult = new LinearGroupLocResult(((LinearLocalisation) preAbstractLocalisation));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -244,6 +244,7 @@ public class DetectionGrouper {
|
||||
*/
|
||||
public synchronized void closeMotherGroup() {
|
||||
processFirstGroup(motherGroup);
|
||||
if (maxInterGroupSamples==null) return;
|
||||
motherGroup = new FirstGrouping(maxInterGroupSamples.length, 0, null);
|
||||
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ import Localiser.detectionGroupLocaliser.LocalisationChi2;
|
||||
import PamDetection.AbstractLocalisation;
|
||||
import PamDetection.LocContents;
|
||||
import PamguardMVC.PamDataUnit;
|
||||
import group3dlocaliser.algorithm.Chi2Data;
|
||||
import pamMaths.PamVector;
|
||||
|
||||
public class LinearLocalisation extends AbstractLocalisation implements LocalisationChi2{
|
||||
@ -12,6 +13,7 @@ public class LinearLocalisation extends AbstractLocalisation implements Localisa
|
||||
private double[] angles;
|
||||
private Double range;
|
||||
private GpsData referencePosition;
|
||||
private Chi2Data chi2Dat;
|
||||
|
||||
public LinearLocalisation(PamDataUnit pamDataUnit, int referenceHydrophones, PamVector[] arrayAxes, double bearing, Double range) {
|
||||
super(pamDataUnit, LocContents.HAS_BEARING | LocContents.HAS_AMBIGUITY, referenceHydrophones);
|
||||
@ -42,8 +44,7 @@ public class LinearLocalisation extends AbstractLocalisation implements Localisa
|
||||
|
||||
@Override
|
||||
public Double getChi2() {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
return chi2Dat.getChi2();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -84,6 +85,14 @@ public class LinearLocalisation extends AbstractLocalisation implements Localisa
|
||||
public void setReferencePosition(GpsData referencePosition) {
|
||||
this.referencePosition = referencePosition;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Chi2 data
|
||||
* @param chi2dat
|
||||
*/
|
||||
public void setChi2(Chi2Data chi2dat) {
|
||||
this.chi2Dat = chi2dat;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
@ -58,7 +58,13 @@ public class Group3DOfflineTask extends OfflineTask<PamDataUnit>{
|
||||
|
||||
@Override
|
||||
public boolean processDataUnit(PamDataUnit dataUnit) {
|
||||
try {
|
||||
group3DProcess.newData(null, dataUnit);
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
//System.out.println("New data unit added: " +dataUnit);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -210,7 +210,7 @@ public class DLClassifyProcess extends PamProcess {
|
||||
*/
|
||||
@Override
|
||||
public void newData(PamObservable obs, PamDataUnit pamRawData) {
|
||||
//System.out.println("NEW SEGMENTER DATA: " + PamCalendar.formatDateTime2(pamRawData.getTimeMilliseconds(), "dd MMM yyyy HH:mm:ss.SSS", false) + " " + pamRawData.getUID() + " " + pamRawData.getChannelBitmap() + " " + pamRawData);
|
||||
//System.out.println("NEW SEGMENTER DATA: " + PamCalendar.formatDateTime2(pamRawData.getTimeMilliseconds(), "dd MMM yyyy HH:mm:ss.SSS", false) + " " + pamRawData.getUID() + " " + pamRawData.getChannelBitmap() + " " + pamRawData);
|
||||
|
||||
//if grouped data then just run the classifier on the group - do not try and create a buffer.
|
||||
if (pamRawData instanceof SegmenterDetectionGroup) {
|
||||
@ -618,10 +618,11 @@ public class DLClassifyProcess extends PamProcess {
|
||||
*/
|
||||
public void forceRunClassifier(PamDataUnit dataUnit) {
|
||||
|
||||
// System.out.println("CLASSIFICATION BUFFER: " + classificationBuffer.size());
|
||||
//System.out.println("CLASSIFICATION BUFFER: " + classificationBuffer.size());
|
||||
|
||||
if (this.classificationBuffer.size()>0) {
|
||||
if (classificationBuffer.get(0) instanceof GroupedRawData) {
|
||||
//System.out.println("Run raw model on: " + classificationBuffer.get(0) + " " + classificationBuffer.get(1));
|
||||
runRawModel(); //raw data or raw data units
|
||||
}
|
||||
if (classificationBuffer.get(0) instanceof SegmenterDetectionGroup) {
|
||||
@ -689,10 +690,12 @@ public class DLClassifyProcess extends PamProcess {
|
||||
DataUnitBaseData basicData = groupDataBuffer.get(0).getBasicData().clone();
|
||||
basicData.setMillisecondDuration(1000.*rawdata[0].length/this.sampleRate);
|
||||
basicData.setSampleDuration((long) (groupDataBuffer.size()*dlControl.getDLParams().rawSampleSize));
|
||||
|
||||
|
||||
// System.out.println("Model result: " + modelResult.size());
|
||||
DLDetection dlDetection = new DLDetection(basicData, rawdata, getSampleRate());
|
||||
addDLAnnotation(dlDetection,modelResult);
|
||||
dlDetection.setFrequency(new double[] {0, this.getSampleRate()/2});
|
||||
|
||||
//create the data unit
|
||||
return dlDetection;
|
||||
|
@ -139,9 +139,8 @@ public class ArchiveModelWorker extends GenericModelWorker {
|
||||
System.out.println(modelParams.dlTransforms);
|
||||
ArrayList<DLTransform> transforms = DLTransformsFactory.makeDLTransforms(modelParams.dlTransforms);
|
||||
|
||||
// ///HACK here for now to fix an issue with dB and Ketos transforms having zero length somehow...
|
||||
// for (int i=0; i<modelParams.dlTransforms.size(); i++) {
|
||||
// System.out.println(modelParams.dlTransforms.get(i));
|
||||
// System.out.println(modelParams.dlTransforms.get(i).toString());
|
||||
// }
|
||||
|
||||
//only load new transforms if defaults are selected
|
||||
|
@ -11,6 +11,7 @@ import org.jamdev.jdl4pam.transforms.DLTransformsFactory;
|
||||
import org.jamdev.jdl4pam.utils.DLUtils;
|
||||
import org.jamdev.jpamutils.wavFiles.AudioData;
|
||||
|
||||
import PamUtils.PamArrayUtils;
|
||||
import PamguardMVC.PamDataUnit;
|
||||
import rawDeepLearningClassifier.DLControl;
|
||||
import rawDeepLearningClassifier.DLStatus;
|
||||
@ -82,10 +83,17 @@ public abstract class DLModelWorker<T> {
|
||||
DLTransform transform = modelTransforms.get(0);
|
||||
for (int i =0; i<modelTransforms.size(); i++) {
|
||||
transform = modelTransforms.get(i).transformData(transform);
|
||||
|
||||
// //TEMP
|
||||
// if (transform instanceof FreqTransform) {
|
||||
// transformedData = ((FreqTransform) transform).getSpecTransfrom().getTransformedData();
|
||||
// System.out.println("DLModelWorker: transform : " + modelTransforms.get(i).getDLTransformType() + " "+ i + transformedData.length + " " + transformedData[0].length + " minmax: " + PamArrayUtils.minmax(transformedData)[0] + " " + PamArrayUtils.minmax(transformedData)[1]);
|
||||
// }
|
||||
|
||||
// //TEMP
|
||||
// if (transform instanceof WaveTransform) {
|
||||
// transformedData1 = ((WaveTransform) transform).getWaveData().getScaledSampleAmplitudes();
|
||||
// System.out.println("DLModelWorker: transform : " + modelTransforms.get(i).getDLTransformType() + " "+ i + " " + transformedData1.length + " " + PamArrayUtils.minmax(transformedData1)[0] + " " + PamArrayUtils.minmax(transformedData1)[1]);
|
||||
// }
|
||||
}
|
||||
|
||||
|
@ -104,6 +104,7 @@ public class GenericModelWorker extends DLModelWorker<StandardPrediction> {
|
||||
}
|
||||
|
||||
setModelTransforms(genericParams.dlTransfroms);
|
||||
|
||||
|
||||
//is this a waveform or a spectrogram model?
|
||||
setWaveFreqModel(genericParams);
|
||||
|
@ -583,10 +583,11 @@ public class DLSettingsPane extends SettingsPane<RawDLParams>{
|
||||
|
||||
@Override
|
||||
public void setParams(RawDLParams currParams) {
|
||||
|
||||
sourcePane.setParams(currParams.groupedSourceParams);
|
||||
|
||||
|
||||
sourcePane.sourceChanged();
|
||||
sourcePane.setSourceList();
|
||||
sourcePane.setParams(currParams.groupedSourceParams);
|
||||
|
||||
dlControl.createDataSelector(sourcePane.getSource());
|
||||
|
||||
|
@ -59,7 +59,7 @@ public class PeakTrimTransformPane extends DLTransformPane {
|
||||
});
|
||||
|
||||
//spinner for changing filter order.
|
||||
targetLenSpinner = new Spinner<Integer>(1,50,4,1);
|
||||
targetLenSpinner = new Spinner<Integer>(1,Integer.MAX_VALUE,4,1);
|
||||
targetLenSpinner.valueProperty().addListener((obsVal, oldVal, newVal)->{
|
||||
this.notifySettingsListeners();
|
||||
});
|
||||
|
@ -1,5 +1,7 @@
|
||||
package rawDeepLearningClassifier.offline;
|
||||
|
||||
import java.util.ListIterator;
|
||||
|
||||
import PamController.PamController;
|
||||
import PamguardMVC.PamDataUnit;
|
||||
import PamguardMVC.PamObservable;
|
||||
@ -8,6 +10,7 @@ import dataMap.OfflineDataMapPoint;
|
||||
import matchedTemplateClassifer.MTClassifierControl;
|
||||
import offlineProcessing.OfflineTask;
|
||||
import rawDeepLearningClassifier.DLControl;
|
||||
import rawDeepLearningClassifier.segmenter.GroupedRawData;
|
||||
import rawDeepLearningClassifier.segmenter.SegmenterDetectionGroup;
|
||||
import rawDeepLearningClassifier.segmenter.SegmenterProcess;
|
||||
|
||||
@ -48,7 +51,7 @@ public class DLOfflineTask extends OfflineTask<PamDataUnit<?,?>>{
|
||||
@Override
|
||||
public boolean processDataUnit(PamDataUnit<?, ?> dataUnit) {
|
||||
// System.out.println("--------------");
|
||||
// System.out.println("Offline task start: " + dataUnit.getUpdateCount() + " UID " + dataUnit.getUID());
|
||||
//System.out.println("Offline task start: " + dataUnit.getUpdateCount() + " UID " + dataUnit.getUID() + " " + dlControl.getDLParams().enableSegmentation);
|
||||
boolean saveBinary = false;
|
||||
try {
|
||||
|
||||
@ -79,9 +82,16 @@ public class DLOfflineTask extends OfflineTask<PamDataUnit<?,?>>{
|
||||
//detection has been added we force the classifier to run on all the segments generated from
|
||||
//the raw data.
|
||||
|
||||
//Process a data unit
|
||||
//Process a data unit within the segmenter
|
||||
dlControl.getSegmenter().newData(dataUnit);
|
||||
|
||||
|
||||
//System.out.println("Segments: " + dlControl.getSegmenter().getSegmenterDataBlock().getUnitsCount());
|
||||
//need to add the segmenter data units into the classification buffer
|
||||
ListIterator<GroupedRawData> iterator = dlControl.getSegmenter().getSegmenterDataBlock().getListIterator(0);
|
||||
while (iterator.hasNext()) {
|
||||
dlControl.getDLClassifyProcess().newData(dlControl.getSegmenter().getSegmenteGroupDataBlock(), iterator.next());
|
||||
}
|
||||
|
||||
//force click data save
|
||||
dlControl.getDLClassifyProcess().forceRunClassifier(dataUnit);
|
||||
|
||||
|
@ -96,7 +96,7 @@ public class SegmenterProcess extends PamProcess {
|
||||
segmenterDataBlock = new SegmenterDataBlock("Segmented Raw Data", this,
|
||||
dlControl.getDLParams().groupedSourceParams.getChanOrSeqBitmap());
|
||||
|
||||
segmenterGroupDataBlock = new SegmenterGroupDataBlock("Segmented data units", this,
|
||||
segmenterGroupDataBlock = new SegmenterGroupDataBlock("Segmented Group Data", this,
|
||||
dlControl.getDLParams().groupedSourceParams.getChanOrSeqBitmap());
|
||||
|
||||
addOutputDataBlock(segmenterDataBlock);
|
||||
@ -241,7 +241,7 @@ public class SegmenterProcess extends PamProcess {
|
||||
newRawDataUnit(pamRawData);
|
||||
}
|
||||
else if (pamRawData instanceof ClickDetection) {
|
||||
newClickData( pamRawData);
|
||||
newClickData(pamRawData);
|
||||
}
|
||||
else if (pamRawData instanceof ClipDataUnit) {
|
||||
newClipData(pamRawData);
|
||||
@ -491,7 +491,7 @@ public class SegmenterProcess extends PamProcess {
|
||||
public void newClickData(PamDataUnit pamRawData) {
|
||||
|
||||
//the raw data units should appear in sequential channel order
|
||||
// System.out.println("New raw data in: chan: " + PamUtils.getSingleChannel(pamRawData.getChannelBitmap()) + " Size: " + pamRawData.getSampleDuration());
|
||||
//System.out.println("New raw data in: chan: " + PamUtils.getSingleChannel(pamRawData.getChannelBitmap()) + " Size: " + pamRawData.getSampleDuration());
|
||||
|
||||
ClickDetection clickDataUnit = (ClickDetection) pamRawData;
|
||||
|
||||
@ -542,6 +542,12 @@ public class SegmenterProcess extends PamProcess {
|
||||
//segment the data unit into different chunks.
|
||||
newRawData(pamDataUnit,
|
||||
rawDataChunk[i], chans[i], dlControl.getDLParams().rawSampleSize, dlControl.getDLParams().sampleHop, true);
|
||||
//the way that the newRawdata works is it waits for the next chunk and copies all relevant bits
|
||||
//from previous chunks into segments. This is fine for continuous data but means that chunks of data
|
||||
//don't get their last hop...
|
||||
|
||||
//got to save the last chunk of raw data -even if the segment has not been filled.
|
||||
saveRawGroupData(true);
|
||||
}
|
||||
else {
|
||||
// //send the whole data chunk to the deep learning unit
|
||||
@ -551,13 +557,8 @@ public class SegmenterProcess extends PamProcess {
|
||||
// pamDataUnit.getStartSample(), rawDataChunk[i].length, rawDataChunk[i].length);
|
||||
}
|
||||
|
||||
//the way that the newRawdata works is it waits for the next chunk and copies all relevant bits
|
||||
//from previous chunks into segments. This is fine for continuous data but means that chunks of data
|
||||
//don't get their last hop...
|
||||
}
|
||||
|
||||
//got to save the last chunk of raw data -even if the segment has not been filled.
|
||||
saveRawGroupData(true);
|
||||
}
|
||||
|
||||
|
||||
@ -600,7 +601,7 @@ public class SegmenterProcess extends PamProcess {
|
||||
long timeMilliseconds = unit.getTimeMilliseconds();
|
||||
long startSampleTime = unit.getStartSample();
|
||||
|
||||
//System.out.println("Segmenter: RawDataIn: chan: 1 " + getSourceParams().countChannelGroups() + currentRawChunks);
|
||||
//System.out.println("Segmenter: RawDataIn: chan: 1 " + getSourceParams().countChannelGroups() + currentRawChunks + " rawSampleSize " + rawSampleSize + " rawSampleHop: " +rawSampleHop);
|
||||
|
||||
if (currentRawChunks==null) {
|
||||
System.err.println("Current raw chunk arrays are null");
|
||||
@ -753,6 +754,7 @@ public class SegmenterProcess extends PamProcess {
|
||||
* @param forceSave - true to also save the remaining unfilled segment.
|
||||
*/
|
||||
private void saveRawGroupData(boolean forceSave) {
|
||||
//System.out.println("Segmenter process: saveRawGroupData(boolean forceSave)");
|
||||
for (int i=0; i<getSourceParams().countChannelGroups(); i++) {
|
||||
saveRawGroupData(i, forceSave);
|
||||
}
|
||||
@ -771,6 +773,7 @@ public class SegmenterProcess extends PamProcess {
|
||||
* @param i - the group index.
|
||||
*/
|
||||
private void saveRawGroupData(int i) {
|
||||
//System.out.println("Segmenter process: saveRawGroupData(int i)");
|
||||
saveRawGroupData(i, false);
|
||||
}
|
||||
|
||||
|
@ -1,23 +1,33 @@
|
||||
package test.rawDeepLearningClassifier;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import org.jamdev.jdl4pam.transforms.DLTransform;
|
||||
import org.jamdev.jdl4pam.transforms.DLTransformsFactory;
|
||||
import org.jamdev.jdl4pam.transforms.DLTransfromParams;
|
||||
import org.jamdev.jdl4pam.transforms.SimpleTransformParams;
|
||||
import org.jamdev.jdl4pam.transforms.WaveTransform;
|
||||
import org.jamdev.jdl4pam.utils.DLMatFile;
|
||||
import org.jamdev.jpamutils.wavFiles.AudioData;
|
||||
import org.jamdev.jdl4pam.transforms.DLTransform.DLTransformType;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import PamUtils.PamArrayUtils;
|
||||
import rawDeepLearningClassifier.dlClassification.animalSpot.StandardModelParams;
|
||||
import rawDeepLearningClassifier.dlClassification.archiveModel.ArchiveModelWorker;
|
||||
import rawDeepLearningClassifier.dlClassification.genericModel.GenericModelParams;
|
||||
import rawDeepLearningClassifier.dlClassification.genericModel.GenericModelWorker;
|
||||
import rawDeepLearningClassifier.dlClassification.genericModel.StandardPrediction;
|
||||
import rawDeepLearningClassifier.segmenter.GroupedRawData;
|
||||
import us.hebi.matlab.mat.format.Mat5;
|
||||
import us.hebi.matlab.mat.format.Mat5File;
|
||||
import us.hebi.matlab.mat.types.MatFile;
|
||||
import us.hebi.matlab.mat.types.Matrix;
|
||||
import us.hebi.matlab.mat.types.Struct;
|
||||
|
||||
@ -26,18 +36,231 @@ import us.hebi.matlab.mat.types.Struct;
|
||||
* Model from Thomas webber which is a good way to test the click based stuff is working in PAMGUard.
|
||||
*/
|
||||
public class ClickDLTest {
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Test just one click using the zipped classifier
|
||||
* @throws
|
||||
*/
|
||||
@Test
|
||||
public void clickDLTest() {
|
||||
public void aclickDLTestZip() {
|
||||
|
||||
System.out.println("*****CLickDLTest: Single click test zip*****");
|
||||
|
||||
float SAMPLE_RATE = 500000;
|
||||
//relative paths to the resource folders.
|
||||
String relModelPath = "/home/jamiemac/Dropbox/PAMGuard_dev/Deep_Learning/click_classifier_Thomas/model_v2/model_pb.zip";
|
||||
String clicksPath = "/home/jamiemac/Dropbox/PAMGuard_dev/Deep_Learning/click_classifier_Thomas/model_v2/example_2000021.mat";
|
||||
|
||||
// String matout = "/home/jamiemac/MATLAB-Drive/MATLAB/PAMGUARD/deep_learning/generic_classifier/example_2000021_transforms.mat";
|
||||
String matout=null;
|
||||
// load the click data up.
|
||||
Path clkPath = Paths.get(clicksPath);
|
||||
PredGroupedRawData clickData = null;
|
||||
|
||||
Struct matclkStruct = Mat5.newStruct();
|
||||
try {
|
||||
Mat5File mfr = Mat5.readFromFile(clkPath.toAbsolutePath().normalize().toString());
|
||||
|
||||
// //get array of a name "my_array" from file
|
||||
Struct mlArrayRetrived = mfr.getStruct( "newStruct" );
|
||||
|
||||
Matrix clickWavM = mlArrayRetrived.get("wave", 0);
|
||||
|
||||
double[][] clickWaveform= PamArrayUtils.matrix2array(clickWavM);
|
||||
clickWaveform=PamArrayUtils.transposeMatrix(clickWaveform);
|
||||
|
||||
Matrix clickUID= mlArrayRetrived.get("UID", 0);
|
||||
Matrix pred= mlArrayRetrived.get("pred", 0);
|
||||
|
||||
//create a click object whihc we can pass through transforms etc.
|
||||
clickData = new PredGroupedRawData(0L, 1, 0, clickWaveform[0].length, clickWaveform[0].length);
|
||||
clickData.setUID(clickUID.getLong(0));
|
||||
clickData.setRawData(clickWaveform);
|
||||
clickData.setPrediction(new double[] {pred.getDouble(0)});
|
||||
|
||||
// load the model up
|
||||
Path path = Paths.get(relModelPath);
|
||||
|
||||
ArchiveModelWorker genericModelWorker = new ArchiveModelWorker();
|
||||
|
||||
StandardModelParams genericModelParams = new StandardModelParams();
|
||||
genericModelParams.modelPath = path.toAbsolutePath().normalize().toString();
|
||||
|
||||
//prep the model - all setting are included within the model
|
||||
genericModelWorker.prepModel(genericModelParams, null);
|
||||
System.out.println("seglen: " + genericModelParams.defaultSegmentLen);
|
||||
|
||||
ArrayList<GroupedRawData> groupedData = new ArrayList<GroupedRawData>();
|
||||
groupedData.add(clickData);
|
||||
|
||||
System.out.println("Waveform input: " + groupedData.get(0).getRawData().length + " " + groupedData.get(0).getRawData()[0].length);
|
||||
|
||||
ArrayList<StandardPrediction> genericPrediction = genericModelWorker.runModel(groupedData,96000, 0);
|
||||
|
||||
float[] outputPAMGuard = genericPrediction.get(0).getPrediction();
|
||||
|
||||
System.out.println("Model output PAMGuard: " + outputPAMGuard[0]);
|
||||
assertEquals(outputPAMGuard[0], 0.99, 0.05);
|
||||
|
||||
}
|
||||
catch (Exception e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
assertTrue(false); //make sure the unit test fails
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test just one click
|
||||
* @throws
|
||||
*/
|
||||
@Test
|
||||
public void aclickDLTest() {
|
||||
|
||||
System.out.println("*****CLickDLTest: Single click test*****");
|
||||
|
||||
//relative paths to the resource folders.
|
||||
System.out.println("*****Click classification Deep Learning C*****");
|
||||
|
||||
// //relative paths to the resource folders.
|
||||
// String relModelPath = "/Users/jdjm/Library/CloudStorage/Dropbox/PAMGuard_dev/Deep_Learning/click_classifier_Thomas/model_v2/model_pb/saved_model.pb";
|
||||
// String clicksPath = "/Users/jdjm/Library/CloudStorage/Dropbox/PAMGuard_dev/Deep_Learning/click_classifier_Thomas/model_v2/example_2000021.mat";
|
||||
|
||||
//relative paths to the resource folders.
|
||||
String relModelPath = "./src/test/resources/rawDeepLearningClassifier/Generic/risso_click/uniform_model/saved_model.pb";
|
||||
String clicksPath = "./src/test/resources/rawDeepLearningClassifier/Generic/risso_click/clicks.mat";
|
||||
String relModelPath = "/home/jamiemac/Dropbox/PAMGuard_dev/Deep_Learning/click_classifier_Thomas/model_v2/model_pb/saved_model.pb";
|
||||
String clicksPath = "/home/jamiemac/Dropbox/PAMGuard_dev/Deep_Learning/click_classifier_Thomas/model_v2/example_2000021.mat";
|
||||
//load the click up
|
||||
|
||||
// String matout = "/home/jamiemac/MATLAB-Drive/MATLAB/PAMGUARD/deep_learning/generic_classifier/example_2000021_transforms.mat";
|
||||
String matout=null;
|
||||
// load the click data up.
|
||||
Path clkPath = Paths.get(clicksPath);
|
||||
PredGroupedRawData clickData = null;
|
||||
|
||||
Struct matclkStruct = Mat5.newStruct();
|
||||
try {
|
||||
Mat5File mfr = Mat5.readFromFile(clkPath.toAbsolutePath().normalize().toString());
|
||||
|
||||
// //get array of a name "my_array" from file
|
||||
Struct mlArrayRetrived = mfr.getStruct( "newStruct" );
|
||||
|
||||
|
||||
Matrix clickWavM = mlArrayRetrived.get("wave", 0);
|
||||
Matrix modelInputM= mlArrayRetrived.get("wave_pad", 0);
|
||||
|
||||
double[][] clickWaveform= PamArrayUtils.matrix2array(clickWavM);
|
||||
clickWaveform=PamArrayUtils.transposeMatrix(clickWaveform);
|
||||
|
||||
//get the raw model input so we can test the model directly.
|
||||
double[][] pythonModelInput= PamArrayUtils.matrix2array(modelInputM);
|
||||
pythonModelInput = PamArrayUtils.transposeMatrix(pythonModelInput);
|
||||
float[] pythonModelInputF = PamArrayUtils.double2Float(pythonModelInput[0]);
|
||||
|
||||
Matrix clickUID= mlArrayRetrived.get("UID", 0);
|
||||
Matrix pred= mlArrayRetrived.get("pred", 0);
|
||||
|
||||
//create a click object whihc we can pass through transforms etc.
|
||||
clickData = new PredGroupedRawData(0L, 1, 0, clickWaveform[0].length, clickWaveform[0].length);
|
||||
clickData.setUID(clickUID.getLong(0));
|
||||
clickData.setRawData(clickWaveform);
|
||||
clickData.setPrediction(new double[] {pred.getDouble(0)});
|
||||
|
||||
|
||||
// load the model up
|
||||
Path path = Paths.get(relModelPath);
|
||||
|
||||
GenericModelWorker genericModelWorker = new GenericModelWorker();
|
||||
|
||||
GenericModelParams genericModelParams = new GenericModelParams();
|
||||
genericModelParams.modelPath = path.toAbsolutePath().normalize().toString();
|
||||
|
||||
|
||||
//create the transforms.
|
||||
ArrayList<DLTransfromParams> dlTransformParamsArr = new ArrayList<DLTransfromParams>();
|
||||
|
||||
//waveform transforms.
|
||||
dlTransformParamsArr.add(new SimpleTransformParams(DLTransformType.DECIMATE_SCIPY, 96000.));
|
||||
dlTransformParamsArr.add(new SimpleTransformParams(DLTransformType.NORMALISE_WAV, 0., 1, AudioData.ZSCORE)); //needs to be here
|
||||
dlTransformParamsArr.add(new SimpleTransformParams(DLTransformType.PEAK_TRIM, 64, 1));
|
||||
|
||||
genericModelParams.dlTransfromParams = dlTransformParamsArr;
|
||||
genericModelParams.dlTransfroms = DLTransformsFactory.makeDLTransforms((ArrayList<DLTransfromParams>)genericModelParams.dlTransfromParams);
|
||||
|
||||
//create the clicks.
|
||||
path = Paths.get(clicksPath);
|
||||
|
||||
//prep the model
|
||||
genericModelWorker.prepModel(genericModelParams, null);
|
||||
|
||||
ArrayList<GroupedRawData> groupedData = new ArrayList<GroupedRawData>();
|
||||
groupedData.add(clickData);
|
||||
|
||||
System.out.println("Waveform input: " + groupedData.get(0).getRawData().length + " " + groupedData.get(0).getRawData()[0].length);
|
||||
|
||||
ArrayList<StandardPrediction> genericPrediction = genericModelWorker.runModel(groupedData,96000, 0);
|
||||
|
||||
// System.out.println("PAMGuard input len: " + pythonModelInputF.length);
|
||||
|
||||
float[] outputPAMGuard = genericPrediction.get(0).getPrediction();
|
||||
|
||||
System.out.println("Model output PAMGuard: " + outputPAMGuard[0]);
|
||||
|
||||
//run the transforms so we can take a look at the inpout
|
||||
((WaveTransform) genericModelParams.dlTransfroms.get(0)).setWaveData(new AudioData(groupedData.get(0).getRawData()[0], 248000));;
|
||||
//create the transformed wave
|
||||
DLTransform transform = genericModelParams.dlTransfroms.get(0);
|
||||
double[] audioOut = null;
|
||||
for (int i=0; i<genericModelParams.dlTransfroms .size(); i++) {
|
||||
transform = genericModelParams.dlTransfroms.get(i).transformData(transform);
|
||||
audioOut = ((WaveTransform) transform).getWaveData().getScaledSampleAmplitudes();
|
||||
matclkStruct.set(transform.getDLTransformType().getJSONString(), DLMatFile.array2Matrix(audioOut));
|
||||
}
|
||||
|
||||
//RUN THE RAW MODEL with Python transformed input
|
||||
|
||||
// System.out.println("Python input len: " + pythonModelInputF.length);
|
||||
// float[] outPutPython = genericModelWorker.getModel().runModel(new float[][] {PamArrayUtils.double2Float(audioOut)});
|
||||
|
||||
float[] outPutPython = genericModelWorker.getModel().runModel(new float[][] {pythonModelInputF});
|
||||
|
||||
System.out.println("Model output Python: " + outPutPython[0]);
|
||||
|
||||
assertEquals(outputPAMGuard[0], outPutPython[0], 0.05);
|
||||
|
||||
}
|
||||
catch (Exception e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
assertTrue(false); //make sure the unit test fails
|
||||
return;
|
||||
}
|
||||
|
||||
if (matout!=null) {
|
||||
// Create MAT file with a scalar in a nested struct
|
||||
MatFile matFile = Mat5.newMatFile()
|
||||
.addArray("click_transforms", matclkStruct);
|
||||
// Serialize to disk using default configurations
|
||||
try {
|
||||
Mat5.writeToFile(matFile, matout);
|
||||
} catch (IOException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Test
|
||||
public void clicksDLTest() {
|
||||
|
||||
float SAMPLE_RATE = 96000;
|
||||
//relative paths to the resource folders.
|
||||
System.out.println("*****CLickDLTest: Clicks test*****");
|
||||
|
||||
//relative paths to the resource folders.
|
||||
String relModelPath = "/home/jamiemac/Dropbox/PAMGuard_dev/Deep_Learning/click_classifier_Thomas/model_v2/model_pb/saved_model.pb";
|
||||
String clicksPath = "/home/jamiemac/Dropbox/PAMGuard_dev/Deep_Learning/click_classifier_Thomas/model_v2/Click_Detector_Click_Detector_Clicks_20220603_111000_classified.mat";
|
||||
|
||||
Path path = Paths.get(relModelPath);
|
||||
|
||||
@ -46,59 +269,67 @@ public class ClickDLTest {
|
||||
GenericModelParams genericModelParams = new GenericModelParams();
|
||||
genericModelParams.modelPath = path.toAbsolutePath().normalize().toString();
|
||||
|
||||
|
||||
|
||||
//create the transforms.
|
||||
ArrayList<DLTransfromParams> dlTransformParamsArr = new ArrayList<DLTransfromParams>();
|
||||
|
||||
//waveform transforms.
|
||||
dlTransformParamsArr.add(new SimpleTransformParams(DLTransformType.DECIMATE_SCIPY, 248000.));
|
||||
dlTransformParamsArr.add(new SimpleTransformParams(DLTransformType.PEAK_TRIM, 128, 1));
|
||||
dlTransformParamsArr.add(new SimpleTransformParams(DLTransformType.DECIMATE_SCIPY, 96000.));
|
||||
dlTransformParamsArr.add(new SimpleTransformParams(DLTransformType.NORMALISE_WAV, 0., 1, AudioData.ZSCORE));
|
||||
|
||||
dlTransformParamsArr.add(new SimpleTransformParams(DLTransformType.PEAK_TRIM, 64, 1));
|
||||
|
||||
genericModelParams.dlTransfromParams = dlTransformParamsArr;
|
||||
genericModelParams.dlTransfroms = DLTransformsFactory.makeDLTransforms((ArrayList<DLTransfromParams>)genericModelParams.dlTransfromParams);
|
||||
|
||||
|
||||
//create the clicks.
|
||||
path = Paths.get(clicksPath);
|
||||
ArrayList<PredGroupedRawData> clicks = importClicks(path.toAbsolutePath().normalize().toString(), SAMPLE_RATE);
|
||||
|
||||
ArrayList<PredGroupedRawData> clicks = importClicks(path.toAbsolutePath().normalize().toString(), SAMPLE_RATE);
|
||||
|
||||
//prep the model
|
||||
genericModelWorker.prepModel(genericModelParams, null);
|
||||
|
||||
System.out.println("Model has loaded");
|
||||
|
||||
ArrayList<GroupedRawData> groupedData = new ArrayList<GroupedRawData>();
|
||||
|
||||
for (int i=0; i<1; i++) {
|
||||
|
||||
System.out.println("Model has loaded: n clicks " + clicks.size());
|
||||
|
||||
float count = 0;
|
||||
long timeStart = System.currentTimeMillis();
|
||||
for (int i=0; i<clicks.size(); i++) {
|
||||
|
||||
float prediction = (float) clicks.get(i).getPrediction()[0];
|
||||
|
||||
|
||||
ArrayList<GroupedRawData> groupedData = new ArrayList<GroupedRawData>();
|
||||
groupedData.add(clicks.get(i)); //TODO for loop
|
||||
|
||||
//System.out.println("Waveform input: " + groupedData.get(i).getRawData().length + " " + groupedData.get(i).getRawData()[0].length);
|
||||
|
||||
ArrayList<StandardPrediction> genericPrediction = genericModelWorker.runModel(groupedData,SAMPLE_RATE, 0);
|
||||
|
||||
float[] output = genericPrediction.get(i).getPrediction();
|
||||
|
||||
System.out.println(String.format("Click %d Predicted output: %.2f true output: %.2f passed: %b", clicks.get(i).getUID(),
|
||||
output[0], prediction, output[0]>prediction*0.9 && output[0]<prediction*1.1));
|
||||
|
||||
}
|
||||
|
||||
|
||||
//System.out.println("Waveform input: " + groupedData.get(i).getRawData().length + " " + groupedData.get(i).getRawData()[0].length);
|
||||
|
||||
ArrayList<StandardPrediction> genericPrediction = genericModelWorker.runModel(groupedData,SAMPLE_RATE, 0);
|
||||
|
||||
float[] output = genericPrediction.get(0).getPrediction();
|
||||
|
||||
System.out.println(String.format("Click %d Predicted output: %.4f true output: %.4f passed: %b delta %.2f", clicks.get(i).getUID(),
|
||||
output[0], prediction, output[0]>prediction*0.9 && output[0]<prediction*1.1, (Math.abs(output[0] -prediction))));
|
||||
|
||||
if (output[0]>prediction*0.9 && output[0]<prediction*1.1) {
|
||||
count++;
|
||||
}
|
||||
|
||||
}
|
||||
long timeEnd = System.currentTimeMillis();
|
||||
|
||||
double perctrue = count/clicks.size();
|
||||
|
||||
System.out.println(String.format("Percentage clicks passed: %.2f TIme to process %d clicks - %2f seconds", perctrue, clicks.size(), ((double) (timeEnd-timeStart))/1000.));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Import a bunch of clicks from a .mat file
|
||||
*/
|
||||
public static ArrayList<PredGroupedRawData> importClicks(String filePath, float sR) {
|
||||
|
||||
try {
|
||||
Mat5File mfr = Mat5.readFromFile(filePath);
|
||||
Mat5File mfr = Mat5.readFromFile(filePath);
|
||||
|
||||
// //get array of a name "my_array" from file
|
||||
Struct mlArrayRetrived = mfr.getStruct( "clickpreds" );
|
||||
Struct mlArrayRetrived = mfr.getStruct( "binarydata" );
|
||||
|
||||
int numClicks= mlArrayRetrived.getNumCols();
|
||||
ArrayList<PredGroupedRawData> clicks = new ArrayList<PredGroupedRawData>(numClicks);
|
||||
@ -106,12 +337,12 @@ public class ClickDLTest {
|
||||
PredGroupedRawData clickData;
|
||||
for (int i=0; i<numClicks; i++) {
|
||||
Matrix clickWav= mlArrayRetrived.get("wave", i);
|
||||
|
||||
|
||||
double[][] clickwaveform= PamArrayUtils.matrix2array(clickWav);
|
||||
|
||||
|
||||
clickwaveform = PamArrayUtils.transposeMatrix(clickwaveform);
|
||||
//System.out.println("click: " + click[0].length + " num: " + numClicks);
|
||||
|
||||
|
||||
Matrix clickUID= mlArrayRetrived.get("UID", i);
|
||||
Matrix clickmillis= mlArrayRetrived.get("millis", i);
|
||||
Matrix channelMap= mlArrayRetrived.get("channelMap", i);
|
||||
@ -122,11 +353,11 @@ public class ClickDLTest {
|
||||
clickData = new PredGroupedRawData(clickmillis.getLong(0), channelMap.getInt(0), startSample.getLong(0), sampleDuration.getLong(0), sampleDuration.getInt(0));
|
||||
clickData.setUID(clickUID.getLong(0));
|
||||
clickData.setRawData(clickwaveform);
|
||||
clickData.setPrediction(new double[] {pred.getDouble(0), pred.getDouble(1)});
|
||||
|
||||
clickData.setPrediction(new double[] {pred.getDouble(0)});
|
||||
|
||||
clicks.add(clickData);
|
||||
}
|
||||
|
||||
|
||||
return clicks;
|
||||
}
|
||||
catch (Exception e) {
|
||||
@ -135,11 +366,11 @@ public class ClickDLTest {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static class PredGroupedRawData extends GroupedRawData {
|
||||
|
||||
|
||||
private double[] prediction;
|
||||
|
||||
|
||||
public double[] getPrediction() {
|
||||
return prediction;
|
||||
}
|
||||
@ -151,10 +382,10 @@ public class ClickDLTest {
|
||||
public PredGroupedRawData(long timeMilliseconds, int channelBitmap, long startSample, long duration, int samplesize) {
|
||||
super(timeMilliseconds, channelBitmap, startSample, duration, samplesize);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user