웨이블릿 변환의 개요

웨이블릿 변환은 주어진 신호를 고주파와 저주파 성분으로 나누는 기법이다. 이를 통해 원신호의 다양한 주파수 대역을 효과적으로 분석할 수 있다. 1차원 신호의 경우, 시간-주파수 분석을 통해 특정 주파수 대역의 정보 추출이 가능한다. 웨이블릿 변환은 다중 해상도 분석(Multi-Resolution Analysis, MRA) 에 기반하여 신호를 점진적으로 세분화한다. 이 과정에서 각 수준의 세부 정보를 획득하기 위해 웨이블릿과 스케일 함수가 사용된다.

1차원 신호 \mathbf{f}(t)를 웨이블릿 변환을 통해 분석하기 위해서는 다음과 같은 기본적인 과정이 필요하다.

이산 웨이블릿 변환(DWT)의 수학적 표현

이산 웨이블릿 변환(DWT)은 신호를 다양한 수준의 해상도로 분해하여 시간과 주파수 정보를 동시에 제공한다. 신호 \mathbf{f}(t)에 대해, DWT는 다음과 같은 수식으로 정의된다.

\mathbf{W}_{j,k} = \int_{-\infty}^{\infty} \mathbf{f}(t) \cdot \psi_{j,k}(t) \, dt

여기서 \mathbf{W}_{j,k}는 특정 수준 j와 위치 k에서의 웨이블릿 계수를 나타내며, \psi_{j,k}(t)모함수 \psi(t)를 수준 j와 위치 k에 따라 변환한 웨이블릿 함수이다. 이때 웨이블릿 함수는 다음과 같이 표현할 수 있다.

\psi_{j,k}(t) = 2^{-\frac{j}{2}} \psi\left( 2^{-j} t - k \right)

필터뱅크(FIR 필터) 설계

DWT를 구현하기 위해서는 고주파 및 저주파 필터를 사용하는 필터뱅크(Filter Bank) 구조가 필요하다. 필터뱅크를 이용해 신호를 저주파와 고주파 성분으로 분해한다. 이러한 필터는 일반적으로 고정된 계수를 가지며, 해머(Haar), 다우베쉬(Daubechies) 등의 웨이블릿을 구현할 때 주로 사용된다.

이산 웨이블릿 변환의 기본적인 단계는 다음과 같다. 1. 신호를 저주파 필터 \mathbf{h}[n]와 고주파 필터 \mathbf{g}[n]를 통해 분해한다. 2. 저주파 성분과 고주파 성분을 각각 다운샘플링하여 다음 수준으로 전송한다.

필터 적용 수식

입력 신호 \mathbf{f}[n]에 대해 저주파 성분을 추출하는 저주파 필터 \mathbf{h}[n]의 적용은 다음과 같이 계산된다.

\mathbf{f}_{low}[n] = \sum_{k} \mathbf{f}[k] \cdot \mathbf{h}[2n - k]

유사하게, 고주파 필터 \mathbf{g}[n]을 통한 고주파 성분 추출은 다음과 같이 표현된다.

\mathbf{f}_{high}[n] = \sum_{k} \mathbf{f}[k] \cdot \mathbf{g}[2n - k]

이러한 필터 적용을 통해 입력 신호의 저주파 성분과 고주파 성분을 계산할 수 있으며, 이후 단계에서는 이를 이용해 신호를 세부적으로 분석할 수 있다.

C++ 코드 구현 예제

다음은 C++ 코드로 간단한 1차원 DWT를 구현한 예제이다. 이 코드에서는 Haar 웨이블릿 필터를 사용하여 신호를 저주파와 고주파 성분으로 분해한다. 코드에서는 저주파 필터 h[]와 고주파 필터 g[]를 각각 정의하고, 입력 신호 f[]를 적용하여 각 성분을 추출한다.

#include <iostream>
#include <vector>
#include <cmath>

// Haar 웨이블릿 필터 계수
const double h[] = { 0.70710678118, 0.70710678118 };
const double g[] = { 0.70710678118, -0.70710678118 };

// 신호 분해 함수
void decompose(const std::vector<double>& signal, std::vector<double>& low, std::vector<double>& high) {
    int n = signal.size() / 2;
    low.resize(n);
    high.resize(n);

    for (int i = 0; i < n; i++) {
        low[i] = signal[2 * i] * h[0] + signal[2 * i + 1] * h[1];
        high[i] = signal[2 * i] * g[0] + signal[2 * i + 1] * g[1];
    }
}

