Haar 웨이블릿 변환은 가장 단순하고 기본적인 형태의 이산 웨이블릿 변환(DWT)으로, 데이터의 압축과 신호 분석에 자주 사용된다. 이 변환은 간단한 수학적 연산을 통해 신호를 저주파 성분(평균)과 고주파 성분(차이)으로 나누는 방식으로 동작한다. 여기에서는 1차원 신호를 대상으로 Haar 웨이블릿을 적용하는 과정을 단계별로 설명한다.
Haar 웨이블릿의 정의
Haar 웨이블릿 함수는 단순하지만 강력한 특징을 가지고 있으며, 다음과 같은 두 가지 필터로 표현된다.
- 스케일링 함수 \phi(t):
- 웨이블릿 함수 \psi(t):
이러한 정의를 기반으로, Haar 웨이블릿 변환은 입력 신호를 간단한 연산을 통해 평균과 차이를 계산하는 방식으로 동작한다.
변환 과정
Haar 웨이블릿 변환은 신호의 길이가 2^n인 경우에 효과적이며, 변환 과정은 다음의 단계로 이루어진다.
1. 입력 신호 예시
예를 들어, 다음과 같은 8개의 샘플을 가진 신호 \mathbf{x}가 있다고 가정하자:
2. 1단계 변환
1단계 변환에서는 인접한 두 샘플의 평균과 차이를 계산한다. 이때, 평균은 저주파 성분(스케일링 계수), 차이는 고주파 성분(디테일 계수)을 나타낸다.
평균과 차이를 구하는 식은 다음과 같다:
적용하면:
여기서 \mathbf{c}^{(1)}는 1단계의 스케일링 계수 벡터이며, \mathbf{d}^{(1)}는 1단계의 디테일 계수 벡터이다.
3. 2단계 변환
1단계에서 구한 스케일링 계수 \mathbf{c}^{(1)}에 대해 다시 동일한 변환을 적용한다. 즉, \mathbf{c}^{(1)}의 평균과 차이를 계산한다:
4. 3단계 변환
마지막으로 \mathbf{c}^{(2)}에 대해 같은 연산을 적용한다:
따라서 최종적으로 Haar 웨이블릿 변환의 결과는 아래와 같다:
Haar 웨이블릿 변환의 역변환
Haar 웨이블릿 변환은 역변환을 통해 원래 신호를 재구성할 수 있다. 역변환은 변환 과정의 반대 방향으로 진행되며, 각 단계에서 평균과 차이를 사용하여 원래의 샘플을 복원한다.
1. 역변환의 개념
역변환은 다음의 공식을 기반으로 진행된다. 주어진 평균 a와 차이 d가 있을 때, 원래의 두 샘플 x_1과 x_2는 다음과 같이 복원된다:
이 공식을 단계별로 적용하여 원래의 신호를 복원한다.
2. 단계별 역변환 과정
(a) 3단계 복원
3단계 변환의 결과는 \mathbf{c}^{(3)} = [12.5]와 \mathbf{d}^{(3)} = [-4.5]이다. 이를 이용해 2단계의 스케일링 계수를 복원한다:
따라서, \mathbf{c}^{(2)} = [8, 17].
(b) 2단계 복원
이제 \mathbf{c}^{(2)} = [8, 17]과 \mathbf{d}^{(2)} = [-3, -2]를 사용하여 1단계의 스케일링 계수를 복원한다:
따라서, \mathbf{c}^{(1)} = [5, 11, 15, 19].
(c) 1단계 복원
마지막으로, \mathbf{c}^{(1)} = [5, 11, 15, 19]과 \mathbf{d}^{(1)} = [-1, -1, -1, -1]을 이용하여 원래의 샘플을 복원한다:
따라서 원래의 신호는 \mathbf{x} = [4, 6, 10, 12, 14, 16, 18, 20]로 정확하게 복원된다.
C++ 코드 예제
아래의 C++ 코드는 위의 Haar 웨이블릿 변환 및 역변환 과정을 구현한 예제이다. 코드에서는 1차원 배열을 입력으로 받아서 변환과 역변환을 수행한다.
#include <iostream>
#include <vector>
void haarTransform(std::vector<double>& data) {
int n = data.size();
std::vector<double> temp(n);
while (n > 1) {
n /= 2;
for (int i = 0; i < n; i++) {
temp[i] = (data[2 * i] + data[2 * i + 1]) / 2;
temp[n + i] = (data[2 * i] - data[2 * i + 1]) / 2;
}
for (int i = 0; i < 2 * n; i++) {
data[i] = temp[i];
}
}
}
void inverseHaarTransform(std::vector<double>& data) {
int n = 1;
std::vector<double> temp(data.size());
while (n < data.size()) {
for (int i = 0; i < n; i++) {
temp[2 * i] = data[i] + data[n + i];
temp[2 * i + 1] = data[i] - data[n + i];
}
for (int i = 0; i < 2 * n; i++) {
data[i] = temp[i];
}
n *= 2;
}
}
int main() {
std::vector<double> data = {4, 6, 10, 12, 14, 16, 18, 20};
haarTransform(data);
std::cout << "Haar Transform: ";
for (double d : data) std::cout << d << " ";
std::cout << std::endl;
inverseHaarTransform(data);
std::cout << "Inverse Haar Transform: ";
for (double d : data) std::cout << d << " ";
std::cout << std::endl;
return 0;
}
이 예제 코드에서 haarTransform
함수는 입력 데이터를 변환하여 Haar 계수를 계산하고, inverseHaarTransform
함수는 이를 기반으로 역변환을 수행한다. 코드 실행 결과는 Haar 변환된 계수와 원래 신호의 복원 결과를 출력한다.