연속 웨이블릿 변환 개요

연속 웨이블릿 변환(Continuous Wavelet Transform, CWT)은 입력 신호를 다양한 주파수와 시간 구간으로 분해하여 분석하는 방법이다. 이 변환은 다음과 같은 일반 수식을 통해 정의된다:

W_{\psi}(a, b) = \int_{-\infty}^{\infty} f(t) \cdot \psi^{*}\left( \frac{t - b}{a} \right) \, dt

여기서, - f(t)는 시간 도메인에서의 원본 신호이다. - \psi(t)는 모 웨이블릿(mother wavelet) 함수이다. - a는 스케일(scale) 매개변수로, 신호의 주파수 정보에 영향을 미친다. - b는 시간 이동(translation) 매개변수이다. - \psi^{*}는 모 웨이블릿의 복소 켤레이다.

C++에서의 기본 구현 구조

C++에서 CWT를 구현하려면 위의 수식을 기반으로 연속적인 스케일링과 이동 작업을 효율적으로 수행할 수 있도록 설계해야 한다. 이때 중요한 점은 연속 웨이블릿 변환이 연속적인 스케일 값과 이동 값에 대해 무한히 많은 웨이블릿 계수를 계산하게 되어 메모리와 연산량이 상당히 커진다는 점이다.

주요 클래스와 함수 설계

  1. Wavelet 클래스: 모 웨이블릿 함수를 정의하고, 필요한 연산을 지원하는 클래스이다.
  2. CWT 클래스: 주어진 신호에 대해 특정 스케일과 이동 값을 기반으로 변환을 수행하는 클래스로, 내부적으로 Wavelet 클래스와 신호 데이터 처리를 담당한다.

Wavelet 클래스의 기본 정의

모 웨이블릿 함수로 주로 사용되는 모르레(Morlet) 웨이블릿을 예제로 들 수 있다. 모르레 웨이블릿은 복소수 기반의 함수로, 다음과 같은 식으로 정의된다:

\psi(t) = \pi^{-\frac{1}{4}} e^{i \omega_0 t} e^{-\frac{t^2}{2}}

여기서, \omega_0는 중심 주파수(center frequency)이다. 이 식을 기반으로 Wavelet 클래스의 코드를 작성할 수 있다.

class Wavelet {
public:
    Wavelet(double omega0) : omega0(omega0) {}

    std::complex<double> morlet(double t) const {
        return std::pow(M_PI, -0.25) * std::exp(std::complex<double>(0, omega0 * t)) * std::exp(-0.5 * t * t);
    }

private:
    double omega0; // 중심 주파수
};

CWT 클래스에서의 변환 함수 구현

CWT 클래스는 Wavelet 클래스를 이용해 특정 스케일과 이동에 대해 웨이블릿 변환을 수행한다. 변환의 주요 수식은 스케일 a와 시간 이동 b에 대한 반복 처리를 포함해야 하며, 이때 입력 신호 f(t)와 웨이블릿 \psi(t) 간의 컨볼루션을 수행한다.

다음은 CWT 클래스의 기본 구조이다.

class CWT {
public:
    CWT(const std::vector<double>& signal, double omega0) 
        : signal(signal), wavelet(omega0) {}

    std::vector<std::complex<double>> transform(double scale, double translation) {
        std::vector<std::complex<double>> coefficients;
        for (size_t i = 0; i < signal.size(); ++i) {
            double t = static_cast<double>(i);
            std::complex<double> result = 0.0;
            for (size_t j = 0; j < signal.size(); ++j) {
                double tj = static_cast<double>(j);
                double scaledT = (tj - translation) / scale;
                result += signal[j] * wavelet.morlet(scaledT);
            }
            coefficients.push_back(result);
        }
        return coefficients;
    }

private:
    std::vector<double> signal;
    Wavelet wavelet;
};

위 코드에서 transform 함수는 주어진 스케일과 이동 값에 대해 변환을 수행하며, 결과는 웨이블릿 계수의 벡터로 반환된다.

주요 함수 설명

스케일과 이동 매개변수의 활용

연속 웨이블릿 변환에서 주파수 정보를 다양한 해상도로 분석하기 위해 스케일이동 매개변수를 적절히 설정하는 것이 중요하다. CWT의 구현에서는 여러 스케일과 이동 값에 대한 반복 연산을 수행하므로, 스케일 a와 이동 b의 범위와 간격을 설정하는 방식에 따라 결과의 해상도와 연산 비용이 크게 달라질 수 있다.

