Audio Classification
PyTorch
LiteRT
LiteRT
kernel-insect-cnn
insect
bioacoustics
experimental
local-inference
Instructions to use ganesh333/kernel-insect-classifier with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- LiteRT
How to use ganesh333/kernel-insect-classifier with LiteRT:
# No code snippets available yet for this library. # To use this model, check the repository files and the library's documentation. # Want to help? PRs adding snippets are welcome at: # https://github.com/huggingface/huggingface.js
- Notebooks
- Google Colab
- Kaggle
Download src/audio.py from ganesh333/kernel-insect-classifier: direct link, hf CLI and curl.
- Browser
- Download file 4.09 kB
-
https://huggingface.co/ganesh333/kernel-insect-classifier/resolve/main/src/audio.py
- Command line
-
hf download hf://ganesh333/kernel-insect-classifier/src/audio.py
-
curl -L -o audio.py https://huggingface.co/ganesh333/kernel-insect-classifier/resolve/main/src/audio.py
4.09 kB
| """Deterministic 16 kHz waveform -> log-mel features; standard-library/scipy implementation.""" | |
| import numpy as np | |
| from scipy.io import wavfile | |
| from scipy.signal import resample_poly, stft | |
| from scipy.fft import dct | |
| def to_float(y): | |
| """Scale PCM samples to float32 in [-1,1]; must run before mixing to mono, which drops the integer dtype.""" | |
| y=np.asarray(y) | |
| if y.dtype==np.uint8: return (y.astype(np.float32)-128.)/128. | |
| if np.issubdtype(y.dtype,np.integer): | |
| return y.astype(np.float32)/max(abs(np.iinfo(y.dtype).min),np.iinfo(y.dtype).max) | |
| return y.astype(np.float32) | |
| def read_audio(path): | |
| """(sample_rate, mono float32 waveform) from WAV via scipy, or FLAC/OGG/MP3/... via the optional soundfile package.""" | |
| path=str(path) | |
| if path.lower().endswith(('.wav','.wave')): | |
| try: | |
| sr,y=wavfile.read(path); y=to_float(y) | |
| except ValueError: | |
| # Compressed or unusual WAV encodings scipy cannot parse; libsndfile often can. | |
| sr,y=_read_soundfile(path) | |
| else: sr,y=_read_soundfile(path) | |
| if y.ndim==2: y=y.mean(axis=1) | |
| return int(sr),y.astype(np.float32) | |
| def _read_soundfile(path): | |
| try: import soundfile as sf | |
| except ImportError: | |
| raise RuntimeError(f"Reading {path} needs the soundfile package (pip install soundfile), " | |
| "or convert it first: ffmpeg -i input.mp3 -ac 1 output.wav") from None | |
| y,sr=sf.read(path,dtype='float32',always_2d=False) | |
| return sr,y | |
| SR=16000 | |
| DURATION=2.0 | |
| N_MELS=64 | |
| N_FFT=512 | |
| HOP=160 | |
| def _hz_to_mel(hz): return 2595.0*np.log10(1.0+np.asarray(hz)/700.0) | |
| def _mel_to_hz(mel): return 700.0*(10.0**(np.asarray(mel)/2595.0)-1.0) | |
| def mel_filterbank(sr=SR, n_fft=N_FFT, n_mels=N_MELS): | |
| points=_mel_to_hz(np.linspace(_hz_to_mel(0),_hz_to_mel(sr/2),n_mels+2)) | |
| bins=np.floor((n_fft+1)*points/sr).astype(int) | |
| fb=np.zeros((n_mels,n_fft//2+1),dtype=np.float32) | |
| for m in range(1,n_mels+1): | |
| left,center,right=bins[m-1:m+2] | |
| if center>left: | |
| fb[m-1,left:center]=np.arange(left,center)-left | |
| fb[m-1,left:center]/=(center-left) | |
| if right>center: | |
| fb[m-1,center:right]=(right-np.arange(center,right))/(right-center) | |
| return fb | |
| _FB=mel_filterbank() | |
| def resample(y, sr): | |
| y=np.asarray(y,dtype=np.float32).reshape(-1) | |
| if sr != SR: | |
| import math | |
| g=math.gcd(int(sr),SR) | |
| y=resample_poly(y,SR//g,int(sr)//g).astype(np.float32) | |
| return y | |
| def crop_offsets(n_samples, k): | |
| """k evenly spaced 2 s crop starts covering a 16 kHz waveform; k=1 gives the center crop.""" | |
| n=int(SR*DURATION) | |
| if n_samples<=n or k<=1: return [None] | |
| return [int(round(o)) for o in np.linspace(0,n_samples-n,k)] | |
| def prepare_waveform(y, sr, offset=None): | |
| y=resample(y,sr) | |
| n=int(SR*DURATION) | |
| if len(y)>n: | |
| # Center crop by default: deterministic inference and evaluation. | |
| start=(len(y)-n)//2 if offset is None else min(max(0,int(offset)),len(y)-n) | |
| y=y[start:start+n] | |
| elif len(y)<n: | |
| y=np.pad(y,(0,n-len(y))) | |
| peak=float(np.max(np.abs(y))) if len(y) else 0. | |
| if peak>1e-8: y=y/peak | |
| return y | |
| def logmel(y, sr=SR, offset=None): | |
| y=prepare_waveform(y,sr,offset) | |
| _,_,z=stft(y,fs=SR,nperseg=N_FFT,noverlap=N_FFT-HOP, | |
| nfft=N_FFT,boundary='zeros',padded=True) | |
| power=(np.abs(z)**2).astype(np.float32) | |
| mel=_FB @ power | |
| feat=np.log(np.maximum(mel,1e-8)) | |
| # Per-record normalization stabilizes recorder gain; retain a consistent transformation. | |
| feat=(feat-feat.mean())/(feat.std()+1e-5) | |
| return feat.astype(np.float32)[None,:,:] | |
| def audio_quality(y): | |
| y=np.asarray(y,dtype=np.float32) | |
| if y.size==0: return False, ['empty_audio'] | |
| rms=float(np.sqrt(np.mean(y*y))) | |
| peak=float(np.max(np.abs(y))) | |
| reasons=[] | |
| if rms<0.002: reasons.append('low_level_or_silence') | |
| if peak>0.999: reasons.append('possible_clipping') | |
| return len(reasons)==0, reasons | |