😎 κ³΅λΆ€ν•˜λŠ” μ§•μ§•μ•ŒνŒŒμΉ΄λŠ” μ²˜μŒμ΄μ§€?

[μŒμ„±]μŒμ„± 처리 λΆ„μ•Όμ—μ„œμ˜ Deep Learning λ³Έλ¬Έ

πŸ‘©‍πŸ’» 인곡지λŠ₯ (ML & DL)/ML & DL

[μŒμ„±]μŒμ„± 처리 λΆ„μ•Όμ—μ„œμ˜ Deep Learning

μ§•μ§•μ•ŒνŒŒμΉ΄ 2022. 1. 24. 23:47
728x90
λ°˜μ‘ν˜•

220124 μž‘μ„±

<λ³Έ λΈ”λ‘œκ·ΈλŠ” Kaggle 을 μ°Έκ³ ν•΄μ„œ μ €λ§Œμ˜ 풀이λ₯Ό μž‘μ„±ν•˜μ˜€μŠ΅λ‹ˆλ‹€>

https://engineering.linecorp.com/ko/blog/voice-waveform-arbitrary-signal-to-noise-ratio-python/

 

λ”₯ λŸ¬λ‹ μŒμ„± 인식에 ν•„μš”ν•œ ν›ˆλ ¨ 데이터λ₯Ό 직접 λ§Œλ“€μ–΄λ³΄μž - LINE ENGINEERING

μ•ˆλ…•ν•˜μ„Έμš”, LINEμ—μ„œ κ΄‘κ³  ν”Œλž«νΌ κ°œλ°œμ„ 맑고 μžˆλŠ” 1λ…„μ°¨ μ‹ μž…μ‚¬μ› Kunihiko Satoμž…λ‹ˆλ‹€. 이번 λΈ”λ‘œκ·Έμ—μ„œλŠ” Python을 μ‚¬μš©ν•΄μ„œ μž„μ˜μ˜ Signal-to-Noise ratio(SNλΉ„)λ₯Ό 가진 μŒμ„± νŒŒν˜•μ„ λ§Œλ“œλŠ” 방법을 μ†Œκ°œ

engineering.linecorp.com

 

 

 

 

 

1. μŒμ› 뢄리

: μ—¬λŸ¬ 개의 μŒμ›μ΄ μ„žμ—¬ μžˆλŠ” μž…λ ₯ νŒŒν˜•μ„ κ°œλ³„ μŒμ›μ˜ νŒŒν˜•μœΌλ‘œ λΆ„λ¦¬ν•˜λŠ” 것

: μŒμ„± κ°•μ‘° or 작음 제거

: μŒμ„±κ³Ό 작음이 μ„žμ—¬ μžˆλŠ” μž…λ ₯ νŒŒν˜•μ„ μŒμ„± νŒŒν˜•κ³Ό 작음 νŒŒν˜•μœΌλ‘œ 각각 λΆ„λ¦¬ν•΄λ‚΄λŠ” 것

μŒμ„±κ³Ό 작음으둜 뢄리

ex) 작음제거, νŠΉμ • 인물 μŒμ„± μΆ”μΆœ, 악기별 μŒμ› 뢄리

 

 

 

 

2. λ”₯λŸ¬λ‹μ— ν•„μš”ν•œ ν›ˆλ ¨ 데이터 μ œμž‘

- ν›ˆλ ¨ 데이터

: μŒμ„±κ³Ό 작음이 μ„žμ—¬ μžˆλŠ” νŒŒν˜•μ΄ ν•„μš”

: 이 데이터λ₯Ό 톡해 μ‹ κ²½ νšŒλ‘œλ§μ€ 작음이 μ„žμ—¬ μžˆλŠ” μŒμ„± νŒŒν˜•μ—μ„œ μŒμ„±λ§Œ μΆ”μΆœν•˜λ„λ‘ ν›ˆλ ¨

 

 

 

 

