Skip to content

Commit

Permalink
clutch of new models,
Browse files Browse the repository at this point in the history
changes to make convert specs work
some efficiencies in the model code
  • Loading branch information
Mattk70 committed Dec 11, 2024
1 parent ccddbee commit f8f6036
Show file tree
Hide file tree
Showing 54 changed files with 158 additions and 57 deletions.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.

Large diffs are not rendered by default.

Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.

Large diffs are not rendered by default.

Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.

Large diffs are not rendered by default.

Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.

Large diffs are not rendered by default.

Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.

Large diffs are not rendered by default.

Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion chirpity_model_config.json

Large diffs are not rendered by default.

22 changes: 17 additions & 5 deletions js/BirdNet2.4.js
Original file line number Diff line number Diff line change
Expand Up @@ -91,8 +91,7 @@ onmessage = async (e) => {
return myModel.makeSpectrogram(bufferTensor);
});
let spec = myModel.fixUpSpecBatch(tf.expandDims(imageTensor, 0), spec_height, spec_width);
const spec_max = tf.max(spec);
return spec.mul(255).div(spec_max).dataSync();
return spec.dataSync();
});

response = {
Expand Down Expand Up @@ -197,13 +196,26 @@ class Model {
*/
//specBatch = tf.log1p(specBatch).mul(20);
// Swap axes to fit output shape

specBatch = tf.transpose(specBatch, [0, 2, 1]);
specBatch = tf.reverse(specBatch, [1]);
// Add channel axis
specBatch = tf.expandDims(specBatch, -1);

//specBatch = tf.slice4d(specBatch, [0, 1, 0, 0], [-1, img_height, img_width, -1]);
specBatch = tf.image.resizeBilinear(specBatch, [img_height, img_width], true);
return this.normalise(specBatch)
//specBatch = tf.image.resizeBilinear(specBatch, [img_height, img_width], true);
// Slice to exclude the bottom 10 rows
const sliced_tensor = tf.slice3d(specBatch, [0, 1, 0], [-1, img_height - 10, img_width])

// Slice the bottom 10 rows
const bottomRows = tf.slice3d(sliced_tensor, [0, img_height - 20, 0], [-1, 10, img_width]);

// Reduce the values of the bottom rows
const attenuatedRows = bottomRows.div(tf.scalar(5));

// Concatenate the sliced tensor with the bottom rows
specBatch = tf.concat([sliced_tensor, attenuatedRows], 1)
specBatch = this.normalise(specBatch)
return tf.expandDims(specBatch, -1);
})
}
normalise(spec) {
Expand Down
29 changes: 19 additions & 10 deletions js/model.js
Original file line number Diff line number Diff line change
Expand Up @@ -120,8 +120,7 @@ onmessage = async (e) => {
return myModel.makeSpectrogram(bufferTensor);
});
let spec = myModel.fixUpSpecBatch(tf.expandDims(imageTensor, 0), spec_height, spec_width);
const spec_max = tf.max(spec);
return spec.mul(255).div(spec_max).dataSync();
return spec.dataSync();
});
response = {
message: "spectrogram",
Expand Down Expand Up @@ -365,13 +364,26 @@ class Model {
*/
//specBatch = tf.log1p(specBatch).mul(20);
// Swap axes to fit output shape

specBatch = tf.transpose(specBatch, [0, 2, 1]);
specBatch = tf.reverse(specBatch, [1]);
// Add channel axis
specBatch = tf.expandDims(specBatch, -1);

//specBatch = tf.slice4d(specBatch, [0, 1, 0, 0], [-1, img_height, img_width, -1]);
specBatch = tf.image.resizeBilinear(specBatch, [img_height, img_width], true);
return this.normalise(specBatch)
//specBatch = tf.image.resizeBilinear(specBatch, [img_height, img_width], true);
// Slice to exclude the bottom 10 rows
const sliced_tensor = tf.slice3d(specBatch, [0, 1, 0], [-1, img_height - 10, img_width])

// Slice the bottom 10 rows
const bottomRows = tf.slice3d(sliced_tensor, [0, img_height - 20, 0], [-1, 10, img_width]);

// Reduce the values of the bottom rows
const attenuatedRows = bottomRows.div(tf.scalar(5));

// Concatenate the sliced tensor with the bottom rows
specBatch = tf.concat([sliced_tensor, attenuatedRows], 1)
specBatch = this.normalise(specBatch)
return tf.expandDims(specBatch, -1);
})
}
normalise_audio_batch = (tensor) => {
Expand Down Expand Up @@ -415,11 +427,8 @@ class Model {
audioBuffer.dispose();
const bufferList = this.version !== 'v4' ? this.normalise_audio_batch(buffers) : buffers;
const specBatch = tf.tidy(() => {
const bufferArray = tf.unstack(bufferList);
const toStack = bufferArray.map(x => {
return this.makeSpectrogram(x)
})
return this.fixUpSpecBatch(tf.stack(toStack))
const toStack = tf.unstack(bufferList).map(x => this.makeSpectrogram(x));
return this.fixUpSpecBatch(tf.stack(toStack));
});
buffers.dispose();
bufferList.dispose();
Expand Down
4 changes: 2 additions & 2 deletions js/ui.js
Original file line number Diff line number Diff line change
Expand Up @@ -2791,7 +2791,7 @@ function centreSpec(){
}
},
D: function (e) {
if (( e.ctrlKey || e.metaKey)) worker.postMessage({ action: 'create-dataset' });
if (( e.ctrlKey || e.metaKey)) worker.postMessage({ action: 'convert-dataset' });
},
e: function (e) {
if (( e.ctrlKey || e.metaKey) && region) exportAudio();
Expand Down Expand Up @@ -3105,7 +3105,7 @@ function centreSpec(){
metadata = undefined
}) {
fileLoaded = true, locationID = location;
bufferBegin = fileBegin;

clearTimeout(loadingTimeout)
// Clear the loading animation
DOM.loading.classList.add('d-none');
Expand Down
149 changes: 112 additions & 37 deletions js/worker.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ const ffmpeg = require('fluent-ffmpeg');
const png = require('fast-png');
const {utimesSync} = require('utimes');
const {writeToPath} = require('@fast-csv/format');

const { execSync } = require('child_process');

const merge = require('lodash.merge');
import { State } from './state.js';
import { sqlite3 } from './database.js';
Expand All @@ -16,9 +19,9 @@ import {extractWaveMetadata} from './metadata.js';
let isWin32 = false;

const DATASET = true;
const DATABASE = 'archive_test'
const adding_chirpity_additions = false;
const dataset_database = DATASET;
const DATASET_SAVE_LOCATION = "/media/matt/36A5CC3B5FA24585/DATASETS/MISSING_PNGS";
const DATASET_SAVE_LOCATION = "/media/matt/36A5CC3B5FA24585/DATASETS/ATTENUATED_pngs/call";
let ntsuspend;
if (process.platform === 'win32') {
ntsuspend = require('ntsuspend');
Expand Down Expand Up @@ -194,7 +197,7 @@ const createDB = async (file) => {

if (archiveMode) {
for (let i = 0; i < LABELS.length; i++) {
const [sname, cname] = LABELS[i].replaceAll("'", "''").split('_');
const [sname, cname] = LABELS[i].split('_');
await db.runAsync('INSERT INTO species VALUES (?,?,?)', i, sname, cname);
}
} else {
Expand Down Expand Up @@ -242,7 +245,7 @@ async function loadDB(path) {
modelLabels.push("Unknown Sp._Unknown Sp.");
const num_labels = modelLabels.length;
LABELS = modelLabels; // these are the default english labels
const file = dataset_database ? p.join(path, `archive_dataset${num_labels}.sqlite`) : p.join(path, `archive${num_labels}.sqlite`)
const file = DATASET ? p.join(path, `${DATABASE}${num_labels}.sqlite`) : p.join(path, `archive${num_labels}.sqlite`)

if (!fs.existsSync(file)) {
await createDB(file);
Expand Down Expand Up @@ -1257,41 +1260,37 @@ function checkBacklog(ffmpegCommand = null) {
console.warn('Ffmpeg process already exited in check backlog call')
return
}
let firstRun = true, hysteresis_factor = 2;
const hysteresis_factor = 2;

if (!aborted && AUDIO_BACKLOG < NUM_WORKERS * hysteresis_factor && ! STATE.processingPaused[pid]) {
console.log('no need to check further, backlog: ', AUDIO_BACKLOG, ' pid paused: ', pid, STATE.processingPaused[pid])
resolve(true);
return;
}

const interval = setInterval(() => {
const backlog = AUDIO_BACKLOG;
console.log('Backlog in interval is:', backlog)
if (aborted) {
console.log('clearing interval, aborted')
clearInterval(interval);
resolve(false);
return;
}

if (firstRun) {
DEBUG && console.log('backlog (initial):', backlog);
firstRun = false; // Ensure this logs only on the first call
}

if (backlog >= NUM_WORKERS * hysteresis_factor) {
if (! STATE.processingPaused[pid]) {
if (! STATE.processingPaused[pid] && backlog >= NUM_WORKERS * hysteresis_factor) {
pauseFfmpeg(ffmpegCommand, pid);
DEBUG && console.log(`pause signal sent to ffmpeg(${pid}), backlog: ${backlog}`);
STATE.processingPaused[pid] = true;
}
} else if (backlog <= NUM_WORKERS) {
DEBUG && console.log('backlog (final):', backlog);
if (STATE.processingPaused[pid]) {
resumeFfmpeg(ffmpegCommand, pid);
DEBUG && console.log(`resume signal sent to ffmpeg(${pid}), backlog: ${backlog}`);
STATE.processingPaused[pid] = false;
}
clearInterval(interval); // Backlog is within limits, stop checking
resolve(true); // Resolve the promise
}
else if (backlog <= NUM_WORKERS) {
DEBUG && console.log('backlog (final):', backlog);
if (STATE.processingPaused[pid]) {
resumeFfmpeg(ffmpegCommand, pid);
DEBUG && console.log(`resume signal sent to ffmpeg(${pid}), backlog: ${backlog}`);
STATE.processingPaused[pid] = false;
}
console.log('clearing interval, pressure eased')
clearInterval(interval); // Backlog is within limits, stop checking
resolve(true); // Resolve the promise
}
}, 200);
});
Expand All @@ -1304,6 +1303,7 @@ function pauseFfmpeg(ffmpegCommand, pid){
DEBUG && console.log(message)
} else {
ffmpegCommand.kill('SIGSTOP')
console.log('paused ', pid)
}
}

Expand All @@ -1314,6 +1314,7 @@ function resumeFfmpeg(ffmpegCommand, pid){
DEBUG && console.log(message)
} else {
ffmpegCommand.kill('SIGCONT')
console.log('resumded ', pid)
}
}

Expand Down Expand Up @@ -1358,6 +1359,8 @@ const getPredictBuffers = async ({
}

async function processAudio (file, start, end, chunkStart, highWaterMark, samplesInBatch){
const MAX_CHUNKS = NUM_WORKERS * 2;
let isPaused = false;
return new Promise((resolve, reject) => {
// Many compressed files start with a small section of silence due to encoder padding, which affects predictions
// To compensate, we move the start back a small amount, and slice the data to remove the silence
Expand All @@ -1378,12 +1381,27 @@ async function processAudio (file, start, end, chunkStart, highWaterMark, sample
reject(error)
}
});
command.on('progress', (progress) => {
DEBUG && console.log('progress: ', progress.timemark)
checkBacklog(command)
})
// command.on('progress', (progress) => {
// DEBUG && console.log('progress: ', progress.timemark)
// checkBacklog(command)
// })
const STREAM = command.pipe();
STREAM.on('data', (chunk) => {
if (! isPaused && AUDIO_BACKLOG >= MAX_CHUNKS){
isPaused = true;
const pid = command.ffmpegProc?.pid
pid && pauseFfmpeg(command, pid)
console.log('about to set the interval ', pid)
const interval = setInterval(() =>{
console.log('Backlog', AUDIO_BACKLOG)
if (AUDIO_BACKLOG < NUM_WORKERS){
resumeFfmpeg(command, pid)
console.log('resumed ', pid)
isPaused = false;
clearInterval(interval)
}
}, 100)
}
if (aborted) {
STREAM.destroy();
return;
Expand Down Expand Up @@ -1614,29 +1632,86 @@ const speciesMatch = (path, sname) => {
return species.includes(sname)
}

function findFile(pathParts, filename, species) {
const baseDir = pathParts.slice(0, 5).concat(['XC_ALL_mp3']).join(p.sep);

// List of suffixes to check, in order
const suffixes = ['', ' (call)', ' (fc)', ' (nfc)', ' (song)'];

// Extract existing suffix from species, if present
const suffixPattern = / \((call|fc|nfc|song)\)$/;
let speciesBase = species;
let existingSuffix = '';

const match = species.match(suffixPattern);
if (match) {
existingSuffix = match[0]; // e.g., " (call)"
speciesBase = species.replace(suffixPattern, ''); // Remove suffix
}

// First, check the species with its existing suffix
if (existingSuffix) {
const folder = p.join(baseDir, species);
const filePath = p.join(folder, filename + '.mp3');
if (fs.existsSync(filePath)) {
console.log(`File found: ${filePath}`);
return [filePath, species];
}
}

// Check species with other suffixes, removing the existing one
for (const suffix of suffixes) {
if (suffix === existingSuffix) continue; // Skip the suffix already checked
const found_calltype = speciesBase + suffix
const folder = p.join(baseDir, found_calltype);
const filePath = p.join(folder, filename + '.mp3');
if (fs.existsSync(filePath)) {
console.log(`File found: ${filePath}`);

return [filePath, found_calltype];
}
}

console.log('File not found in any directory');
return [null, null];
}
const convertSpecsFromExistingSpecs = async (path) => {
path ??= '/mnt/608E21D98E21A88C/Users/simpo/PycharmProjects/Data/New_Dataset';
path ??= '/media/matt/36A5CC3B5FA24585/DATASETS/MISSING/NEW_DATASET_WITHOUT_ALSO_MERGED';
const file_list = await getFiles([path], true);
for (let i = 0; i < file_list.length; i++) {
if (i % 100 === 0){
console.log(`${i} records processed`)
}
const parts = p.parse(file_list[i]);
let species = parts.dir.split(p.sep);
species = species[species.length - 1];
let path_parts = parts.dir.split(p.sep);
let species = path_parts[path_parts.length - 1];
const species_parts = species.split('~');
species = species_parts[1] + '~' + species_parts[0]
const [filename, time] = parts.name.split('_');
const [start, end] = time.split('-');
const path_to_save = path.replace('New_Dataset', 'New_Dataset_Converted') + p.sep + species;
const file_to_save = p.join(path_to_save, parts.base);
// const path_to_save = path.replace('New_Dataset', 'New_Dataset_Converted') + p.sep + species;
let path_to_save = '/media/matt/36A5CC3B5FA24585/DATASETS/ATTENUATED_pngs/converted' + p.sep + species;
let file_to_save = p.join(path_to_save, parts.base);
if (fs.existsSync(file_to_save)) {
DEBUG && console.log("skipping file as it is already saved")
} else {
const file_to_analyse = parts.dir.replace('New_Dataset', 'XC_ALL_mp3') + p.sep + filename + '.mp3';
const AudioBuffer = await fetchAudioBuffer({
const [file_to_analyse, confirmed_species_folder] = findFile(path_parts, filename, species)
path_to_save = path_to_save.replace(species, confirmed_species_folder)
file_to_save = p.join(path_to_save, parts.base);
if (fs.existsSync(file_to_save)){
console.log("skipping file as it is already saved")
continue;
}
if (! file_to_analyse) continue
//parts.dir.replace('MISSING/NEW_DATASET_WITHOUT_ALSO_MERGED', 'XC_ALL_mp3') + p.sep + filename + '.mp3';
const [AudioBuffer, begin] = await fetchAudioBuffer({
start: parseFloat(start), end: parseFloat(end), file: file_to_analyse
})
if (AudioBuffer) { // condition to prevent barfing when audio snippet is v short i.e. fetchAudioBUffer false when < 0.1s
if (++workerInstance === NUM_WORKERS) {
workerInstance = 0;
}
const buffer = AudioBuffer.getChannelData(0);
const buffer = getMonoChannelData(AudioBuffer);
predictWorkers[workerInstance].postMessage({
message: 'get-spectrogram',
filepath: path_to_save,
Expand Down Expand Up @@ -3221,7 +3296,7 @@ async function onChartRequest(args) {
const pointStart = dateRange.start ??= Date.UTC(2020, 0, 0, 0, 0, 0);
UI.postMessage({
event: 'chart-data', // Restore species name
species: args.species ? args.species.replace("''", "'") : undefined,
species: args.species ? args.species : undefined,
results: results,
rate: rate,
total: total,
Expand Down
2 changes: 1 addition & 1 deletion labels/V2.4/BirdNET_GLOBAL_6K_V2.4_Labels_en.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1722,7 +1722,7 @@ Cuculus optatus_Oriental Cuckoo
Cuculus poliocephalus_Lesser Cuckoo
Cuculus rochii_Madagascar Cuckoo
Cuculus saturatus_Himalayan Cuckoo
Cuculus solitarius_Red-chested Cuckoo
Cuculus solitarius_Red-chestiberiaeed Cuckoo
Culicicapa ceylonensis_Gray-headed Canary-Flycatcher
Culicicapa helianthea_Citrine Canary-Flycatcher
Culicivora caudacuta_Sharp-tailed Tyrant
Expand Down

0 comments on commit f8f6036

Please sign in to comment.