{"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":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"\nNotebook credits go to: Kadircan İdrisoğlu, https://www.kaggle.com/code/kadircandrisolu/transforming-audio-to-mel-spec-birdclef-25\n\nI simply coppied the notebook and created spectrograms of size 224x224 instead of 256x256\n\n# **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-03T07:21:54.543249Z","iopub.execute_input":"2025-05-03T07:21:54.543744Z","iopub.status.idle":"2025-05-03T07:21:54.549344Z","shell.execute_reply.started":"2025-05-03T07:21:54.543697Z","shell.execute_reply":"2025-05-03T07:21:54.548187Z"}},"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 = (224, 224)  \n    \n    N_MAX = 50 if DEBUG_MODE else None  \n\nconfig = Config()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-03T07:21:54.550860Z","iopub.execute_input":"2025-05-03T07:21:54.551277Z","iopub.status.idle":"2025-05-03T07:21:54.575456Z","shell.execute_reply.started":"2025-05-03T07:21:54.551234Z","shell.execute_reply":"2025-05-03T07:21:54.574338Z"}},"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-03T07:21:54.577738Z","iopub.execute_input":"2025-05-03T07:21:54.578147Z","iopub.status.idle":"2025-05-03T07:21:54.706915Z","shell.execute_reply.started":"2025-05-03T07:21:54.578096Z","shell.execute_reply":"2025-05-03T07:21:54.705786Z"}},"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))\n# total_sampels = config.N_MAX if not None else 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-03T07:21:54.708145Z","iopub.execute_input":"2025-05-03T07:21:54.708500Z","iopub.status.idle":"2025-05-03T07:21:54.764277Z","shell.execute_reply.started":"2025-05-03T07:21:54.708462Z","shell.execute_reply":"2025-05-03T07:21:54.763307Z"}},"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-03T07:21:54.765285Z","iopub.execute_input":"2025-05-03T07:21:54.765570Z","iopub.status.idle":"2025-05-03T07:21:54.771408Z","shell.execute_reply.started":"2025-05-03T07:21:54.765546Z","shell.execute_reply":"2025-05-03T07:21:54.770458Z"}},"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\n# Save all spectrograms to a .npy file\noutput_path = os.path.join(config.OUTPUT_DIR, 'birdclef2025_melspec_5sec_224_224.npy')\nprint(f\"Saving all spectrograms to {output_path}\")\nnp.save(output_path, all_bird_data)\nprint(f\"Successfully saved {len(all_bird_data)} spectrograms to {output_path}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-03T07:22:06.995298Z","iopub.execute_input":"2025-05-03T07:22:06.995659Z","iopub.status.idle":"2025-05-03T07:52:09.757286Z","shell.execute_reply.started":"2025-05-03T07:22:06.995632Z","shell.execute_reply":"2025-05-03T07:52:09.754325Z"}},"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-03T07:22:02.203198Z","iopub.status.idle":"2025-05-03T07:22:02.203541Z","shell.execute_reply":"2025-05-03T07:22:02.203407Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}