3. Signal-to-Noise ratio ( SNλΉ„, μ‹ ν˜Έ λŒ€λΉ„ 작음 λΉ„ )

: μ‹ ν˜Έμ˜ 크기가 작음의 크기보닀 μ–Όλ§ˆλ‚˜ 큰지 λ‚˜νƒ€λ‚΄λŠ” λΉ„μœ¨

: SNλΉ„μ˜ λ‹¨μœ„λŠ” dB(λ°μ‹œλ²¨)

: Signal -> μŒμ„±, Noise κ·Έ μ™Έ μ†Œλ¦¬ -> ν™”μ΄νŠΈ λ…Έμ΄μ¦ˆ, ν™˜κ²½μŒ

 

: SN λΉ„ λ†’μ„μˆ˜λ‘ μŒμ„± > 작음

: 0 db λŠ” μŒμ„±κ³Ό 작음 크기 동일

: - db λŠ” μŒμ„± < 작음

 

: μž„μ˜μ˜ Signal-to-Noise ratoi λ₯Ό 가진 μŒμ„± νŒŒν˜•μ„ λ§Œλ“ λ‹€

=> μ›ν•˜λŠ” dB λΉ„μœ¨λ‘œ μŒμ„±κ³Ό 작음이 μ„žμ—¬ μžˆλŠ” μŒμ„± νŒŒν˜•μ„ λ§Œλ“ λ‹€

 

- 계산 방법

SNλΉ„

Asignal : μŒμ„±μ˜ 크기 or μ„ΈκΈ°

Anoise : 작음의 크기 or μ„ΈκΈ° ( μ„ΈκΈ° : μ§„ν­κ°’μ˜ 평균 제곱근_Root Mean Square, RMS )

 

1) μŒμ„±μ˜ 진폭값이 λ§ˆμ΄λ„ˆμŠ€ 수치둜 λ‚˜μ˜¬ μˆ˜λ„ μžˆμœΌλ‹ˆ 진폭값 제곱

2) μ œκ³±ν•œ 값을 λ”ν•œ λ’€ κ·Έ κ°’μ˜ 평균 ꡬ함

3) ν‰κ· ν•œ κ°’μ˜ μ œκ³±κ·Όμ„ κ³„μ‚°ν•˜λ©΄ μ†Œλ¦¬μ˜ μ„ΈκΈ° ꡬ함

: νŒŒν˜•μ— 무음 ꡬ간, νŠΉμ • κ΅¬κ°„λ§Œ λΉ„μ •μƒμ μœΌλ‘œ 진폭값 큼 -> μ†Œλ¦¬μ˜ μ„ΈκΈ°λ‘œ μ‚¬μš© X

 

 

 

 

 

4. Python 으둜 μž„μ˜ Signal-to-Noise ratio 의 μŒμ„± νŒŒν˜• μ œμž‘

: μŒμ„±μ— μž„μ˜ 크기의 작음 쀑첩

 

 

1) μŒμ„± 파일 포맷 확인

: .wav 파일 μ‚¬μš©

 

 

2) wav 파일 λ‘œλ”©