이를 효율적으로 처리하기 위해서: - 스케일 값 a는 로그 스케일에서 일정한 간격으로 설정하는 것이 일반적이다. 작은 스케일에서는 고주파 성분이, 큰 스케일에서는 저주파 성분이 더 잘 나타나기 때문이다. - 이동 값 b는 시간 축에 따라 일정한 간격으로 설정하여 변환이 전체 신호에 걸쳐 균등하게 수행되도록 한다.

스케일과 이동을 위한 코드 추가

스케일과 이동을 자동으로 설정하기 위해 CWT 클래스에 스케일과 이동 매개변수를 정의하고, 이를 반복하여 CWT를 수행하는 함수를 추가할 수 있다.

class CWT {
public:
    CWT(const std::vector<double>& signal, double omega0) 
        : signal(signal), wavelet(omega0) {}

    std::vector<std::vector<std::complex<double>>> fullTransform(double minScale, double maxScale, double scaleStep, double translationStep) {
        std::vector<std::vector<std::complex<double>>> allCoefficients;

        for (double scale = minScale; scale <= maxScale; scale += scaleStep) {
            std::vector<std::complex<double>> scaleCoefficients;
            for (double translation = 0; translation < signal.size(); translation += translationStep) {
                scaleCoefficients.push_back(transformSingleScale(scale, translation));
            }
            allCoefficients.push_back(scaleCoefficients);
        }
        return allCoefficients;
    }

private:
    std::vector<double> signal;
    Wavelet wavelet;

    std::complex<double> transformSingleScale(double scale, double translation) {
        std::complex<double> result = 0.0;
        for (size_t j = 0; j < signal.size(); ++j) {
            double tj = static_cast<double>(j);
            double scaledT = (tj - translation) / scale;
            result += signal[j] * wavelet.morlet(scaledT);
        }
        return result;
    }
};

위 코드에서는 다음과 같은 함수를 정의하였다: - fullTransform(double minScale, double maxScale, double scaleStep, double translationStep): 사용자에게 스케일과 이동 범위 및 간격을 설정할 수 있도록 하여, 연속 웨이블릿 변환을 모든 스케일과 이동 값에 대해 자동으로 수행한다. - transformSingleScale(double scale, double translation): 주어진 스케일과 이동 값에 대해 웨이블릿 변환을 수행하는 보조 함수로, fullTransform 내에서 반복 호출된다.

결과 시각화를 위한 데이터 구조

CWT의 결과는 스케일과 시간 축에 따른 웨이블릿 계수로 구성되며, 이를 행렬 형식으로 표현할 수 있다. 각 행은 특정 스케일에 대한 계수의 집합이며, 열은 시간 축을 따라 이동된 변환 결과이다. 이 데이터는 이미지로 시각화하거나 주파수 스펙트럼 형태로 변환하여 분석할 수 있다.

결과 시각화를 위한 다이어그램 예시

다이어그램을 사용하여 결과 데이터의 흐름과 구조를 시각적으로 나타낼 수 있다. 다음은 웨이블릿 변환 과정의 흐름을 보여주는 다이어그램이다.

flowchart TD A[입력 신호] --> B[스케일 반복] B --> C[각 스케일에 대해 이동 반복] C --> D[Wavelet 계수 계산] D --> E[계수 저장 및 출력] E --> F[웨이블릿 계수 행렬]

CWT 결과를 이용한 시간-주파수 분석

CWT의 결과는 시간과 주파수 정보를 동시에 포함하므로, 이를 통해 신호의 특정 시간 구간에서의 주파수 성분을 쉽게 파악할 수 있다. 특히 비정상 신호(non-stationary signals)에서 CWT는 시간과 주파수 해상도를 모두 제공하므로, 신호의 변화 패턴이나 특정 주파수 대역의 강도 변화를 탐지하는 데 유용하다.

시간-주파수 분석을 위한 추가 함수

신호의 특정 시간 구간에서 특정 주파수 대역의 변화를 분석하기 위해, CWT 클래스에 특정 스케일 대역의 평균값이나 최대값을 계산하는 함수를 추가할 수 있다.

std::vector<double> analyzeFrequencyBand(double minScale, double maxScale) {
    std::vector<double> frequencyAnalysis(signal.size(), 0.0);
    for (const auto& scaleCoefficients : fullTransform(minScale, maxScale, 1.0, 1.0)) {
        for (size_t i = 0; i < scaleCoefficients.size(); ++i) {
            frequencyAnalysis[i] += std::abs(scaleCoefficients[i]);
        }
    }
    return frequencyAnalysis;
}

이 함수는 특정 스케일 범위 내에서의 주파수 성분 변화를 분석하여, 각 시간 구간의 주파수 대역 강도를 계산한다. 이를 통해 특정 시간 구간에서의 에너지 변화나 특정 주파수 대역의 활성도를 파악할 수 있다.