From 59e378791c701c0f0d159ff2b5a5c7bf2f87e8b7 Mon Sep 17 00:00:00 2001 From: Nachtzuster Date: Sat, 27 Sep 2025 13:41:33 +0200 Subject: [PATCH] cleanup privacy filter, also make sure a human chunk their neighbour is also overwritten (#475) --- scripts/server.py | 96 +++++++++++++++++++++++++++++++---------------- 1 file changed, 63 insertions(+), 33 deletions(-) diff --git a/scripts/server.py b/scripts/server.py index ef16f34..b9a88a3 100644 --- a/scripts/server.py +++ b/scripts/server.py @@ -1,4 +1,3 @@ -import datetime import logging import math import operator @@ -25,7 +24,7 @@ userDir = os.path.expanduser('~') INTERPRETER, M_INTERPRETER, INCLUDE_LIST, EXCLUDE_LIST = (None, None, None, None) PREDICTED_SPECIES_LIST = [] WEEK = None -model, priv_thresh, sf_thresh = (None, None, None) +model, sf_thresh = (None, None) mdata, mdata_params = (None, None) @@ -222,17 +221,7 @@ def predict(sample, sensitivity): # Sort by score p_sorted = sorted(p_labels.items(), key=operator.itemgetter(1), reverse=True) - human_cutoff = max(10, int(len(p_sorted) * priv_thresh / 100.0)) - - log.debug("DATABASE SIZE: %d", len(p_sorted)) - log.debug("HUMAN-CUTOFF AT: %d", human_cutoff) - - for i in range(min(10, len(p_sorted))): - if p_sorted[i][0] == 'Human_Human': - with open(userDir + '/BirdNET-Pi/HUMAN.txt', 'a') as rfile: - rfile.write(str(datetime.datetime.now()) + str(p_sorted[i]) + ' ' + str(human_cutoff) + '\n') - - return p_sorted[:human_cutoff] + return p_sorted def analyzeAudioData(chunks, lat, lon, week, sens, overlap,): @@ -240,7 +229,7 @@ def analyzeAudioData(chunks, lat, lon, week, sens, overlap,): sensitivity = max(0.5, min(1.0 - (sens - 1.0), 1.5)) - detections = {} + detections = [] start = time.time() log.info('ANALYZING AUDIO...') @@ -252,35 +241,77 @@ def analyzeAudioData(chunks, lat, lon, week, sens, overlap,): mdata = get_metadata(lat, lon, week) # Parse every chunk - pred_start = 0.0 for c in chunks: - # Prepare as input signal sig = np.expand_dims(c, 0) # Make prediction p = predict([sig, mdata], sensitivity) -# print("PPPPP",p) - HUMAN_DETECTED = False + log.debug("PPPPP: %s", p) + detections.append(p) - # Catch if Human is recognized - for x in range(len(p)): - if "Human" in p[x][0]: - HUMAN_DETECTED = True - - # Save result and timestamp + labeled = {} + pred_start = 0.0 + for p in filter_humans(detections): + # Save timestamp and result pred_end = pred_start + 3.0 - - # If human detected set all detections to human to make sure voices are not saved - if HUMAN_DETECTED is True: - p = [('Human_Human', 0.0)] * 10 - - detections[str(pred_start) + ';' + str(pred_end)] = p + labeled[str(pred_start) + ';' + str(pred_end)] = p pred_start = pred_end - overlap log.info('DONE! Time %.2f SECONDS', time.time() - start) - return detections + return labeled + + +def filter_humans(detections): + conf = get_settings() + priv_thresh = conf.getfloat('PRIVACY_THRESHOLD') + human_cutoff = max(10, int(len(detections[0]) * priv_thresh / 100.0)) + log.debug("DATABASE SIZE: %d", len(detections[0])) + log.debug("HUMAN-CUTOFF AT: %d", human_cutoff) + + censored_detections = [] + for detection in detections: + p = detection[:human_cutoff] + human_detected = False + # Catch if Human is recognized in any of the predictions + for x in p: + if 'Human' in x[0]: + human_detected = True + + # If human detected set detection to human to make sure voices are not saved + if human_detected is True: + p = [('Human_Human', 0.0)] + else: + p = p[:10] + + censored_detections.append(p) + + # now overwrite detections that have a human neighbour too + try: + extraction_length = conf.getint('EXTRACTION_LENGTH') + except ValueError: + extraction_length = 6 + if extraction_length > 9: + log.warning("EXTRACTION_LENGTH is set to %d. Privacy filter might miss human sound, " + "if you care about privacy, set EXTRACTION_LENGTH to below 9 or leave empty.", extraction_length) + human_neighbour_mask = [False] * len(censored_detections) + for i, detection in enumerate(censored_detections): + if i != 0: + if censored_detections[i - 1][0][0] == 'Human_Human': + human_neighbour_mask[i] = True + if i != len(censored_detections) - 1: + if censored_detections[i + 1][0][0] == 'Human_Human': + human_neighbour_mask[i] = True + + clean_detections = [] + for i, (has_human_neighbour, detection) in enumerate(zip(human_neighbour_mask, censored_detections)): + if has_human_neighbour and detection[0][0] != 'Human_Human': + log.debug('Overwriting detection %d %s - Has Human neighbour', i + 1, detection[0]) + detection = [('Human_Human', 0.0)] + clean_detections.append(detection) + + return clean_detections def get_metadata(lat, lon, week): @@ -296,10 +327,9 @@ def get_metadata(lat, lon, week): def load_global_model(): global INTERPRETER - global model, priv_thresh, sf_thresh + global model, sf_thresh conf = get_settings() model = conf['MODEL'] - priv_thresh = conf.getfloat('PRIVACY_THRESHOLD') sf_thresh = conf.getfloat('SF_THRESH') INTERPRETER = loadModel()