{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.14","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"none","dataSources":[{"sourceId":70203,"databundleVersionId":8068726,"sourceType":"competition"}],"dockerImageVersionId":30761,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"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, re\n\nfrom  sklearn.model_selection  import StratifiedKFold\ntqdm.pandas()","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2024-08-26T07:01:30.835873Z","iopub.execute_input":"2024-08-26T07:01:30.836440Z","iopub.status.idle":"2024-08-26T07:01:33.688439Z","shell.execute_reply.started":"2024-08-26T07:01:30.836384Z","shell.execute_reply":"2024-08-26T07:01:33.686660Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df = pd.read_csv('../input/birdclef-2024/train_metadata.csv')\ndf.shape","metadata":{"execution":{"iopub.status.busy":"2024-08-26T07:01:39.061727Z","iopub.execute_input":"2024-08-26T07:01:39.062602Z","iopub.status.idle":"2024-08-26T07:01:39.322044Z","shell.execute_reply.started":"2024-08-26T07:01:39.062543Z","shell.execute_reply":"2024-08-26T07:01:39.320293Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df.head()","metadata":{"execution":{"iopub.status.busy":"2024-08-26T07:01:46.025242Z","iopub.execute_input":"2024-08-26T07:01:46.027056Z","iopub.status.idle":"2024-08-26T07:01:46.065868Z","shell.execute_reply.started":"2024-08-26T07:01:46.026985Z","shell.execute_reply":"2024-08-26T07:01:46.064529Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df['secondary_labels'] = df['secondary_labels'].apply(lambda x: re.findall(r\"'(\\w+)'\", x))\ndf['len_sec_labels'] = df['secondary_labels'].map(len)","metadata":{"execution":{"iopub.status.busy":"2024-08-26T07:01:53.975909Z","iopub.execute_input":"2024-08-26T07:01:53.976467Z","iopub.status.idle":"2024-08-26T07:01:54.062498Z","shell.execute_reply.started":"2024-08-26T07:01:53.976414Z","shell.execute_reply":"2024-08-26T07:01:54.060156Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df[df.len_sec_labels>0].sample(3)","metadata":{"execution":{"iopub.status.busy":"2024-08-26T07:02:14.781456Z","iopub.execute_input":"2024-08-26T07:02:14.781907Z","iopub.status.idle":"2024-08-26T07:02:14.811416Z","shell.execute_reply.started":"2024-08-26T07:02:14.781865Z","shell.execute_reply":"2024-08-26T07:02:14.809874Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from sklearn.model_selection import train_test_split\nimport pandas as pd\n\ndef birds_stratified_split(df, target_col, test_size=0.2):\n    class_counts = df[target_col].value_counts()\n    low_count_classes = class_counts[class_counts < 2].index.tolist() ### Birds with single counts\n\n    df['train'] = df[target_col].isin(low_count_classes)\n\n    train_df, val_df = train_test_split(df[~df['train']], test_size=test_size, stratify=df[~df['train']][target_col], random_state=42)\n\n    train_df = pd.concat([train_df, df[df['train']]], axis=0).reset_index(drop=True)\n\n    # Remove the 'valid' column\n    train_df.drop('train', axis=1, inplace=True)\n    val_df.drop('train', axis=1, inplace=True)\n\n    return train_df, val_df","metadata":{"execution":{"iopub.status.busy":"2024-08-26T07:02:27.460729Z","iopub.execute_input":"2024-08-26T07:02:27.461176Z","iopub.status.idle":"2024-08-26T07:02:27.472652Z","shell.execute_reply.started":"2024-08-26T07:02:27.461134Z","shell.execute_reply":"2024-08-26T07:02:27.470703Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_df, valid_df = birds_stratified_split(df, 'primary_label', 0.2)","metadata":{"execution":{"iopub.status.busy":"2024-08-26T07:02:30.135784Z","iopub.execute_input":"2024-08-26T07:02:30.136578Z","iopub.status.idle":"2024-08-26T07:02:30.281029Z","shell.execute_reply.started":"2024-08-26T07:02:30.136509Z","shell.execute_reply":"2024-08-26T07:02:30.279332Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import os\n\n# Create a new directory\nos.makedirs(\"../working/specs/train\", exist_ok=True)\nos.makedirs(\"../working/specs/valid\", exist_ok=True)","metadata":{"execution":{"iopub.status.busy":"2024-08-25T04:33:23.718723Z","iopub.execute_input":"2024-08-25T04:33:23.719152Z","iopub.status.idle":"2024-08-25T04:33:23.726504Z","shell.execute_reply.started":"2024-08-25T04:33:23.719104Z","shell.execute_reply":"2024-08-25T04:33:23.725354Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class Config:\n    sampling_rate = 32000\n    duration = 5\n    fmin = 0\n    fmax = None\n    audios_path = Path(\"../input/birdclef-2024/train_audio\")\n    out_dir_train = Path(\"specs/train\")\n    out_dir_valid = Path(\"specs/valid\")","metadata":{"execution":{"iopub.status.busy":"2024-08-26T07:03:42.621209Z","iopub.execute_input":"2024-08-26T07:03:42.621735Z","iopub.status.idle":"2024-08-26T07:03:42.629544Z","shell.execute_reply.started":"2024-08-26T07:03:42.621690Z","shell.execute_reply":"2024-08-26T07:03:42.627774Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"Config.out_dir_train.mkdir(exist_ok=True, parents=True)\nConfig.out_dir_valid.mkdir(exist_ok=True, parents=True)","metadata":{"execution":{"iopub.status.busy":"2024-08-26T07:03:44.750791Z","iopub.execute_input":"2024-08-26T07:03:44.751301Z","iopub.status.idle":"2024-08-26T07:03:44.760453Z","shell.execute_reply.started":"2024-08-26T07:03:44.751254Z","shell.execute_reply":"2024-08-26T07:03:44.758209Z"},"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":{"execution":{"iopub.status.busy":"2024-08-25T04:33:23.755525Z","iopub.execute_input":"2024-08-25T04:33:23.755990Z","iopub.status.idle":"2024-08-25T04:33:23.765982Z","shell.execute_reply.started":"2024-08-25T04:33:23.755903Z","shell.execute_reply":"2024-08-25T04:33:23.764594Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def add_path_df(df):\n    \n    df[\"path\"] = [str(Config.audios_path/filename) for filename in df.filename]\n    df = df.reset_index(drop=True)\n    pool = joblib.Parallel(2)\n    mapper = joblib.delayed(get_audio_info)\n    tasks = [mapper(filepath) for filepath in df.path]\n    df2 =  pd.DataFrame(pool(tqdm(tasks))).reset_index(drop=True)\n    df = pd.concat([df,df2], axis=1).reset_index(drop=True)\n\n    return df","metadata":{"execution":{"iopub.status.busy":"2024-08-25T04:33:23.767338Z","iopub.execute_input":"2024-08-25T04:33:23.767887Z","iopub.status.idle":"2024-08-25T04:33:23.784679Z","shell.execute_reply.started":"2024-08-25T04:33:23.767830Z","shell.execute_reply":"2024-08-25T04:33:23.783090Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"tqdm.pandas()","metadata":{"execution":{"iopub.status.busy":"2024-08-25T04:33:23.786516Z","iopub.execute_input":"2024-08-25T04:33:23.787154Z","iopub.status.idle":"2024-08-25T04:33:23.798747Z","shell.execute_reply.started":"2024-08-25T04:33:23.787099Z","shell.execute_reply":"2024-08-25T04:33:23.797398Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# train_df_sample = train_df.sample(frac=0.5, random_state=42)\n\ntrain_df = add_path_df(train_df)\n# train_df = add_path_df(train_df_sample)","metadata":{"execution":{"iopub.status.busy":"2024-08-25T04:33:23.800301Z","iopub.execute_input":"2024-08-25T04:33:23.800689Z","iopub.status.idle":"2024-08-25T04:33:39.267401Z","shell.execute_reply.started":"2024-08-25T04:33:23.800650Z","shell.execute_reply":"2024-08-25T04:33:39.265495Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# valid_df_sample = valid_df.sample(frac=0.5, random_state=42)\n\nvalid_df = add_path_df(valid_df)\n# valid_df = add_path_df(valid_df_sample)","metadata":{"execution":{"iopub.status.busy":"2024-08-25T04:33:39.272730Z","iopub.execute_input":"2024-08-25T04:33:39.273188Z","iopub.status.idle":"2024-08-25T04:33:42.853668Z","shell.execute_reply.started":"2024-08-25T04:33:39.273145Z","shell.execute_reply":"2024-08-25T04:33:42.852396Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_df.to_csv('train.csv', index=False)\nvalid_df.to_csv('valid.csv', index=False)","metadata":{"execution":{"iopub.status.busy":"2024-08-25T04:33:42.855193Z","iopub.execute_input":"2024-08-25T04:33:42.855651Z","iopub.status.idle":"2024-08-25T04:33:43.147977Z","shell.execute_reply.started":"2024-08-25T04:33:42.855598Z","shell.execute_reply":"2024-08-25T04:33:43.146801Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def compute_melspec(y, sr, n_mels, fmin, fmax):\n    \"\"\"\n    Computes a mel-spectrogram and puts it at decibel scale\n    Arguments:\n        y {np array} -- signal\n        params {AudioParams} -- Parameters to use for the spectrogram. Expected to have the attributes sr, n_mels, f_min, f_max\n    Returns:\n        np array -- Mel-spectrogram\n    \"\"\"\n    melspec = lb.feature.melspectrogram(\n        y=y, sr=sr, n_mels=n_mels, fmin=fmin, fmax=fmax,\n    )\n\n    melspec = lb.power_to_db(melspec).astype(np.float32)\n    return melspec","metadata":{"execution":{"iopub.status.busy":"2024-08-25T04:33:43.149422Z","iopub.execute_input":"2024-08-25T04:33:43.149784Z","iopub.status.idle":"2024-08-25T04:33:43.157165Z","shell.execute_reply.started":"2024-08-25T04:33:43.149743Z","shell.execute_reply":"2024-08-25T04:33:43.155943Z"},"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":{"execution":{"iopub.status.busy":"2024-08-25T04:33:43.158637Z","iopub.execute_input":"2024-08-25T04:33:43.159104Z","iopub.status.idle":"2024-08-25T04:33:43.172292Z","shell.execute_reply.started":"2024-08-25T04:33:43.159049Z","shell.execute_reply":"2024-08-25T04:33:43.171058Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class AudioToImage:\n    def __init__(self, sr=Config.sampling_rate, n_mels=128, fmin=Config.fmin, fmax=Config.fmax, duration=Config.duration, step=None, res_type=\"kaiser_fast\", resample=True, train=True):\n        self.sr = sr\n        self.n_mels = n_mels\n        self.fmin = fmin\n        self.fmax = fmax or self.sr//2\n        self.duration = duration\n        self.audio_length = self.duration * self.sr\n        self.step = step or self.audio_length\n        self.res_type = res_type\n        self.resample = resample\n        self.train = train\n\n    def audio_to_image(self, audio):\n        melspec = compute_melspec(audio, self.sr, self.n_mels, self.fmin, self.fmax)\n        image = mono_to_color(melspec)\n        return image\n\n    def __call__(self, row, save=True):\n        audio, orig_sr = sf.read(row.path, 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            # Remove the original extension (e.g., .ogg) and save with .npy extension\n            base_filename = row.filename.split('.')[0]\n            if self.train:\n                path = Config.out_dir_train / f\"{base_filename}.npy\"\n            else:\n                path = Config.out_dir_valid / f\"{base_filename}.npy\"\n\n            path.parent.mkdir(exist_ok=True, parents=True)\n            np.save(str(path), images)\n        else:\n            return row.filename, images\n","metadata":{"execution":{"iopub.status.busy":"2024-08-25T04:33:43.174208Z","iopub.execute_input":"2024-08-25T04:33:43.175073Z","iopub.status.idle":"2024-08-25T04:33:43.192433Z","shell.execute_reply.started":"2024-08-25T04:33:43.175012Z","shell.execute_reply":"2024-08-25T04:33:43.190883Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"tqdm.pandas()","metadata":{"execution":{"iopub.status.busy":"2024-08-25T04:33:43.193807Z","iopub.execute_input":"2024-08-25T04:33:43.194370Z","iopub.status.idle":"2024-08-25T04:33:43.213692Z","shell.execute_reply.started":"2024-08-25T04:33:43.194315Z","shell.execute_reply":"2024-08-25T04:33:43.212337Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def get_audios_as_images(df, train = True):\n    pool = joblib.Parallel(2)\n    \n    converter = AudioToImage(step=int(Config.duration*0.666*Config.sampling_rate),train=train)\n    mapper = joblib.delayed(converter)\n    tasks = [mapper(row) for row in df.itertuples(False)]\n    pool(tqdm(tasks))","metadata":{"execution":{"iopub.status.busy":"2024-08-25T04:33:43.215389Z","iopub.execute_input":"2024-08-25T04:33:43.215793Z","iopub.status.idle":"2024-08-25T04:33:43.225961Z","shell.execute_reply.started":"2024-08-25T04:33:43.215745Z","shell.execute_reply":"2024-08-25T04:33:43.224692Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"get_audios_as_images(train_df, train = True)","metadata":{"execution":{"iopub.status.busy":"2024-08-25T04:33:43.227551Z","iopub.execute_input":"2024-08-25T04:33:43.228013Z","iopub.status.idle":"2024-08-25T04:53:37.688390Z","shell.execute_reply.started":"2024-08-25T04:33:43.227964Z","shell.execute_reply":"2024-08-25T04:53:37.686335Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"get_audios_as_images(valid_df, train = False)","metadata":{"execution":{"iopub.status.busy":"2024-08-25T04:53:37.691969Z","iopub.execute_input":"2024-08-25T04:53:37.692616Z","iopub.status.idle":"2024-08-25T04:58:08.100432Z","shell.execute_reply.started":"2024-08-25T04:53:37.692556Z","shell.execute_reply":"2024-08-25T04:58:08.098997Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]}]}