{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"none","dataSources":[{"sourceId":59093,"databundleVersionId":7469972,"sourceType":"competition"},{"sourceId":163169665,"sourceType":"kernelVersion"}],"dockerImageVersionId":30664,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"![image.png](attachment:6de8d68a-8c01-4b7b-b36e-57087b37bc96.png)","metadata":{},"attachments":{"6de8d68a-8c01-4b7b-b36e-57087b37bc96.png":{"image/png":"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QIAAAQLHBLonBn/CE53HsrA3AQIECBAgQIAAAQIErhNQ4L7OUksECBAgQIAAgZcJ5J+6z09v58+DWAgQIECAAAECBAgQIPDJAgrcnzz6cidAgAABAgRuI9D9z93yd24tBAgQIECAAAECBAgQ+GQBBe5PHn25EyBAgAABArcQ6P7nknmaO+9bCBAgQIAAAQIECBAg8MkCCtyfPPpyJ0CAAAECBG4h0P3PJf/Fv/uvt4hdkAQIECBAgAABAgQIEHilgAL3K3W1TYAAAQIECBC4QCB/a3v++9v//r/8nwta1gQBAgQIECBAgAABAgTuLaDAfe/xEz0BAgQIECDw5gJ/8z/+7+8Vt//Zv/qbN89aegQIECBAgAABAgQIENgnoMC9z8leBAgQIECAAIFvEej+55L/8j/892+JRacECBAgQIAAAQIECBD4aQIK3D9tRMRDgAABAgQIEPgngfxPJPO09vznSfJUt4UAAQIECBAgQIAAAQIEfvlFgdssIECAAAECBAj8UIF/+5/+1+8Vt//5v/7PPzRaYREgQIAAAQIECBAgQODrBRS4v95cjwQIECBAgACBXQLd/1zy3/zH/7nrWDsRIECAAAECBAgQIEDgEwQUuD9hlOVIgAABAgQI3E6g+59L5k+V/Lf//f9ul4uACRAgQIAAAQIECBAg8CoBBe5XyWqXAAECBAgQIPCEQP7+dn2CO3+aJH+yxEKAAAECBAgQIECAAAECDwEF7oeFLQIECBAgQIAAAQIECBAgQIAAAQIECBC4kYAC940GS6gECBAgQIAAAQIECBAgQIAAAQIECBAg8BBQ4H5Y2CJAgAABAgQIECBAgAABAgQIECBAgACBGwkocN9osIRKgAABAgQIECBAgAABAgQIECBAgAABAg8BBe6HhS0CBAgQIECAAAECBAgQIECAAAECBAgQuJGAAveNBkuoBAgQIECAAAECBAgQIECAAAECBAgQIPAQUOB+WNgiQIAAAQIECBAgQIAAAQIECBAgQIAAgRsJKHDfaLCESoAAAQIECBAgQIAAAQIECBAgQIAAAQIPAQXuh4UtAgQIECBAgAABAgQIECBAgAABAgQIELiRgAL3jQZLqAQIECBAgAABAgQIECBAgAABAgQIECDwEFDgfljYIkCAAAECBAgQIECAAAECBAgQIECAAIEbCShw32iwhEqAAAECBAgQIECAAAECBAgQIECAAAECDwEF7oeFLQIECBAgQIAAAQIECBAgQIAAAQIECBC4kYAC940GS6gECBAgQIAAAQIECBAgQIAAAQIECBAg8BBQ4H5Y2CJAgAABAgQIECBAgAABAgQIECBAgACBGwkocN9osIRKgAABAgQIECBAgAABAgQIECBAgAABAg8BBe6HhS0CBAgQIECAAAECBAgQIECAAAECBAgQuJGAAveNBkuoBAgQIECAAAECBAgQIECAAAECBAgQIPAQUOB+WNgiQIAAAQIECBAgQIAAAQIECBAgQIAAgRsJKHDfaLCESoAAAQIECBAgQIAAAQIECBAgQIAAAQIPAQXuh4UtAgQIECBAgAABAgQIECBAgAABAgQIELiRgAL3jQZLqAQIECBAgAABAgQIECBAgAABAgQIECDwEFDgfljYIkCAAAECBAgQIECAAAECBAgQIECAAIEbCShw32iwhEqAAAECBAgQIECAAAECBAgQIECAAAECDwEF7oeFLQIECBAgQIAAAQIECBAgQIAAAQIECBC4kYAC940GS6gECBAgQIAAAQIECBAgQIAAAQIECBAg8BBQ4H5Y2CJAgAABAgQIECBAgAABAgQIECBAgACBGwkocN9osIRKgAABAgQIECBAgAABAgQIECBAgAABAg8BBe6HhS0CBAgQIECAAAECBAgQIECAAAECBAgQuJGAAveNBkuoBAgQIECAAAECBAgQIECAAAECBAgQIPAQUOB+WNgiQIAAAQIECBAgQIAAAQIECBAgQIAAgRsJKHDfaLCESoAAAQIECBAgQIAAAQIECBAgQIAAAQIPAQXuh4UtAgQIECBAgAABAgQIECBAgAABAgQIELiRgAL3jQZLqAQIECBAgAABAgQIECBAgAABAgQIECDwEFDgfljYIkCAAAECBAgQIECAAAECBAgQIECAAIEbCShw32iwhEqAAAECBAgQIECAAAECBAgQIECAAAECDwEF7oeFLQIECBAgQIAAAQIECBAgQIAAAQIECBC4kYAC940GS6gECBAgQIAAAQIECBAgQIAAAQIECBAg8BBQ4H5Y2CJAgAABAgQIECBAgAABAgQIECBAgACBGwkocN9osIRKgAABAgQIECBAgAABAgQIECBAgAABAg8BBe6HhS0CBAgQIECAAAECBAgQIECAAAECBAgQuJGAAveNBkuoBAgQIECAAAECBAgQIECAAAECBAgQIPAQUOB+WNgiQIAAAQIECBAgQIAAAQIECBAgQIAAgRsJKHDfaLCESoAAAQIECBAgQIAAAQIECBAgQIAAAQIPAQXuh4UtAgQIECBAgAABAgQIECBAgAABAgQIELiRgAL3jQZLqAQIECBAgAABAgQIECBAgAABAgQIECDwEFDgfljYIkCAAAECBAgQIECAAAECBAgQIECAAIEbCShw32iwhEqAAAECBAgQIECAAAECBAgQIECAAAECDwEF7oeFLQIECBAgQIAAAQIECBAgQIAAAQIECBC4kYAC940GS6gECBAgQIAAAQIECBAgQIAAAQIECBAg8BBQ4H5Y2CJAgAABAgQIECBAgAABAgQIECBAgACBGwkocN9osIRKgAABAgQIECBAgAABAgQIECBAgAABAg8BBe6HhS0CBAgQIECAAAECBAgQIECAAAECBAgQuJGAAveNBkuoBAgQIECAAAECBAgQIECAAAECBAgQIPAQUOB+WNgiQIAAAQIECBAgQIAAAQIECBAgQIAAgRsJKHDfaLCESoAAAQIECBAgQIAAAQIECBAgQIAAAQIPAQXuh4UtAgQIECBAgAABAgQIECBAgAABAgQIELiRgAL3jQZLqAQIECBAgAABAgQIECBAgAABAgQIECDwEFDgfljYIkCAAAECBAgQIECAAAECBAgQIECAAIEbCShw32iwhEqAAAECBAgQIECAAAECBAgQIECAAAECDwEF7oeFLQIECBAgQIAAAQIECBAgQIAAAQIECBC4kYAC940GS6gECBAgQIAAAQIECBAgQIAAAQIECBAg8BBQ4H5Y2CJAgAABAgQIECBAgAABAgQIECBAgACBGwkocN9osIRKgAABAgQIECBAgAABAgQIECBAgAABAg8BBe6HhS0CBAgQIECAAAECBAgQIECAAAECBAgQuJGAAveNBkuoBAgQIECAAAECBAgQIECAAAECBAgQIPAQUOB+WNgiQIAAAQIECBAgQIAAAQIECBAgQIAAgRsJKHDfaLCESoAAAQIECBAgQIAAAQIECBAgQIAAAQIPAQXuh4UtAgQIECBAgAABAgQIECBAgAABAgQIELiRgAL3jQZLqAQIECBAgAABAgQIECBAgAABAgQIECDwEFDgfljYIkCAAAECBAgQIECAAAECBAgQIECAAIEbCShw32iwhEqAAAECBAgQIECAAAECBAgQIECAAAECDwEF7oeFLQIECBAgQIAAAQIECBAgQIAAAQIECBC4kYAC940GS6gECBAgQIAAAQIECBAgQIAAAQIECBAg8BBQ4H5Y2CJAgAABAgQIECBAgAABAgQIECBAgACBGwkocN9osIRKgAABAgQIECBAgAABAgQIECBAgAABAg8BBe6HhS0CBAgQIECAAAECBAgQIECAAAECBAgQuJGAAveNBkuoBAgQIECAAAECBAgQIECAAAECBAgQIPAQUOB+WNgiQIAAAQIECBAgQIAAAQIECBAgQIAAgRsJKHDfaLCESoAAAQIECBAgQIAAAQIECBAgQIAAAQIPAQXuh4UtAgQIECBAgAABAgQIECBAgAABAgQIELiRgAL3jQZLqAQIECBAgAABAgQIECBAgAABAgQIECDwEFDgfljYIkCAAAECBAgQIECAAAECBAgQIECAAIEbCShw32iwhEqAAAECBAgQIECAAAECBAgQIECAAAECDwEF7oeFLQIECBAgQIAAAQIECBAgQIAAAQIECBC4kYAC940GS6gECBAgQIAAAQIECBAgQIAAAQIECBAg8BBQ4H5Y2CJAgAABAgQIECBAgAABAgQIECBAgACBGwkocN9osIRKgAABAgQIECBAgAABAgQIECBAgAABAg8BBe6HhS0CBAgQIECAAAECBAgQIECAAAECBAgQuJGAAveNFMbxLQAAIABJREFUBkuoBAgQIECAAAECBAgQIECAAAECBAgQIPAQUOB+WNgiQIAAAQIECBAgQIAAAQIECBAgQIAAgRsJKHDfaLCESoAAAQIECBAgQIAAAQIECBAgQIAAAQIPAQXuh4UtAgQIECBAgAABAgQIECBAgAABAgQIELiRgAL3jQZLqAQIECBAgAABAgQIECBAgAABAgQIECDwEFDgfljYIkCAAAECBAgQIECAAAECBAgQIECAAIEbCShw32iwhEqAAAECBAgQIECAAAECBAgQIECAAAECDwEF7oeFLQIECBAgQIAAAQIECBAgQIAAAQIECBC4kYAC940GS6gECBAgQIAAAQIECBAgQIAAAQIECBAg8BBQ4H5Y2CJAgAABAgQIXC7wd3/3d7/82Z/92S9/+Id/+Msf/MEf/MPPn/zJn/zyu9/97vK+0mD6Gv28qo+XBK5RAgT+QcA5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AAECBAgQIEDg7gL1idU8/WohQIAAAQIECBC4v4AC9/3HUAYECBAgQIAAAQIECGwI7PlzLxtN+JgAAQIECBAgQOAHCihw/8BBERIBAgQIECBAgAABAtcK5O80jz9Pkr9TbCFAgAABAgQIEHgPAQXu9xhHWRAgQIAAAQIECBAgsCIw/ueFKXKP/0ngyu4+IkCAAAECBAgQuImAAvdNBkqYBAgQIECAAAECBAicE/jd737369PbKXTnz5VYCBAgQIAAAQIE3kNAgfs9xlEWBAgQIECAAAECBAgQIECAAAECBAgQ+DgBBe6PG3IJEyBAgAABAgQIECBAgAABAgQIECBA4D0EFLjfYxxlQYAAAQIECBAgQIAAAQIECBAgQIAAgY8TUOD+uCGXMAECBAgQIECAAAECBAgQIECAAAECBN5DQIH7PcZRFgQIECBAgAABAgQIECBAgAABAgQIEPg4AQXujxtyCRMgQIAAAQIECBAgQIAAAQIECBAgQOA9BBS432McZUGAAAECBAgQIECAAAECBAgQIECAAIGPE1Dg/rghlzABAgQIECBAgAABAgQIECBAgAABAgTeQ0CB+z3GURYECBAgQIAAAQIECBAgQIAAAQIECBD4OAEF7o8bcgkTIECAAAECBAgQIECAAAECBAgQIEDgPQQUuN9jHGVBgAABAgQIECBAgAABAgQIECBAgACBjxNQ4P64IZcwAQIECBAgQIAAAQIECBAgQIAAAQIE3kNAgfs9xlEWBAgQIECAAAECBAgQIECAAAECBAgQ+DgBBe6PG3IJEyBAgAABAgQIECBAgAABAgQIECBA4D0EFLjfYxxlQYAAAQIECBAgQIAAAQIECBAgQIAAgY8TUOB4MzddAAAILElEQVT+uCGXMAECBAgQIECAAAECBAgQIECAAAECBN5DQIH7PcZRFgQIECBAgAABAgQIECBAgAABAgQIEPg4AQXujxtyCRMgQIAAAQIECBAgQIAAAQIECBAgQOA9BBS432McZUGAAAECBAgQIECAAAECBAgQIECAAIGPE1Dg/rghlzABAgQIECBAgAABAgQIECBAgAABAgTeQ0CB+z3GURYECBAgQIAAAQIECBAgQIAAAQIECBD4OAEF7o8bcgkTIECAAAECBAgQIECAAAECBAgQIEDgPQQUuN9jHGVBgAABAgQIECBAgAABAgQIECBAgACBjxNQ4P64IZcwAQIECBAgQIAAAQIECBAgQIAAAQIE3kNAgfs9xlEWBAgQIECAAAECBAgQIECAAAECBAgQ+DgBBe6PG3IJEyBAgAABAgQIECBAgAABAgQIECBA4D0EFLjfYxxlQYAAAQIECBAgQIAAAQIECBAgQIAAgY8TUOD+uCGXMAECBAgQIECAAAECBAgQIECAAAECBN5DQIH7PcZRFgQIECBAgAABAgQIECBAgAABAgQIEPg4AQXujxtyCRMgQIAAAQIECBAgQIAAAQIECBAgQOA9BBS432McZUGAAAECBAgQIECAAAECBAgQIECAAIGPE1Dg/rghlzABAgQIECBAgAABAgQIECBAgAABAgTeQ0CB+z3GURYECBAgQIAAAQIECBAgQIAAAQIECBD4OAEF7o8bcgkTIECAAAECBAgQIECAAAECBAgQIEDgPQQUuN9jHGVBgAABAgQIECBAgAABAgQIECBAgACBjxNQ4P64IZcwAQIECBAgQIAAAQIECBAgQIAAAQIE3kNAgfs9xlEWBAgQIECAAAECBAgQIECAAAECBAgQ+DgBBe6PG3IJEyBAgAABAgQIECBAgAABAgQIECBA4D0EFLjfYxxlQYAAAQIECBAgQIAAAQIECBAgQIAAgY8TUOD+uCGXMAECBAgQIECAAAECBAgQIECAAAECBN5DQIH7PcZRFgQIECBAgAABAgQIECBAgAABAgQIEPg4AQXujxtyCRMgQIAAAQIECBAgQIAAAQIECBAgQOA9BBS432McZUGAAAECBAgQIECAAAECBAgQIECAAIGPE1Dg/rghlzABAgQIECBAgAABAgQIECBAgAABAgTeQ0CB+z3GURYECBAgQIAAAQIECBAgQIAAAQIECBD4OAEF7o8bcgkTIECAAAECBAgQIECAAAECBAgQIEDgPQQUuN9jHGVBgAABAgQIECBAgAABAgQIECBAgACBjxNQ4P64IZcwAQIECBAgQIAAAQIECBAgQIAAAQIE3kNAgfs9xlEWBAgQIECAAAECBAgQIECAAAECBAgQ+DgBBe6PG3IJEyBAgAABAgQIECBAgAABAgQIECBA4D0EFLjfYxxlQYAAAQIECBAgQIAAAQIECBAgQIAAgY8TUOD+uCGXMAECBAgQIECAAAECBAgQIECAAAECBN5DQIH7PcZRFgQIECBAgAABAgQIECBAgAABAgQIEPg4AQXujxtyCRMgQIAAAQIECBAgQIAAAQIECBAgQOA9BBS432McZUGAAAECBAgQIECAAAECBAgQIECAAIGPE1Dg/rghlzABAgQIECBAgAABAgQIECBAgAABAgTeQ0CB+z3GURYECBAgQIAAAQIECBAgQIAAAQIECBD4OAEF7o8bcgkTIECAAAECBAgQIECAAAECBAgQIEDgPQQUuN9jHGVBgAABAgQIECBAgAABAgQIECBAgACBjxNQ4P64IZcwAQIECBAgQIAAAQIECBAgQIAAAQIE3kNAgfs9xlEWBAgQIECAAAECBAgQIECAAAECBAgQ+DgBBe6PG3IJEyBAgAABAgQIECBAgAABAgQIECBA4D0EFLjfYxxlQYAAAQIECBAgQIAAAQIECBAgQIAAgY8TUOD+uCGXMAECBAgQIECAAAECBAgQIECAAAECBN5DQIH7PcZRFgQIECBAgAABAgQIECBAgAABAgQIEPg4AQXujxtyCRMgQIAAAQIECBAgQIAAAQIECBAgQOA9BBS432McZUGAAAECBAgQIECAAAECBAgQIECAAIGPE1Dg/rghlzABAgQIECBAgAABAgQIECBAgAABAgTeQ0CB+z3GURYECBAgQIAAAQIECBAgQIAAAQIECBD4OAEF7o8bcgkTIECAAAECBAgQIECAAAECBAgQIEDgPQQUuN9jHGVBgAABAgQIECBAgAABAgQIECBAgACBjxNQ4P64IZcwAQIECBAgQIAAAQIECBAgQIAAAQIE3kNAgfs9xlEWBAgQIECAAAECBAgQIECAAAECBAgQ+DgBBe6PG3IJEyBAgAABAgQIECBAgAABAgQIECBA4D0EFLjfYxxlQYAAAQIECBAgQIAAAQIECBAgQIAAgY8TUOD+uCGXMAECBAgQIECAAAECBAgQIECAAAECBN5DQIH7PcZRFgQIECBAgAABAgQIECBAgAABAgQIEPg4AQXujxtyCRMgQIAAAQIECBAgQIAAAQIECBAgQOA9BBS432McZUGAAAECBAgQIECAAAECBAgQIECAAIGPE1Dg/rghlzABAgQIECBAgAABAgQIECBAgAABAgTeQ0CB+z3GURYECBAgQIAAAQIECBAgQIAAAQIECBD4OAEF7o8bcgkTIECAAAECBAgQIECAAAECBAgQIEDgPQQUuN9jHGVBgAABAgQIECBAgAABAgQIECBAgACBjxNQ4P64IZcwAQIECBAgQIAAAQIECBAgQIAAAQIE3kPg/wNKq+16qmSswwAAAABJRU5ErkJggg=="}}},{"cell_type":"markdown","source":"## Introduction","metadata":{}},{"cell_type":"markdown","source":"🙋🙋🙋This is my first competition related to signals, and I am also a beginner in this field. Therefore, I have spent some time understanding the knowledge in this domain, which I have gathered from the internet and ChatGPT. I have made every effort to ensure the accuracy of the content I have written, but if there are any errors, please feel free to point them out\n\nIn this competition, we need to process signal data and its corresponding spectrogram data, which can be treated as images. Therefore, the modules involved here include `librosa`, `torchaudio`, `audiomentations` (`torch_audiomentations`), and `keras_cv` .\n\n\nSome of the functions in these modules provide derived features, such as the ones in the librosa.feature module, while others provide feature augmentation, such as the image augmentation functions in keras_cv. These functions enrich our solutions, and I believe some of them can help improve our scores. ✨**The purpose of this article is to compile functions that can be used for signal generation and signal enhancement, and add personal annotations for easy understanding and subsequent application.**✨ (Don't worry, I will try my best to display it in code and images, and use as little text as possible to explain it)\n\n\nThrough these few days of learning, I have also gained a preliminary understanding of how to process signal data (thanks to Kaggle for organizing this competition, otherwise I might not have had the motivation to learn). I hope this notebook can help beginners like me. Learning never stops, so let’s Kaggle together! 😄😄😄","metadata":{}},{"cell_type":"markdown","source":"Key Terminologies(some of them are commonly seen parameters in functions😂):\n1. **Frequency**: It represents the number of cycles or vibrations of a periodic process repeated or oscillated per unit of time. \n2. **Sample Rate**: it refers to the frequency at which a continuous signal is discretized. \n    1. The sample rate is typically measured in Hertz (Hz) and represents the number of samples taken per second. For example, a sample rate of 1000Hz means 1000 data points are captured per second.\n    2. (Sample rate is the frequency at which analog signals are sampled, while frequency refers to the rate or periodicity of the signal itself.)\n3. **Amplitude**: It refers to the maximum deviation of a signal waveform or the maximum intensity or magnitude of a signal.\n4. **Decibel (dB)**: Decibel is a relative unit used to measure the difference in loudness between two sounds or the difference in sound intensity compared to a reference standard.\n5. **Signal-to-Noise Ratio (SNR)**: It is an indicator used to measure the relative strength between a signal and the accompanying noise. It is usually expressed in decibels (dB):\n    1. A smaller SNR indicates a lower ratio between the signal and noise, where noise dominates the final audio data, \n    2. When the SNR is greater than 1, it means that the signal’s power is greater than the noise power.\n6. **Spectrum**: Raw spectrogram, power spectrogram, and Mel-scale spectrogram are three different representations of the frequency spectrum.\n    1. Raw spectrogram is the frequency spectrum image obtained by applying Short-Time Fourier Transform (STFT) to the audio signal.\n    2. Power spectrogram is the square of the raw spectrogram, representing the power information of the spectrum.\n    3. Mel-scale spectrogram is the frequency spectrum image obtained by converting the frequencies to mel-scale.\n7. **Frequency Resolution**: Frequency resolution refers to the ability to distinguish the smallest interval or difference between two adjacent frequencies in spectral analysis. \n","metadata":{}},{"cell_type":"markdown","source":"When using Short-Time Fourier Transform (STFT), it's necessary to set several important parameters(**several params we will often see**😂):\n1. **n_fft (FFT window size)**: n_fft refers to the number of points used for the Fourier transform within each window during STFT.\n2. **Hop length**: Hop length refers to the stride length when moving the window during STFT of the signal. It represents the number of samples shifted forward during each Fourier transform of the signal.\n3. **Window length**: Window length refers to the length of each window used during STFT.\n\n","metadata":{}},{"cell_type":"markdown","source":"## Import","metadata":{}},{"cell_type":"code","source":"%matplotlib inline","metadata":{"execution":{"iopub.status.busy":"2024-02-29T07:04:59.898472Z","iopub.execute_input":"2024-02-29T07:04:59.898855Z","iopub.status.idle":"2024-02-29T07:04:59.944033Z","shell.execute_reply.started":"2024-02-29T07:04:59.898826Z","shell.execute_reply":"2024-02-29T07:04:59.942683Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from pylab import *\nimport matplotlib.pyplot as plt\nimport pandas as pd\nimport numpy as np\nimport os\nimport math \n\nimport torch\nimport tensorflow_datasets as tfds\nimport keras\nimport tensorflow as tf\n\nimport librosa\n\nimport warnings\nwarnings.filterwarnings(\"ignore\", category=RuntimeWarning)","metadata":{"execution":{"iopub.status.busy":"2024-02-29T07:04:59.946783Z","iopub.execute_input":"2024-02-29T07:04:59.947579Z","iopub.status.idle":"2024-02-29T07:05:23.409045Z","shell.execute_reply.started":"2024-02-29T07:04:59.947532Z","shell.execute_reply":"2024-02-29T07:05:23.407991Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Utils","metadata":{}},{"cell_type":"code","source":"def plot_withlogwithT(img, print_val=''):\n    '''\n    The parts with a value of 0 will be displayed as white in the graph.\n    '''\n    print(f'**************** {print_val} ****************')\n    imshow(np.log(img).T, cmap='viridis', aspect='auto', origin='lower')\n    plt.colorbar()\n    plt.show()\n    \ndef plot_withT(img, print_val=''):\n    print(f'**************** {print_val} ****************')\n    imshow(img.T, cmap='viridis', aspect='auto', origin='lower')\n    plt.colorbar()\n    plt.show()\n    \ndef plot_waveform(waveform: np.array, sample_split: int = 250, plt_title: str = '', position: str = 'start'):\n    if position == 'start':\n        pd_series = pd.Series(waveform[:sample_split])\n    elif position == 'end':\n        pd_series = pd.Series(waveform[-sample_split:])\n    else:\n        pd_series = pd.Series([])\n    \n    pd_series.plot()\n    plt.title(plt_title)\n    plt.show()\n    \n","metadata":{"execution":{"iopub.status.busy":"2024-02-29T07:05:23.410623Z","iopub.execute_input":"2024-02-29T07:05:23.411412Z","iopub.status.idle":"2024-02-29T07:05:23.422201Z","shell.execute_reply.started":"2024-02-29T07:05:23.411375Z","shell.execute_reply":"2024-02-29T07:05:23.420695Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Data Reading","metadata":{}},{"cell_type":"code","source":"sample_rate = 44100  \nduration = 5  \nfrequency = 440  \namplitude = 0.5  \n\nt = np.linspace(0, duration, int(sample_rate * duration), endpoint=False)\nwaveform = amplitude * np.sin(2 * np.pi * frequency * t)  ","metadata":{"execution":{"iopub.status.busy":"2024-02-29T07:05:23.425471Z","iopub.execute_input":"2024-02-29T07:05:23.425832Z","iopub.status.idle":"2024-02-29T07:05:23.466458Z","shell.execute_reply.started":"2024-02-29T07:05:23.425802Z","shell.execute_reply":"2024-02-29T07:05:23.465322Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"eeg = pd.read_parquet('/kaggle/input/hms-harmful-brain-activity-classification/train_eegs/1000913311.parquet')","metadata":{"execution":{"iopub.status.busy":"2024-02-29T07:05:23.468410Z","iopub.execute_input":"2024-02-29T07:05:23.468877Z","iopub.status.idle":"2024-02-29T07:05:23.666815Z","shell.execute_reply.started":"2024-02-29T07:05:23.468845Z","shell.execute_reply":"2024-02-29T07:05:23.665515Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"onespec_path = '/kaggle/input/hms-harmful-brain-activity-classification/train_spectrograms/32728416.parquet'\nonespec = pd.read_parquet(onespec_path)","metadata":{"execution":{"iopub.status.busy":"2024-02-29T07:05:23.669984Z","iopub.execute_input":"2024-02-29T07:05:23.670557Z","iopub.status.idle":"2024-02-29T07:05:23.736764Z","shell.execute_reply.started":"2024-02-29T07:05:23.670515Z","shell.execute_reply":"2024-02-29T07:05:23.735760Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"spec_img = onespec.filter(regex='^LL', axis=1).values  # (time, spec); (height, weight)\n\nspec_img_3D = np.tile(spec_img[..., None], [1, 1, 3])","metadata":{"execution":{"iopub.status.busy":"2024-02-29T07:05:23.738006Z","iopub.execute_input":"2024-02-29T07:05:23.738565Z","iopub.status.idle":"2024-02-29T07:05:23.748467Z","shell.execute_reply.started":"2024-02-29T07:05:23.738532Z","shell.execute_reply":"2024-02-29T07:05:23.747457Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plot_withlogwithT(spec_img, print_val='raw_spec with log')","metadata":{"execution":{"iopub.status.busy":"2024-02-29T07:05:23.749942Z","iopub.execute_input":"2024-02-29T07:05:23.750989Z","iopub.status.idle":"2024-02-29T07:05:24.268388Z","shell.execute_reply.started":"2024-02-29T07:05:23.750956Z","shell.execute_reply":"2024-02-29T07:05:24.267008Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plot_withT(spec_img, print_val='raw_spec')","metadata":{"execution":{"iopub.status.busy":"2024-02-29T07:05:24.270316Z","iopub.execute_input":"2024-02-29T07:05:24.271055Z","iopub.status.idle":"2024-02-29T07:05:24.669580Z","shell.execute_reply.started":"2024-02-29T07:05:24.271008Z","shell.execute_reply":"2024-02-29T07:05:24.668357Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## <p style = \"font-family: Georgia;font-weight: bold; font-size: 30px; color: #1192AA; text-align:left\">Modules Import</p>\n\n","metadata":{}},{"cell_type":"code","source":"import torchaudio\nfrom torchaudio.utils import download_asset\n\nprint(\"torchaudio's version: \", torchaudio.__version__)","metadata":{"execution":{"iopub.status.busy":"2024-02-29T07:05:24.674466Z","iopub.execute_input":"2024-02-29T07:05:24.674868Z","iopub.status.idle":"2024-02-29T07:05:25.499621Z","shell.execute_reply.started":"2024-02-29T07:05:24.674837Z","shell.execute_reply":"2024-02-29T07:05:25.498062Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import albumentations \nprint(\"albumentations's version: \", albumentations.__version__)","metadata":{"execution":{"iopub.status.busy":"2024-02-29T07:05:25.501791Z","iopub.execute_input":"2024-02-29T07:05:25.502683Z","iopub.status.idle":"2024-02-29T07:05:26.372457Z","shell.execute_reply.started":"2024-02-29T07:05:25.502637Z","shell.execute_reply":"2024-02-29T07:05:26.370989Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"!pip install -U albumentations","metadata":{"execution":{"iopub.status.busy":"2024-02-29T07:05:26.374235Z","iopub.execute_input":"2024-02-29T07:05:26.375030Z","iopub.status.idle":"2024-02-29T07:05:43.446876Z","shell.execute_reply.started":"2024-02-29T07:05:26.374995Z","shell.execute_reply":"2024-02-29T07:05:43.445641Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import keras_cv\nprint(\"KerasCV's version: \", keras_cv.__version__)","metadata":{"execution":{"iopub.status.busy":"2024-02-29T07:05:43.449132Z","iopub.execute_input":"2024-02-29T07:05:43.449690Z","iopub.status.idle":"2024-02-29T07:05:44.001676Z","shell.execute_reply.started":"2024-02-29T07:05:43.449635Z","shell.execute_reply":"2024-02-29T07:05:44.000433Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"!pip install -U torch_audiomentations","metadata":{"execution":{"iopub.status.busy":"2024-02-29T07:05:44.003194Z","iopub.execute_input":"2024-02-29T07:05:44.003605Z","iopub.status.idle":"2024-02-29T07:06:03.948654Z","shell.execute_reply.started":"2024-02-29T07:05:44.003571Z","shell.execute_reply":"2024-02-29T07:06:03.947300Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"!pip install audiomentations","metadata":{"execution":{"iopub.status.busy":"2024-02-29T07:06:03.951383Z","iopub.execute_input":"2024-02-29T07:06:03.951933Z","iopub.status.idle":"2024-02-29T07:06:20.093853Z","shell.execute_reply.started":"2024-02-29T07:06:03.951877Z","shell.execute_reply":"2024-02-29T07:06:20.092204Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import audiomentations\nprint(\"audiomentations's version: \", audiomentations.__version__)\n\nimport torch_audiomentations\nprint(\"torch_audiomentations's version: \", torch_audiomentations.__version__)","metadata":{"execution":{"iopub.status.busy":"2024-02-29T07:06:20.096161Z","iopub.execute_input":"2024-02-29T07:06:20.097954Z","iopub.status.idle":"2024-02-29T07:06:20.335902Z","shell.execute_reply.started":"2024-02-29T07:06:20.097899Z","shell.execute_reply":"2024-02-29T07:06:20.334404Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import librosa","metadata":{"execution":{"iopub.status.busy":"2024-02-29T07:06:20.337851Z","iopub.execute_input":"2024-02-29T07:06:20.338455Z","iopub.status.idle":"2024-02-29T07:06:20.345282Z","shell.execute_reply.started":"2024-02-29T07:06:20.338411Z","shell.execute_reply":"2024-02-29T07:06:20.343785Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## <p style = \"font-family: Georgia;font-weight: bold; font-size: 30px; color: #1192AA; text-align:left\">librosa</p>","metadata":{}},{"cell_type":"markdown","source":"doc: https://github.com/librosa/librosa#Documentation\n\nLibrosa is a Python library for audio and music analysis. It provides a wide range of functions and tools for tasks such as audio loading, feature extraction, and audio manipulation.\n\nIn this notebook, I foucs on functions in `librosa.feature`, in which we can generate more features about signal. (On the other hand, this module includes many functions for processing music signal data, which will not be involved here)","metadata":{}},{"cell_type":"code","source":"# dir(librosa.feature)","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## - poly_features\n\n- Type of input data: **frequency domain**\n- Function's role: AddNewFeature\n\nThe function `librosa.feature.poly_features` is used to obtain the coefficients of fitting an n-th order polynomial to the columns of a spectrogram. This function computes the polynomial coefficients for each column of the spectrogram and returns a matrix where each column corresponds to the polynomial coefficients of a column of the spectrogram.\n\nWe use an n-th order polynomial to approximate each column of the spectrogram. The form of an n-th order polynomial can be represented as: y = c0 + c1x + c2x2 + … + cn*xn. Additionally, the calculation proceeds as follows:\n\nFor each column of the spectrogram:\n1. construct a vector x of length n+1 containing the index values on that column. \n2. Take the values of the spectrogram on that column as vector y. \n3. Using least squares method or other fitting algorithms, calculate the coefficients c0, c1, …, cn of the fitting polynomial by minimizing the difference between the fitting polynomial and the actual data. \n4. Store these coefficients in a matrix where each column corresponds to the polynomial coefficients of a column of the spectrogram.\n","metadata":{}},{"cell_type":"code","source":"# help(librosa.feature.poly_features)\n\np0 = librosa.feature.poly_features(S=spec_img, order=0)  # (freq, time)\np1 = librosa.feature.poly_features(S=spec_img, order=1)\np2 = librosa.feature.poly_features(S=spec_img, order=2)\n\n# polynomial coefficients for each frame. \n# returns: [shape=(..., order+1, t)]","metadata":{"execution":{"iopub.status.busy":"2024-02-29T07:06:36.658071Z","iopub.execute_input":"2024-02-29T07:06:36.658942Z","iopub.status.idle":"2024-02-29T07:06:36.691327Z","shell.execute_reply.started":"2024-02-29T07:06:36.658902Z","shell.execute_reply":"2024-02-29T07:06:36.689523Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"fig, ax = plt.subplots(nrows=3, sharex=True, figsize=(8, 8))\ntimes = librosa.times_like(p0)\nax[0].plot(times, p0[0], label='order=0', alpha=0.8)\nax[0].plot(times, p1[1], label='order=1', alpha=0.8)\nax[0].plot(times, p2[2], label='order=2', alpha=0.8)\nax[0].legend()\nax[0].set_xlabel('Time')\nax[0].label_outer()\nax[0].set(ylabel='Constant term ')\nax[1].plot(times, p1[0], label='order=1', alpha=0.8)\nax[1].plot(times, p2[1], label='order=2', alpha=0.8)\nax[1].set(ylabel='Linear term')\nax[1].label_outer()\nax[1].set_xlabel('Time')\nax[1].legend()\nax[2].plot(times, p2[0], label='order=2', alpha=0.8)\nax[2].set(ylabel='Quadratic term')\nax[2].set_xlabel('Time')\nax[2].legend()","metadata":{"execution":{"iopub.status.busy":"2024-02-29T07:06:36.714966Z","iopub.execute_input":"2024-02-29T07:06:36.716385Z","iopub.status.idle":"2024-02-29T07:06:37.636940Z","shell.execute_reply.started":"2024-02-29T07:06:36.716311Z","shell.execute_reply":"2024-02-29T07:06:37.635562Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## - rms\n\n- Type of input data: **frequency domain**\n- Function's role: AddNewFeature\n\nComputing the RMS(root_mean_square) value\n\nThe` librosa.feature.rms` function aggregates data along the frequency axis when computing the root mean square (RMS) energy for each time frame, with aggregation performed along the time axis (axis=1). In other words, it aggregates data across all frequencies to obtain an RMS energy value along the time axis.","metadata":{}},{"cell_type":"code","source":"# help(librosa.feature.rms)\n\nrms = librosa.feature.rms(S=spec_img, frame_length=spec_img.shape[-2]*2-1)  # freq, time","metadata":{"execution":{"iopub.status.busy":"2024-02-29T07:06:37.638598Z","iopub.execute_input":"2024-02-29T07:06:37.638952Z","iopub.status.idle":"2024-02-29T07:06:37.645649Z","shell.execute_reply.started":"2024-02-29T07:06:37.638924Z","shell.execute_reply":"2024-02-29T07:06:37.644278Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"fig, ax = plt.subplots(nrows=2, sharex=True)\ntimes = librosa.times_like(rms)\n\nax[0].semilogy(times, rms[0], label='RMS Energy')\nax[0].set(xticks=[])\nax[0].legend()\nax[0].label_outer()\nax[0].set_xlabel('Time')\n","metadata":{"execution":{"iopub.status.busy":"2024-02-29T07:06:37.647555Z","iopub.execute_input":"2024-02-29T07:06:37.647988Z","iopub.status.idle":"2024-02-29T07:06:38.370113Z","shell.execute_reply.started":"2024-02-29T07:06:37.647956Z","shell.execute_reply":"2024-02-29T07:06:38.368905Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## - spectral_X\n\n- Type of input data: **frequency domain**\n- Function's role: DataAugment\n\nAll functions belows aggregate data along the time dimension and merge data along the frequency dimension.\n\nFunctions: \n- `spectral_flatness`: This function is used to compute the spectral flatness of an audio signal. Spectral flatness is a feature that describes the shape of a spectrum, measuring the uniformity of frequency components in the signal's spectrum. Spectral flatness is commonly used to analyze the harmonic structure and noise components of a signal, as well as to assess the timbral characteristics of the signal.\n- `spectral_bandwidth`: Computes the spectral bandwidth feature of an audio signal. Spectral bandwidth is typically used to describe the width of a signal's spectrum, reflecting the range of distribution of the signal's spectrum.\n- `spectral_centroid`: Computes the spectral centroid feature of an audio signal. The spectral centroid is the weighted average frequency of the spectrum, used to describe the central position of the signal's spectrum.\n- `spectral_contrast`: Computes the spectral contrast feature of an audio signal, which represents the energy differences between different frequency bands in the spectrum. Spectral contrast is a feature used to describe the dynamic range and harmonic structure of the signal's spectrum.\n- `spectral_rolloff`: Computes the spectral rolloff feature of an audio signal, which represents the frequency position where a certain percentage of energy in the spectrum is located. Spectral rolloff is commonly used to describe the high-frequency decay rate and shape of the signal's spectrum.","metadata":{}},{"cell_type":"code","source":"# help(librosa.feature.spectral_flatness)  \n# help(librosa.feature.spectral_bandwidth)\n# help(librosa.feature.spectral_centroid)\n# help(librosa.feature.spectral_contrast)\n# help(librosa.feature.spectral_rolloff)","metadata":{"execution":{"iopub.status.busy":"2024-02-29T07:06:38.372068Z","iopub.execute_input":"2024-02-29T07:06:38.372767Z","iopub.status.idle":"2024-02-29T07:06:38.377277Z","shell.execute_reply.started":"2024-02-29T07:06:38.372730Z","shell.execute_reply":"2024-02-29T07:06:38.375843Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"res = librosa.feature.spectral_flatness(S=spec_img)  # assumed that the input is raw_spectrogram\n\nprint(res.shape)","metadata":{"execution":{"iopub.status.busy":"2024-02-29T07:06:38.378610Z","iopub.execute_input":"2024-02-29T07:06:38.378960Z","iopub.status.idle":"2024-02-29T07:06:38.399867Z","shell.execute_reply.started":"2024-02-29T07:06:38.378933Z","shell.execute_reply":"2024-02-29T07:06:38.398346Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"res = librosa.feature.spectral_bandwidth(S=spec_img)  # assumed that the input is raw_spectrogram\n\nprint(res.shape)","metadata":{"execution":{"iopub.status.busy":"2024-02-29T07:06:38.402100Z","iopub.execute_input":"2024-02-29T07:06:38.402961Z","iopub.status.idle":"2024-02-29T07:06:38.415962Z","shell.execute_reply.started":"2024-02-29T07:06:38.402908Z","shell.execute_reply":"2024-02-29T07:06:38.414782Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"res = librosa.feature.spectral_centroid(S=spec_img)  # assumed that the input is raw_spectrogram\n\nprint(res.shape)","metadata":{"execution":{"iopub.status.busy":"2024-02-29T07:06:38.418426Z","iopub.execute_input":"2024-02-29T07:06:38.418924Z","iopub.status.idle":"2024-02-29T07:06:38.429715Z","shell.execute_reply.started":"2024-02-29T07:06:38.418880Z","shell.execute_reply":"2024-02-29T07:06:38.428122Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"res = librosa.feature.spectral_contrast(S=spec_img)  # assumed that the input is raw_spectrogram ，need to know sample_rate\n\nprint(res.shape)","metadata":{"execution":{"iopub.status.busy":"2024-02-29T07:06:38.443063Z","iopub.execute_input":"2024-02-29T07:06:38.443872Z","iopub.status.idle":"2024-02-29T07:06:38.455641Z","shell.execute_reply.started":"2024-02-29T07:06:38.443830Z","shell.execute_reply":"2024-02-29T07:06:38.454386Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"res = librosa.feature.spectral_rolloff(S=spec_img)  # assumed that the input is raw_spectrogram , need to know sample_rate\n\nprint(res.shape)","metadata":{"execution":{"iopub.status.busy":"2024-02-29T07:06:38.457733Z","iopub.execute_input":"2024-02-29T07:06:38.458110Z","iopub.status.idle":"2024-02-29T07:06:38.466129Z","shell.execute_reply.started":"2024-02-29T07:06:38.458080Z","shell.execute_reply":"2024-02-29T07:06:38.464632Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## - mfcc","metadata":{}},{"cell_type":"markdown","source":"- Type of input data: **frequency domain**\n- Function's role: AddNewFeature\n\nMFCC (Mel Frequency Cepstral Coefficients) is a method of feature extraction based on frequencies. It combines Mel scale and cepstral analysis to extract the spectral characteristics of audio signals.\n","metadata":{}},{"cell_type":"markdown","source":"The original spectrogram and MFCC spectrogram focus on different aspects of representing audio signal features.\n\n1. The original spectrogram varies in both time and frequency, while the MFCC spectrogram mainly exhibits differences between different frequencies. \n2. **MFCC process redistributes frequencies, providing higher resolution in the lower frequency region and lower resolution in the higher frequency region.** \n\n","metadata":{}},{"cell_type":"code","source":"NAMES = ['LL','LP','RP','RR']\n\nFEATS = [['Fp1','F7','T3','T5','O1'],\n         ['Fp1','F3','C3','P3','O1'],\n         ['Fp2','F8','T4','T6','O2'],\n         ['Fp2','F4','C4','P4','O2']]\n\nUSE_WAVELET = None\n\ndef spectrogram_from_eeg_adj(parquet_path):\n    \n    '''\n    adjust from Chris's code: keep mel_spec as mel_spectram (don't turn to power spectram)\n    '''\n    \n    # LOAD MIDDLE 50 SECONDS OF EEG SERIES\n    eeg = pd.read_parquet(parquet_path)\n    middle = (len(eeg)-10_000)//2\n    eeg = eeg.iloc[middle:middle+10_000]\n    \n    # VARIABLE TO HOLD SPECTROGRAM\n    img = np.zeros((128,256,4),dtype='float32')\n    \n    if display: plt.figure(figsize=(10,7))\n    signals = []\n    for k in range(4):\n        COLS = FEATS[k]\n        \n        for kk in range(4):\n        \n            # COMPUTE PAIR DIFFERENCES\n            x = eeg[COLS[kk]].values - eeg[COLS[kk+1]].values\n\n            # FILL NANS\n            m = np.nanmean(x)\n            if np.isnan(x).mean()<1: x = np.nan_to_num(x,nan=m)\n            else: x[:] = 0\n\n            # DENOISE\n            if USE_WAVELET:\n                x = denoise(x, wavelet=USE_WAVELET)\n            signals.append(x)\n\n            # RAW SPECTROGRAM\n            mel_spec = librosa.feature.melspectrogram(y=x, sr=200, hop_length=len(x)//256, \n                  n_fft=1024, n_mels=128, fmin=0, fmax=20, win_length=128)\n            \n            # LOG TRANSFORM\n            width = (mel_spec.shape[1]//32)*32\n            mel_spec = mel_spec[:,:width]\n\n            # STANDARDIZE TO -1 TO 1\n            mel_spec = (mel_spec+40)/40 \n            img[:,:,k] += mel_spec\n                \n        # AVERAGE THE 4 MONTAGE DIFFERENCES\n        img[:,:,k] /= 4.0\n        \n    return img\n","metadata":{"execution":{"iopub.status.busy":"2024-02-29T07:06:38.467556Z","iopub.execute_input":"2024-02-29T07:06:38.467981Z","iopub.status.idle":"2024-02-29T07:06:38.486671Z","shell.execute_reply.started":"2024-02-29T07:06:38.467951Z","shell.execute_reply":"2024-02-29T07:06:38.485424Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# adjust chris's code from \"spectrogram_from_eeg\"\neeg_img = spectrogram_from_eeg_adj('/kaggle/input/hms-harmful-brain-activity-classification/train_eegs/1000913311.parquet')\n\neeg_imgone = eeg_img[:, :, 0]\n\neeg_imgone = eeg_imgone[None, ...]","metadata":{"execution":{"iopub.status.busy":"2024-02-29T07:06:38.488784Z","iopub.execute_input":"2024-02-29T07:06:38.489180Z","iopub.status.idle":"2024-02-29T07:06:38.798179Z","shell.execute_reply.started":"2024-02-29T07:06:38.489148Z","shell.execute_reply":"2024-02-29T07:06:38.796531Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plt.figure(figsize=(10, 4))\nplt.imshow(eeg_imgone[0],  origin='lower', aspect='auto')\nplt.colorbar()\nplt.title('spec raw')\nplt.xlabel('Frame')\nplt.ylabel('tk')\nplt.tight_layout()\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2024-02-29T07:06:38.806026Z","iopub.execute_input":"2024-02-29T07:06:38.806834Z","iopub.status.idle":"2024-02-29T07:06:39.433179Z","shell.execute_reply.started":"2024-02-29T07:06:38.806799Z","shell.execute_reply":"2024-02-29T07:06:39.431699Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Apply mel filterbank\nn_mels_tk = 128  # Number of mel filters\nn_fft_tk = 255\nmel = librosa.filters.mel(sr=200, \n                          n_fft=n_fft_tk,\n                          n_mels=n_mels_tk,  #  Number of Mel filters used to project the spectrogram onto the Mel frequency axis\n                          fmin=0,  #  Minimum frequency of the Mel filter bank (in Hz)\n                          fmax=20  # Maximum frequency of the Mel filter bank (in Hz)\n                         )\n\nprint('mel.shape: ', mel.shape)  # shape: (n_mels_tk, n_fft_tk//2+1)  # Due to the nature of FFT, performing FFT on a signal of length N will result in N/2+1 unique frequency points\n\nmel_specgram = mel.dot(eeg_imgone[0])\n\n# Log transform\nlog_mel_specgram = librosa.amplitude_to_db(mel_specgram)  # amplitude_to_db  power_to_db\n\n# Compute LFCC\nn_mfcc = 12 # Number of LFCC coefficients → feature numbers\nmfcc = librosa.feature.mfcc(S=log_mel_specgram, n_mfcc=n_mfcc)\n\nprint('mfcc.shape: ', mfcc.shape)","metadata":{"execution":{"iopub.status.busy":"2024-02-29T07:06:39.435383Z","iopub.execute_input":"2024-02-29T07:06:39.436147Z","iopub.status.idle":"2024-02-29T07:06:39.463774Z","shell.execute_reply.started":"2024-02-29T07:06:39.436102Z","shell.execute_reply":"2024-02-29T07:06:39.461906Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"When calculating MFCC, adjusting the value of the n_mfcc parameter significantly impacts the final spectrogram results.\n1. Lower n_mfcc values may be more suitable for tasks like audio classification or higher-level feature extraction in speech recognition. \n2. higher n_mfcc values may be more suitable for tasks requiring detailed spectral information, \n\nApproach to tuning n_mfcc:\n1. It is a reasonable approach to set a proper value for n_mfcc based on the ability to discover higher frequency resolution in the low-frequency region. As mentioned earlier, the low-frequency region exhibits higher frequency resolution, making lower n_mfcc values more appropriate for capturing spectral details in that region. ","metadata":{}},{"cell_type":"code","source":"plt.figure(figsize=(10, 4))\nplt.imshow(mfcc,  origin='lower', aspect='auto')\nplt.colorbar()\nplt.title('mfcc')\nplt.xlabel('Frame')\nplt.ylabel('fea_numbers')\nplt.tight_layout()\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2024-02-29T07:06:39.466641Z","iopub.execute_input":"2024-02-29T07:06:39.467859Z","iopub.status.idle":"2024-02-29T07:06:40.101042Z","shell.execute_reply.started":"2024-02-29T07:06:39.467818Z","shell.execute_reply":"2024-02-29T07:06:40.099706Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Understanding the results obtained through the MFCC function:\n1. The features extracted by the MFCC spectrogram are primarily based on frequencies, capturing the spectral characteristics of the audio signal. \n2. **In the low-frequency region, especially near some key frequencies, higher frequency resolution may occur, meaning that the changes between adjacent frequencies are more pronounced.** \n3. For higher frequency regions, where the frequency resolution is lower and changes between adjacent frequencies are smaller, the variations between frequencies in these high-frequency areas may be less pronounced in the MFCC spectrogram.\n","metadata":{}},{"cell_type":"markdown","source":"## <p style = \"font-family: Georgia;font-weight: bold; font-size: 30px; color: #1192AA; text-align:left\">torchaudio</p>","metadata":{}},{"cell_type":"markdown","source":"TorchAudio is a Python library built on top of PyTorch, specifically designed for audio data processing and analysis.\n\ndocs: https://pytorch.org/audio/stable/index.html","metadata":{}},{"cell_type":"code","source":"# dir(torchaudio)","metadata":{"execution":{"iopub.status.busy":"2024-02-29T10:11:34.372927Z","iopub.execute_input":"2024-02-29T10:11:34.373930Z","iopub.status.idle":"2024-02-29T10:11:34.789762Z","shell.execute_reply.started":"2024-02-29T10:11:34.373888Z","shell.execute_reply":"2024-02-29T10:11:34.788124Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## - MuLawEncoding & MuLawDecoding","metadata":{}},{"cell_type":"markdown","source":"- Type of input data: **time domain**\n- Function's role: AddNewFeature\n\nOne of the main purposes of μ-law encoding is to provide higher quantization accuracy to preserve details in low-amplitude signals. In μ-law encoding, the encoded values for low-amplitude signals are larger, allowing for better differentiation between small differences in low-amplitude signals, thereby improving accuracy and fidelity in capturing low-amplitude signals.\n","metadata":{}},{"cell_type":"code","source":"# help(torchaudio.transforms.MuLawEncoding)\n\nwaveform = torch.randn(1, 160)  # signal has been scaled to between -1 and 1 and\nmu_law_encoding = torchaudio.transforms.MuLawEncoding(quantization_channels=256) # a larg number means more amplitude values will be mapped to different discrete values, improving the encoding accuracy\nencoded_waveform = mu_law_encoding(waveform)","metadata":{"execution":{"iopub.status.busy":"2024-02-29T07:06:40.102669Z","iopub.execute_input":"2024-02-29T07:06:40.103209Z","iopub.status.idle":"2024-02-29T07:06:40.169471Z","shell.execute_reply.started":"2024-02-29T07:06:40.103162Z","shell.execute_reply":"2024-02-29T07:06:40.168192Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plt.figure(figsize=(12, 6))\n\nplt.subplot(2, 1, 1)\nplt.plot(waveform.squeeze().numpy())\nplt.title('raw waveform')\nplt.xlabel('samples')\nplt.ylabel('amplitude')\n\nplt.subplot(2, 1, 2)\nplt.plot(encoded_waveform.squeeze().numpy())\nplt.title('waveform after encoding')\nplt.xlabel('samples')\nplt.ylabel('amplitude')\n\nplt.tight_layout()\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2024-02-29T07:06:40.170912Z","iopub.execute_input":"2024-02-29T07:06:40.171336Z","iopub.status.idle":"2024-02-29T07:06:40.904809Z","shell.execute_reply.started":"2024-02-29T07:06:40.171300Z","shell.execute_reply":"2024-02-29T07:06:40.903629Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"mu_law_decoding = torchaudio.transforms.MuLawDecoding(quantization_channels=256)\n\ndecoded_waveform = mu_law_decoding(encoded_waveform)\n\nplt.figure(figsize=(12, 3))\nplt.plot(decoded_waveform.squeeze().numpy())\nplt.title('wave after decoding')\nplt.xlabel('samples')\nplt.ylabel('amplitude')\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2024-02-29T07:06:40.906545Z","iopub.execute_input":"2024-02-29T07:06:40.907010Z","iopub.status.idle":"2024-02-29T07:06:41.242590Z","shell.execute_reply.started":"2024-02-29T07:06:40.906969Z","shell.execute_reply":"2024-02-29T07:06:41.241284Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## - SlidingWindowCmn\n\n- Type of input data: **frequency domain**\n- Function's role: AddNewFeature\n\nSliding window cepstral mean normalization is a common speech signal processing technique used to normalize cepstral coefficients of speech signals. Its purpose is to reduce volume differences between different utterances, thereby extracting more robust speech features.(Perform a sliding average calculation on the spectrogram. 😂)\n\n","metadata":{}},{"cell_type":"code","source":"# help(torchaudio.transforms.SlidingWindowCmn)\n\nSlidingWindowCmn = torchaudio.transforms.SlidingWindowCmn(cmn_window=60)\n\nres = SlidingWindowCmn(torch.tensor(spec_img))  # (..., time, freq)","metadata":{"execution":{"iopub.status.busy":"2024-02-29T07:06:41.244114Z","iopub.execute_input":"2024-02-29T07:06:41.244735Z","iopub.status.idle":"2024-02-29T07:06:41.300273Z","shell.execute_reply.started":"2024-02-29T07:06:41.244697Z","shell.execute_reply":"2024-02-29T07:06:41.298788Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plt.imshow(spec_img.T, cmap='viridis', aspect='auto', origin='lower')\nplt.colorbar()","metadata":{"execution":{"iopub.status.busy":"2024-02-29T07:06:41.302281Z","iopub.execute_input":"2024-02-29T07:06:41.302794Z","iopub.status.idle":"2024-02-29T07:06:41.789436Z","shell.execute_reply.started":"2024-02-29T07:06:41.302749Z","shell.execute_reply":"2024-02-29T07:06:41.788366Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plt.imshow(res.T, cmap='viridis', aspect='auto', origin='lower')\nplt.colorbar()","metadata":{"execution":{"iopub.status.busy":"2024-02-29T07:06:41.790728Z","iopub.execute_input":"2024-02-29T07:06:41.791904Z","iopub.status.idle":"2024-02-29T07:06:42.268926Z","shell.execute_reply.started":"2024-02-29T07:06:41.791854Z","shell.execute_reply":"2024-02-29T07:06:42.267417Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## - ComputeDeltas\n\n- Type of input data: **frequency domain**\n- Function's role: AddNewFeature\n\nCompute the delta coefficients of a tensor, typically a spectrogram. \n1. The function takes a tensor as input, which typically represents a sequence of audio features such as MFCCs or spectrogram frames. \n2. It then computes the delta coefficients by calculating the first-order differences between neighboring frames of the input tensor.","metadata":{}},{"cell_type":"code","source":"# help(torchaudio.transforms.ComputeDeltas)\n\nComputeDeltas = torchaudio.transforms.ComputeDeltas(win_length=5,  # windows to cal delta\n                                                    mode='replicate'  # \"replicate\"  \"constant\"\n                                                   )  \n\nres = ComputeDeltas(torch.tensor(spec_img.T))  # input: (freq, time)","metadata":{"execution":{"iopub.status.busy":"2024-02-29T07:06:42.270803Z","iopub.execute_input":"2024-02-29T07:06:42.271342Z","iopub.status.idle":"2024-02-29T07:06:42.354721Z","shell.execute_reply.started":"2024-02-29T07:06:42.271298Z","shell.execute_reply":"2024-02-29T07:06:42.353710Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plot_withT(spec_img, print_val='raw spec_img')","metadata":{"execution":{"iopub.status.busy":"2024-02-29T07:06:42.356165Z","iopub.execute_input":"2024-02-29T07:06:42.356804Z","iopub.status.idle":"2024-02-29T07:06:42.776935Z","shell.execute_reply.started":"2024-02-29T07:06:42.356768Z","shell.execute_reply":"2024-02-29T07:06:42.773342Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plot_withT(res.numpy().T, print_val='spec_img after deltas')","metadata":{"execution":{"iopub.status.busy":"2024-02-29T07:06:42.778982Z","iopub.execute_input":"2024-02-29T07:06:42.780597Z","iopub.status.idle":"2024-02-29T07:06:43.181908Z","shell.execute_reply.started":"2024-02-29T07:06:42.780535Z","shell.execute_reply":"2024-02-29T07:06:43.178352Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## - Speed & SpeedPerturbation\n\n- Type of input data: **time domain**\n- Function's role: DataAugment\n \n \n- `Speed`: Adjusts waveform speed.\n    - The speed of a signal usually refers to the sampling rate of the signal. The sampling rate represents the number of times a signal is sampled per second,\n- `SpeedPerturbation`: Applies the speed perturbation augmentation introduced in Audio augmentation for speech recognition\n\n","metadata":{}},{"cell_type":"code","source":"# help(torchaudio.transforms.Speed)\n\n# help(torchaudio.transforms.SpeedPerturbation)","metadata":{"execution":{"iopub.status.busy":"2024-02-29T07:06:43.184073Z","iopub.execute_input":"2024-02-29T07:06:43.184694Z","iopub.status.idle":"2024-02-29T07:06:43.190363Z","shell.execute_reply.started":"2024-02-29T07:06:43.184648Z","shell.execute_reply":"2024-02-29T07:06:43.189298Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"sample_rate = 44100\nduration = 5\nfrequency = 440\namplitude = 0.5\n\nt = np.linspace(0, duration, int(sample_rate * duration), endpoint=False)\nwaveform = amplitude * np.sin(2 * np.pi * frequency * t)\n\n\nwaveform_tensor = torch.tensor(waveform)\n\n\nspeed_transform = torchaudio.transforms.Speed(orig_freq=50,  # sample_rate\n                                   factor=1.1\n                                  )\n\nres, _ = speed_transform(waveform_tensor.float())\n\nres = res.numpy()","metadata":{"execution":{"iopub.status.busy":"2024-02-29T07:06:43.191600Z","iopub.execute_input":"2024-02-29T07:06:43.192832Z","iopub.status.idle":"2024-02-29T07:06:43.242237Z","shell.execute_reply.started":"2024-02-29T07:06:43.192781Z","shell.execute_reply":"2024-02-29T07:06:43.240757Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print('len of raw waveform: ', len(waveform))\nprint()\nprint('len of waveform_after_speed', len(res))","metadata":{"execution":{"iopub.status.busy":"2024-02-29T07:06:43.244120Z","iopub.execute_input":"2024-02-29T07:06:43.245425Z","iopub.status.idle":"2024-02-29T07:06:43.254068Z","shell.execute_reply.started":"2024-02-29T07:06:43.245363Z","shell.execute_reply":"2024-02-29T07:06:43.252664Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## - Vol\n\n- Type of input data: **time domain**\n- Function's role: DataAugment\n\nAdjust volume of waveform.","metadata":{}},{"cell_type":"code","source":"# help(torchaudio.transforms.Vol)\n\nVol = torchaudio.transforms.Vol(gain=1.5, \n                                gain_type='amplitude'  # 'amplitude', 'power', 'db'\n                               )  \n\nres = Vol(torch.tensor(waveform))","metadata":{"execution":{"iopub.status.busy":"2024-02-29T07:06:43.256111Z","iopub.execute_input":"2024-02-29T07:06:43.257114Z","iopub.status.idle":"2024-02-29T07:06:43.269733Z","shell.execute_reply.started":"2024-02-29T07:06:43.257077Z","shell.execute_reply":"2024-02-29T07:06:43.268707Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plot_waveform(waveform, plt_title='raw waveform')","metadata":{"execution":{"iopub.status.busy":"2024-02-29T07:07:39.207978Z","iopub.execute_input":"2024-02-29T07:07:39.208572Z","iopub.status.idle":"2024-02-29T07:07:39.468786Z","shell.execute_reply.started":"2024-02-29T07:07:39.208532Z","shell.execute_reply":"2024-02-29T07:07:39.467352Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plot_waveform(res.numpy(), plt_title='waveform_after_gain')","metadata":{"execution":{"iopub.status.busy":"2024-02-29T07:07:44.162606Z","iopub.execute_input":"2024-02-29T07:07:44.163027Z","iopub.status.idle":"2024-02-29T07:07:44.430680Z","shell.execute_reply.started":"2024-02-29T07:07:44.162996Z","shell.execute_reply":"2024-02-29T07:07:44.429094Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## - TimeMasking\n\n- Type of input data: **frequency domain**\n- Function's role: DataAugment\n\nApply masking to a spectrogram in the time domain.","metadata":{}},{"cell_type":"code","source":"TimeMasking_tk = torchaudio.transforms.TimeMasking(time_mask_param=100)\nmasked = TimeMasking_tk(torch.tensor(spec_img.T))  # input (spec ,time) ","metadata":{"execution":{"iopub.status.busy":"2024-02-29T07:07:48.869300Z","iopub.execute_input":"2024-02-29T07:07:48.869720Z","iopub.status.idle":"2024-02-29T07:07:48.888705Z","shell.execute_reply.started":"2024-02-29T07:07:48.869689Z","shell.execute_reply":"2024-02-29T07:07:48.887336Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plot_withlogwithT(masked.T.numpy(), print_val='spec_img after TimeMasking')","metadata":{"execution":{"iopub.status.busy":"2024-02-29T07:07:50.406055Z","iopub.execute_input":"2024-02-29T07:07:50.406544Z","iopub.status.idle":"2024-02-29T07:07:50.803372Z","shell.execute_reply.started":"2024-02-29T07:07:50.406511Z","shell.execute_reply":"2024-02-29T07:07:50.802349Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## - TimeStretch\n\n- Type of input data: **frequency domain**\n- Function's role: DataAugment\n\nStretch stft in time without modifying pitch for a given rate.","metadata":{}},{"cell_type":"code","source":"TimeStretch_tk = torchaudio.transforms.TimeStretch(n_freq=100)\nprint(spec_img.T.shape)\n\nTimeStretch_res = TimeStretch_tk(torch.tensor(spec_img.T), 0.9)\nprint(TimeStretch_res.real.numpy().shape)","metadata":{"execution":{"iopub.status.busy":"2024-02-29T07:07:55.337445Z","iopub.execute_input":"2024-02-29T07:07:55.337872Z","iopub.status.idle":"2024-02-29T07:07:55.368596Z","shell.execute_reply.started":"2024-02-29T07:07:55.337841Z","shell.execute_reply":"2024-02-29T07:07:55.367225Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plot_withlogwithT(TimeStretch_res.T.real.numpy(), print_val='spec_img after TimeStretch')","metadata":{"execution":{"iopub.status.busy":"2024-02-29T07:07:58.235976Z","iopub.execute_input":"2024-02-29T07:07:58.237538Z","iopub.status.idle":"2024-02-29T07:07:58.683018Z","shell.execute_reply.started":"2024-02-29T07:07:58.237483Z","shell.execute_reply":"2024-02-29T07:07:58.681348Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## - SpectralCentroid\n\n- Type of input data: **time domain**\n- Function's role: AddNewFeature\n\nCompute the spectral centroid for each channel along the time axis.","metadata":{}},{"cell_type":"code","source":"SpectralCentroid = torchaudio.transforms.SpectralCentroid(sample_rate=200)\n\nres = SpectralCentroid(torch.tensor(waveform))","metadata":{"execution":{"iopub.status.busy":"2024-02-29T07:08:01.448889Z","iopub.execute_input":"2024-02-29T07:08:01.449375Z","iopub.status.idle":"2024-02-29T07:08:01.523090Z","shell.execute_reply.started":"2024-02-29T07:08:01.449339Z","shell.execute_reply":"2024-02-29T07:08:01.521543Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print('raw waveform.shape: ', waveform.shape)\n\nprint('waveform_after_SpectralCentroid.shape: ', res.shape)  ","metadata":{"execution":{"iopub.status.busy":"2024-02-29T07:08:03.687487Z","iopub.execute_input":"2024-02-29T07:08:03.687962Z","iopub.status.idle":"2024-02-29T07:08:03.695280Z","shell.execute_reply.started":"2024-02-29T07:08:03.687931Z","shell.execute_reply":"2024-02-29T07:08:03.693815Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## - FrequencyMasking\n\nType of input data: **frequency domain**\n\nApply masking to a spectrogram in the frequency domain.","metadata":{}},{"cell_type":"code","source":"masking = torchaudio.transforms.FrequencyMasking(freq_mask_param=80)\n\nmasked = masking(torch.tensor(spec_img).T)  # (spec, time)\n\nplot_withlogwithT(masked.T.real.numpy(), print_val='spec_img after TimeStretch')","metadata":{"execution":{"iopub.status.busy":"2024-02-29T07:08:05.692583Z","iopub.execute_input":"2024-02-29T07:08:05.693026Z","iopub.status.idle":"2024-02-29T07:08:06.141274Z","shell.execute_reply.started":"2024-02-29T07:08:05.692993Z","shell.execute_reply":"2024-02-29T07:08:06.139806Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## - Resample\n\n- Type of input data: **frequency domain**\n- Function's role: DataAugment\n\nResample a signal from one frequency to another. A resampling method can be given.","metadata":{}},{"cell_type":"code","source":"# help(torchaudio.transforms.Resample)\n\n# Resample a signal from one frequency to another. A resampling method can be given.\n\nResample = torchaudio.transforms.Resample(orig_freq=44100, new_freq=10000)\n\nres = Resample(torch.tensor(waveform).float())","metadata":{"execution":{"iopub.status.busy":"2024-02-29T07:08:08.469010Z","iopub.execute_input":"2024-02-29T07:08:08.469550Z","iopub.status.idle":"2024-02-29T07:08:08.484195Z","shell.execute_reply.started":"2024-02-29T07:08:08.469514Z","shell.execute_reply":"2024-02-29T07:08:08.483164Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print('len of wave_after_resample: ', len(res))","metadata":{"execution":{"iopub.status.busy":"2024-02-29T07:08:10.877719Z","iopub.execute_input":"2024-02-29T07:08:10.878133Z","iopub.status.idle":"2024-02-29T07:08:10.885781Z","shell.execute_reply.started":"2024-02-29T07:08:10.878102Z","shell.execute_reply":"2024-02-29T07:08:10.884308Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## - SpecAugment\n\n- Type of input data: **time domain**\n- Function's role: DataAugment\n\nApply time and frequency masking to a spectrogram.","metadata":{}},{"cell_type":"code","source":"# help(torchaudio.transforms.SpecAugment)\n\nSpecAugment = torchaudio.transforms.SpecAugment(n_time_masks=3, time_mask_param=15, n_freq_masks=3, freq_mask_param=20, p=1, zero_masking=False)\n\nres = SpecAugment(torch.tensor(spec_img))","metadata":{"execution":{"iopub.status.busy":"2024-02-29T07:08:12.161160Z","iopub.execute_input":"2024-02-29T07:08:12.162506Z","iopub.status.idle":"2024-02-29T07:08:12.175401Z","shell.execute_reply.started":"2024-02-29T07:08:12.162459Z","shell.execute_reply":"2024-02-29T07:08:12.173917Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plot_withlogwithT(res, print_val='spec_img after SpecAugment')","metadata":{"execution":{"iopub.status.busy":"2024-02-29T07:08:14.346878Z","iopub.execute_input":"2024-02-29T07:08:14.347356Z","iopub.status.idle":"2024-02-29T07:08:15.564496Z","shell.execute_reply.started":"2024-02-29T07:08:14.347323Z","shell.execute_reply":"2024-02-29T07:08:15.563160Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## <p style = \"font-family: Georgia;font-weight: bold; font-size: 30px; color: #1192AA; text-align:left\">keras_cv</p>","metadata":{}},{"cell_type":"markdown","source":"https://keras.io/api/keras_cv/layers/\n\n`keras_cv` is not specifically designed for signal data processing but rather for image data processing. However, we can utilize its methods to perform data augmentation on spectrograms.","metadata":{}},{"cell_type":"markdown","source":"## - FourierMix\n\n- Type of input data: **frequency domain(image)**\n- Function's role: DataAugment\n\nFourierMix implements the FMix data augmentation technique. FMix generates random masks by applying Fourier transform to the image, and then mixes the original image with these masks to generate a new enhanced image.\n","metadata":{}},{"cell_type":"code","source":"(images, labels), _ = keras.datasets.cifar10.load_data()\nlabels = tf.one_hot(labels, 10)\nlabels = tf.cast(tf.squeeze(labels), tf.float32)\nsample_size = 4","metadata":{"execution":{"iopub.status.busy":"2024-02-29T07:08:18.596072Z","iopub.execute_input":"2024-02-29T07:08:18.596742Z","iopub.status.idle":"2024-02-29T07:08:25.497021Z","shell.execute_reply.started":"2024-02-29T07:08:18.596700Z","shell.execute_reply":"2024-02-29T07:08:25.495882Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"sample_images = images[:sample_size]\nsample_labels = labels[:sample_size]","metadata":{"execution":{"iopub.status.busy":"2024-02-29T07:09:29.010985Z","iopub.execute_input":"2024-02-29T07:09:29.011441Z","iopub.status.idle":"2024-02-29T07:09:29.018383Z","shell.execute_reply.started":"2024-02-29T07:09:29.011409Z","shell.execute_reply":"2024-02-29T07:09:29.016728Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# help(keras_cv.layers.FourierMix)\n\nfouriermix = keras_cv.layers.FourierMix()\noutput = fouriermix({'images': tf.convert_to_tensor(sample_images), 'labels': sample_labels})","metadata":{"execution":{"iopub.status.busy":"2024-02-29T07:12:51.913047Z","iopub.execute_input":"2024-02-29T07:12:51.913480Z","iopub.status.idle":"2024-02-29T07:12:52.864648Z","shell.execute_reply.started":"2024-02-29T07:12:51.913449Z","shell.execute_reply":"2024-02-29T07:12:52.863528Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"fig, axs = plt.subplots(2, 2, figsize=(8, 8))\n\nfor i in range(4):\n    row = i // 2\n    col = i % 2\n    axs[row, col].imshow(images[i, :, :, -1])\n    axs[row, col].set_title(f'Image {i+1}')\n    axs[row, col].axis('off')\n\nplt.suptitle('Four origin Images Display channel(3)')\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2024-02-29T07:12:56.219298Z","iopub.execute_input":"2024-02-29T07:12:56.219705Z","iopub.status.idle":"2024-02-29T07:12:56.707031Z","shell.execute_reply.started":"2024-02-29T07:12:56.219676Z","shell.execute_reply":"2024-02-29T07:12:56.705480Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"fig, axs = plt.subplots(2, 2, figsize=(8, 8))\n\nfor i in range(4):\n    row = i // 2\n    col = i % 2\n    axs[row, col].imshow(output['images'][i, :, :, -1])\n    axs[row, col].set_title(f'Image {i+1}')\n    axs[row, col].axis('off')\n\nplt.suptitle('Four output Images Display channel(3)')\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2024-02-29T07:13:03.270031Z","iopub.execute_input":"2024-02-29T07:13:03.271318Z","iopub.status.idle":"2024-02-29T07:13:03.749614Z","shell.execute_reply.started":"2024-02-29T07:13:03.271242Z","shell.execute_reply":"2024-02-29T07:13:03.748320Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## - Mosaic\n\n- Type of input data: **frequency domain(image)**\n- Function's role: DataAugment\n\nMosaic data augmentation first takes 4 images from the batch and makes agrid. After that based on the offset, a crop is taken to form the mosaic image. Labels are in the same ratio as the area of their images in the output image. Bounding boxes are translated according to the position of the 4 images. ","metadata":{}},{"cell_type":"code","source":"(images, labels), _ = keras.datasets.cifar10.load_data() \nlabels = tf.one_hot(labels, 10) \nlabels = tf.cast(tf.squeeze(labels), tf.float32) \nsample_size = 4 \nsample_images = images[:sample_size] \nsample_labels = labels[:sample_size]\n\n\nmosaic = keras_cv.layers.Mosaic()\noutput = mosaic({'images': tf.convert_to_tensor(sample_images), 'labels': sample_labels})\n\nprint('sample_images.shape: ',sample_images.shape)\nprint('')\nprint('output_images.shape: ',output['images'].shape)","metadata":{"execution":{"iopub.status.busy":"2024-02-29T07:13:39.574859Z","iopub.execute_input":"2024-02-29T07:13:39.575288Z","iopub.status.idle":"2024-02-29T07:13:40.575843Z","shell.execute_reply.started":"2024-02-29T07:13:39.575242Z","shell.execute_reply":"2024-02-29T07:13:40.574449Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"fig, axs = plt.subplots(2, 2, figsize=(8, 8))\n\nfor i in range(4):\n    row = i // 2\n    col = i % 2\n    axs[row, col].imshow(images[i, :, :, -1])\n    axs[row, col].set_title(f'Image {i+1}')\n    axs[row, col].axis('off')\n\nplt.suptitle('Four origin Images Display channel(3)')\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2024-02-29T07:13:44.931868Z","iopub.execute_input":"2024-02-29T07:13:44.932298Z","iopub.status.idle":"2024-02-29T07:13:45.404913Z","shell.execute_reply.started":"2024-02-29T07:13:44.932242Z","shell.execute_reply":"2024-02-29T07:13:45.403698Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"fig, axs = plt.subplots(2, 2, figsize=(8, 8))\n\nfor i in range(4):\n    row = i // 2\n    col = i % 2\n    axs[row, col].imshow(output['images'][i, :, :, -1])\n    axs[row, col].set_title(f'Image {i+1}')\n    axs[row, col].axis('off')\n\nplt.suptitle('Four output Images Display channel(3)')\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2024-02-29T07:13:47.664631Z","iopub.execute_input":"2024-02-29T07:13:47.665080Z","iopub.status.idle":"2024-02-29T07:13:48.149010Z","shell.execute_reply.started":"2024-02-29T07:13:47.665049Z","shell.execute_reply":"2024-02-29T07:13:48.147769Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## - Mixup\n\n- Type of input data: **frequency domain(image)**\n- Function's role: DataAugment\n\nMixUp implements the MixUp data augmentation technique.","metadata":{}},{"cell_type":"code","source":"(images, labels), _ = keras.datasets.cifar10.load_data()\n\nsample_size = 2  # sample_images = images[:sample_size] sample_labels = labels[:sample_size]\nsample_images = images[:sample_size] \nsample_labels = labels[:sample_size]\n\nsample_labels_oht = tf.cast(tf.one_hot(sample_labels, 10), tf.float32)  # to use mixup, Labels must be floating-point and one-hot encoded","metadata":{"execution":{"iopub.status.busy":"2024-02-29T07:14:01.944829Z","iopub.execute_input":"2024-02-29T07:14:01.945303Z","iopub.status.idle":"2024-02-29T07:14:02.957498Z","shell.execute_reply.started":"2024-02-29T07:14:01.945244Z","shell.execute_reply":"2024-02-29T07:14:02.956442Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# help(keras_cv.layers.MixUp)\n\nmixup = keras_cv.layers.MixUp(alpha=0.5,  # Float between 0 and 1. This controls the shape of the distribution from which the smoothing values are sampled. Defaults to 0.2,\n                              seed=33)\naugmented_data = mixup(\n    {'images': tf.convert_to_tensor(sample_images), 'labels': sample_labels_oht}\n)","metadata":{"execution":{"iopub.status.busy":"2024-02-29T07:14:33.399457Z","iopub.execute_input":"2024-02-29T07:14:33.399904Z","iopub.status.idle":"2024-02-29T07:14:33.421981Z","shell.execute_reply.started":"2024-02-29T07:14:33.399870Z","shell.execute_reply":"2024-02-29T07:14:33.420736Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"fig, axs = plt.subplots(1, 2, figsize=(8, 4)) \n\nfor i in range(2): \n    axs[i].imshow(sample_images[i, :, :, -1])\n    axs[i].set_title(f'Image {i+1}')\n    axs[i].axis('off')\n\nplt.suptitle('Two raw Images Display channel(3)') \nplt.show()\n\n# #######################################################\n\nfig, axs = plt.subplots(1, 2, figsize=(8, 4))  \n\nfor i in range(2):  \n    axs[i].imshow(augmented_data['images'][i, :, :, -1])\n    axs[i].set_title(f'Image {i+1}')\n    axs[i].axis('off')\n\nplt.suptitle('Two output Images Display channel(3)')\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2024-02-29T07:14:37.071640Z","iopub.execute_input":"2024-02-29T07:14:37.072090Z","iopub.status.idle":"2024-02-29T07:14:37.581827Z","shell.execute_reply.started":"2024-02-29T07:14:37.072057Z","shell.execute_reply":"2024-02-29T07:14:37.580401Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## - CutMix\n\n- Type of input data: **frequency domain(image)**\n- Function's role: DataAugment\n\nCutmix generates new training samples by randomly cropping and mixing two different images in the input image. This helps the model to better generalize and improve its performance.","metadata":{}},{"cell_type":"code","source":"(images, labels), _ = keras.datasets.cifar10.load_data()\n\nsample_size = 2 #\nsample_images = images[:sample_size] \nsample_labels = labels[:sample_size]\n\nsample_labels_oht = tf.cast(tf.one_hot(sample_labels, 10), tf.float32)  # Labels must be floating-point and one-hot encoded\n\n# mixup = keras_cv.layers.MixUp(alpha=0.5, seed=42)\ncutmix = keras_cv.layers.CutMix(alpha=0.5, seed=42)\naugmented_data = cutmix(\n    {'images': tf.convert_to_tensor(sample_images), 'labels': sample_labels_oht}\n)","metadata":{"execution":{"iopub.status.busy":"2024-02-29T07:14:57.152239Z","iopub.execute_input":"2024-02-29T07:14:57.152693Z","iopub.status.idle":"2024-02-29T07:14:58.174409Z","shell.execute_reply.started":"2024-02-29T07:14:57.152662Z","shell.execute_reply":"2024-02-29T07:14:58.172764Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"fig, axs = plt.subplots(1, 2, figsize=(8, 4)) \n\nfor i in range(2):  \n    axs[i].imshow(sample_images[i, :, :, -1])\n    axs[i].set_title(f'Image {i+1}')\n    axs[i].axis('off')\n\nplt.suptitle('Two raw Images Display channel(3)') \nplt.show()","metadata":{"execution":{"iopub.status.busy":"2024-02-29T07:14:58.869080Z","iopub.execute_input":"2024-02-29T07:14:58.870101Z","iopub.status.idle":"2024-02-29T07:14:59.082983Z","shell.execute_reply.started":"2024-02-29T07:14:58.870063Z","shell.execute_reply":"2024-02-29T07:14:59.081484Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"fig, axs = plt.subplots(1, 2, figsize=(8, 4))  #\n\nfor i in range(2):  \n    axs[i].imshow(augmented_data['images'][i, :, :, -1])\n    axs[i].set_title(f'Image {i+1}')\n    axs[i].axis('off')\n\nplt.suptitle('Two output Images Display channel(3)')  # \nplt.show()","metadata":{"execution":{"iopub.status.busy":"2024-02-29T07:15:01.822239Z","iopub.execute_input":"2024-02-29T07:15:01.822759Z","iopub.status.idle":"2024-02-29T07:15:02.050789Z","shell.execute_reply.started":"2024-02-29T07:15:01.822720Z","shell.execute_reply":"2024-02-29T07:15:02.049804Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## - RandomCrop\n\n- Type of input data: **frequency domain(image)**\n- Function's role: DataAugment\n\nA preprocessing layer which randomly crops images.\n\nDuring training, \n1. this layer will randomly choose a location to crop images down to a target size. \n2. The layer will crop all the images in the same batch to the same cropping location.\n\nAt inference time, and during training \n1. if an input image is smaller than the target size, the input will be resized and cropped so as to return the largest possible window in the image that matches the target aspect ratio. \n2. If you need to apply random cropping at inference time, set training to True when calling the layer.","metadata":{}},{"cell_type":"code","source":"# help(keras_cv.layers.RandomCrop)  #  RandomCrop(height, width, seed=None, bounding_box_format=None, **kwargs)\n\nRandomCrop = keras_cv.layers.RandomCrop(height=200, width=50, seed=42)\n\nres = RandomCrop(spec_img_3D)","metadata":{"execution":{"iopub.status.busy":"2024-02-29T07:15:13.932838Z","iopub.execute_input":"2024-02-29T07:15:13.933350Z","iopub.status.idle":"2024-02-29T07:15:13.991076Z","shell.execute_reply.started":"2024-02-29T07:15:13.933317Z","shell.execute_reply":"2024-02-29T07:15:13.989555Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plot_withlogwithT(res[:, :, -1], print_val='RandomCrop')","metadata":{"execution":{"iopub.status.busy":"2024-02-29T07:15:16.391840Z","iopub.execute_input":"2024-02-29T07:15:16.392276Z","iopub.status.idle":"2024-02-29T07:15:16.790207Z","shell.execute_reply.started":"2024-02-29T07:15:16.392223Z","shell.execute_reply":"2024-02-29T07:15:16.788715Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## - RandomFlip\n\n- Type of input data: **frequency domain(image)**\n- Function's role: DataAugment\n\nA preprocessing layer which randomly flips images  ","metadata":{}},{"cell_type":"code","source":"# help(keras_cv.layers.RandomFlip)\n\nrandom_flip_layer = keras_cv.layers.RandomFlip(mode='horizontal',  # 'vertical'  ; 'horizontal'\n                                               rate=1, \n                                               seed=42)  \n\noutput_image = random_flip_layer(spec_img_3D)","metadata":{"execution":{"iopub.status.busy":"2024-02-29T07:15:19.014541Z","iopub.execute_input":"2024-02-29T07:15:19.015022Z","iopub.status.idle":"2024-02-29T07:15:19.054689Z","shell.execute_reply.started":"2024-02-29T07:15:19.014989Z","shell.execute_reply":"2024-02-29T07:15:19.052923Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plot_withlogwithT(spec_img_3D[:, :, -1], print_val='raw spec_img')","metadata":{"execution":{"iopub.status.busy":"2024-02-29T07:15:22.468426Z","iopub.execute_input":"2024-02-29T07:15:22.468923Z","iopub.status.idle":"2024-02-29T07:15:22.867390Z","shell.execute_reply.started":"2024-02-29T07:15:22.468888Z","shell.execute_reply":"2024-02-29T07:15:22.866044Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plot_withlogwithT(output_image[:, :, -1], print_val='spec_img after RandomFlip')","metadata":{"execution":{"iopub.status.busy":"2024-02-29T07:15:25.113445Z","iopub.execute_input":"2024-02-29T07:15:25.113861Z","iopub.status.idle":"2024-02-29T07:15:25.527496Z","shell.execute_reply.started":"2024-02-29T07:15:25.113830Z","shell.execute_reply":"2024-02-29T07:15:25.526128Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## - RandomCutout\n\n- Type of input data: **frequency domain(image)**\n- Function's role: DataAugment\n\nRandomly cut out rectangles from images and fill them. Image augmentation layers are expecting inputs to be rank 3 (HWC) or 4D (NHWC) tensors","metadata":{}},{"cell_type":"code","source":"# help(keras_cv.layers.RandomCutout) \n\nrandom_cutout_layer = keras_cv.layers.RandomCutout(\n    height_factor=(1.0, 1.0),  # percentage tuples\n    width_factor=(0.06, 0.1), \n    fill_mode='constant',  # fill_mode: constant, gaussian_noise \n    seed=42)\n\noutput_image = random_cutout_layer(spec_img_3D)\n\nplot_withlogwithT(output_image[:, :, -1], print_val='spec_img after RandomCutout')","metadata":{"execution":{"iopub.status.busy":"2024-02-29T07:15:28.229808Z","iopub.execute_input":"2024-02-29T07:15:28.231658Z","iopub.status.idle":"2024-02-29T07:15:28.731688Z","shell.execute_reply.started":"2024-02-29T07:15:28.231589Z","shell.execute_reply":"2024-02-29T07:15:28.730670Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## - RandomRotation\n\n- Type of input data: **frequency domain(image)**\n- Function's role: DataAugment\n\nA preprocessing layer which randomly rotates images. ","metadata":{}},{"cell_type":"code","source":"# help(keras_cv.layers.RandomRotation) \n\nrandom_rotation_layer = keras_cv.layers.RandomRotation(factor=0.05, \n                                                       fill_mode='reflect'  # one of {\"constant\", \"reflect\", \"wrap\", \"nearest\"}\n                                                      ) \n\noutput_image = random_rotation_layer(spec_img_3D)\n\nplot_withlogwithT(output_image[:, :, -1], print_val='spec_img after RandomRotation')","metadata":{"execution":{"iopub.status.busy":"2024-02-29T07:15:30.957911Z","iopub.execute_input":"2024-02-29T07:15:30.959515Z","iopub.status.idle":"2024-02-29T07:15:31.442948Z","shell.execute_reply.started":"2024-02-29T07:15:30.959451Z","shell.execute_reply":"2024-02-29T07:15:31.441662Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## - RandomCutout\n\n- Type of input data: **frequency domain(image)**\n- Function's role: DataAugment\n\nRandomly cut out rectangles from images and fill them.\n","metadata":{}},{"cell_type":"code","source":"# help(keras_cv.layers.RandomCutout)\n\nrandom_cutout = keras_cv.layers.RandomCutout(height_factor=0.1, \n                                             width_factor=0.5, \n                                             fill_mode='constant',  # one of {\"constant\", \"gaussian_noise\"}\n                                             seed=42\n                                            )\noutput_image = random_cutout(spec_img_3D)\n\nplot_withlogwithT(output_image[:, :, -1], print_val='spec_img after RandomCutout')","metadata":{"execution":{"iopub.status.busy":"2024-02-29T07:15:32.848573Z","iopub.execute_input":"2024-02-29T07:15:32.849042Z","iopub.status.idle":"2024-02-29T07:15:33.344108Z","shell.execute_reply.started":"2024-02-29T07:15:32.849007Z","shell.execute_reply":"2024-02-29T07:15:33.342556Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## - RandomZoom\n\n- Type of input data: **frequency domain(image)**\n- Function's role: DataAugment\n\nA preprocessing layer which randomly zooms images. This layer will randomly zoom in or out on each axis of an image  independently, filling empty space according to `fill_mode`.","metadata":{}},{"cell_type":"code","source":"# help(keras_cv.layers.RandomZoom)\n\nRandomZoom = keras_cv.layers.RandomZoom(height_factor=(-0.2, 0.3), \n                                                      width_factor=(-0.2, 0.3), \n                                                      fill_mode=\"nearest\"  # {\"constant\", \"reflect\", \"wrap\", \"nearest\"}\n                                                     )\n\nres = RandomZoom(spec_img_3D)","metadata":{"execution":{"iopub.status.busy":"2024-02-29T07:15:35.453746Z","iopub.execute_input":"2024-02-29T07:15:35.454374Z","iopub.status.idle":"2024-02-29T07:15:35.484475Z","shell.execute_reply.started":"2024-02-29T07:15:35.454329Z","shell.execute_reply":"2024-02-29T07:15:35.482810Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plot_withT(res.numpy()[:, :, -1])","metadata":{"execution":{"iopub.status.busy":"2024-02-29T07:15:36.874441Z","iopub.execute_input":"2024-02-29T07:15:36.875659Z","iopub.status.idle":"2024-02-29T07:15:37.266056Z","shell.execute_reply.started":"2024-02-29T07:15:36.875619Z","shell.execute_reply":"2024-02-29T07:15:37.264641Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## - RandomTranslation\n\n- Type of input data: **frequency domain(image)**\n- Function's role: DataAugment\n\nA preprocessing layer which randomly translates images.","metadata":{}},{"cell_type":"code","source":"# help(keras_cv.layers.RandomTranslation)  #\n\n# height_factor=(-0.2, 0.3)` results in an output shifted by a random amount in the range `[-20%, +30%]\nRandomTranslation = keras_cv.layers.RandomTranslation(height_factor=(-0.2, 0.3), \n                                                      width_factor=(-0.2, 0.3), \n                                                      fill_mode=\"nearest\"  # {\"constant\", \"reflect\", \"wrap\", \"nearest\"}\n                                                     )\n\nres = RandomTranslation(spec_img_3D)","metadata":{"execution":{"iopub.status.busy":"2024-02-29T07:15:38.689747Z","iopub.execute_input":"2024-02-29T07:15:38.690190Z","iopub.status.idle":"2024-02-29T07:15:38.713503Z","shell.execute_reply.started":"2024-02-29T07:15:38.690157Z","shell.execute_reply":"2024-02-29T07:15:38.711417Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plot_withT(res.numpy()[:, :, -1])","metadata":{"execution":{"iopub.status.busy":"2024-02-29T07:15:40.000369Z","iopub.execute_input":"2024-02-29T07:15:40.001480Z","iopub.status.idle":"2024-02-29T07:15:40.395289Z","shell.execute_reply.started":"2024-02-29T07:15:40.001437Z","shell.execute_reply":"2024-02-29T07:15:40.394011Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## - RandomAspectRatio\n\n- Type of input data: **frequency domain(image)**\n- Function's role: DataAugment\n\nAn image enhancement technique used to randomly distort the aspect ratio of an image. Changing the aspect ratio of an image","metadata":{}},{"cell_type":"code","source":"# help(keras_cv.layers.RandomAspectRatio)\n\nrandom_aspect_ratio = keras_cv.layers.RandomAspectRatio(factor=0.2)\n\nres = random_aspect_ratio(spec_img_3D)\n\nprint('shape of specimg_after_RandomAspectRatio: ',res[:,:, -1].shape)","metadata":{"execution":{"iopub.status.busy":"2024-02-29T07:15:42.442541Z","iopub.execute_input":"2024-02-29T07:15:42.443008Z","iopub.status.idle":"2024-02-29T07:15:42.503900Z","shell.execute_reply.started":"2024-02-29T07:15:42.442975Z","shell.execute_reply":"2024-02-29T07:15:42.502297Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## - RandomCropAndResize\n\n- Type of input data: **frequency domain(image)**\n- Function's role: DataAugment\n\nRandomly crops a part of an image and resizes it to provided size. This implementation takes an intuitive approach, where we crop the images to a random height and width, and then resize them.","metadata":{}},{"cell_type":"code","source":"# help(keras_cv.layers.RandomCropAndResize)\n\nrandom_flip_layer = keras_cv.layers.RandomCropAndResize(\n    target_size=(300,50),  # A tuple of two integers used as the target size to ultimately crop images to\n    crop_area_factor=(0.8,1.0),  # The ratio of area of the cropped part to that of original image is sampled using this factor\n    aspect_ratio_factor=(3/4.0,4/3.0)  # the aspect ratio sampled represents a value to distort the aspect ratio by.  (3/4, 4/3)  (1.0, 1.0)\n\n)  \n\noutput_image = random_flip_layer(spec_img_3D)","metadata":{"execution":{"iopub.status.busy":"2024-02-29T07:15:44.054449Z","iopub.execute_input":"2024-02-29T07:15:44.055857Z","iopub.status.idle":"2024-02-29T07:15:44.083787Z","shell.execute_reply.started":"2024-02-29T07:15:44.055816Z","shell.execute_reply":"2024-02-29T07:15:44.082334Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plot_withlogwithT(spec_img_3D[:, :, -1], print_val='raw spec_img')","metadata":{"execution":{"iopub.status.busy":"2024-02-29T07:15:46.226880Z","iopub.execute_input":"2024-02-29T07:15:46.227329Z","iopub.status.idle":"2024-02-29T07:15:46.697095Z","shell.execute_reply.started":"2024-02-29T07:15:46.227297Z","shell.execute_reply":"2024-02-29T07:15:46.695724Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plot_withlogwithT(output_image[:, :, -1], print_val='random_flip_layer')","metadata":{"execution":{"iopub.status.busy":"2024-02-29T07:15:49.240441Z","iopub.execute_input":"2024-02-29T07:15:49.240909Z","iopub.status.idle":"2024-02-29T07:15:49.687129Z","shell.execute_reply.started":"2024-02-29T07:15:49.240878Z","shell.execute_reply":"2024-02-29T07:15:49.685593Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## - RandomShear\n\n- Type of input data: **frequency domain(image)**\n- Function's role: DataAugment\n\nA preprocessing layer which randomly shears images.\n\nThis layer will apply random shearings to each image, filling empty space according to fill_mode.\n\nInput pixel values can be of any range and any data type.","metadata":{}},{"cell_type":"code","source":"# help(keras_cv.layers.RandomShear)\n\nrandom_shear_layer = keras_cv.layers.RandomShear(0.5, 0.5)  \noutput_image = random_shear_layer(spec_img_3D)\n\nplot_withlogwithT(output_image[:, :, -1], print_val='RandomShear')","metadata":{"execution":{"iopub.status.busy":"2024-02-29T07:15:52.101613Z","iopub.execute_input":"2024-02-29T07:15:52.102072Z","iopub.status.idle":"2024-02-29T07:15:52.583034Z","shell.execute_reply.started":"2024-02-29T07:15:52.102033Z","shell.execute_reply":"2024-02-29T07:15:52.581665Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## <p style = \"font-family: Georgia;font-weight: bold; font-size: 30px; color: #1192AA; text-align:left\">audiomentations</p>","metadata":{}},{"cell_type":"markdown","source":"`audiomentations` is a Python library designed for audio data augmentation. It offers a comprehensive set of audio transformation techniques that can be applied to audio signals\n\n`torch_audiomentations` is inspired by `audiomentations` and contains similiar functions\n\ndocs:\n- https://github.com/iver56/audiomentations\n- https://iver56.github.io/audiomentations/\n- https://pytorch.org/audio/stable/index.html\n\n","metadata":{}},{"cell_type":"markdown","source":"## - PitchShift\n\n- Type of input data: **time domain**\n- Function's role: DataAugment\n\nPitch shift the sound up or down without changing the tempo\n\nA converter for audio pitch shifting. It can alter the pitch of an audio by raising or lowering it by a specified number of semitones. The input object for the function is the time-domain waveform data. It directly performs pitch shifting on the audio waveform without converting it into spectral domain data.\n1. The PitchShift function operates on time-domain data primarily by adjusting the pitch to change the frequency of the audio.\n2. The parameter min_semitones controls the minimum unit of pitch variation, but in the implementation process, it may involve amplitude adjustment.\n3. When the pitch changes, the frequency also changes, which may result in amplitude variations in the audio.\n4. Therefore, when achieving PitchShift by adjusting the pitch, it may involve adjusting the amplitude to maintain the overall quality and balance of the audio. Thus, it can be understood that while adjusting the pitch, there may also be some degree of amplitude adjustment.\"\n \n","metadata":{}},{"cell_type":"code","source":"PitchShift = audiomentations.PitchShift(min_semitones=-10,  # Minimum semitones to shift. Negative number means shift down.  \n                                max_semitones=0,  # Maximum semitones to shift. Positive number means shift up.  \n                                p=1) \n\nres = PitchShift(waveform, sample_rate=44100)","metadata":{"execution":{"iopub.status.busy":"2024-02-29T07:15:54.827927Z","iopub.execute_input":"2024-02-29T07:15:54.828370Z","iopub.status.idle":"2024-02-29T07:15:56.782135Z","shell.execute_reply.started":"2024-02-29T07:15:54.828340Z","shell.execute_reply":"2024-02-29T07:15:56.780861Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plot_waveform(waveform, sample_split=250, plt_title='raw waveform')","metadata":{"execution":{"iopub.status.busy":"2024-02-29T07:15:56.784342Z","iopub.execute_input":"2024-02-29T07:15:56.784761Z","iopub.status.idle":"2024-02-29T07:15:57.074305Z","shell.execute_reply.started":"2024-02-29T07:15:56.784728Z","shell.execute_reply":"2024-02-29T07:15:57.073037Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plot_waveform(res, sample_split=250, plt_title='waveform after PitchShift')","metadata":{"execution":{"iopub.status.busy":"2024-02-29T07:15:57.321102Z","iopub.execute_input":"2024-02-29T07:15:57.321548Z","iopub.status.idle":"2024-02-29T07:15:57.606456Z","shell.execute_reply.started":"2024-02-29T07:15:57.321517Z","shell.execute_reply":"2024-02-29T07:15:57.605315Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## - Shift\n\n- Type of input data: **time domain**\n- Function's role: DataAugment\n\nShift the samples forwards or backwards, with or without rollover\n","metadata":{"execution":{"iopub.status.busy":"2024-02-25T09:45:42.812797Z","iopub.execute_input":"2024-02-25T09:45:42.813373Z","iopub.status.idle":"2024-02-25T09:45:42.820683Z","shell.execute_reply.started":"2024-02-25T09:45:42.813342Z","shell.execute_reply":"2024-02-25T09:45:42.819994Z"}}},{"cell_type":"code","source":"Shift = audiomentations.Shift(min_shift=0, # Minimum amount of shifting in time.\n                           max_shift=0.01, # Maximum amount of shifting in time.\n                           p=1, \n                           shift_unit='fraction',  # 'fraction'(Fraction of the total sound length), 'samples'(Number of audio samples), 'seconds'(Number of seconds)\n                           rollover=False,  # rollover=False results in an empty space (with zeroes).\n                           fade_duration=0.0)\n\nres = Shift(waveform, sample_rate=44100)","metadata":{"execution":{"iopub.status.busy":"2024-02-29T07:15:59.984502Z","iopub.execute_input":"2024-02-29T07:15:59.985334Z","iopub.status.idle":"2024-02-29T07:15:59.992036Z","shell.execute_reply.started":"2024-02-29T07:15:59.985293Z","shell.execute_reply":"2024-02-29T07:15:59.990553Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plot_waveform(waveform, sample_split=1000, plt_title='raw waveform')","metadata":{"execution":{"iopub.status.busy":"2024-02-29T07:16:01.083140Z","iopub.execute_input":"2024-02-29T07:16:01.083984Z","iopub.status.idle":"2024-02-29T07:16:01.392794Z","shell.execute_reply.started":"2024-02-29T07:16:01.083923Z","shell.execute_reply":"2024-02-29T07:16:01.391361Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plot_waveform(res, sample_split=1000, plt_title='waveform after Shift')","metadata":{"execution":{"iopub.status.busy":"2024-02-29T07:16:03.840116Z","iopub.execute_input":"2024-02-29T07:16:03.840859Z","iopub.status.idle":"2024-02-29T07:16:04.127039Z","shell.execute_reply.started":"2024-02-29T07:16:03.840822Z","shell.execute_reply":"2024-02-29T07:16:04.125540Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## - TimeStretch\n\nType of input data: **time domain**\n\nTime stretch the signal without changing the pitch","metadata":{"execution":{"iopub.status.busy":"2024-02-25T09:45:44.784462Z","iopub.execute_input":"2024-02-25T09:45:44.784742Z","iopub.status.idle":"2024-02-25T09:45:44.798181Z","shell.execute_reply.started":"2024-02-25T09:45:44.784720Z","shell.execute_reply":"2024-02-25T09:45:44.797142Z"}}},{"cell_type":"markdown","source":"If you set leave_length_unchanged to True, the function will attempt to maintain the length of the waveform unchanged during time stretching. This means that even if time stretching is applied, the waveform’s number of samples will still remain the same as the original waveform. This may result in some changes, such as a potential variation in the audio signal’s sampling rate to maintain the length of the waveform.","metadata":{}},{"cell_type":"code","source":"# help(audiomentations.TimeStretch)\n\nTimeStretch = audiomentations.TimeStretch(min_rate=1, max_rate=3, p=1, leave_length_unchanged=True)\n\nres = TimeStretch(waveform, sample_rate=44100)","metadata":{"execution":{"iopub.status.busy":"2024-02-29T07:16:06.552665Z","iopub.execute_input":"2024-02-29T07:16:06.553115Z","iopub.status.idle":"2024-02-29T07:16:06.659001Z","shell.execute_reply.started":"2024-02-29T07:16:06.553084Z","shell.execute_reply":"2024-02-29T07:16:06.657849Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print('waveform.mean(): {:.6f}'.format(waveform.mean()))\nprint('waveform.std(): {:.6f}'.format(waveform.std()))\n\nprint('')\nprint('res.mean(): {:.6f}'.format(res.mean()))\nprint('res.std(): {:.6f}'.format(res.std()))","metadata":{"execution":{"iopub.status.busy":"2024-02-29T07:16:08.698649Z","iopub.execute_input":"2024-02-29T07:16:08.699072Z","iopub.status.idle":"2024-02-29T07:16:08.710052Z","shell.execute_reply.started":"2024-02-29T07:16:08.699044Z","shell.execute_reply":"2024-02-29T07:16:08.708626Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## - Clip\n\n- Type of input data: **time domain**\n- Function's role: DataAugment\n\nClip audio by specified values. e.g. set a_min=-1.0 and a_max=1.0 to ensure that no samples in the audio exceed that extent.","metadata":{"execution":{"iopub.status.busy":"2024-02-25T09:45:44.877382Z","iopub.execute_input":"2024-02-25T09:45:44.877618Z","iopub.status.idle":"2024-02-25T09:45:44.886177Z","shell.execute_reply.started":"2024-02-25T09:45:44.877600Z","shell.execute_reply":"2024-02-25T09:45:44.885353Z"}}},{"cell_type":"code","source":"# help(audiomentations.Clip)\n\nClip = audiomentations.Clip(a_min=-0.1, a_max=0.1, p=1) \n\nres = Clip(waveform, sample_rate=44100)","metadata":{"execution":{"iopub.status.busy":"2024-02-29T07:16:12.325774Z","iopub.execute_input":"2024-02-29T07:16:12.326261Z","iopub.status.idle":"2024-02-29T07:16:12.334335Z","shell.execute_reply.started":"2024-02-29T07:16:12.326214Z","shell.execute_reply":"2024-02-29T07:16:12.332605Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print('waveform.max: {:.1f}'.format(waveform.max()))\nprint('waveform_after_clip.max: {:.1f}'.format(res.max()))","metadata":{"execution":{"iopub.status.busy":"2024-02-29T07:16:14.188171Z","iopub.execute_input":"2024-02-29T07:16:14.189762Z","iopub.status.idle":"2024-02-29T07:16:14.197856Z","shell.execute_reply.started":"2024-02-29T07:16:14.189689Z","shell.execute_reply":"2024-02-29T07:16:14.196081Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## - Gain\n\n- Type of input data: **time domain**\n- Function's role: DataAugment\n\nMultiply the audio by a random amplitude factor to reduce or increase the volume","metadata":{}},{"cell_type":"code","source":"Gain = audiomentations.Gain(min_gain_db=1, max_gain_db=5, p=1)  \n\nres = Gain(waveform, 44100).max()\n\nprint('waveform.max: {:.1f}'.format(waveform.max()))\nprint('waveform_after_Gain.max: {:.1f}'.format(res.max()))","metadata":{"execution":{"iopub.status.busy":"2024-02-29T07:16:15.470205Z","iopub.execute_input":"2024-02-29T07:16:15.471129Z","iopub.status.idle":"2024-02-29T07:16:15.480794Z","shell.execute_reply.started":"2024-02-29T07:16:15.471078Z","shell.execute_reply":"2024-02-29T07:16:15.479313Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## - Add_X_Noise","metadata":{}},{"cell_type":"markdown","source":"### - AddGaussianNoise\n\n- Type of input data: **time domain**\n- Function's role: DataAugment\n\nAdd gaussian noise to the samples","metadata":{}},{"cell_type":"code","source":"# help(audiomentations.AddGaussianNoise)\n\nAddGaussianNoise = audiomentations.AddGaussianNoise(min_amplitude=0.001,  # Minimum noise amplification factor\n                                      max_amplitude=0.1,  # Maximum noise amplification factor\n                                      p=1)\n\nres = AddGaussianNoise(waveform, sample_rate=44100)\n\nprint('waveform.mean: {:.1f}'.format(waveform.max()))\nprint('waveform_after_AddGaussianNoise.mean: {:.1f}'.format(res.max()))","metadata":{"execution":{"iopub.status.busy":"2024-02-29T07:16:16.977763Z","iopub.execute_input":"2024-02-29T07:16:16.978516Z","iopub.status.idle":"2024-02-29T07:16:16.994721Z","shell.execute_reply.started":"2024-02-29T07:16:16.978480Z","shell.execute_reply":"2024-02-29T07:16:16.993220Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### - AddGaussianSNR\n\n- Type of input data: **time domain**\n- Function's role: DataAugment\n\nAdd gaussian noise to the input. A random Signal to Noise Ratio (SNR) will be picked uniformly in the decibel scale. This aligns with human hearing, which is more logarithmic than linear.\n","metadata":{}},{"cell_type":"code","source":"# help(audiomentations.AddGaussianSNR)  # min_snr_db max_snr_db\n\nAddGaussianSNR = audiomentations.AddGaussianSNR(min_snr_db=0.01,  # Minimum signal-to-noise ratio in dB. A lower number means more noise.\n                                                max_snr_db=0.1,  # Maximum signal-to-noise ratio in dB. A greater number means less noise.\n                                                p=1)\n\nres = AddGaussianSNR(waveform, sample_rate=44100).max()\n\nprint('waveform.mean: {:.1f}'.format(waveform.max()))\nprint('waveform_after_AddGaussianSNR.mean: {:.1f}'.format(res.max()))","metadata":{"execution":{"iopub.status.busy":"2024-02-29T07:16:20.191087Z","iopub.execute_input":"2024-02-29T07:16:20.191550Z","iopub.status.idle":"2024-02-29T07:16:20.208138Z","shell.execute_reply.started":"2024-02-29T07:16:20.191518Z","shell.execute_reply":"2024-02-29T07:16:20.207210Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### - AddColoredNoise\n\n- Type of input data: **time domain**\n- Function's role: DataAugment\n\ncolored noise refers to noise that has specific spectral characteristics, rather than being flat or white noise, meaning that different frequencies exist with varying intensities. This may include pink noise, brown noise, or other noise types with specific frequency characteristics. Adding colored noise to audio can bring specific texture or characteristics to the sound and can be creatively used in audio processing and manipulation.","metadata":{}},{"cell_type":"code","source":"# help(torch_audiomentations.AddColoredNoise)\n\nAddColoredNoise = torch_audiomentations.AddColoredNoise(min_snr_in_db=3, # minimum SNR in dB.\n                                           max_snr_in_db=30.0, # maximum SNR in dB.\n                                           mode='per_example' # demo signal [cols, time, used to every single cols\n                                          )\n\nres = AddColoredNoise(torch.tensor(waveform[None, ...][None, ...]), sample_rate=44100)  # If your audio is mono, you can use a shape like [batch_size, 1, num_samples].","metadata":{"execution":{"iopub.status.busy":"2024-02-29T07:16:21.929887Z","iopub.execute_input":"2024-02-29T07:16:21.930492Z","iopub.status.idle":"2024-02-29T07:16:21.942759Z","shell.execute_reply.started":"2024-02-29T07:16:21.930448Z","shell.execute_reply":"2024-02-29T07:16:21.941460Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## - HighPassFilter\n\n- Type of input data: **time domain**\n- Function's role: DataAugment\n\n(May not be useful in the current competition as we do'nt know the frequency of the signal.😅)\n\n`HighPassFilter` is used to apply a high-pass filter to the input audio, removing low-frequency signals and preserving high-frequency signals. The function allows high-pass filtering of the audio based on parameterized filter slopes (6/12/18… dB/octave)","metadata":{}},{"cell_type":"code","source":"# help(audiomentations.HighPassFilter)\n\nsample_rate = 44100 \nduration = 5  \namplitude = 0.5  \n\nt = np.linspace(0, duration, int(sample_rate * duration), endpoint=False)\n\nfrequency1 = 240\nfrequency2 = 440\n\nwaveform_more_freq = amplitude * (np.sin(2 * np.pi * frequency1 * t) + np.sin(2 * np.pi * frequency2 * t))\n\naugment = audiomentations.HighPassFilter(min_cutoff_freq=430, \n                                         min_rolloff=6, \n                                         max_rolloff=18, \n                                         p=1)  # min_bandwidth_fraction, max_bandwidth_fraction\naugmented_waveform = augment(waveform_more_freq, sample_rate=sample_rate)\n\nstart = 100\nend = start + 1500\n\nplt.figure(figsize=(12, 6))\nplt.plot(t[start: end], waveform_more_freq[start: end], label='Original Signal')\nplt.plot(t[start: end], augmented_waveform[start: end], label='Filtered Signal')\nplt.xlabel('Time (s)')\nplt.ylabel('Amplitude')\nplt.legend()\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2024-02-29T07:16:28.536647Z","iopub.execute_input":"2024-02-29T07:16:28.537106Z","iopub.status.idle":"2024-02-29T07:16:28.923921Z","shell.execute_reply.started":"2024-02-29T07:16:28.537074Z","shell.execute_reply":"2024-02-29T07:16:28.922827Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"sample_rate = 44100  \nduration = 5  \nfrequency = 440  \namplitude = 0.5 \n\nt = np.linspace(0, duration, int(sample_rate * duration), endpoint=False)\nwaveform = amplitude * np.sin(2 * np.pi * frequency * t)  # ","metadata":{"execution":{"iopub.status.busy":"2024-02-29T07:16:31.944626Z","iopub.execute_input":"2024-02-29T07:16:31.945063Z","iopub.status.idle":"2024-02-29T07:16:31.959516Z","shell.execute_reply.started":"2024-02-29T07:16:31.945030Z","shell.execute_reply":"2024-02-29T07:16:31.958074Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plt.figure(figsize=(12, 6))\nplt.plot(t[start: end], waveform[start: end], label='waveform with only 440HZ')\nplt.plot(t[start: end], augmented_waveform[start: end], label='Filtered Signal')\nplt.xlabel('Time (s)')\nplt.ylabel('Amplitude')\nplt.title('Correctly filtered out signals with a frequency of 440Hz, but changed the amplitude')\nplt.legend()\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2024-02-29T07:16:33.967392Z","iopub.execute_input":"2024-02-29T07:16:33.968516Z","iopub.status.idle":"2024-02-29T07:16:34.380273Z","shell.execute_reply.started":"2024-02-29T07:16:33.968462Z","shell.execute_reply":"2024-02-29T07:16:34.378722Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plt.figure(figsize=(12, 6))\nplt.plot(t[start: end], augmented_waveform[start: end], label='Filtered Signal')\nplt.xlabel('Time (s)')\nplt.ylabel('Amplitude')\nplt.legend()\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2024-02-29T07:16:37.259725Z","iopub.execute_input":"2024-02-29T07:16:37.261387Z","iopub.status.idle":"2024-02-29T07:16:37.591759Z","shell.execute_reply.started":"2024-02-29T07:16:37.261334Z","shell.execute_reply":"2024-02-29T07:16:37.590308Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## - LowPassFilter\n\nsimilar to HighPassFilter\n\n(May not be useful in the current competition as we do'nt know the frequency of the signal.😅)","metadata":{}},{"cell_type":"code","source":"# help(audiomentations.LowPassFilter)\n\nsample_rate = 44100 \nduration = 5  \namplitude = 0.5  \n\nt = np.linspace(0, duration, int(sample_rate * duration), endpoint=False)\n\nfrequency1 = 240\nfrequency2 = 440\n\nwaveform_more_freq = amplitude * (np.sin(2 * np.pi * frequency1 * t) + np.sin(2 * np.pi * frequency2 * t))\n\naugment = audiomentations.LowPassFilter(min_cutoff_freq=100, \n                                        max_cutoff_freq=300,\n                                        min_rolloff=12, \n                                        max_rolloff=18, \n                                        p=1)  # min_bandwidth_fraction, max_bandwidth_fraction\naugmented_waveform = augment(waveform_more_freq, sample_rate=sample_rate)\n\nstart = 100\nend = start + 1500\n\nplt.figure(figsize=(12, 6))\nplt.plot(t[start: end], waveform_more_freq[start: end], label='Original Signal')\nplt.plot(t[start: end], augmented_waveform[start: end], label='Filtered Signal')\nplt.xlabel('Time (s)')\nplt.ylabel('Amplitude')\nplt.legend()\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2024-02-29T07:16:41.422003Z","iopub.execute_input":"2024-02-29T07:16:41.422421Z","iopub.status.idle":"2024-02-29T07:16:41.810951Z","shell.execute_reply.started":"2024-02-29T07:16:41.422393Z","shell.execute_reply":"2024-02-29T07:16:41.809477Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"sample_rate = 44100  \nduration = 5  \nfrequency = 240  \namplitude = 0.5 \n\nt = np.linspace(0, duration, int(sample_rate * duration), endpoint=False)\nwaveform = amplitude * np.sin(2 * np.pi * frequency * t)  # ","metadata":{"execution":{"iopub.status.busy":"2024-02-29T07:16:44.170847Z","iopub.execute_input":"2024-02-29T07:16:44.171270Z","iopub.status.idle":"2024-02-29T07:16:44.186088Z","shell.execute_reply.started":"2024-02-29T07:16:44.171225Z","shell.execute_reply":"2024-02-29T07:16:44.184270Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plt.figure(figsize=(12, 6))\nplt.plot(t[start: end], waveform[start: end], label='waveform with only 240HZ')\nplt.plot(t[start: end], augmented_waveform[start: end], label='Filtered Signal')\nplt.xlabel('Time (s)')\nplt.ylabel('Amplitude')\nplt.title('Correctly filtered out signals with a frequency of 440Hz, but changed the amplitude')\nplt.legend()\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2024-02-29T07:16:46.975789Z","iopub.execute_input":"2024-02-29T07:16:46.976273Z","iopub.status.idle":"2024-02-29T07:16:47.323954Z","shell.execute_reply.started":"2024-02-29T07:16:46.976225Z","shell.execute_reply":"2024-02-29T07:16:47.322522Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## - AdjustDuration\n\n- Type of input data: **time domain**\n- Function's role: DataAugment\n\nTrim or pad the audio to the specified length/duration in samples or seconds. If the input sound is longer than the target duration, pick a random offset and crop the sound to the target duration. If the input sound is shorter than the target duration, pad the sound so the duration matches the target duration.\n","metadata":{}},{"cell_type":"code","source":"AdjustDuration = audiomentations.AdjustDuration(duration_samples=220600, \n                                                padding_mode='silence',  # 'silence'  \"wrap\" or \"reflect\".\n                                                padding_position='end', \n                                                p=1)  \n\nres = AdjustDuration(waveform, sample_rate=44100)\n\nprint('len(waveform): ', len(waveform))\n\nprint('len(waveform_after_AdjustDuration): ', len(res))","metadata":{"execution":{"iopub.status.busy":"2024-02-29T07:16:50.435064Z","iopub.execute_input":"2024-02-29T07:16:50.435506Z","iopub.status.idle":"2024-02-29T07:16:50.445757Z","shell.execute_reply.started":"2024-02-29T07:16:50.435475Z","shell.execute_reply":"2024-02-29T07:16:50.443809Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plot_waveform(waveform=res, sample_split=1000, plt_title='waveform_after_AdjustDuration', position='end')","metadata":{"execution":{"iopub.status.busy":"2024-02-29T07:16:52.233801Z","iopub.execute_input":"2024-02-29T07:16:52.234323Z","iopub.status.idle":"2024-02-29T07:16:52.462984Z","shell.execute_reply.started":"2024-02-29T07:16:52.234280Z","shell.execute_reply":"2024-02-29T07:16:52.461682Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## - PolarityInversion\n\n- Type of input data: **time domain**\n- Function's role: DataAugment\n\nFlip the audio samples upside-down, reversing their polarity. In other words, multiply the waveform by -1, so negative values become positive, and vice versa.\n\nThe difference between `audiomentations.Reverse` and `audiomentations.PolarityInversion` is that the former is a reversal on the time level, while the latter is a reversal of the sign of the sample values.","metadata":{}},{"cell_type":"code","source":"# help(audiomentations.PolarityInversion)\n\nsample_rate = 44100\nduration = 0.01\namplitude = 0.5\n\nt = np.linspace(0, duration, int(sample_rate * duration), endpoint=False)\n\nwaveform = amplitude * np.sin(2 * np.pi * 440 * t)\n\naugment = audiomentations.PolarityInversion(p=1)\n\ninverted_waveform = augment(samples=waveform, sample_rate=sample_rate)\n\nplt.figure(figsize=(12, 6))\nplt.subplot(2, 1, 1)\nplt.plot(t, waveform, label='Original waveform')\nplt.xlabel('Time (s)')\nplt.ylabel('Amplitude')\nplt.legend()\n\nplt.subplot(2, 1, 2)\nplt.plot(t, inverted_waveform, label='Inverted waveform')\nplt.xlabel('Time (s)')\nplt.ylabel('Amplitude')\nplt.legend()\n\nplt.tight_layout()\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2024-02-29T07:17:19.137457Z","iopub.execute_input":"2024-02-29T07:17:19.137907Z","iopub.status.idle":"2024-02-29T07:17:19.653559Z","shell.execute_reply.started":"2024-02-29T07:17:19.137875Z","shell.execute_reply":"2024-02-29T07:17:19.652431Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## - HighShelfFilter\n\n- Type of input data: **time domain**\n- Function's role: DataAugment\n\n(May not be useful in the current competition as we do'nt know the frequency of the signal.😅)\n\nA high shelf filter is a filter that either boosts (increases amplitude) or cuts (decreases amplitude) frequencies above a certain center frequency. This transform applies a high-shelf filter at a specific center frequency in hertz.\n\n","metadata":{}},{"cell_type":"code","source":"# help(audiomentations.HighShelfFilter)\n\nsample_rate = 44100\nduration = 5\namplitude = 0.5\n\nt = np.linspace(0, duration, int(sample_rate * duration), endpoint=False)\n\nfrequency1 = 320\nfrequency2 = 350\nfrequency3 = 440\n\nwaveform_more_freq = amplitude * (np.sin(2 * np.pi * frequency1 * t) + np.sin(2 * np.pi * frequency2 * t) + np.sin(2 * np.pi * frequency3 * t))\n\nhigh_shelf = audiomentations.HighShelfFilter(min_center_freq=420, \n                                             max_center_freq=460, \n                                             min_gain_db=-18.0 , # The magnitude of reduction\n                                             max_gain_db=18.0, \n                                             min_q=0.99, # proboblity to reduction \n                                             max_q=0.01, \n                                             p=1.0)\naugmented_waveform = high_shelf(waveform_more_freq, sample_rate=sample_rate)","metadata":{"execution":{"iopub.status.busy":"2024-02-29T07:17:44.867913Z","iopub.execute_input":"2024-02-29T07:17:44.868348Z","iopub.status.idle":"2024-02-29T07:17:44.895163Z","shell.execute_reply.started":"2024-02-29T07:17:44.868316Z","shell.execute_reply":"2024-02-29T07:17:44.893686Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"start = 100\nend = start + 1500\n\nplt.figure(figsize=(12, 6))\nplt.plot(t[start:end], waveform_more_freq[start:end], label='Original Signal')\nplt.plot(t[start:end], augmented_waveform[start:end], label='Filtered Signal')\nplt.xlabel('Time (s)')\nplt.ylabel('Amplitude')\nplt.legend()\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2024-02-29T07:17:46.464601Z","iopub.execute_input":"2024-02-29T07:17:46.465031Z","iopub.status.idle":"2024-02-29T07:17:46.768021Z","shell.execute_reply.started":"2024-02-29T07:17:46.465002Z","shell.execute_reply":"2024-02-29T07:17:46.766579Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## - Resample\n\n- Type of input data: **time domain**\n- Function's role: DataAugment\n\nResample signal using librosa.core.resample. To do downsampling only set both minimum and maximum sampling rate lower than original sampling rate and vice versa to do upsampling only.","metadata":{}},{"cell_type":"code","source":"# help(audiomentations.Resample)","metadata":{"execution":{"iopub.status.busy":"2024-02-29T07:06:43.457194Z","iopub.status.idle":"2024-02-29T07:06:43.457799Z","shell.execute_reply.started":"2024-02-29T07:06:43.457503Z","shell.execute_reply":"2024-02-29T07:06:43.457527Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"sample_rate = 44100 \nduration = 5  \nfrequency = 440  \namplitude = 0.5  \n\nt = np.linspace(0, duration, int(sample_rate * duration), endpoint=False)\nwaveform = amplitude * np.sin(2 * np.pi * frequency * t)  ","metadata":{"execution":{"iopub.status.busy":"2024-02-29T07:17:51.243238Z","iopub.execute_input":"2024-02-29T07:17:51.243702Z","iopub.status.idle":"2024-02-29T07:17:51.256610Z","shell.execute_reply.started":"2024-02-29T07:17:51.243655Z","shell.execute_reply":"2024-02-29T07:17:51.254973Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"Resample = audiomentations.Resample(min_sample_rate=800,  # Minimum sample rate\n                              max_sample_rate=1000,  # Maximum sample rate\n                              p=1)  #\n\nres = Resample(waveform, sample_rate=44100)","metadata":{"execution":{"iopub.status.busy":"2024-02-29T07:17:54.192949Z","iopub.execute_input":"2024-02-29T07:17:54.193469Z","iopub.status.idle":"2024-02-29T07:17:54.206634Z","shell.execute_reply.started":"2024-02-29T07:17:54.193425Z","shell.execute_reply":"2024-02-29T07:17:54.205138Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print('len of wavefrom: ', len(waveform))\n\nprint('len of waveform_after_resample: ', len(res))","metadata":{"execution":{"iopub.status.busy":"2024-02-29T07:17:56.272071Z","iopub.execute_input":"2024-02-29T07:17:56.272596Z","iopub.status.idle":"2024-02-29T07:17:56.281436Z","shell.execute_reply.started":"2024-02-29T07:17:56.272558Z","shell.execute_reply":"2024-02-29T07:17:56.280015Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plot_waveform(waveform=res, sample_split=250, plt_title='waveform_after_resample', position='start')","metadata":{"execution":{"iopub.status.busy":"2024-02-29T07:18:09.509883Z","iopub.execute_input":"2024-02-29T07:18:09.510299Z","iopub.status.idle":"2024-02-29T07:18:09.770075Z","shell.execute_reply.started":"2024-02-29T07:18:09.510269Z","shell.execute_reply":"2024-02-29T07:18:09.768780Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## - Reverse\n\n- Type of input data: **time domain**\n- Function's role: DataAugment\n\nThe difference between `audiomentations.Reverse` and `audiomentations.PolarityInversion` is that the former is a time-domain reversal, while the latter is a reversal of the sample amplitude sign","metadata":{}},{"cell_type":"code","source":"Reverse = audiomentations.Reverse(p=1)\n\nres = Reverse(waveform, sample_rate=44100)","metadata":{"execution":{"iopub.status.busy":"2024-02-29T07:18:18.180219Z","iopub.execute_input":"2024-02-29T07:18:18.180714Z","iopub.status.idle":"2024-02-29T07:18:18.188116Z","shell.execute_reply.started":"2024-02-29T07:18:18.180682Z","shell.execute_reply":"2024-02-29T07:18:18.186275Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plot_waveform(waveform=waveform, sample_split=1000, plt_title='waveform', position='start')","metadata":{"execution":{"iopub.status.busy":"2024-02-29T07:18:19.502031Z","iopub.execute_input":"2024-02-29T07:18:19.502511Z","iopub.status.idle":"2024-02-29T07:18:19.742948Z","shell.execute_reply.started":"2024-02-29T07:18:19.502479Z","shell.execute_reply":"2024-02-29T07:18:19.741591Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plot_waveform(waveform=res, sample_split=1000, plt_title='waveform_after_Reverse', position='start')","metadata":{"execution":{"iopub.status.busy":"2024-02-29T07:18:22.061680Z","iopub.execute_input":"2024-02-29T07:18:22.062164Z","iopub.status.idle":"2024-02-29T07:18:22.296463Z","shell.execute_reply.started":"2024-02-29T07:18:22.062129Z","shell.execute_reply":"2024-02-29T07:18:22.294981Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## - Normalize\n\n- Type of input data: **time domain**\n- Function's role: DataAugment\n\nApply a constant amount of gain, so that highest signal level present in the sound becomes 0 dBFS, i.e. the loudest level allowed if all samples must be between -1 and 1. Also known as peak normalization.","metadata":{}},{"cell_type":"code","source":"# help(audiomentations.Normalize)\n\nNormalize = audiomentations.Normalize(apply_to='all', p=1)\n\nres = Normalize(waveform, sample_rate=44100)","metadata":{"execution":{"iopub.status.busy":"2024-02-29T07:18:26.573219Z","iopub.execute_input":"2024-02-29T07:18:26.573769Z","iopub.status.idle":"2024-02-29T07:18:26.582895Z","shell.execute_reply.started":"2024-02-29T07:18:26.573733Z","shell.execute_reply":"2024-02-29T07:18:26.581121Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plot_waveform(waveform=res, sample_split=1000, plt_title='waveform_after_Normalize', position='start')","metadata":{"execution":{"iopub.status.busy":"2024-02-29T07:18:29.127388Z","iopub.execute_input":"2024-02-29T07:18:29.128768Z","iopub.status.idle":"2024-02-29T07:18:29.397087Z","shell.execute_reply.started":"2024-02-29T07:18:29.128713Z","shell.execute_reply":"2024-02-29T07:18:29.395552Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## - BandPassFilter\n\n- Type of input data: **time domain**\n- Function's role: DataAugment\n\n(May not be useful in the current competition as we do'nt know the frequency of the signal.😅)\n\n`audiomentations.BandPassFilter` function is used to apply a band-pass filtering operation to the input audio. Band-pass filtering is a signal processing technique that allows only a certain range of frequencies to pass through while suppressing other frequency signals. ","metadata":{}},{"cell_type":"code","source":"# help(audiomentations.BandStopFilter)\n\nsample_rate = 44100 \nduration = 5  \namplitude = 0.5  \n\nt = np.linspace(0, duration, int(sample_rate * duration), endpoint=False)\n\nfrequency1 = 140\nfrequency2 = 440\n\n\nwaveform_with_freq = amplitude * (np.sin(2 * np.pi * frequency1 * t) + np.sin(2 * np.pi * frequency2 * t))\n\n# Min_center_freq and max_center_freq represent the center frequency range of the bandpass filter. The filter will only allow signals within this frequency range to pass through\naugment = audiomentations.BandPassFilter(min_center_freq=420, \n                                         max_center_freq=450,\n                                         min_bandwidth_fraction=0.5, \n                                         max_bandwidth_fraction=1, \n                                         p=1)  # min_bandwidth_fraction, max_bandwidth_fraction\naugmented_waveform = augment(waveform_with_freq, sample_rate=sample_rate)","metadata":{"execution":{"iopub.status.busy":"2024-02-29T07:18:32.537036Z","iopub.execute_input":"2024-02-29T07:18:32.538673Z","iopub.status.idle":"2024-02-29T07:18:32.565913Z","shell.execute_reply.started":"2024-02-29T07:18:32.538589Z","shell.execute_reply":"2024-02-29T07:18:32.563688Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"start = 1000\nend = start+1000\n\nplt.figure(figsize=(12, 6))\nplt.plot(t[start:end], waveform_with_freq[start:end], label='Original Signal')\nplt.plot(t[start:end], augmented_waveform[start:end], label='Filtered Signal')\nplt.xlabel('Time (s)')\nplt.ylabel('Amplitude')\nplt.legend()\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2024-02-29T07:18:37.379415Z","iopub.execute_input":"2024-02-29T07:18:37.379852Z","iopub.status.idle":"2024-02-29T07:18:37.720955Z","shell.execute_reply.started":"2024-02-29T07:18:37.379822Z","shell.execute_reply":"2024-02-29T07:18:37.719501Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## - BandStopFilter\n\n- Type of input data: **time domain**\n- Function's role: DataAugment\n\n(May not be useful in the current competition as we do'nt know the frequency of the signal.😅)\n\nsimilar to BandpassFilter","metadata":{}},{"cell_type":"code","source":"# help(audiomentations.BandStopFilter)\n\nsample_rate = 44100 \nduration = 5  \namplitude = 0.5  \n\nt = np.linspace(0, duration, int(sample_rate * duration), endpoint=False)\n\nfrequency1 = 320\nfrequency2 = 350\nfrequency3 = 440\n\nwaveform_more_freq = amplitude * (np.sin(2 * np.pi * frequency1 * t) + np.sin(2 * np.pi * frequency2 * t) + np.sin(2 * np.pi * frequency3 * t))\n\naugment = audiomentations.BandStopFilter(min_center_freq=315, max_center_freq=355, p=1)  # min_bandwidth_fraction, max_bandwidth_fraction\naugmented_waveform = augment(waveform_more_freq, sample_rate=sample_rate)","metadata":{"execution":{"iopub.status.busy":"2024-02-29T07:18:41.402491Z","iopub.execute_input":"2024-02-29T07:18:41.402901Z","iopub.status.idle":"2024-02-29T07:18:41.432808Z","shell.execute_reply.started":"2024-02-29T07:18:41.402872Z","shell.execute_reply":"2024-02-29T07:18:41.431600Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"start = 0\nend = start+1500\n\nplt.figure(figsize=(12, 6))\nplt.plot(t[start:end], waveform_more_freq[start:end], label='Original Signal')\nplt.plot(t[start:end], augmented_waveform[start:end], label='Filtered Signal')\nplt.xlabel('Time (s)')\nplt.ylabel('Amplitude')\nplt.legend()\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2024-02-29T07:18:43.448382Z","iopub.execute_input":"2024-02-29T07:18:43.448797Z","iopub.status.idle":"2024-02-29T07:18:43.812566Z","shell.execute_reply.started":"2024-02-29T07:18:43.448766Z","shell.execute_reply":"2024-02-29T07:18:43.811107Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Thanks for watching!","metadata":{}},{"cell_type":"markdown","source":"**If you find this notebook useful, please upvoted, thank you!** ⭐️⭐️⭐️⭐️⭐️","metadata":{}}]}