SignatureMedian 클래스의 All Median · Middle Median 모드, 4 가지 경계 처리 (Symmetric · Replicate · ZeroPad · Adaptive), Median 계산 알고리즘, 병렬화 구조를 코드 · 다이어그램 · 인터랙티브 데모로 심층 분석합니다.
1D 수치 시퀀스에 Running Median 스무딩을 적용합니다. 반경 R (→ 윈도우 W = 2R + 1) 을 지정하면 각 데이터 포인트에 대해 주변 W 개 값의 중앙값을 계산합니다.
' ComputeMediansByRadius - R → W 변환 후 ComputeMedians 호출 Dim w64 As Long = CLng(kernelRadius) * 2L + 1L ' 오버플로 방지 If w64 > Integer.MaxValue Then Throw ... Dim w As Integer = CInt(w64) ' 항상 홀수 ≥ 1 Return ComputeMedians(input, useMiddle, kernelWidth:=w, ...)
ComputeMedians() 내부에서 useMiddle 플래그에 따라 처리 경로가 완전히 분기됩니다.
| 항목 | All Median | Middle Median |
|---|---|---|
| 처리 범위 | startIdx = 0, endIdx = n − 1 | startIdx = borderCount, endIdx = n − borderCount − 1 |
| 경계 처리 | BoundaryMode 4 가지 선택 | 대칭 축소 (BoundaryMode 무시) |
| 윈도우 크기 | 항상 W (Adaptive 제외) | 가변 (경계에서 대칭 축소) |
| 경계 데이터 | 합성 / 반사 / 복제 / 0 | 원본 값 그대로 보존 |
| use case | 전체 시퀀스 스무딩 | 내부 영역만 스무딩, 경계 원본 유지 |
Public Enum BoundaryMode Symmetric ' Whole-sample 반사 : … 3, 2 | 1, 2, 3, 4, 5 | 4, 3 … Replicate ' 가장자리 복제 : … 1, 1, 1 | 1, 2, 3, 4, 5 | 5, 5, 5 … ZeroPad ' 0 패딩 : … 0, 0, 0 | 1, 2, 3, 4, 5 | 0, 0, 0 … Adaptive ' 대칭 축소 : reach = min(R, i, n−1−i), W = 2·reach+1 End Enum
중앙값(Median)의 정식 수학 정의, 핵심 통계 성질, 그리고 1D 스무딩 필터로서의 특장점을 체계적으로 정리합니다.
유한 크기 집합 S = {x₀, x₁, …, x_{W − 1}} 에서 중앙값은 순서 통계량 (Order Statistic) 으로 정의됩니다.
중앙값은 집합의 모든 원소까지의 절댓값 거리 합(L₁ 노름) 을 최소화하는 값입니다. 이에 반해 평균 (Mean) 은 L₂ (제곱 거리 합) 최소화 값입니다.
인덱스 i 를 0 부터 n − 1 까지 슬라이드하면서 매 위치마다 W 개 원소의 중앙값을 구합니다. 경계(i < R 또는 i > n − 1 − R)에서는 BoundaryMode 에 따라 창 바깥 값을 처리합니다.
Breakdown Point 는 추정량이 무너지기 전까지 견딜 수 있는 이상치 비율의 상한입니다.
창 내 절반 미만의 값 중 임의의 값에 이상치가 발생하더라도 중앙값은 변하지 않습니다. 평균 (Mean) 의 Breakdown Point 는 0% (단 하나의 극단값으로도 무너짐)입니다.
중앙값은 순위 (Rank) 정보만 사용합니다. 구체적 값의 크기 (Magnitude) 가 아닌 대소 관계만이 결과에 영향을 줍니다.
W 가 홀수일 때 중앙값은 반드시 창 안의 실제 데이터 값 중 하나입니다.
이는 스무딩 결과가 "존재했던 값" 이므로 물리적으로 의미없는 값 (예 : 음의 카운트, 범위 외 밝기) 이 출력되지 않습니다. W 짝수 시 두 중앙값의 평균이 되므로 범위 내에 있습니다.
이미 단조 증가·감소하는 시퀀스에 Running Median 을 적용하면 출력이 입력과 동일합니다.
선형 필터 (평균 계열) 는 단조 신호도 경계에서 왜곡할 수 있지만, Median 은 이를 완전히 보존합니다.
같은 윈도우 W 로 Running Median 을 두 번 적용해도 결과가 변하지 않습니다.
평균 기반 필터를 반복 적용하면 신호가 계속 뭉개지지만, Running Median 은 수렴 후 추가 변화가 없습니다.
Running Median 을 반복 적용했을 때 더 이상 변하지 않는 신호를 Root Signal 이라 합니다.
Root Signal 은 창 안에서 단조 구간으로만 구성되며, 수렴 보장은 Running Median 이 특정 형태의 신호 구조를 향해 안정적으로 수렴함을 증명합니다.
Running Median 이 경계 (급격한 계단 변화) 를 보존하는 이유를 창 내 값 분포로 설명합니다.
창 내 Low 값과 High 값이 균등할 때, 중앙값은 에지 E 자체 또는 Low · High 의 경계값으로 수렴합니다. Moving Average 였다면 (L + L + E + H + H) / 5 로 흐릿해집니다.
단 하나의 스파이크 (S) 가 있어도 창의 대다수 값이 L이므로 중앙값 = L. 스파이크는 출력에 전혀 반영되지 않습니다.
핵심 통찰 - 중앙값은 창 안 값들의 크기 (Magnitude) 가 아닌 순위 (Rank) 로 결정됩니다. 이상치가 아무리 크더라도 순위만 최상단으로 이동할 뿐, W / 2 를 초과하지 않으면 중앙 순위에 닿지 못합니다.
| 특장점 | 수학적 근거 | 실무 효과 |
|---|---|---|
| 이상치 강인성 | Breakdown Point = 50%. L₁ 최소화는 극단값 가중치를 제한 | 센서 스파이크, 결측값 대체 오류 등이 출력에 전파되지 않음 |
| 에지·스텝 보존 | 창이 경계를 넘을 때 과반을 차지하는 다수결 (Majority) 원리로 중앙값이 한쪽으로 수렴 | 신호의 실제 변화 지점 위치 · 크기가 스무딩 후에도 유지됨 |
| 비모수 · 분포 독립 | 순위 기반 통계량. 어떤 확률 분포도 가정하지 않음 | 로그 스케일, 헤비테일, 이분 분포 데이터에 모두 동일하게 적용 가능 |
| 출력 범위 보장 | median(S) ∈ [min(S), max(S)] - 항상 입력 범위 내 값 | 물리량(온도, 밝기, 카운트) 스무딩 시 범위 외 값이 절대 출력되지 않음 |
| 멱등 수렴성 | M(M(x)) = M(x). Root Signal 로의 안정 수렴 보장 | 반복 적용 시 신호가 점점 소멸하지 않고 일정 형태를 유지 |
| 단조 신호 보존 | 단조 수열의 중앙값은 항상 중심 원소 = 원본과 동일 | 꾸준히 증가·감소하는 트렌드 구간을 왜곡 없이 통과 |
| 단일 파라미터 | W(= 2R + 1) 하나로 스무딩 강도 전체 결정 | 튜닝 공간 최소. 윈도우를 키울수록 더 강하게 스무딩, 직관적 |
| 병렬화 친화 | y[i] 는 y[j] (i ≠ j) 에 독립 - 데이터 의존성 없음 | Parallel.For 로 코어 수에 비례한 선형 속도 향상 달성 |
Moving Average, Binomial / Gaussian, Savitzky-Golay 등 대표적인 1D 스무딩 필터들과의 특성 비교를 통해 Running Median 의 선택 근거를 정리합니다.
동일한 윈도우 W = 2R + 1 에서 각 필터가 출력값 y[i] 를 어떻게 결정하는지를 비교합니다.
균등 가중치. 창 안 모든 값을 동등하게 합산.
중심에 높은 가중치. 가우시안/이항계수 커널.
최소자승 다항 피팅 계수. 피크 형태 보존.
정렬 후 중앙값. 어떤 선형 가중합도 사용하지 않음.
| 속성 | Moving Average | Binomial / Gaussian | Savitzky-Golay | Running Median ✓ |
|---|---|---|---|---|
| 이상치 (Outlier) 내성 | ❌ 취약 단 하나의 스파이크가 창 전체 평균을 왜곡 |
❌ 취약 가중 평균이므로 동일하게 영향받음 |
△ 보통 다항 피팅이 이상치를 완화하나 제거 불가 |
✅ 강력 중앙값은 이상치가 50% 미만이면 출력에 영향 無 |
| 에지 (급격한 변화) 보존 | ❌ 뭉개짐 에지가 W 구간에 걸쳐 완만하게 번짐 |
❌ 뭉개짐 가우시안 특성상 에지 번짐이 더 심함 |
✅ 우수 피크·에지 형태를 다항식으로 보존 |
✅ 우수 에지 양측 값이 뚜렷이 다르면 중앙값이 한쪽에 수렴 |
| 스파이크 노이즈 제거 | ❌ 잔류 평균화로 에너지가 분산될 뿐, 제거 아님 |
❌ 잔류 마찬가지로 번짐 발생 |
△ 부분 큰 폭의 스파이크는 피팅에 영향 |
✅ 완전 제거 단일 스파이크는 정렬 시 가장자리로 밀려 중앙값에서 탈락 |
| 실시간 · 스트리밍 적합성 | ✅ 우수 슬라이딩 합산으로 O(1) 온라인 갱신 가능 |
✅ 우수 커널 고정이면 O(W) 합산 |
△ 보통 계수 테이블 사전 계산 후 O(W) 적용 가능하나 차수 변경 시 재계산 |
✅ 적합 고정 윈도우 크기 → 매 포인트마다 O(W log W) 정렬, 배치 병렬화로 실용적 |
| 매끄러움 (Smoothness) | ✅ 부드러움 | ✅ 매우 부드러움 | ✅ 부드러움 다항 차수에 따라 조절 가능 |
△ 계단 현상 가능 출력이 입력값 중 하나로 고정 → 작은 W 값에서 계단식 출력 |
| 분포 가정 불필요 | △ 정규분포 가정 | ❌ 가우시안 가정 | △ 국소 다항 가정 | ✅ 가정 없음 비모수(Non-parametric). 어떠한 분포에도 동일하게 동작 |
| 파라미터 복잡도 | ✅ 낮음 W 하나 |
✅ 낮음 W + σ |
△ 중간 W + 다항 차수 (m) |
✅ 낮음 W (= 2R + 1) 하나 |
SignatureMedian 은 사전 수집된 1D 시퀀스 전체에 대해 일괄 처리(Batch)하는 설계입니다. 이 맥락에서 Running Median 의 실시간성을 분석합니다.
Running Median 은 절대 속도는 평균 계열보다 느리지만, W 가 고정된 상황에서 O(n · W log W) 는 예측 가능하고 선형 확장합니다.
Parallel.For 로 병렬화Moving Average 의 O(n) 이 병렬화 한계에 부딪히는 반면, Running Median 은 O(n · W log W / C) 로 코어가 늘수록 유리해집니다.
스트리밍 온라인 환경에서 Running Median 을 O(log W) 로 갱신하려면 두 힙 (Max-Heap + Min-Heap) 구조를 사용합니다. SignatureMedian 은 배치 설계이므로 채택하지 않았으나, 실시간 단일 스트림이 필요한 경우 이 확장을 적용할 수 있습니다.
| 신호 특성 / 목적 | 권장 필터 | 이유 |
|---|---|---|
| 센서 스파이크·이상치 포함, 실제값 복원 | Running Median ✓ | 이상치가 출력에 영향을 주지 않는 유일한 필터 |
| 분포 가정 없는 범용 스무딩 | Running Median ✓ | 비모수 특성으로 어떤 데이터 분포에서도 동일하게 동작 |
| 에지·스텝 변화 보존 필요 | Running Median ✓ / Savitzky-Golay | 선형 필터 중 SG 가 피크 보존 최강, Median 은 에지 위치 보존 |
| 부드러운 트렌드 추출 (노이즈 ≈ 가우시안) | Gaussian / Binomial | 주파수 응답이 가장 매끄럽고 진동 없음 |
| 스펙트럼 분석용 미분 계수 필요 | Savitzky-Golay | 피팅 다항식을 미분하여 1차·2차 도함수를 직접 얻음 |
| 단순·초고속 구현, 실시간 단일 채널 | Moving Average | O(1) 슬라이딩 합산, 코드 최소 |
| 의료·음악·금융 데이터, 이상치 빈발 | Running Median ✓ | 이상치에 의한 잘못된 분석 결론 방지에 가장 효과적 |
단일 스파이크나 극단값이 빈번한 1D 시계열에서 평균 계열 필터는 신뢰도가 낮습니다. Running Median 은 이상치가 창의 절반 미만인 한 출력에 전혀 영향을 주지 않습니다 - 다른 어떤 선형 필터도 제공하지 못하는 보증입니다.
Moving Average · Gaussian 은 신호의 급격한 변화 지점을 W 구간에 걸쳐 번지게 합니다. Running Median 은 경계 양측의 값 분포가 뚜렷이 다를 때 중앙값이 어느 한쪽으로 수렴하여 경계의 위치와 크기를 보존합니다.
Gaussian 은 노이즈가 정규분포를 따른다고 가정하고, Savitzky-Golay 는 신호가 국소적으로 저차 다항식 형태라고 가정합니다. Running Median 은 어떤 분포 가정도 하지 않아, 실제 데이터가 이 가정을 위반해도 성능이 저하되지 않습니다.
매 포인트의 Median 계산은 인접 포인트와 완전히 독립적인 특성 덕분에 SignatureMedian 은 Parallel.For 로 전체 배열을 코어 수에 비례하여 분할 처리합니다. 절대 복잡도는 O(n · W log W) 이지만 병렬 실효 처리속도는 Moving Average 와 실용적으로 경쟁합니다.
Savitzky-Golay 는 윈도우 크기와 다항 차수를 함께 결정해야 하고, Gaussian 은 σ 를 별도로 지정합니다. Running Median 은 반경 R (또는 W = 2R + 1) 하나만으로 전체 동작이 결정됩니다. 파라미터 탐색 공간이 작아 실무 적용이 직관적입니다.
전체 데이터 (인덱스 0 ~ n − 1) 에 Running Median 을 적용합니다. 커널이 데이터 경계를 벗어나는 위치에서는 BoundaryMode 에 따라 범위 밖 값을 합성합니다.
offsetLow = (W − 1) \ 2 · offsetHigh = (W − 1) − offsetLow · W 가 홀수이면 offsetLow = offsetHigh = R
startIdx = 0, endIdx = n − 1 → 전체 데이터를 순회합니다.
Adaptive : reach = min(R, i, n−1−i) 로 윈도우를 대칭 축소하여 항상 i 중심 유지. W = 2·reach+1.
기타 모드 : 각 pos 에 대해 GetValueWithBoundary(arr, i + k, mode) 로 범위 밖 값을 합성합니다.
GetWindowMedian(win, length) → Array.Sort 후 중앙값 반환. 짝수 길이는 두 중앙값의 평균.
Adaptive 는 합성 값을 사용하지 않고, 윈도우를 항상 i 중심으로 유지하면서 경계에서 대칭적으로 축소합니다. reach = min(R, i, n−1−i) 로 양쪽 도달 거리를 균등하게 제한하여 위상 변이 (phase shift) 를 원천 차단합니다.
' Adaptive 분기 핵심 코드 - 대칭 축소 Dim reach As Integer = Math.Min(offsetLow, Math.Min(i, n - 1 - i)) Dim W As Integer = 2 * reach + 1 For pos As Integer = 0 To W - 1 win(pos) = arr(i - reach + pos) ' i 중심 대칭 수집 Next buffer(i) = GetWindowMedian(win, W)
' Symmetric / Replicate / ZeroPad 경로 For pos As Integer = 0 To kernelWidth - 1 Dim k As Integer = pos - offsetLow ' 중심 기준 오프셋 win(pos) = GetValueWithBoundary(arr, i + k, boundaryMode) Next buffer(i) = GetWindowMedian(win, kernelWidth)
i + k가 범위 밖이면 mode 에 따라 반사 (Symmetric), 클램핑 (Replicate), 0.0 (ZeroPad) 을 반환합니다. Adaptive 는 이 경로를 타지 않습니다.borderCount 만큼 양쪽 경계를 제외한 내부 영역만 Median 을 적용합니다. BoundaryMode 는 무시되며, 경계 데이터는 원본 값 그대로 유지됩니다.
b = Max(0, borderCount) · startIdx = b · endIdx = n - b - 1 · 2b ≥ n 이면 처리 없이 원본 반환.
reach = Min(offsetLow, i, n−1−i) · W = 2·reach + 1 → 경계에서 윈도우가 i 중심으로 대칭적으로 축소됩니다. 위상 변이 (phase shift) 가 발생하지 않습니다.
축소된 길이 (length) 의 윈도우로 GetWindowMedian() 호출. 짝수 길이도 처리됩니다.
' useMiddle = True 경로 - BoundaryMode 무시 ' 대칭 축소: 윈도우를 항상 i 중심으로 유지, ' 경계에서 양쪽 reach 를 균등하게 줄임. Dim reach As Integer = Math.Min(offsetLow, Math.Min(i, n - 1 - i)) Dim W As Integer = 2 * reach + 1 For k As Integer = 0 To W - 1 win(k) = arr(i - reach + k) Next buffer(i) = GetWindowMedian(win, W)
| borderCount | 처리 범위 | 경계 데이터 |
|---|---|---|
| 0 | 0 ~ n − 1 (전체) | 대칭 축소된 윈도우로 Median 적용 |
| R (= kernelRadius) | R ~ n − R − 1 | 원본 값 보존 (가장 일반적) |
| n / 2 이상 | 처리 없음 | 전체 원본 반환 |
Middle Median 은 경계 문제를 다른 모드처럼 "합성"으로 해결하지 않고 "처리 대상에서 제외"하는 방식으로 접근합니다. 이 근본적인 차이에서 아래의 수학적 장단점이 도출됩니다.
커널이 데이터 경계를 벗어날 때 범위 밖 인덱스에 어떤 값을 할당할지 결정합니다. All Median 모드 (useMiddle = False) 에서만 적용됩니다.
W = 5 (R = 2) 인 커널을 i = 1 에 적용하면 인덱스 −1 을 참조합니다. 이 범위 밖 인덱스에 대한 처리가 BoundaryMode 입니다.
Private Shared Function GetValueWithBoundary( data As Double(), idx As Integer, mode As BoundaryMode) As Double Dim n As Integer = If(data Is Nothing, 0, data.Length) If n = 0 Then Return 0.0 Select Case mode Case BoundaryMode.Symmetric If n = 1 Then Return data(0) Dim period As Long = 2L * (CLng(n) - 1L) ' 64-bit 주기 Dim m As Long = idx Mod period If m < 0 Then m += period Dim mapped = If(m < n, m, 2L * (n − 1L) − m) Return data(CInt(mapped)) Case BoundaryMode.Replicate If idx < 0 Then idx = 0 ElseIf idx >= n Then idx = n - 1 Return data(idx) Case BoundaryMode.ZeroPad If idx < 0 OrElse idx >= n Then Return 0.0 Return data(idx) Case BoundaryMode.Adaptive ' Symmetric 과 동일한 64-bit 주기 반사 ... End Select End Function
Whole-sample 반사는 경계 값을 중복하지 않고 바로 다음 값부터 반사합니다. 주기 P = 2·(N − 1) 로 64-bit Mod 를 사용해 음수 · 양수 인덱스 모두 안전하게 처리합니다. MATLAB ‘symmetric’ / NumPy ‘reflect’ / OpenCV BORDER_REFLECT_101 과 동치합니다.
Dim period As Long = 2L * (CLng(n) - 1L) ' 64-bit 주기 Dim m As Long = idx Mod period If m < 0 Then m += period Dim mapped = If(m < n, m, 2L * (n − 1L) − m)
양 끝 값을 그대로 반복하여 경계를 채웁니다. 경계 평탄화 (Plateau) 효과가 발생하며 경계 값이 과다 반영됩니다. 구현이 가장 단순하고 빠릅니다.
If idx < 0 Then idx = 0 ElseIf idx >= n Then idx = n - 1 Return data(idx)
범위 밖을 모두 0 으로 처리합니다. 분모 (윈도우 크기 W) 는 그대로 유지되므로 경계 근처에서 에너지 감쇠 (값 저하) 가 발생합니다. Median 에서는 0 이 중앙값을 0 방향으로 편향시킵니다.
If idx < 0 OrElse idx >= n Then Return 0.0 Return data(idx)
합성 샘플을 전혀 생성하지 않습니다. 경계 근방에서 윈도우를 i 중심으로 대칭적으로 축소하여 (reach = min(R, i, n−1−i), W = 2·reach+1) 항상 진짜 데이터만 사용합니다. 위상 변이 (phase shift) 가 원천적으로 발생하지 않습니다. All Median 내에서 별도 분기로 처리됩니다.
Dim reach As Integer = Math.Min(offsetLow, Math.Min(i, n - 1 - i)) Dim W As Integer = 2 * reach + 1 ' GetValueWithBoundary 를 거치지 않음 For pos = 0 To W - 1 : win(pos) = arr(i - reach + pos)
| Mode | 경계 왜곡 | 특성 | 권장 용도 |
|---|---|---|---|
| Symmetric | 거의 없음 | Whole-sample 반사, 경계 값 비중복 | 일반 용도 (기본값) |
| Replicate | 약간 (평탄화) | 가장 단순, 빠름 | 경계 평탄화 허용 시 |
| ZeroPad | 있음 (감쇠) | 경계에서 0 방향 편향 | 주파수 / 신호 처리 |
| Adaptive | 없음 | 대칭 축소, 위상 변이 없음 | 합성 거부 시 |
AvocadoSmoothie 의 기본 경계 모드인 Symmetric 은 Whole-sample 반사 (경계 값 비중복) 를 채택합니다. 이것이 Running Median 알고리즘에 최적화된 방식인 수학적 · 통계적 근거를 단계별로 분석합니다.
경계 반사에는 크게 두 가지 방식이 존재합니다. 핵심 차이는 경계 값 (Edge Value) 을 반사 구간에 한 번 더 복제하느냐 여부입니다.
symmetric, NumPy reflect, OpenCV BORDER_REFLECT_101symmetric (1-based), SciPy reflect의 일부 해석Running Median 은 윈도우 안에서 정렬 후 가운데 값을 뽑는, 일종의 다수결 (Majority Vote) 입니다. 윈도우 안에 특정 값이 여러 번 등장하면 그 값이 중앙값으로 선택될 확률이 비례적으로 높아집니다.
데이터가 [10, 20, 30, 40, 50] 이고 W = 5 일 때, i = 0 에서의 윈도우를 비교합니다.
이 예에서는 결과가 같지만, 경계 값이 극단값 (이상치) 이거나 데이터 분포가 비대칭인 경우 Half-sample 의 중복이 Median 을 경계 값 방향으로 편향시킵니다. Whole-sample 은 경계 값을 윈도우에 1 회만 포함하므로 Median 의 다수결 중립성 (Median Neutrality) 을 보존합니다.
Whole-sample 은 경계 값을 한 번만 포함하므로, 윈도우 안에서 각 값의 등장 횟수가 자연 데이터의 통계적 빈도를 왜곡하지 않습니다.
Running Median 은 순위 (Rank) 로만 결과가 결정되므로, 값의 등장 횟수가 인위적으로 변하면 곧 Median 이 편향됩니다. Whole-sample 은 이 등장 횟수를 보존합니다.
센서 데이터에서 첫 샘플이 이상치 (예: 스파이크 1000) 인 경우를 생각합니다.
이 예에서 결과가 같지만, W = 5 일 때 이상치가 2 개 (50% 미만) 이면 Half-sample 은 Breakdown Point 에 가까워지고, Whole-sample 은 여전히 안전합니다. Whole-sample 은 Running Median 의 50% Breakdown Point 를 경계에서도 완전히 보존합니다.
Whole-sample 반사는 경계에서 데이터의 1 차 도함수 (기울기) 를 자연스럽게 보존합니다.
Half-sample 은 경계 값을 반복하므로 기울기를 0 으로 수렴시킵니다 (Plateau 효과). Whole-sample 은 데이터의 자연 기울기를 반사하여 신호의 국소 트렌드를 보존합니다. 이는 Replicate 모드의 "경계 평탄화" 문제를 근본적으로 피합니다.
Whole-sample 반사는 신호 처리 분야의 사실상 표준 (de facto standard) 입니다.
| 라이브러리 | API 이름 | 방식 |
|---|---|---|
| MATLAB | medfilt1(x, W) | Whole-sample (기본값) |
| NumPy / SciPy | ndimage.median_filter(mode='reflect') | Whole-sample |
| OpenCV | medianBlur(BORDER_REFLECT_101) | Whole-sample |
| AvocadoSmoothie | BoundaryMode.Symmetric | Whole-sample ✓ |
AvocadoSmoothie 가 Whole-sample 을 채택함으로써, MATLAB · SciPy · OpenCV 의 결과와 동일한 경계 처리를 보장합니다. 라이브러리 간 결과 비교 · 검증이 직접 가능합니다.
| # | 특장점 | 설명 |
|---|---|---|
| 1 | Median Neutrality | 경계 값 비중복으로 각 값의 등장 횟수 보존 → 순위 불변 → Median 편향 없음 |
| 2 | Breakdown Point 보존 | 경계에서도 이상치 1 개 = 1 회 등장 → 50% Breakdown Point 완전 유지 |
| 3 | 도함수 연속성 | 경계 기울기를 반사하여 Plateau 효과 없이 자연 트렌드 보존 |
| 4 | 멱등성 호환 | M(M(x)) = M(x) 성질이 경계에서도 성립 (인위 중복이 없으므로) |
| 5 | 산업 표준 호환 | MATLAB · SciPy · OpenCV 와 동일 경계 처리 → 교차 검증 가능 |
윈도우 내 값을 정렬하여 중앙값을 구합니다. Array.Sort in-place 정렬 후, 홀수 길이는 가운데 원소, 짝수 길이는 두 중앙 원소의 평균을 반환합니다.
Private Shared Function GetWindowMedian( win() As Double, length As Integer) As Double Array.Sort(win, 0, length) ' 앞 length 개만 in-place 정렬 Dim mid = length \ 2 If (length And 1) = 0 Then ' 짝수 길이 : 두 중앙값의 평균 Return (win(mid - 1) + win(mid)) / 2.0 Else ' 홀수 길이 : 정확한 가운데 원소 Return win(mid) End If End Function
| 윈도우 | 정렬 후 | mid | 결과 | 유형 |
|---|---|---|---|---|
| [5, 2, 8, 1, 4] | [1, 2, 4, 5, 8] | 2 | 4 | 홀수 (5) |
| [3, 7, 1, 9] | [1, 3, 7, 9] | 2 | (3 + 7) / 2 = 5 | 짝수 (4) |
| [10] | [10] | 0 | 10 | 홀수 (1) |
| [4, 6] | [4, 6] | 1 | (4 + 6) / 2 = 5 | 짝수 (2) |
' ComputeMediansByRadius 내부 Dim w64 As Long = CLng(kernelRadius) * 2L + 1L If w64 > Integer.MaxValue Then Throw New ArgumentOutOfRangeException(...) End If Dim w As Integer = CInt(w64) ' 항상 홀수, ≥ 1
| R | W = 2R + 1 | offsetLow | offsetHigh | 윈도우 범위 |
|---|---|---|---|---|
| 1 | 3 | 1 | 1 | [i − 1, i, i + 1] |
| 2 | 5 | 2 | 2 | [i − 2, i − 1, i, i + 1, i + 2] |
| 3 | 7 | 3 | 3 | [i − 3 .. i + 3] |
| 5 | 11 | 5 | 5 | [i − 5 .. i + 5] |
(W − 1) \ 2 로 계산합니다.입력 데이터 · 모드 · 경계 처리 · 반경을 설정하고 실시간으로 결과를 확인합니다. 각 인덱스별 윈도우 수집 → 정렬 → Median 과정을 단계별로 로그합니다.
SignatureMedian 은 .NET 의 Parallel.For, ThreadLocal, Interlocked 등을 활용해 멀티코어에서 효율적으로 동작합니다.
Parallel.For(startIdx, endIdx + 1, Sub(i) ' 각 데이터 포인트 독립 처리 ' 입력 arr 은 읽기 전용 → 경쟁 조건 없음 ' buffer(i) 쓰기는 인덱스 i 고유 → 동기화 불필요 End Sub)
Dim localWin As New ThreadLocal(Of Double())( Function() New Double(kernelWidth - 1) {} ) ' Parallel.For 내부에서: Dim win = localWin.Value ' 스레드별 고유 배열
win 배열이 필요하지만, 매 반복마다 new 하면 GC 부담이 큽니다. ThreadLocal 로 스레드당 1 회만 할당하여 재사용합니다.Dim cnt = Interlocked.Increment(processed) If cnt Mod reportInterval = 0 Then progress?.Report(Math.Min(n, cnt)) End If
' GetWindowMedian - 별도 배열 할당 없음 Array.Sort(win, 0, length) ' win 배열의 앞 length 개만 정렬 ' win은 ThreadLocal 버퍼 → GC 없음, 할당 없음
win.Take(length).ToArray() 로 매번 새 배열을 할당했습니다. 현재는 in-place 정렬로 GC 부담을 제거했습니다.| 항목 | 상세 |
|---|---|
| 시간 복잡도 | O(n · W · log W) - 각 포인트에서 W 개 정렬 |
| 공간 복잡도 | O(n + W · threads) - buffer + 스레드별 win 배열 |
| 병렬화 | Parallel.For - 코어 수에 비례하는 속도 향상 |
| GC 부담 | 최소 - ThreadLocal 재사용, in-place 정렬 |
| 진행 보고 | ~0.5% 간격 IProgress<Integer> 콜백 |
AvocadoSmoothie 핵심 애플리케이션 코드, .NET Standard 2.0 기반 Running Median 라이브러리 NuGet 패키지, 그리고 라이브러리 기능을 직접 실험해볼 수 있는 실습용 Tasting 애플리케이션을 아래 링크에서 확인하실 수 있습니다.
AvocadoSmoothie 의 핵심 소스 코드 저장소입니다. VB.NET Windows Forms 기반으로 제작된 Running Median 노이즈 제거 애플리케이션으로, 커널 반경 (Radius) 과 경계 개수 (Border Count) 를 직접 설정하고, MiddleMedian 또는 AllMedian 방식을 선택하여 수치 시퀀스를 처리할 수 있습니다. 병렬 처리와 진행률 표시를 지원하여 대용량 데이터셋에서도 반응성 높은 UI 환경을 제공합니다.
AvocadoSmoothie 는 "Avocado"와 "Smoothie"의 합성어입니다. 아보카도가 단단한 겉면 안에 부드럽고 영양 가득한 속살을 품고 있듯, 노이즈가 섞인 원시 데이터를 내부적으로 부드럽게 정제하여 신뢰할 수 있는 값으로 블렌딩한다는 철학을 담고 있습니다.
.NET Standard 2.0 및 .NET Framework 4.8 을 기반으로 하는 고성능 Running Median 평활화 & 내보내기 툴킷 NuGet 패키지입니다. MiddleMedian 및 AllMedian 필터를 구성 가능한 윈도우 반경과 경계 보존 옵션으로 제공하며, 대용량 데이터셋을 위한 자동 파티셔닝을 갖춘 CSV 내보내기와 차트 및 문서 속성이 포함된 Excel 내보내기 (COM 자동화) 를 지원합니다. 병렬화된 평활화 처리와 진행률·취소 지원으로 배치 및 인터랙티브 시나리오 모두에 적합합니다.
' MiddleMedian - 경계 원본 보존, 내부만 스무딩 Dim result = SignatureMedian.ComputeMediansByRadius( data, kernelRadius:=2, useMiddle:=True, borderCount:=2, boundaryMode:=BoundaryMode.Symmetric) ' AllMedian - 전체 스무딩, Boundary Mode 적용 Dim result = SignatureMedian.ComputeMediansByRadius( data, kernelRadius:=2, useMiddle:=False, borderCount:=0, boundaryMode:=BoundaryMode.Symmetric)
AvocadoSmoothie.Barista 라이브러리의 기능을 직접 체험하고 학습할 수 있는 실습용 동반 (companion) 애플리케이션입니다. 본격적인 프로덕션 애플리케이션을 구현하기 전에 Running Median 평활화, 경계 처리 옵션, 그리고 CSV · Excel 내보내기 워크플로우를 자유롭게 실험하고 익힐 수 있는 테이스팅 룸 (Tasting Room) 역할을 합니다.
AvocadoSmoothie.Barista 는 원재료 (데이터) 를 정밀하게 다듬어 완성된 스무디로 제공하는 바리스타처럼, 수치 시퀀스를 Running Median 으로 정제하는 VB.NET 라이브러리이며, AvocadoSmoothie.Barista.Tasting 은 그 라이브러리를 위한 시음 (Tasting) 공간 - 완성된 레시피에 앞서 맛을 확인하고 조율하는 실습 샘플입니다.