import argparse     # ν”„λ‘œκ·Έλž¨μ„ μ‹€ν–‰μ‹œμ— μ»€λ§¨λ“œ 라인에 인수λ₯Ό λ°›μ•„ 처리λ₯Ό κ°„λ‹¨νžˆ
import array
import math
import numpy as np
import random
import wave
def get_args() :
    # μΈμžκ°’μ„ 받을 수 μžˆλŠ” μΈμŠ€ν„΄μŠ€ 생성
    parser = argparse.ArgumentParser()
    # parser.add_argument둜 받아듀일 인수λ₯Ό μΆ”κ°€
    parser.add_argument('--clean_file', type = str, required = True)    # μŒμ„±λ§Œ μžˆλŠ” 파일 μ ˆλŒ€ 경둜
    parser.add_argument('--noise_file', type = str, required = True)    # 작음만 μžˆλŠ” 파일 μ ˆλŒ€ 경둜
    parser.add_argument('--output_mixed_file', type = str, default = '', required = True)   # 처리 μ™„λ£Œλœ μŒμ„±λ§Œ μžˆλŠ” 파일 μ ˆλŒ€ 경둜
    parser.add_argument('--output_clean_file', type = str, default = '')    # 처리 μ™„λ£Œλœ 작음만 μžˆλŠ” 파일 μ ˆλŒ€ 경둜
    parser.add_argument('--output_noise_file', type = str, default = '')    # μž„μ˜ SNλΉ„μ˜ μŒμ„± 파일 μ ˆλŒ€ 경둜
    parser.add_argument('--snr', type = float, default = '', required = True)   # ν•©μ„±ν•˜λ €λŠ” SN λΉ„μ˜ 크기
    # 인수λ₯Ό 뢄석, μ €μž₯
    args = parser.parse_args()
    return args
# 각각의 진폭을 λ”ν•œ 진폭값이 wav 파일의 μ–‘μžν™” bit수, 16bit(32767)
def cal_adjusted_rms(clean_rms, snr) :
    a = float(snr) / 20
    noise_rms = clean_rms / (10**a)
    return noise_rms
# μŒμ„± νŒŒν˜•μ˜ 진폭값 μ·¨λ“ν•˜κΈ°
def cal_amp(wf) :
    # wf.readframes(n) : μ΅œλŒ€ n개의 μ˜€λ””μ˜€ ν”„λ ˆμž„ μ½μ–΄μ„œ bytes 객체 λ°˜ν™˜
    # wf.getnframes() : μ˜€λ””μ˜€ ν”„λ ˆμž„ 수 λ°˜ν™˜
    # => wav 파일의 λͺ¨λ“  진폭값을 취득
    buffer = wf.readframes(wf.getnframes())     
    # The dtype depends on the value of pulse-code modulation
    # The int16 is set for 16-bit PCM
    amptitude = (np.frombuffer(buffer, dtype = "int16")).astype(np.float64)
    return amptitude
# μ§„ν­κ°’μ˜ 평균 제곱근(Root Mean Square, RMS) κ΅¬ν•˜κΈ°
# 작음 데이터 νŒŒν˜•μ„ μŒμ„± 데이터 νŒŒν˜• 길이둜 자λ₯΄κΈ°
# 작음 νŒŒμΌμ—μ„œ μž˜λΌλ‚Έ νŒŒν˜•, μŒμ„± νŒŒν˜•μ˜ RMSλ₯Ό 각각 계산 -> μž„μ˜ SNλΉ„κ°€ λ‚˜μ˜€λ„λ‘ 쀑첩
def cal_rms(amp) :
    # np.square : μ–΄λ ˆμ΄μ˜ μš”μ†Œ λ‹¨μœ„λ‘œ μ œκ³±μ„ λ°˜ν™˜
    # SN λΉ„ : 진폭값 제곱 -> 평균 -> 제곱근
    return np.sqrt(np.mean(np.square(amp), axis = -1))
def save_waveform(output_path, params, amp) :
    output_file = wave.Wave_write(output_path)
    output_file.setparams(params) #nchannels, sampwidth, framerate, nframes, comptype, compname
    output_file.writeframes(array.array('h', amp.astype(np.int16)).tobytes())
    output_file.close()
