diff --git a/Help/settings.html b/Help/settings.html index b267bead..f2bff590 100644 --- a/Help/settings.html +++ b/Help/settings.html @@ -17,20 +17,9 @@ A value around 50% provides a good balance between detecting genuine calls and minimizing false positives. - - - - - - - - - - - Search for -

Birds in your Region. Exclude birds unlikely to be found in your location.

+

Local Birds. Exclude birds unlikely to be found in your location.

Nocturnal Birds. Just report detections relating to these species' calls (song detections are excluded).

All birds. Include birdsong as well as calls, also includes species not known to call at night. diff --git a/css/style.css b/css/style.css index 08261ee1..c5f3d536 100644 --- a/css/style.css +++ b/css/style.css @@ -296,11 +296,6 @@ footer { } #fullscreen { - margin: 5px; - position: absolute; - z-index: 1; - top: 1em; - right: 0em; max-width: 70px; } diff --git a/index.html b/index.html index fbfc65f0..7559b8c3 100644 --- a/index.html +++ b/index.html @@ -260,7 +260,7 @@

Saved Records
-
+
Sum Detections by: diff --git a/js/BirdNet2.4.js b/js/BirdNet2.4.js index e158c238..38ad7653 100644 --- a/js/BirdNet2.4.js +++ b/js/BirdNet2.4.js @@ -1,13 +1,13 @@ const tf = require('@tensorflow/tfjs-node'); const fs = require('node:fs'); const path = require('node:path'); -let DEBUG = true; +let DEBUG = false; let BACKEND; //GLOBALS let myModel; -const MIGRANTS = new Set(["Pluvialis dominica_American Golden Plover", "Acanthis hornemanni_Arctic Redpoll", "Sterna paradisaea_Arctic Tern", "Recurvirostra avosetta_Avocet", "Porzana pusilla_Baillon's Crake", "Limosa lapponica_Bar-tailed Godwit", "Tyto alba_Barn Owl", "Branta leucopsis_Barnacle Goose", "Cygnus columbianus_Bewick's Swan", "Botaurus stellaris_Bittern", "Chroicocephalus ridibundus_Black-headed Gull", "Podiceps nigricollis_Black-necked Grebe", "Limosa limosa_Black-tailed Godwit", "Turdus merula_Blackbird", "Sylvia atricapilla_Blackcap", "Fringilla montifringilla_Brambling", "Branta bernicla_Brent Goose", "Branta canadensis_Canada Goose", "Larus cachinnans_Caspian Gull", "Phylloscopus collybita_Chiffchaff", "Loxia curvirostra_Common Crossbill", "Larus canus_Common Gull", "Acanthis flammea_Common Redpoll", "Actitis hypoleucos_Common Sandpiper", "Melanitta nigra_Common Scoter", "Sterna hirundo_Common Tern", "Fulica atra_Coot", "Crex crex_Corncrake", "Cuculus canorus_Cuckoo", "Calidris ferruginea_Curlew Sandpiper", "Numenius arquata_Curlew", "Charadrius morinellus_Dotterel", "Calidris alpina_Dunlin", "Prunella modularis_Dunnock", "Alopochen aegyptiaca_Egyptian Goose", "Turdus pilaris_Fieldfare", "Mareca strepera_Gadwall", "Sylvia borin_Garden Warbler", "Spatula querquedula_Garganey", "Regulus regulus_Goldcrest", "Pluvialis apricaria_Golden Plover", "Bucephala clangula_Goldeneye", "Mergus merganser_Goosander", "Locustella naevia_Grasshopper Warbler", "Larus marinus_Great Black-backed Gull", "Podiceps cristatus_Great Crested Grebe", "Tringa ochropus_Green Sandpiper", "Tringa nebularia_Greenshank", "Ardea cinerea_Grey Heron", "Perdix perdix_Grey Partridge", "Phalaropus fulicarius_Grey", "Pluvialis squatarola_Grey Plover", "Motacilla cinerea_Grey Wagtail ", "Anser anser_Greylag Goose", "Delichon urbicum_House Martin", "Coccothraustes coccothraustes_Hawfinch", "Larus argentatus_Herring Gull", "Lymnocryptes minimus_Jack Snipe", "Alcedo atthis_Kingfisher", "Calidris canutus_Knot", "Calcarius lapponicus_Lapland Bunting", "Larus fuscus_Lesser Black-backed Gull", "Acanthis cabaret_Lesser Redpoll ", "Sylvia curruca_Lesser Whitethroat", "Linaria cannabina_Linnet", "Egretta garzetta_Little Egret", "Tachybaptus ruficollis_Little Grebe", "Hydrocoloeus minutus_Little Gull", "Athene noctua_Little Owl", "Charadrius dubius_Little Ringed Plover", "Calidris minuta_Little Stint ", "Sternula albifrons_Little Tern", "Asio otus_Long-eared Owl", "Clangula hyemalis_Long-tailed Duck", "Anas platyrhynchos_Mallard", "Aix galericulata_Mandarin Duck", "Anthus pratensis_Meadow Pipit", "Ichthyaetus melanocephalus_Mediterranean Gull", "Turdus viscivorus_Mistle Thrush", "Gallinula chloropus_Moorhen", "Nycticorax nycticorax_Night Heron", "Luscinia megarhynchos_Nightingale", "Luscinia megarhynchos_Nightingale (song)", "Caprimulgus europaeus_Nightjar", "Anthus hodgsoni_Olive-backed Pipit", "Emberiza hortulana_Ortolan Bunting", "Emberiza pusilla_Little Bunting", "Haematopus ostralegus_Oystercatcher", "Ficedula hypoleuca_Pied Flycatcher", "Motacilla alba_Pied Wagtail", "Anser brachyrhynchus_Pink-footed Goose", "Anas acuta_Pintail", "Aythya ferina_Pochard", "Calidris maritima_Purple Sandpiper", "Coturnix coturnix_Quail", "Mergus serrator_Red-breasted Merganser", "Netta rufina_Red-crested Pochard", "Alectoris rufa_Red-legged Partridge", "Tringa totanus_Redshank", "Phoenicurus phoenicurus_Redstart", "Turdus iliacus_Redwing", "Emberiza schoeniclus_Reed Bunting", "Acrocephalus scirpaceus_Reed Warbler", "Turdus torquatus_Ring Ouzel", "Charadrius hiaticula_Ringed Plover", "Erithacus rubecula_Robin (flight call)", "Anthus petrosus_Rock Pipit", "Sterna dougallii_Roseate Tern", "Calidris pugnax_Ruff", "Riparia riparia_Sand Martin", "Calidris alba_Sanderling", "Thalasseus sandvicensis_Sandwich Tern", "Aythya marila_Scaup", "Loxia scotica_Scottish Crossbill", "Acrocephalus schoenobaenus_Sedge Warbler", "Tadorna tadorna_Shelduck", "Asio flammeus_Short-eared Owl", "Spatula clypeata_Shoveler", "Spinus spinus_Siskin", "Alauda arvensis_Skylark", "Gallinago gallinago_Snipe", "Plectrophenax nivalis_Snow Bunting", "Turdus philomelos_Song Thrush", "Porzana porzana_Spotted Crake", "Muscicapa striata_Spotted Flycatcher", "Tringa erythropus_Spotted Redshank", "Burhinus oedicnemus_Stone-curlew", "Saxicola rubicola_Stonechat", "Hirundo rustica_Swallow", "Apus apus_Swift", "Anser fabalis_Taiga Bean Goose", "Strix aluco_Tawny Owl", "Anas crecca_Teal", "Anthus trivialis_Tree Pipit", "Aythya fuligula_Tufted Duck", "Anser serrirostris_Tundra Bean Goose", "Arenaria interpres_Turnstone", "Anthus spinoletta_Water Pipit", "Rallus aquaticus_Water Rail", "Numenius phaeopus_Whimbrel", "Anser albifrons_White-fronted Goose", "Sylvia communis_Whitethroat", "Cygnus cygnus_Whooper Swan", "Mareca penelope_Wigeon", "Phylloscopus trochilus_Willow Warbler", "Tringa glareola_Wood Sandpiper", "Scolopax rusticola_Woodcock", "Lullula arborea_Woodlark", "Larus michahellis_Yellow-legged Gull", "Motacilla flava_Yellow Wagtail", "Emberiza citrinella_Yellowhammer"]); -const NOT_BIRDS = ['Ambient Noise_Ambient Noise', 'Animal_Animal', 'Cat_Cat', 'Church Bells_Church Bells', 'Cough_Cough', 'Dog_Dog', 'Human_Human', 'Laugh_Laugh', 'Rain_Rain', 'Red Fox_Red Fox', 'Sneeze_Sneeze', 'Snoring_Snoring', 'Thunder_Thunder', 'Vehicle_Vehicle', 'Water Drops_Water Drops', 'Waves_Waves', 'Wind_Wind']; +// const MIGRANTS = new Set(["Pluvialis dominica_American Golden Plover", "Acanthis hornemanni_Arctic Redpoll", "Sterna paradisaea_Arctic Tern", "Recurvirostra avosetta_Avocet", "Porzana pusilla_Baillon's Crake", "Limosa lapponica_Bar-tailed Godwit", "Tyto alba_Barn Owl", "Branta leucopsis_Barnacle Goose", "Cygnus columbianus_Bewick's Swan", "Botaurus stellaris_Bittern", "Chroicocephalus ridibundus_Black-headed Gull", "Podiceps nigricollis_Black-necked Grebe", "Limosa limosa_Black-tailed Godwit", "Turdus merula_Blackbird", "Sylvia atricapilla_Blackcap", "Fringilla montifringilla_Brambling", "Branta bernicla_Brent Goose", "Branta canadensis_Canada Goose", "Larus cachinnans_Caspian Gull", "Phylloscopus collybita_Chiffchaff", "Loxia curvirostra_Common Crossbill", "Larus canus_Common Gull", "Acanthis flammea_Common Redpoll", "Actitis hypoleucos_Common Sandpiper", "Melanitta nigra_Common Scoter", "Sterna hirundo_Common Tern", "Fulica atra_Coot", "Crex crex_Corncrake", "Cuculus canorus_Cuckoo", "Calidris ferruginea_Curlew Sandpiper", "Numenius arquata_Curlew", "Charadrius morinellus_Dotterel", "Calidris alpina_Dunlin", "Prunella modularis_Dunnock", "Alopochen aegyptiaca_Egyptian Goose", "Turdus pilaris_Fieldfare", "Mareca strepera_Gadwall", "Sylvia borin_Garden Warbler", "Spatula querquedula_Garganey", "Regulus regulus_Goldcrest", "Pluvialis apricaria_Golden Plover", "Bucephala clangula_Goldeneye", "Mergus merganser_Goosander", "Locustella naevia_Grasshopper Warbler", "Larus marinus_Great Black-backed Gull", "Podiceps cristatus_Great Crested Grebe", "Tringa ochropus_Green Sandpiper", "Tringa nebularia_Greenshank", "Ardea cinerea_Grey Heron", "Perdix perdix_Grey Partridge", "Phalaropus fulicarius_Grey", "Pluvialis squatarola_Grey Plover", "Motacilla cinerea_Grey Wagtail ", "Anser anser_Greylag Goose", "Delichon urbicum_House Martin", "Coccothraustes coccothraustes_Hawfinch", "Larus argentatus_Herring Gull", "Lymnocryptes minimus_Jack Snipe", "Alcedo atthis_Kingfisher", "Calidris canutus_Knot", "Calcarius lapponicus_Lapland Bunting", "Larus fuscus_Lesser Black-backed Gull", "Acanthis cabaret_Lesser Redpoll ", "Sylvia curruca_Lesser Whitethroat", "Linaria cannabina_Linnet", "Egretta garzetta_Little Egret", "Tachybaptus ruficollis_Little Grebe", "Hydrocoloeus minutus_Little Gull", "Athene noctua_Little Owl", "Charadrius dubius_Little Ringed Plover", "Calidris minuta_Little Stint ", "Sternula albifrons_Little Tern", "Asio otus_Long-eared Owl", "Clangula hyemalis_Long-tailed Duck", "Anas platyrhynchos_Mallard", "Aix galericulata_Mandarin Duck", "Anthus pratensis_Meadow Pipit", "Ichthyaetus melanocephalus_Mediterranean Gull", "Turdus viscivorus_Mistle Thrush", "Gallinula chloropus_Moorhen", "Nycticorax nycticorax_Night Heron", "Luscinia megarhynchos_Nightingale", "Luscinia megarhynchos_Nightingale (song)", "Caprimulgus europaeus_Nightjar", "Anthus hodgsoni_Olive-backed Pipit", "Emberiza hortulana_Ortolan Bunting", "Emberiza pusilla_Little Bunting", "Haematopus ostralegus_Oystercatcher", "Ficedula hypoleuca_Pied Flycatcher", "Motacilla alba_Pied Wagtail", "Anser brachyrhynchus_Pink-footed Goose", "Anas acuta_Pintail", "Aythya ferina_Pochard", "Calidris maritima_Purple Sandpiper", "Coturnix coturnix_Quail", "Mergus serrator_Red-breasted Merganser", "Netta rufina_Red-crested Pochard", "Alectoris rufa_Red-legged Partridge", "Tringa totanus_Redshank", "Phoenicurus phoenicurus_Redstart", "Turdus iliacus_Redwing", "Emberiza schoeniclus_Reed Bunting", "Acrocephalus scirpaceus_Reed Warbler", "Turdus torquatus_Ring Ouzel", "Charadrius hiaticula_Ringed Plover", "Erithacus rubecula_Robin (flight call)", "Anthus petrosus_Rock Pipit", "Sterna dougallii_Roseate Tern", "Calidris pugnax_Ruff", "Riparia riparia_Sand Martin", "Calidris alba_Sanderling", "Thalasseus sandvicensis_Sandwich Tern", "Aythya marila_Scaup", "Loxia scotica_Scottish Crossbill", "Acrocephalus schoenobaenus_Sedge Warbler", "Tadorna tadorna_Shelduck", "Asio flammeus_Short-eared Owl", "Spatula clypeata_Shoveler", "Spinus spinus_Siskin", "Alauda arvensis_Skylark", "Gallinago gallinago_Snipe", "Plectrophenax nivalis_Snow Bunting", "Turdus philomelos_Song Thrush", "Porzana porzana_Spotted Crake", "Muscicapa striata_Spotted Flycatcher", "Tringa erythropus_Spotted Redshank", "Burhinus oedicnemus_Stone-curlew", "Saxicola rubicola_Stonechat", "Hirundo rustica_Swallow", "Apus apus_Swift", "Anser fabalis_Taiga Bean Goose", "Strix aluco_Tawny Owl", "Anas crecca_Teal", "Anthus trivialis_Tree Pipit", "Aythya fuligula_Tufted Duck", "Anser serrirostris_Tundra Bean Goose", "Arenaria interpres_Turnstone", "Anthus spinoletta_Water Pipit", "Rallus aquaticus_Water Rail", "Numenius phaeopus_Whimbrel", "Anser albifrons_White-fronted Goose", "Sylvia communis_Whitethroat", "Cygnus cygnus_Whooper Swan", "Mareca penelope_Wigeon", "Phylloscopus trochilus_Willow Warbler", "Tringa glareola_Wood Sandpiper", "Scolopax rusticola_Woodcock", "Lullula arborea_Woodlark", "Larus michahellis_Yellow-legged Gull", "Motacilla flava_Yellow Wagtail", "Emberiza citrinella_Yellowhammer"]); +// const NOT_BIRDS = ['Ambient Noise_Ambient Noise', 'Animal_Animal', 'Cat_Cat', 'Church Bells_Church Bells', 'Cough_Cough', 'Dog_Dog', 'Human_Human', 'Laugh_Laugh', 'Rain_Rain', 'Red Fox_Red Fox', 'Sneeze_Sneeze', 'Snoring_Snoring', 'Thunder_Thunder', 'Vehicle_Vehicle', 'Water Drops_Water Drops', 'Waves_Waves', 'Wind_Wind']; const MYSTERIES = ['Unknown Sp._Unknown Sp.']; const GRAYLIST = []; const GOLDEN_LIST = [] @@ -24,125 +24,125 @@ onmessage = async (e) => { let response; try { switch (modelRequest) { - case "load": {const version = e.data.model; -if (DEBUG) { - console.log("load request to worker"); -} -const { height: height, width: width, labels: labels, location: location } = JSON.parse(fs.readFileSync(path.join(__dirname, `../${version}_model_config.json`), "utf8")); -const appPath = "../" + location + "/"; -const list = e.data.list; -const batch = e.data.batchSize; -const backend = e.data.backend; -labels.push(...MYSTERIES); -postMessage({ - message: "labels", - labels: labels -}); -if (DEBUG) { - console.log(`model received load instruction. Using list: ${list}, batch size ${batch}`); -} -tf.setBackend(backend).then(async () => { - if (backend === "webgl") { - tf.env().set("WEBGL_FORCE_F16_TEXTURES", true); - tf.env().set("WEBGL_PACK", true); - tf.env().set("WEBGL_EXP_CONV", true); - tf.env().set("TOPK_K_CPU_HANDOFF_THRESHOLD", 128); - tf.env().set("TOPK_LAST_DIM_CPU_HANDOFF_SIZE_THRESHOLD", 0); - } - tf.enableProdMode(); - if (DEBUG) { - console.log(tf.env()); - console.log(tf.env().getFlags()); - } - myModel = new Model(appPath, list, version); - myModel.height = height; - myModel.width = width; - myModel.labels = labels; - myModel.lat = parseFloat(e.data.lat); - myModel.lon = parseFloat(e.data.lon); - myModel.week = parseInt(e.data.week); - await myModel.loadModel(); - postMessage({ - message: "update-list", - blocked: BLOCKED_IDS, - updateResults: false - }); - myModel.warmUp(batch); - BACKEND = tf.getBackend(); - postMessage({ - message: "model-ready", - sampleRate: myModel.config.sampleRate, - chunkLength: myModel.chunkLength, - backend: tf.getBackend(), - labels: labels - }); -}); -break; -} - case "predict": {if (myModel.model_loaded) { - const { chunks: chunks, start: start, fileStart: fileStart, file: file, snr: snr, confidence: confidence, worker: worker, context: context, resetResults: resetResults } = e.data; - myModel.useContext = context; - myModel.selection = !resetResults; - const [result,filename,startPosition] = await myModel.predictChunk(chunks, start, fileStart, file, snr, confidence / 1000); - response = { - message: "prediction", - file: filename, - result: result, - fileStart: startPosition, - worker: worker, - selection: myModel.selection - }; - postMessage(response); - myModel.result = []; -} -break; -} - case "get-spectrogram": {const buffer = e.data.buffer; -if (buffer.length < myModel.chunkLength) { - return; -} -const specFile = e.data.file; -const filepath = e.data.filepath; -const spec_height = e.data.height; -const spec_width = e.data.width; -let image; -const signal = tf.tensor1d(buffer, "float32"); -const bufferTensor = myModel.normalise_audio(signal); -signal.dispose(); -const imageTensor = tf.tidy(() => { - return myModel.makeSpectrogram(bufferTensor); -}); -image = tf.tidy(() => { - 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(); -}); -bufferTensor.dispose(); -imageTensor.dispose(); -response = { - message: "spectrogram", - width: myModel.inputShape[2], - height: myModel.inputShape[1], - channels: myModel.inputShape[3], - image: image, - file: specFile, - filepath: filepath -}; -postMessage(response); -break; -} - case "list": {myModel.list = e.data.list; -if (DEBUG) { - console.log(`Setting list to ${myModel.list}`); -} -await myModel.setList(); -postMessage({ - message: "update-list", - blocked: BLOCKED_IDS, - updateResults: true -}); -break; -} + case "load": { + const version = e.data.model; + DEBUG && console.log("load request to worker"); + const { height: height, width: width, labels: labels, location: location } = JSON.parse(fs.readFileSync(path.join(__dirname, `../${version}_model_config.json`), "utf8")); + const appPath = "../" + location + "/"; + const list = e.data.list; + const batch = e.data.batchSize; + const backend = e.data.backend; + labels.push(...MYSTERIES); + postMessage({ + message: "labels", + labels: labels + }); + DEBUG && console.log(`model received load instruction. Using list: ${list}, batch size ${batch}`); + + tf.setBackend(backend).then(async () => { + if (backend === "webgl") { + tf.env().set("WEBGL_FORCE_F16_TEXTURES", true); + tf.env().set("WEBGL_PACK", true); + tf.env().set("WEBGL_EXP_CONV", true); + tf.env().set("TOPK_K_CPU_HANDOFF_THRESHOLD", 128); + tf.env().set("TOPK_LAST_DIM_CPU_HANDOFF_SIZE_THRESHOLD", 0); + } + tf.enableProdMode(); + if (DEBUG) { + console.log(tf.env()); + console.log(tf.env().getFlags()); + } + myModel = new Model(appPath, list, version); + myModel.height = height; + myModel.width = width; + myModel.labels = labels; + myModel.lat = parseFloat(e.data.lat); + myModel.lon = parseFloat(e.data.lon); + myModel.week = parseInt(e.data.week); + await myModel.loadModel(); + postMessage({ + message: "update-list", + blocked: BLOCKED_IDS, + updateResults: false + }); + myModel.warmUp(batch); + BACKEND = tf.getBackend(); + postMessage({ + message: "model-ready", + sampleRate: myModel.config.sampleRate, + chunkLength: myModel.chunkLength, + backend: tf.getBackend(), + labels: labels + }); + }); + break; + } + case "predict": { + if (myModel.model_loaded) { + const { chunks: chunks, start: start, fileStart: fileStart, file: file, snr: snr, confidence: confidence, worker: worker, context: context, resetResults: resetResults } = e.data; + myModel.useContext = context; + myModel.selection = !resetResults; + const [result,filename,startPosition] = await myModel.predictChunk(chunks, start, fileStart, file, snr, confidence / 1000); + response = { + message: "prediction", + file: filename, + result: result, + fileStart: startPosition, + worker: worker, + selection: myModel.selection + }; + postMessage(response); + myModel.result = []; + } + break; + } + case "get-spectrogram": { + const buffer = e.data.buffer; + const specFile = e.data.file; + const filepath = e.data.filepath; + const spec_height = e.data.height; + const spec_width = e.data.width; + let image; + if (buffer.length !== 72000) { + console.log((`Skipping ${e.data.file} as buffer size is ${buffer.length}`)) + return; + } + const signal = tf.tensor1d(buffer, "float32"); + const bufferTensor = myModel.normalise_audio(signal); + signal.dispose(); + const imageTensor = tf.tidy(() => { + return myModel.makeSpectrogram(bufferTensor); + }); + image = tf.tidy(() => { + 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(); + }); + bufferTensor.dispose(); + imageTensor.dispose(); + response = { + message: "spectrogram", + width: 384, + height: 256, + channels: 1, + image: image, + file: specFile, + filepath: filepath + }; + postMessage(response); + break; + } + case "list": { + myModel.list = e.data.list; + DEBUG && console.log(`Setting list to ${myModel.list}`); + await myModel.setList(); + postMessage({ + message: "update-list", + blocked: BLOCKED_IDS, + updateResults: true + }); + break; + } } } // If worker was respawned @@ -170,10 +170,10 @@ class Model { } async loadModel() { - if (DEBUG) console.log('loading model') + DEBUG && console.log('loading model') if (this.model_loaded === false) { // Model files must be in a different folder than the js, assets files - if (DEBUG) console.log('loading model from', this.appPath + 'model.json') + DEBUG && console.log('loading model from', this.appPath + 'model.json') this.model = await tf.loadLayersModel(this.appPath + 'model.json', { weightPathPrefix: this.appPath }); this.model_loaded = true; @@ -197,7 +197,7 @@ class Model { //this.padBatch(tf.zeros([1, this.inputShape[1], this.inputShape[2], this.inputShape[3]]), { batchSize: this.batchSize }) }) } - if (DEBUG) console.log('WarmUp end', tf.memory().numTensors) + DEBUG && console.log('WarmUp end', tf.memory().numTensors) return true; } @@ -299,7 +299,7 @@ class Model { padBatch(tensor) { return tf.tidy(() => { - if (DEBUG) console.log(`Adding ${this.batchSize - tensor.shape[0]} tensors to the batch`) + DEBUG && console.log(`Adding ${this.batchSize - tensor.shape[0]} tensors to the batch`) const shape = [...tensor.shape]; shape[0] = this.batchSize - shape[0]; const padding = tf.zeros(shape); @@ -348,7 +348,7 @@ class Model { const keysTensor = tf.stack(keys); // + 1 tensor const snr = this.getSNR(TensorBatch) const condition = tf.greaterEqual(snr, threshold); // + 1 tensor - if (DEBUG) console.log('SNR is:', snr.dataSync()) + DEBUG && console.log('SNR is:', snr.dataSync()) snr.dispose(); // Avoid mask cannot be scalar error at end of predictions let newCondition; @@ -368,12 +368,12 @@ class Model { maskedTensorBatch.dispose(); // - 1 tensor maskedKeysTensor.dispose(); // - 1 tensor TensorBatch.dispose(); // - 1 tensor - if (DEBUG) console.log("No surviving tensors in batch", maskedTensorBatch.shape[0]) + DEBUG && console.log("No surviving tensors in batch", maskedTensorBatch.shape[0]) return [] } else { keys = maskedKeysTensor.dataSync(); maskedKeysTensor.dispose(); // - 1 tensor - if (DEBUG) console.log("surviving tensors in batch", maskedTensorBatch.shape[0]) + DEBUG && console.log("surviving tensors in batch", maskedTensorBatch.shape[0]) } } @@ -453,7 +453,7 @@ class Model { }; async predictChunk(audioBuffer, start, fileStart, file, threshold, confidence) { - if (DEBUG) console.log('predictCunk begin', tf.memory().numTensors); + DEBUG && console.log('predictCunk begin', tf.memory().numTensors); audioBuffer = tf.tensor1d(audioBuffer); // check if we need to pad @@ -463,7 +463,7 @@ class Model { // Pad to the nearest full sample paddedBuffer = audioBuffer.pad([[0, this.chunkLength - remainder]]); audioBuffer.dispose(); - if (DEBUG) console.log('Received final chunks') + DEBUG && console.log('Received final chunks') } const buffer = paddedBuffer || audioBuffer; const numSamples = buffer.shape / this.chunkLength; diff --git a/js/ui.js b/js/ui.js index 7d8d130f..b5154bbd 100644 --- a/js/ui.js +++ b/js/ui.js @@ -211,6 +211,7 @@ async function loadAudioFile({ filePath = '', preserveResults = false }) { function updateSpec({ buffer, play = false, position = 0, resetSpec = false }) { + showElement(['spectrogramWrapper'], false); wavesurfer.loadDecodedBuffer(buffer); wavesurfer.seekTo(position); play ? wavesurfer.play() : wavesurfer.pause(); @@ -866,7 +867,7 @@ function fetchLocationAddress(lat, lon) { worker.postMessage({ action: 'get-locations', file: currentFile }); waitForLocations(); } - const storedLocation = LOCATIONS.find(obj => obj.lat === lat && obj.lon === lon); + const storedLocation = LOCATIONS?.find(obj => obj.lat === lat && obj.lon === lon); if (storedLocation) return resolve(storedLocation.place); fetch(`https://nominatim.openstreetmap.org/reverse?format=jsonv2&lat=${lat}&lon=${lon}&zoom=14`) @@ -951,7 +952,7 @@ export2audio.addEventListener('click', batchExportAudio); async function batchExportAudio(e) { const species = isSpeciesViewFiltered(true); // || getSpecies(e.target); - species ? exportData(limit, species) : alert("Filter results by species to export audio files"); + species ? exportData(species, 1000) : alert("Filter results by species to export audio files"); } const export2CSV = () => exportData(isSpeciesViewFiltered(true), Infinity); diff --git a/js/worker.js b/js/worker.js index 934901f6..dc04ccdc 100644 --- a/js/worker.js +++ b/js/worker.js @@ -22,7 +22,7 @@ let SEEN_LIST_UPDATE = false // Prevents list updates from every worker on ever const DEBUG = false; const DATASET = false; -const adding_chirpity_additions = true; +const adding_chirpity_additions = false; const dataset_database = DATASET; const DATASET_SAVE_LOCATION = "E:/DATASETS/BirdNET_pngs"; @@ -1371,6 +1371,7 @@ const convertSpecsFromExistingSpecs = async (path) => { const saveResults2DataSet = ({species}) => { const rootDirectory = DATASET_SAVE_LOCATION; + sampleRate = 24_000; const height = 256, width = 384; let t0 = Date.now() let promise = Promise.resolve(); @@ -1463,6 +1464,7 @@ const saveResults2DataSet = ({species}) => { if (err) return console.log(err); Promise.all(promises).then(() => console.log(`Dataset created. ${count} files saved in ${(Date.now() - t0) / 1000} seconds`)) }) + } const onSpectrogram = async (filepath, file, width, height, data, channels) => { @@ -1493,7 +1495,7 @@ async function uploadOpus({ file, start, end, defaultName, metadata, mode }) { xhr.send(formData); } -const bufferToAudio = ({ +const bufferToAudio = async ({ file = '', start = 0, end = 3, meta = {}, format = undefined }) => { let audioCodec, mimeType, soundFormat; @@ -1535,7 +1537,7 @@ const bufferToAudio = ({ optionList.push('-metadata'); optionList.push(`${k}=${v}`); } - + metadata[file] || await getWorkingFile(file); if (padding) { start -= padding; end += padding; @@ -2210,7 +2212,6 @@ const getResults = async ({ }); } else { - for (let i = 0; i < result.length; i++) { const r = result[i]; if (exportTo) {