{"cells":[{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"code","source":"import cv2\n\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\n\nimport os\n\nimport matplotlib.pyplot as plt\n\nimport re","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"import librosa\nimport librosa.display\nfrom joblib import Parallel, delayed, cpu_count\n\nimport torchaudio\nfrom scipy import signal\n\nfrom zipfile import ZipFile\nimport gc","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"df = pd.read_csv(\"../input/birdsong-recognition/train.csv\")","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Images to Spectogram arrays every 5 seconds of audio\nIf audio is less than 5 seconds first, then it is assumed to have a bird call (so no_call cannot be there) and is zeropadded to complete the 5 seconds of audio. \nIn the other case when audio is more that 5 seconds if the audio has a standard deviation inferior to $0.005$ and a mean smaller than $0.005$ it is assumed to have no call (just white noise), this is very simplistic as a model, one might even want to test with statistical test if the audio is a white noise, but with kaggle limitation on computation time, this seems the only easy way out.\n\nConvert all the audio files into spectogram as numpy arrays, this has several advantages such as having temporal infomation and you can split the spectrogram based on time later during the training solving one of the issues regarding the different length in audio.\n\nI am saving here as numpy compressed format and as `int8` because I am limited by the kaggle disk space of 5GB, this will slow down the loading time of the spectrogram since it will need to decompress the arrays and I am losing some information saving the values as int instead of `float`. If you have the possibility to use it on you personal PC please change the only one line of code:\n\n`\nnp.savez_compressed(image_fname, np.round(log_S,0).astype(\"int8\"))\n`\n\nto \n\n`\nnp.save(image_fname, log_S)\n`","execution_count":null},{"metadata":{"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","trusted":true},"cell_type":"code","source":"zipFile = ZipFile('birdsong.zip', 'w')\n\nSPEC_TIME = 5\nMIN_STD = 0.005\nMIN_MEAN = 0.005\n\ndef load_audio(path, sr):\n    \"This Loads the audio file faster than the librosa library!\"\n    x, x_sr = torchaudio.load(path)\n    x = x.numpy()\n\n    if x.ndim > 1:\n        x = np.mean(x, axis=0)\n        \n    if sr:\n        x = signal.resample(x, int(x.size*float(sr)/x_sr))\n        x_sr = sr\n    return x, x_sr\n\ndef save_spectrogram(audio_fname):\n    \"\"\"\n    This will save in the the numpy file instead of image so the \n    difference of times can be managed later (or may be used by a RNN)\n    \"\"\"\n    try:\n        image_fname = audio_fname.replace(\"/input/\",\n                                          \"/working/\").replace(\"_audio/\", \n                                                               \"_npy/\").replace(\".mp3\",\n                                                                                  \".npz\")\n        fname = re.search(\"/([a-zA-Z0-9\\.]+$)\", audio_fname).group().strip(\"/\")\n        sr_ = 32000\n        #sr_ = int(df.sampling_rate[df.filename==fname].values[0].replace(\" (Hz)\", \"\"))\n        \n        y, sr = load_audio(audio_fname, sr=sr_)\n        duration = np.floor(len(y)/sr).astype(int)\n        \n        if duration >= SPEC_TIME:\n            splits = int(duration/SPEC_TIME)\n            for split in range(splits):\n                y_temp = y[split*sr_*SPEC_TIME:(split+1)*sr_*SPEC_TIME]\n                if np.std(y_temp) < MIN_STD and np.mean(y_temp) < MIN_MEAN:\n                    last = f\"_{split}_nc.npz\"\n                else:\n                    last = f\"_{split}.npz\"\n                fname_temp = image_fname.replace(\".npz\", last)\n                \n                S = librosa.feature.melspectrogram(y_temp, \n                                                   sr=sr, \n                                                   n_mels=96)\n                log_S = librosa.power_to_db(S, ref=np.max)\n                \n                np.savez_compressed(fname_temp, log_S.astype(\"int8\"))\n        else:\n            y_temp = np.zeros((SPEC_TIME*sr,))\n            y_temp[:y.shape[0]] = y\n            \n            fname_temp = image_fname.replace(\".npz\", \"_0.npz\")\n                \n            S = librosa.feature.melspectrogram(y_temp, \n                                               sr=sr, \n                                               n_mels=96)\n            log_S = librosa.power_to_db(S, ref=np.max)\n\n            np.savez_compressed(fname_temp, log_S.astype(\"int8\"))\n        \n        \n        #This part will save the images instead!\n        #librosa.display.specshow(log_S, sr=sr, x_axis='time', y_axis='mel')\n        #fig1 = plt.gcf()\n        #plt.axis('off')\n        #fig1.savefig(image_fname, bbox_inches='tight', \n        #             pad_inches=0, dpi=100)\n    except Exception as e:\n        print(str(e) + \"WTH!?\"+image_fname)\n        pass\n    \ndef create_data(path, parallel=False):\n    count = 0\n    for dirname, _, filenames in os.walk(path):\n        count += 1\n        labels = []\n        features = []\n        image_dir = dirname.replace(\"/input/\",\"/working/\").replace(\"_audio\", \"_npy\")\n        if not os.path.exists(image_dir):\n            os.mkdir(image_dir)\n        print(\"doing \" + dirname + \" number: \" + str(count) + \"\\t\")\n        if parallel:\n            all_paths = []\n            for i, filename in enumerate(filenames):\n                all_paths.append(os.path.join(dirname, filename))\n            Parallel(n_jobs=cpu_count(), verbose=0)(delayed(save_spectrogram)(audio_fname=path) for path in all_paths)        \n        else:\n            all_paths = []\n            for i, filename in enumerate(filenames):\n                print(\"done \" + str(filename), end=\"\\r\")\n                all_paths.append(os.path.join(dirname, filename))\n                save_spectrogram(os.path.join(dirname, filename))\n                \n        for dir_, _, files_ in os.walk(image_dir):\n            for file_ in files_:\n                zipFile.write(os.path.join(dir_,file_))\n                \n        if image_dir != \"/kaggle/working/birdsong-recognition/train_npy/\":     \n            os.system(f\"rm -r {image_dir}\")\n            \n        os.system('echo $(df -h /kaggle/working)')\n        gc.collect()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_kg_hide-output":true},"cell_type":"code","source":"%%time\n\nif not os.path.exists(\"/kaggle/working/birdsong-recognition/\"):\n    os.mkdir(\"/kaggle/working/birdsong-recognition/\")\nelse:\n    os.system(\"rm -r /kaggle/working/birdsong-recognition/\")\n    os.mkdir(\"/kaggle/working/birdsong-recognition/\")\n\ntrain_path = \"/kaggle/input/birdsong-recognition/train_audio/\"\ncreate_data(train_path, parallel=True)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"zipFile.close()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"One of the questions on this notebook was the performace, in terms of time I gained using the Parallel processing of the audios instead of one at a time, well on the 4 CPUs of kaggle it ain't much but it should be a lot while using a decent PC with more processors and the other advantage (probably the bigger one) is the reduction in RAM memory usage since python has to free and return the memory to the system after the completion of each process (so each audio) while processing one audio at a time, python prefers to keep some of freed memory for itself after the completion and after a while a crash can occur due to excess memory (RAM) usage.  ","execution_count":null}],"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":4,"nbformat_minor":4}