if __name__ == '__main__' :
    args = get_args()

    clean_file = args.clean_file
    noise_file = args.noise_file

    clean_wav = wave.open(clean_file, 'r')
    noise_wav = wave.open(noise_file, 'r')

    clean_amp = cal_amp(clean_wav)
    noise_amp = cal_amp(noise_wav)

    clean_rms = cal_rms(clean_amp)

    # μž‘μŒμ„ 자λ₯Ό μœ„μΉ˜λ₯Ό 랜덀으둜 μ •ν•΄μ„œ μŒμ„±μ˜ 길이만큼 μž˜λΌλƒ„
    start = random.randint(0, len(noise_amp) - len(clean_amp))
    divided_noise_amp = noise_amp[start: start + len(clean_amp)]
    noise_rms = cal_rms(divided_noise_amp)

    snr = args.snr
    adjusted_noise_rms = cal_adjusted_rms(clean_rms, snr)

    adjusted_noise_rms = divided_noise_amp * (adjusted_noise_rms / noise_rms)
    mixed_amp = (clean_amp + adjusted_noise_rms)


    # Avoid clipping noise
    max_int16 = np.iinfo(np.int16).max
    min_int16 = np.iinfo(np.int16).min


    # μ„œλ‘œ λ”ν•œ 값이 16bit의 μ΅œλŒ€κ°’μ„ λ„˜μœΌλ©΄, μ΅œλŒ€ 32767 μ•ˆμ— λ“€μ–΄μ˜€λ„λ‘ μ •κ·œν™”
    if mixed_amp.max(axis = 0) > max_int16 or mixed_amp.min(axis = 0) < min_int16 :
        if mixed_amp.max(axis = 0) >= abs(mixed_amp.min(axis = 0)) :
            reduction_rate = max_int16 / mixed_amp.max(axis = 0)
        else :
            reduction_rate = min_int16 / mixed_amp.min(axis = 0)
        
        mixed_amp = mixed_amp * (reduction_rate)
        clean_amp = clean_amp * (reduction_rate)

    # save_waveform(args.output_mixed_file, clean_wav.getparams(), mixed_amp)

    # νŒŒν˜•μ„ wav 파일둜 μ €μž₯
    noise_wave = wave.Wave_write(args.output_noise_file)
    noise_wave.setparams(clean_wav.getparams())     # setparams : wav파일의 포맷을 μ§€μ •ν•˜λŠ” λ©”μ„œλ“œ
    noise_wave.writeframes(array.array('h', mixed_amp.astype(np.int16)).toString())
    noise_wave.close()          # writeframes : 진폭값을 지정. String에 μΊμŠ€νŒ…

    clean_wave = wave.Wave_write(args.output_clean_file)
    clean_wave.setparams(clean_wav.getparams())
    clean_wave.writeframes(array.array('h', clean_amp.astype(np.int16)).toString())
    clean_wave.close()

    noise_wave = wave.Wave_write(args.output_noise_file)
    noise_wave.setparams(clean_wav.getparams())
    noise_wave.writeframes(array.array('h', adjusted_noise_rms.astype(np.int16)).toString())
    noise_wave.close()

 

 

 

+) Signal-to-Noise ratio 계산식을 μ΄μš©ν•΄ μž„μ˜ 크기둜 νŒŒν˜• ν•©μ„±

- μŒμ„±μ— λŒ€ν•΄ μž„μ˜μ˜ SN λΉ„κ°€ λ‚˜μ˜€λ„λ‘ 작음의 RMS κ΅¬ν•˜κΈ°

- 작음의 RMS

: RMS(Anoise) 와 원본 작음의 RMS λΉ„μœ¨μ„ κ³„μ‚°ν•˜μ—¬, κ·Έ λΉ„μœ¨λ§ŒνΌ 원본 작음의 진폭값 μ‘°μ •

: μ‘°μ •ν•œ 작음의 진폭과 μŒμ„± λ‹¨λ…μ˜ 진폭 더함

 

 

 

 

μ°Έκ³ ν•˜λ©΄ 더 쒋은 정보와 방법을 μ•Œ 수 μžˆμ„ 것!

create_mixed_audio_file.py 은 16 bit 용! path λ”ν•΄μ„œ μ‹€ν–‰ν–ˆλŠ”λŽ…,, μ™œ μ•ˆλ κΉŒ μ™• κΆκΈˆν•˜λ‹€

https://github.com/Sato-Kunihiko/audio-SNR

 

