{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.12","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"none","dataSources":[{"sourceId":91844,"databundleVersionId":11361821,"sourceType":"competition"}],"dockerImageVersionId":30918,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# **BirdCLEF 2025 Data Preprocessing Notebook**\nThis notebook demonstrates how we can transform audio data into mel-spectrogram data. This transformation is essential for training 2D Convolutional Neural Networks (CNNs) on audio data, as it converts the one-dimensional audio signals into two-dimensional image-like representations.\nI run this public notebook in debug mode(only a few sample processing). You can find the fully preprocessed mel spectrogram training dataset here --> [BirdCLEF'25 | Mel Spectrograms](https://www.kaggle.com/datasets/kadircandrisolu/birdclef25-mel-spectrograms).\n","metadata":{}},{"cell_type":"code","source":"import os\nimport cv2\nimport math\nimport time\nimport librosa\nimport pandas as pd\nimport numpy as np\nfrom tqdm.notebook import tqdm\n\nimport torch\nimport warnings\nwarnings.filterwarnings(\"ignore\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-19T14:44:27.975820Z","iopub.execute_input":"2025-05-19T14:44:27.976177Z","iopub.status.idle":"2025-05-19T14:44:33.527206Z","shell.execute_reply.started":"2025-05-19T14:44:27.976141Z","shell.execute_reply":"2025-05-19T14:44:33.526176Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class Config:\n \n    DEBUG_MODE = False\n    \n    OUTPUT_DIR = '/kaggle/working/'\n    DATA_ROOT = '/kaggle/input/birdclef-2025'\n    FS = 32000\n    \n    # Mel spectrogram parameters\n    N_FFT = 1024\n    HOP_LENGTH = 512\n    N_MELS = 128\n    FMIN = 50\n    FMAX = 14000\n    \n    TARGET_DURATION = 5.0\n    TARGET_SHAPE = (256, 256)  \n    \n    N_MAX = 50 if DEBUG_MODE else None  \n\nconfig = Config()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-19T14:44:33.528324Z","iopub.execute_input":"2025-05-19T14:44:33.528803Z","iopub.status.idle":"2025-05-19T14:44:33.534409Z","shell.execute_reply.started":"2025-05-19T14:44:33.528760Z","shell.execute_reply":"2025-05-19T14:44:33.532882Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(f\"Debug mode: {'ON' if config.DEBUG_MODE else 'OFF'}\")\nprint(f\"Max samples to process: {config.N_MAX if config.N_MAX is not None else 'ALL'}\")\n\nprint(\"Loading taxonomy data...\")\ntaxonomy_df = pd.read_csv(f'{config.DATA_ROOT}/taxonomy.csv')\nspecies_class_map = dict(zip(taxonomy_df['primary_label'], taxonomy_df['class_name']))\n\nprint(\"Loading training metadata...\")\ntrain_df = pd.read_csv(f'{config.DATA_ROOT}/train.csv')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-19T14:44:33.535630Z","iopub.execute_input":"2025-05-19T14:44:33.535960Z","iopub.status.idle":"2025-05-19T14:44:33.772922Z","shell.execute_reply.started":"2025-05-19T14:44:33.535929Z","shell.execute_reply":"2025-05-19T14:44:33.771796Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"label_list = sorted(train_df['primary_label'].unique())\nlabel_id_list = list(range(len(label_list)))\nlabel2id = dict(zip(label_list, label_id_list))\nid2label = dict(zip(label_id_list, label_list))\n\nprint(f'Found {len(label_list)} unique species')\nworking_df = train_df[['primary_label', 'rating', 'filename']].copy()\nworking_df['target'] = working_df.primary_label.map(label2id)\nworking_df['filepath'] = config.DATA_ROOT + '/train_audio/' + working_df.filename\nworking_df['samplename'] = working_df.filename.map(lambda x: x.split('/')[0] + '-' + x.split('/')[-1].split('.')[0])\nworking_df['class'] = working_df.primary_label.map(lambda x: species_class_map.get(x, 'Unknown'))\ntotal_samples = min(len(working_df), config.N_MAX or len(working_df))\nprint(f'Total samples to process: {total_samples} out of {len(working_df)} available')\nprint(f'Samples by class:')\nprint(working_df['class'].value_counts())","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-19T14:44:33.775020Z","iopub.execute_input":"2025-05-19T14:44:33.775441Z","iopub.status.idle":"2025-05-19T14:44:33.852834Z","shell.execute_reply.started":"2025-05-19T14:44:33.775404Z","shell.execute_reply":"2025-05-19T14:44:33.851854Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def audio2melspec(audio_data):\n    if np.isnan(audio_data).any():\n        mean_signal = np.nanmean(audio_data)\n        audio_data = np.nan_to_num(audio_data, nan=mean_signal)\n\n    mel_spec = librosa.feature.melspectrogram(\n        y=audio_data,\n        sr=config.FS,\n        n_fft=config.N_FFT,\n        hop_length=config.HOP_LENGTH,\n        n_mels=config.N_MELS,\n        fmin=config.FMIN,\n        fmax=config.FMAX,\n        power=2.0\n    )\n\n    mel_spec_db = librosa.power_to_db(mel_spec, ref=np.max)\n    mel_spec_norm = (mel_spec_db - mel_spec_db.min()) / (mel_spec_db.max() - mel_spec_db.min() + 1e-8)\n    \n    return mel_spec_norm","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-19T14:44:33.853977Z","iopub.execute_input":"2025-05-19T14:44:33.854380Z","iopub.status.idle":"2025-05-19T14:44:33.860596Z","shell.execute_reply.started":"2025-05-19T14:44:33.854340Z","shell.execute_reply":"2025-05-19T14:44:33.859525Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(\"Starting audio processing...\")\nprint(f\"{'DEBUG MODE - Processing only 50 samples' if config.DEBUG_MODE else 'FULL MODE - Processing all samples'}\")\nstart_time = time.time()\n\nall_bird_data = {}\nerrors = []\n\nfor i, row in tqdm(working_df.iterrows(), total=total_samples):\n    if config.N_MAX is not None and i >= config.N_MAX:\n        break\n    \n    try:\n        audio_data, _ = librosa.load(row.filepath, sr=config.FS)\n\n        target_samples = int(config.TARGET_DURATION * config.FS)\n\n        if len(audio_data) < target_samples:\n            n_copy = math.ceil(target_samples / len(audio_data))\n            if n_copy > 1:\n                audio_data = np.concatenate([audio_data] * n_copy)\n\n        start_idx = max(0, int(len(audio_data) / 2 - target_samples / 2))\n        end_idx = min(len(audio_data), start_idx + target_samples)\n        center_audio = audio_data[start_idx:end_idx]\n\n        if len(center_audio) < target_samples:\n            center_audio = np.pad(center_audio, \n                                 (0, target_samples - len(center_audio)), \n                                 mode='constant')\n\n        mel_spec = audio2melspec(center_audio)\n\n        if mel_spec.shape != config.TARGET_SHAPE:\n            mel_spec = cv2.resize(mel_spec, config.TARGET_SHAPE, interpolation=cv2.INTER_LINEAR)\n\n        all_bird_data[row.samplename] = mel_spec.astype(np.float32)\n        \n    except Exception as e:\n        print(f\"Error processing {row.filepath}: {e}\")\n        errors.append((row.filepath, str(e)))\n\nend_time = time.time()\nprint(f\"Processing completed in {end_time - start_time:.2f} seconds\")\nprint(f\"Successfully processed {len(all_bird_data)} files out of {total_samples} total\")\nprint(f\"Failed to process {len(errors)} files\")\n\nimport numpy as np\n\noutput_file = os.path.join(config.OUTPUT_DIR,\n                           'birdclef2025_melspec.npy')\nnp.save(output_file, all_bird_data, allow_pickle=True)\nprint(f\"Saved {len(all_bird_data)} mel-spectrograms to:\\n  {output_file}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-19T14:44:33.861614Z","iopub.execute_input":"2025-05-19T14:44:33.861912Z","iopub.status.idle":"2025-05-19T15:16:51.576799Z","shell.execute_reply.started":"2025-05-19T14:44:33.861887Z","shell.execute_reply":"2025-05-19T15:16:51.574443Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import matplotlib.pyplot as plt\n\nsamples = []\ndisplayed_classes = set()\n\nmax_samples = min(4, len(all_bird_data))\n\nfor i, row in working_df.iterrows():\n    if i >= (config.N_MAX or len(working_df)):\n        break\n        \n    if row['samplename'] in all_bird_data:\n        if config.DEBUG_MODE:\n            if row['class'] not in displayed_classes:\n                samples.append((row['samplename'], row['class'], row['primary_label']))\n                displayed_classes.add(row['class'])\n        else:\n            if row['class'] not in displayed_classes:\n                samples.append((row['samplename'], row['class'], row['primary_label']))\n                displayed_classes.add(row['class'])\n        \n        if len(samples) >= max_samples:  \n            break\n\nif samples:\n    plt.figure(figsize=(16, 12))\n    \n    for i, (samplename, class_name, species) in enumerate(samples):\n        plt.subplot(2, 2, i+1)\n        plt.imshow(all_bird_data[samplename], aspect='auto', origin='lower', cmap='viridis')\n        plt.title(f\"{class_name}: {species}\")\n        plt.colorbar(format='%+2.0f dB')\n    \n    plt.tight_layout()\n    debug_note = \"debug_\" if config.DEBUG_MODE else \"\"\n    plt.savefig(f'{debug_note}melspec_examples.png')\n    plt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-19T15:16:51.579770Z","iopub.execute_input":"2025-05-19T15:16:51.582609Z","iopub.status.idle":"2025-05-19T15:16:56.026185Z","shell.execute_reply.started":"2025-05-19T15:16:51.582545Z","shell.execute_reply":"2025-05-19T15:16:56.024901Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}