{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"pygments_lexer":"ipython3","nbconvert_exporter":"python","version":"3.6.4","file_extension":".py","codemirror_mode":{"name":"ipython","version":3},"name":"python","mimetype":"text/x-python"}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# Notes","metadata":{}},{"cell_type":"markdown","source":"I've released a [training kernel](https://www.kaggle.com/kneroma/clean-fast-simple-bird-identifier-training-colab) and an [inference kernel](https://www.kaggle.com/kneroma/clean-fast-simple-bird-identifier-inference). Both of these kernels use a set of pre-computed mels. Which can be found at:\n\n* https://www.kaggle.com/kneroma/kkiller-birdclef-mels-computer-d7-part1\n* https://www.kaggle.com/kneroma/kkiller-birdclef-mels-computer-d7-part2\n* https://www.kaggle.com/kneroma/kkiller-birdclef-mels-computer-d7-part3\n* https://www.kaggle.com/kneroma/kkiller-birdclef-mels-computer-d7-part4\n\nUnfortunately, these mels are static (7s audio extracts) and you can't get any customization from them. Here, I'm releasing the base kernel that generate them in order to allow everyone to be able to play with the params.","metadata":{}},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import numpy as np\nimport librosa as lb\nimport librosa.display as lbd\nimport soundfile as sf\nfrom  soundfile import SoundFile\nimport pandas as pd\nfrom  IPython.display import Audio\nfrom pathlib import Path\n\nfrom matplotlib import pyplot as plt\n\nfrom tqdm.notebook import tqdm\nimport joblib, json\n\nfrom  sklearn.model_selection  import StratifiedKFold","metadata":{"id":"2dt7oG43VAqc","trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"PART_ID = 0 # The start index in the below list, by changing it you will compute mels on another subset\nPART_INDEXES = [0,15718, 31436, 47154, 62874] # The train_set is splitted into 4 subsets","metadata":{"id":"fJB9IS2RVAqe","trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"SR = 32_000\nDURATION = 7 \nSEED = 666\n\nDATA_ROOT = Path(\"../input/birdclef-2021\")\nTRAIN_AUDIO_ROOT = Path(\"../input/birdclef-2021/train_short_audio\")\nTRAIN_AUDIO_IMAGES_SAVE_ROOT = Path(\"audio_images\") # Where to save the mels images\nTRAIN_AUDIO_IMAGES_SAVE_ROOT.mkdir(exist_ok=True, parents=True)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def get_audio_info(filepath):\n    \"\"\"Get some properties from  an audio file\"\"\"\n    with SoundFile(filepath) as f:\n        sr = f.samplerate\n        frames = f.frames\n        duration = float(frames)/sr\n    return {\"frames\": frames, \"sr\": sr, \"duration\": duration}","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def make_df(n_splits=5, seed=SEED, nrows=None):\n    \n    df = pd.read_csv(DATA_ROOT/\"train_metadata.csv\", nrows=nrows)\n\n    LABEL_IDS = {label: label_id for label_id,label in enumerate(sorted(df[\"primary_label\"].unique()))}\n    \n    df = df.iloc[PART_INDEXES[PART_ID]: PART_INDEXES[PART_ID+1]]\n\n    df[\"label_id\"] = df[\"primary_label\"].map(LABEL_IDS)\n\n    df[\"filepath\"] = [str(TRAIN_AUDIO_ROOT/primary_label/filename) for primary_label,filename in zip(df.primary_label, df.filename) ]\n\n    pool = joblib.Parallel(4)\n    mapper = joblib.delayed(get_audio_info)\n    tasks = [mapper(filepath) for filepath in df.filepath]\n\n    df = pd.concat([df, pd.DataFrame(pool(tqdm(tasks)))], axis=1, sort=False)\n    \n    skf = StratifiedKFold(n_splits=n_splits, random_state=seed, shuffle=True)\n    splits = skf.split(np.arange(len(df)), y=df.label_id.values)\n    df[\"fold\"] = -1\n\n    for fold, (train_set, val_set) in enumerate(splits):\n        \n        df.loc[df.index[val_set], \"fold\"] = fold\n\n    return LABEL_IDS, df","metadata":{"id":"Kmh6xx5_NCjJ","outputId":"ad61f09f-6f0e-4204-c658-21112e051785","trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"LABEL_IDS, df = make_df(nrows=None)\n\ndf.to_csv(\"rich_train_metadata.csv\", index=True)\nwith open(\"LABEL_IDS.json\", \"w\") as f:\n    json.dump(LABEL_IDS, f)\n\nprint(df.shape)\ndf.head()","metadata":{"id":"SRRhx1EAN75G","outputId":"f1688387-b564-4f8d-8895-4490c90d4150","trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df[\"fold\"].value_counts()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df[\"primary_label\"].value_counts()","metadata":{"id":"ZRz-DwbNVAqg","outputId":"51f26b97-6a1e-4e39-c6fa-5114cfe27a25","trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df[\"duration\"].hist(bins=20)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df[\"duration\"].quantile(np.arange(0, 1, 0.01)).plot()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{"id":"xh4hfWZhuglm"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class MelSpecComputer:\n    def __init__(self, sr, n_mels, fmin, fmax, **kwargs):\n        self.sr = sr\n        self.n_mels = n_mels\n        self.fmin = fmin\n        self.fmax = fmax\n        kwargs[\"n_fft\"] = kwargs.get(\"n_fft\", self.sr//10)\n        kwargs[\"hop_length\"] = kwargs.get(\"hop_length\", self.sr//(10*4))\n        self.kwargs = kwargs\n\n    def __call__(self, y):\n\n        melspec = lb.feature.melspectrogram(\n            y, sr=self.sr, n_mels=self.n_mels, fmin=self.fmin, fmax=self.fmax, **self.kwargs,\n        )\n\n        melspec = lb.power_to_db(melspec).astype(np.float32)\n        return melspec","metadata":{"id":"7NZq43qTVAqi","trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def mono_to_color(X, eps=1e-6, mean=None, std=None):\n    mean = mean or X.mean()\n    std = std or X.std()\n    X = (X - mean) / (std + eps)\n    \n    _min, _max = X.min(), X.max()\n\n    if (_max - _min) > eps:\n        V = np.clip(X, _min, _max)\n        V = 255 * (V - _min) / (_max - _min)\n        V = V.astype(np.uint8)\n    else:\n        V = np.zeros_like(X, dtype=np.uint8)\n\n    return V\n\ndef crop_or_pad(y, length, is_train=True, start=None):\n    if len(y) < length:\n        y = np.concatenate([y, np.zeros(length - len(y))])\n        \n        n_repeats = length // len(y)\n        epsilon = length % len(y)\n        \n        y = np.concatenate([y]*n_repeats + [y[:epsilon]])\n        \n    elif len(y) > length:\n        if not is_train:\n            start = start or 0\n        else:\n            start = start or np.random.randint(len(y) - length)\n\n        y = y[start:start + length]\n\n    return y","metadata":{"id":"-Nlw4E5UVAqi","trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class AudioToImage:\n    def __init__(self, sr=SR, n_mels=128, fmin=0, fmax=None, duration=DURATION, step=None, res_type=\"kaiser_fast\", resample=True):\n\n        self.sr = sr\n        self.n_mels = n_mels\n        self.fmin = fmin\n        self.fmax = fmax or self.sr//2\n\n        self.duration = duration\n        self.audio_length = self.duration*self.sr\n        self.step = step or self.audio_length\n        \n        self.res_type = res_type\n        self.resample = resample\n\n        self.mel_spec_computer = MelSpecComputer(sr=self.sr, n_mels=self.n_mels, fmin=self.fmin,\n                                                 fmax=self.fmax)\n        \n    def audio_to_image(self, audio):\n        melspec = self.mel_spec_computer(audio) \n        image = mono_to_color(melspec)\n#         image = normalize(image, mean=None, std=None)\n        return image\n\n    def __call__(self, row, save=True):\n#       max_audio_duration = 10*self.duration\n#       init_audio_length = max_audio_duration*row.sr\n        \n#       start = 0 if row.duration <  max_audio_duration else np.random.randint(row.frames - init_audio_length)\n    \n      audio, orig_sr = sf.read(row.filepath, dtype=\"float32\")\n\n      if self.resample and orig_sr != self.sr:\n        audio = lb.resample(audio, orig_sr, self.sr, res_type=self.res_type)\n        \n      audios = [audio[i:i+self.audio_length] for i in range(0, max(1, len(audio) - self.audio_length + 1), self.step)]\n      audios[-1] = crop_or_pad(audios[-1] , length=self.audio_length)\n      images = [self.audio_to_image(audio) for audio in audios]\n      images = np.stack(images)\n        \n      if save:\n        path = TRAIN_AUDIO_IMAGES_SAVE_ROOT/f\"{row.primary_label}/{row.filename}.npy\"\n        path.parent.mkdir(exist_ok=True, parents=True)\n        np.save(str(path), images)\n      else:\n        return  row.filename, images","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def get_audios_as_images(df):\n    pool = joblib.Parallel(2)\n    \n    converter = AudioToImage(step=int(DURATION*0.666*SR))\n    mapper = joblib.delayed(converter)\n    tasks = [mapper(row) for row in df.itertuples(False)]\n    \n    pool(tqdm(tasks))","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"get_audios_as_images(df)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"row = df.loc[df.duration.idxmax()]\nmels = np.load(str((TRAIN_AUDIO_IMAGES_SAVE_ROOT/row.primary_label/row.filename).as_posix() + \".npy\"))\nprint(mels.shape)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"lbd.specshow(mels[0])","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]}]}