GitHub - Sato-Kunihiko/audio-SNR: Mixing an audio file with a noise file at any Signal-to-Noise Ratio (SNR)

Mixing an audio file with a noise file at any Signal-to-Noise Ratio (SNR) - GitHub - Sato-Kunihiko/audio-SNR: Mixing an audio file with a noise file at any Signal-to-Noise Ratio (SNR)

github.com

 

 


전체 μ½”λ“œ

import argparse     # ν”„λ‘œκ·Έλž¨μ„ μ‹€ν–‰μ‹œμ— μ»€λ§¨λ“œ 라인에 인수λ₯Ό λ°›μ•„ 처리λ₯Ό κ°„λ‹¨νžˆ
import array
import math
import numpy as np
import random
import wave


def get_args() :
    # μΈμžκ°’μ„ 받을 수 μžˆλŠ” μΈμŠ€ν„΄μŠ€ 생성
    parser = argparse.ArgumentParser()
    # parser.add_argument둜 받아듀일 인수λ₯Ό μΆ”κ°€
    parser.add_argument('--clean_file', type = str, required = True)    # μŒμ„±λ§Œ μžˆλŠ” 파일 μ ˆλŒ€ 경둜
    parser.add_argument('--noise_file', type = str, required = True)    # 작음만 μžˆλŠ” 파일 μ ˆλŒ€ 경둜
    parser.add_argument('--output_mixed_file', type = str, default = '', required = True)   # 처리 μ™„λ£Œλœ μŒμ„±λ§Œ μžˆλŠ” 파일 μ ˆλŒ€ 경둜
    parser.add_argument('--output_clean_file', type = str, default = '')    # 처리 μ™„λ£Œλœ 작음만 μžˆλŠ” 파일 μ ˆλŒ€ 경둜
    parser.add_argument('--output_noise_file', type = str, default = '')    # μž„μ˜ SNλΉ„μ˜ μŒμ„± 파일 μ ˆλŒ€ 경둜
    parser.add_argument('--snr', type = float, default = '', required = True)   # ν•©μ„±ν•˜λ €λŠ” SN λΉ„μ˜ 크기
    # 인수λ₯Ό 뢄석, μ €μž₯
    args = parser.parse_args()
    return args

# 각각의 진폭을 λ”ν•œ 진폭값이 wav 파일의 μ–‘μžν™” bit수, 16bit(32767)
def cal_adjusted_rms(clean_rms, snr) :
    a = float(snr) / 20
    noise_rms = clean_rms / (10**a)
    return noise_rms


# μŒμ„± νŒŒν˜•μ˜ 진폭값 μ·¨λ“ν•˜κΈ°
def cal_amp(wf) :
    # wf.readframes(n) : μ΅œλŒ€ n개의 μ˜€λ””μ˜€ ν”„λ ˆμž„ μ½μ–΄μ„œ bytes 객체 λ°˜ν™˜
    # wf.getnframes() : μ˜€λ””μ˜€ ν”„λ ˆμž„ 수 λ°˜ν™˜
    # => wav 파일의 λͺ¨λ“  진폭값을 취득
    buffer = wf.readframes(wf.getnframes())     
    # The dtype depends on the value of pulse-code modulation
    # The int16 is set for 16-bit PCM
    amptitude = (np.frombuffer(buffer, dtype = "int16")).astype(np.float64)
    return amptitude


# μ§„ν­κ°’μ˜ 평균 제곱근(Root Mean Square, RMS) κ΅¬ν•˜κΈ°
# 작음 데이터 νŒŒν˜•μ„ μŒμ„± 데이터 νŒŒν˜• 길이둜 자λ₯΄κΈ°
# 작음 νŒŒμΌμ—μ„œ μž˜λΌλ‚Έ νŒŒν˜•, μŒμ„± νŒŒν˜•μ˜ RMSλ₯Ό 각각 계산 -> μž„μ˜ SNλΉ„κ°€ λ‚˜μ˜€λ„λ‘ 쀑첩
def cal_rms(amp) :
    # np.square : μ–΄λ ˆμ΄μ˜ μš”μ†Œ λ‹¨μœ„λ‘œ μ œκ³±μ„ λ°˜ν™˜
    # SN λΉ„ : 진폭값 제곱 -> 평균 -> 제곱근
    return np.sqrt(np.mean(np.square(amp), axis = -1))


