-
Notifications
You must be signed in to change notification settings - Fork 1
BatBot initial testing and integration #305
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
f1c3782
db3834a
4253365
5c3d9a8
1c24339
8878620
e96da72
9623991
a1d5eed
a438016
b1ae312
4aa3016
02d6d27
439dae3
d95e58b
0bed12a
dacd997
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,163 @@ | ||
| # Generated by Django 4.2.23 on 2026-01-27 18:11 | ||
|
|
||
| import django.contrib.postgres.fields | ||
| from django.db import migrations, models | ||
|
|
||
|
|
||
| class Migration(migrations.Migration): | ||
| dependencies = [ | ||
| ('core', '0025_configuration_mark_annotations_completed_enabled_and_more'), | ||
| ] | ||
|
|
||
| operations = [ | ||
| migrations.AlterField( | ||
| model_name='annotations', | ||
| name='end_time', | ||
| field=models.FloatField(blank=True, null=True), | ||
| ), | ||
| migrations.AlterField( | ||
| model_name='annotations', | ||
| name='high_freq', | ||
| field=models.FloatField(blank=True, null=True), | ||
| ), | ||
| migrations.AlterField( | ||
| model_name='annotations', | ||
| name='low_freq', | ||
| field=models.FloatField(blank=True, null=True), | ||
| ), | ||
| migrations.AlterField( | ||
| model_name='annotations', | ||
| name='start_time', | ||
| field=models.FloatField(blank=True, null=True), | ||
| ), | ||
| migrations.AlterField( | ||
| model_name='compressedspectrogram', | ||
| name='length', | ||
| field=models.FloatField(), | ||
| ), | ||
| migrations.AlterField( | ||
| model_name='compressedspectrogram', | ||
| name='starts', | ||
| field=django.contrib.postgres.fields.ArrayField( | ||
| base_field=django.contrib.postgres.fields.ArrayField( | ||
| base_field=models.FloatField(), size=None | ||
| ), | ||
| size=None, | ||
| ), | ||
| ), | ||
| migrations.AlterField( | ||
| model_name='compressedspectrogram', | ||
| name='stops', | ||
| field=django.contrib.postgres.fields.ArrayField( | ||
| base_field=django.contrib.postgres.fields.ArrayField( | ||
| base_field=models.FloatField(), size=None | ||
| ), | ||
| size=None, | ||
| ), | ||
| ), | ||
| migrations.AlterField( | ||
| model_name='compressedspectrogram', | ||
| name='widths', | ||
| field=django.contrib.postgres.fields.ArrayField( | ||
| base_field=django.contrib.postgres.fields.ArrayField( | ||
| base_field=models.FloatField(), size=None | ||
| ), | ||
| size=None, | ||
| ), | ||
| ), | ||
| migrations.AlterField( | ||
| model_name='nabatcompressedspectrogram', | ||
| name='length', | ||
| field=models.FloatField(), | ||
| ), | ||
| migrations.AlterField( | ||
| model_name='nabatcompressedspectrogram', | ||
| name='starts', | ||
| field=django.contrib.postgres.fields.ArrayField( | ||
| base_field=django.contrib.postgres.fields.ArrayField( | ||
| base_field=models.FloatField(), size=None | ||
| ), | ||
| size=None, | ||
| ), | ||
| ), | ||
| migrations.AlterField( | ||
| model_name='nabatcompressedspectrogram', | ||
| name='stops', | ||
| field=django.contrib.postgres.fields.ArrayField( | ||
| base_field=django.contrib.postgres.fields.ArrayField( | ||
| base_field=models.FloatField(), size=None | ||
| ), | ||
| size=None, | ||
| ), | ||
| ), | ||
| migrations.AlterField( | ||
| model_name='nabatcompressedspectrogram', | ||
| name='widths', | ||
| field=django.contrib.postgres.fields.ArrayField( | ||
| base_field=django.contrib.postgres.fields.ArrayField( | ||
| base_field=models.FloatField(), size=None | ||
| ), | ||
| size=None, | ||
| ), | ||
| ), | ||
| migrations.AlterField( | ||
| model_name='nabatspectrogram', | ||
| name='duration', | ||
| field=models.FloatField(), | ||
| ), | ||
| migrations.AlterField( | ||
| model_name='nabatspectrogram', | ||
| name='frequency_max', | ||
| field=models.FloatField(), | ||
| ), | ||
| migrations.AlterField( | ||
| model_name='nabatspectrogram', | ||
| name='frequency_min', | ||
| field=models.FloatField(), | ||
| ), | ||
| migrations.AlterField( | ||
| model_name='nabatspectrogram', | ||
| name='height', | ||
| field=models.FloatField(), | ||
| ), | ||
| migrations.AlterField( | ||
| model_name='nabatspectrogram', | ||
| name='width', | ||
| field=models.FloatField(), | ||
| ), | ||
| migrations.AlterField( | ||
| model_name='sequenceannotations', | ||
| name='end_time', | ||
| field=models.FloatField(blank=True, null=True), | ||
| ), | ||
| migrations.AlterField( | ||
| model_name='sequenceannotations', | ||
| name='start_time', | ||
| field=models.FloatField(blank=True, null=True), | ||
| ), | ||
| migrations.AlterField( | ||
| model_name='spectrogram', | ||
| name='duration', | ||
| field=models.FloatField(), | ||
| ), | ||
| migrations.AlterField( | ||
| model_name='spectrogram', | ||
| name='frequency_max', | ||
| field=models.FloatField(), | ||
| ), | ||
| migrations.AlterField( | ||
| model_name='spectrogram', | ||
| name='frequency_min', | ||
| field=models.FloatField(), | ||
| ), | ||
| migrations.AlterField( | ||
| model_name='spectrogram', | ||
| name='height', | ||
| field=models.FloatField(), | ||
| ), | ||
| migrations.AlterField( | ||
| model_name='spectrogram', | ||
| name='width', | ||
| field=models.FloatField(), | ||
| ), | ||
| ] |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -12,11 +12,11 @@ | |
| class Spectrogram(TimeStampedModel, models.Model): | ||
| recording = models.ForeignKey(Recording, on_delete=models.CASCADE) | ||
| images = GenericRelation(SpectrogramImage) | ||
| width = models.IntegerField() # pixels | ||
| height = models.IntegerField() # pixels | ||
| duration = models.IntegerField() # milliseconds | ||
| frequency_min = models.IntegerField() # hz | ||
| frequency_max = models.IntegerField() # hz | ||
| width = models.FloatField() # pixels | ||
| height = models.FloatField() # pixels | ||
|
Comment on lines
+15
to
+16
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same question here. Would it be more accurate to use integers for height/width? |
||
| duration = models.FloatField() # milliseconds | ||
| frequency_min = models.FloatField() # hz | ||
| frequency_max = models.FloatField() # hz | ||
|
|
||
| @property | ||
| def image_url_list(self): | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -6,10 +6,10 @@ | |
|
|
||
| from bats_ai.core.models import Configuration, ProcessingTask, Species | ||
| from bats_ai.core.models.nabat import NABatRecording, NABatRecordingAnnotation | ||
| from bats_ai.core.utils.batbot_metadata import generate_spectrogram_assets | ||
| from bats_ai.utils.spectrogram_utils import ( | ||
| generate_nabat_compressed_spectrogram, | ||
| generate_nabat_spectrogram, | ||
| generate_spectrogram_assets, | ||
| predict_from_compressed, | ||
| ) | ||
|
|
||
|
|
@@ -60,7 +60,8 @@ def generate_spectrograms( | |
|
|
||
| try: | ||
| config = Configuration.objects.first() | ||
| if config and config.run_inference_on_upload: | ||
| # TODO: Disabled until prediction is in batbot | ||
| if config and config.run_inference_on_upload and False: | ||
|
Comment on lines
+63
to
+64
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is there an issue in batbot or BatAI tracking this? It would be nice to have a link to an issue in either of the GH repos here while we wait for this feature. It might also be nice to add some sort of banner in the UI somewhere that tells users that prediction is disabled for now. |
||
| self.update_state( | ||
| state='Progress', | ||
| meta={'description': 'Running Prediction on Spectrogram'}, | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,6 @@ | ||
| import logging | ||
| import os | ||
| import shutil | ||
| import tempfile | ||
|
|
||
| from django.contrib.contenttypes.models import ContentType | ||
|
|
@@ -15,7 +16,8 @@ | |
| Spectrogram, | ||
| SpectrogramImage, | ||
| ) | ||
| from bats_ai.utils.spectrogram_utils import generate_spectrogram_assets, predict_from_compressed | ||
| from bats_ai.core.utils.batbot_metadata import generate_spectrogram_assets | ||
| from bats_ai.utils.spectrogram_utils import predict_from_compressed | ||
|
|
||
| logging.basicConfig(level=logging.INFO) | ||
| logger = logging.getLogger('NABatDataRetrieval') | ||
|
|
@@ -26,7 +28,15 @@ def recording_compute_spectrogram(recording_id: int): | |
| recording = Recording.objects.get(pk=recording_id) | ||
|
|
||
| with tempfile.TemporaryDirectory() as tmpdir: | ||
| results = generate_spectrogram_assets(recording.audio_file, tmpdir) | ||
| # Copy the audio file from FileField to a temporary file | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this needed because of how batbot handles spectrogram generation?
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yeah it no longer is using a fileHandle or something like that it. It needs a real file. |
||
| audio_filename = os.path.basename(recording.audio_file.name) | ||
| temp_audio_path = os.path.join(tmpdir, audio_filename) | ||
|
|
||
| with recording.audio_file.open('rb') as source_file: | ||
| with open(temp_audio_path, 'wb') as dest_file: | ||
| shutil.copyfileobj(source_file, dest_file) | ||
|
|
||
| results = generate_spectrogram_assets(temp_audio_path, output_folder=tmpdir) | ||
| # Create or get Spectrogram | ||
| spectrogram, _ = Spectrogram.objects.get_or_create( | ||
| recording=recording, | ||
|
|
@@ -79,7 +89,8 @@ def recording_compute_spectrogram(recording_id: int): | |
| ) | ||
|
|
||
| config = Configuration.objects.first() | ||
| if config and config.run_inference_on_upload: | ||
| # TODO: Disabled until prediction is in batbot | ||
| if config and config.run_inference_on_upload and False: | ||
|
Comment on lines
+92
to
+93
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same idea here about a linked issue/visibility for users on the front-end web side. |
||
| predict_results = predict_from_compressed(compressed_obj) | ||
| label = predict_results['label'] | ||
| score = predict_results['score'] | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For these two fields specifically, which I believe are just a pixel count, would it make sense to just keep them as integers?