Skip to content

Commit 89d302c

Browse files
committed
Implement embedding extraction for LargeRecordingAnalyzer
1 parent 505c99c commit 89d302c

3 files changed

Lines changed: 40 additions & 14 deletions

File tree

‎src/birdnetlib/analyzer.py‎

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -568,3 +568,27 @@ def analyze_recording(self, recording):
568568

569569
self.results = results
570570
recording.detection_list = self.detections
571+
572+
def extract_embeddings_for_recording(self, recording):
573+
print("extract_embeddings_for_recording", recording.filename)
574+
start = 0
575+
end = recording.sample_secs
576+
results = []
577+
for segment in read_audio_segments(recording.path, sr=48000):
578+
c = segment["segment"]
579+
if len(c) < recording.sample_secs * 48000:
580+
# If below the minimum segment duration, continue.
581+
del c
582+
continue
583+
start = segment["start_sec"]
584+
end = segment["end_sec"]
585+
586+
data = np.array([c], dtype="float32")
587+
e = self._return_embeddings(data)[0].tolist()
588+
results.append({"start_time": start, "end_time": end, "embeddings": e})
589+
590+
# Increment start and end
591+
start += recording.sample_secs - recording.overlap
592+
end = start + recording.sample_secs
593+
594+
self.embeddings = results

‎src/birdnetlib/main.py‎

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -73,12 +73,6 @@ def analyze(self):
7373
self.analyzed = True
7474

7575
def extract_embeddings(self):
76-
# Check that analyzer is not LargeRecordingAnalyzer
77-
if isinstance(self.analyzer, LargeRecordingAnalyzer):
78-
raise IncompatibleAnalyzerError(
79-
"LargeRecordingAnalyzer can only be used with the LargeRecording class"
80-
)
81-
8276
# Read and analyze.
8377
self.read_audio_data()
8478
self.analyzer.extract_embeddings_for_recording(self)
@@ -480,9 +474,9 @@ def analyze(self):
480474
self.analyzed = True
481475

482476
def extract_embeddings(self):
483-
raise NotImplementedError(
484-
"Extraction of embeddings is not yet implemented for LargeRecordingAnalyzer. Use Analyzer if possible."
485-
)
477+
self.analyzer.extract_embeddings_for_recording(self)
478+
self.embeddings_list = self.analyzer.embeddings
479+
self.embeddings_extracted = True
486480

487481
def get_extract_array(self, start_sec, end_sec):
488482
# Returns ndarray trimmed for start_sec:end_sec

‎tests/test_embeddings.py‎

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -101,8 +101,6 @@ def test_largefile_embeddings():
101101
# pprint(commandline_results)
102102
assert len(commandline_results) == 40
103103

104-
# TODO: Implement for LargeRecording.
105-
# Confirm that LargeRecording return not implemented.
106104
large_analyzer = LargeRecordingAnalyzer()
107105
recording = LargeRecording(
108106
large_analyzer,
@@ -113,6 +111,16 @@ def test_largefile_embeddings():
113111
min_conf=min_conf,
114112
return_all_detections=True,
115113
)
116-
msg = "Extraction of embeddings is not yet implemented for LargeRecordingAnalyzer. Use Analyzer if possible."
117-
with pytest.raises(NotImplementedError, match=msg):
118-
recording.extract_embeddings()
114+
115+
recording.extract_embeddings()
116+
117+
# Check that birdnetlib results match command line results.
118+
assert len(recording.embeddings) == 40
119+
for idx, i in enumerate(commandline_results):
120+
# Specify the tolerance level
121+
tolerance = 1e-4 # 4 decimal points tolerance between BirdNET and birdnetlib.
122+
123+
# Assert that the arrays are almost equal within the tolerance
124+
assert np.allclose(
125+
i["embeddings"], recording.embeddings[idx]["embeddings"], atol=tolerance
126+
)

0 commit comments

Comments
 (0)