def save_waveform(output_path, params, amp) :
    output_file = wave.Wave_write(output_path)
    output_file.setparams(params) #nchannels, sampwidth, framerate, nframes, comptype, compname
    output_file.writeframes(array.array('h', amp.astype(np.int16)).tobytes())
    output_file.close()


if __name__ == '__main__' :
    args = get_args()

    clean_file = args.clean_file
    noise_file = args.noise_file

    clean_wav = wave.open(clean_file, 'r')
    noise_wav = wave.open(noise_file, 'r')

    clean_amp = cal_amp(clean_wav)
    noise_amp = cal_amp(noise_wav)

    clean_rms = cal_rms(clean_amp)

    # μž‘μŒμ„ 자λ₯Ό μœ„μΉ˜λ₯Ό 랜덀으둜 μ •ν•΄μ„œ μŒμ„±μ˜ 길이만큼 μž˜λΌλƒ„
    start = random.randint(0, len(noise_amp) - len(clean_amp))
    divided_noise_amp = noise_amp[start: start + len(clean_amp)]
    noise_rms = cal_rms(divided_noise_amp)

    snr = args.snr
    adjusted_noise_rms = cal_adjusted_rms(clean_rms, snr)

    adjusted_noise_rms = divided_noise_amp * (adjusted_noise_rms / noise_rms)
    mixed_amp = (clean_amp + adjusted_noise_rms)


    # Avoid clipping noise
    max_int16 = np.iinfo(np.int16).max
    min_int16 = np.iinfo(np.int16).min


    # μ„œλ‘œ λ”ν•œ 값이 16bit의 μ΅œλŒ€κ°’μ„ λ„˜μœΌλ©΄, μ΅œλŒ€ 32767 μ•ˆμ— λ“€μ–΄μ˜€λ„λ‘ μ •κ·œν™”
    if mixed_amp.max(axis = 0) > max_int16 or mixed_amp.min(axis = 0) < min_int16 :
        if mixed_amp.max(axis = 0) >= abs(mixed_amp.min(axis = 0)) :
            reduction_rate = max_int16 / mixed_amp.max(axis = 0)
        else :
            reduction_rate = min_int16 / mixed_amp.min(axis = 0)
        
        mixed_amp = mixed_amp * (reduction_rate)
        clean_amp = clean_amp * (reduction_rate)


    # save_waveform(args.output_mixed_file, clean_wav.getparams(), mixed_amp)

    # νŒŒν˜•μ„ wav 파일둜 μ €μž₯
    noise_wave = wave.Wave_write(args.output_noise_file)
    noise_wave.setparams(clean_wav.getparams())     # setparams : wav파일의 포맷을 μ§€μ •ν•˜λŠ” λ©”μ„œλ“œ
    noise_wave.writeframes(array.array('h', mixed_amp.astype(np.int16)).toString())
    noise_wave.close()          # writeframes : 진폭값을 지정. String에 μΊμŠ€νŒ…

    clean_wave = wave.Wave_write(args.output_clean_file)
    clean_wave.setparams(clean_wav.getparams())
    clean_wave.writeframes(array.array('h', clean_amp.astype(np.int16)).toString())
    clean_wave.close()

    noise_wave = wave.Wave_write(args.output_noise_file)
    noise_wave.setparams(clean_wav.getparams())
    noise_wave.writeframes(array.array('h', adjusted_noise_rms.astype(np.int16)).toString())
    noise_wave.close()

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

728x90
λ°˜μ‘ν˜•
Comments