int main() {
    std::vector<double> signal = {1.0, 2.0, 3.0, 4.0};
    std::vector<double> low, high;

    decompose(signal, low, high);

    std::cout << "Low Frequency Component: ";
    for (double val : low) {
        std::cout << val << " ";
    }
    std::cout << std::endl;

    std::cout << "High Frequency Component: ";
    for (double val : high) {
        std::cout << val << " ";
    }
    std::cout << std::endl;

    return 0;
}

위 코드에서 함수 decompose는 입력 신호 signal을 받아 각각의 저주파 성분과 고주파 성분을 계산하여 반환한다. 각 필터 계수는 Haar 웨이블릿의 계수를 사용하여 신호를 분해한다.

다중 레벨 분석의 효과와 응용

다중 해상도 분석을 통한 DWT의 주요 응용 분야는 신호의 특징 추출노이즈 제거이다. 각 수준에서 고주파 성분은 일반적으로 원신호의 급격한 변화, 즉 에지나 잡음과 같은 정보를 포함하고 있다. 반면, 저주파 성분은 신호의 전체적인 형태와 주파수 대역의 큰 변화를 나타내는 정보를 포함한다.

노이즈 제거의 수학적 기법

노이즈 제거를 위해서는 각 수준의 고주파 성분을 적절히 조정하거나 일부를 제거하는 방법이 사용된다. 예를 들어, \mathbf{f}_{high}^{(j)}에서 특정 임계값 T를 설정하여, 해당 수준의 고주파 성분이 T 이하일 경우 이를 0으로 설정하는 하드 스레숄딩(Hard Thresholding) 기법이 있다.

\mathbf{f}_{high}^{(j)} = \begin{cases} 0, & \text{if } |\mathbf{f}_{high}^{(j)}| \leq T \\ \mathbf{f}_{high}^{(j)}, & \text{if } |\mathbf{f}_{high}^{(j)}| > T \end{cases}

이 기법을 통해 고주파 성분에 포함된 노이즈 성분을 제거할 수 있으며, 재구성된 신호는 노이즈가 감소된 상태로 유지된다.

신호 복원(Reconstruction)

DWT의 특징 중 하나는 원 신호를 복원하는 역변환(Inverse DWT)이 가능하다는 점이다. 다중 수준으로 분해된 신호를 다시 합성하여 원래 신호를 복원할 수 있다. 이때, 저주파 성분과 고주파 성분을 결합하여 신호를 복원하며, 역변환은 다음과 같은 수식으로 정의된다.

\mathbf{f}[n] = \sum_{k} \left( \mathbf{f}_{low}[k] \cdot \mathbf{h}[n - 2k] + \mathbf{f}_{high}[k] \cdot \mathbf{g}[n - 2k] \right)

위 수식을 사용하여 각 수준에서 필터링된 신호를 합산하면 원래의 신호 \mathbf{f}[n]를 복원할 수 있다.

C++ 코드로 DWT 역변환 구현

아래의 C++ 코드에서는 DWT를 통해 분해된 신호를 역변환하여 원래 신호를 복원하는 예제를 보여준다. 코드에서는 각 수준에서 저주파 및 고주파 성분을 결합하여 역변환을 수행한다.

#include <iostream>
#include <vector>
#include <cmath>

const double h[] = { 0.70710678118, 0.70710678118 };
const double g[] = { 0.70710678118, -0.70710678118 };

void reconstructLevel(const std::vector<double>& low, const std::vector<double>& high, std::vector<double>& signal) {
    int n = low.size();
    signal.resize(2 * n);

    for (int i = 0; i < n; i++) {
        signal[2 * i] = low[i] * h[0] + high[i] * g[0];
        signal[2 * i + 1] = low[i] * h[1] + high[i] * g[1];
    }
}

int main() {
    // 분해된 신호의 저주파와 고주파 성분
    std::vector<double> low = {1.5, 3.5};
    std::vector<double> high = {0.5, -0.5};
    std::vector<double> signal;

    reconstructLevel(low, high, signal);

    std::cout << "Reconstructed Signal: ";
    for (double val : signal) {
        std::cout << val << " ";
    }
    std::cout << std::endl;

    return 0;
}

이 코드에서 reconstructLevel 함수는 분해된 신호의 저주파 및 고주파 성분을 결합하여 신호를 복원한다. main 함수에서 저주파 및 고주파 성분이 제공되며, 이를 사용하여 원래 신호를 재구성한다.

재구성 신호의 검증

DWT를 통해 신호를 분해한 후, 역변환을 통해 원래의 신호와 거의 동일한 신호를 얻는지 검증할 수 있다. 이때, 원 신호와 재구성된 신호 사이의 차이는 주로 수치적 오차에 기인하며, 이 오차가 작을수록 필터와 변환의 정확도가 높음을 나타낸다.