Normalize (정규화) 처리를 WPF OpenCV 프로젝트에 구현하도록 하겠습니다.
지난 포스팅(#17)에서는 영상의 밝기 분포를 확인하는 히스토그램(Histogram)을 구현했습니다. 히스토그램을 통해 “아, 이 사진은 픽셀들이 너무 어두운 곳에만 몰려있네?” “어라 ! 이 사진은 픽셀들이 너무 밝은 곳에만 몰려있네? “라는 걸 알았다면, 이제는 그걸 수정할 차례입니다.
바로 Normalize (정규화) 기능을 통해서 말이죠. 이번에는 정규화 라는 녀석의 개념을 제가 가능하다면 아주 쉽게 이해(?) 있도록 하고, 프로젝트에 적용해 보겠습니다.
Normalize (정규화)
정규화라는 말이 참 어렵게 들리지만, 사실 우리는 일상에서 자주 쓰고 있습니다. 쉬운 예로 ‘시험 성적’을 들어볼까요?
A 학생: 25문제 중 20문제 정답
B 학생: 20문제 중 19문제 정답
누가 공부를 더 잘했을까요? 단순히 맞힌 개수(20개 vs 19개)만 보면 A가 잘한 것 같지만, 문제 수가 다릅니다. 공정하게 비교하려면 ‘100점 만점’ 기준으로 환산해야겠죠?
- A 학생: (20 / 25) * 100 = 80점
- B 학생: (19 / 20) * 100 = 95점
이렇게 서로 다른 기준(25문제, 20문제)을 하나의 공통된 기준(100점 만점)으로 맞추는 과정, 이것이 바로 정규화(Normalize)입니다.
영상 처리에서도 마찬가지입니다. 픽셀 값이 100~150 사이에 옹기종기 모여 있어서 흐릿한 사진을, 0~255 전체 구간으로 쫙 펴주면(Stretch) 훨씬 선명한 사진이 됩니다. 이를 구간 정규화(MinMax Normalize)라고 부릅니다.
Normalize 구현
이번 구현의 핵심은 “정규화를 실행하고, 그 결과가 어떻게 변했는지 히스토그램 팝업으로 확인하는 것”입니다. 이를 위해 지난 시간에 만든 히스토그램 창을 재활용할 것입니다. 그래서 지난 포스팅 글의 마지막에 다음 포스팅은 더 쉬울 거라고 미리 언급했던 거죠.
- AlgorithmParameters.cs:
NormalizeParams클래스 추가 (Alpha, Beta, Type 설정) - MainWindow.xaml: 정규화 파라미터 조절용 UI 추가
- OpenCVService.cs:
Cv2.Normalize적용 및 결과 히스토그램 재계산 로직 추가 - MainViewModel.cs: 메뉴 등록 및 팝업창 연결
Step 1: NormalizeParams 클래스 정의 (Model)
먼저 AlgorithmParameters.cs에 정규화 설정을 담을 클래스를 만듭니다. OpenCV의 Normalize 함수는 Alpha, Beta, NormType이라는 3가지 핵심 인자를 받습니다.
public class NormalizeParams : AlgorithmParameters
{
// Alpha: MinMax 모드에서는 '최소값(하한선)', 그 외에는 기준값
private double _alpha = 0.0;
public double Alpha
{
get => _alpha;
set { if (_alpha != value) { _alpha = value; OnPropertyChanged(); } }
}
// Beta: MinMax 모드에서는 '최대값(상한선)', 그 외에는 사용 안 함
private double _beta = 255;
public double Beta
{
get => _beta;
set { if (_beta != value) { _beta = value; OnPropertyChanged(); } }
}
// 정규화 방식 (MinMax, L1, L2 등)
private NormTypes _normType = NormTypes.MinMax;
public NormTypes NormType
{
get => _normType;
set { if (_normType != value) { _normType = value; OnPropertyChanged(); } }
}
// 콤보박스용 리스트
public List<NormTypes> NormTypeSource { get; } = new List<NormTypes>
{
NormTypes.MinMax,
NormTypes.L1,
NormTypes.L2,
NormTypes.INF
};
}
Step 2: ViewModel 연결
MainViewModel.cs에서 메뉴를 등록하고, 정규화 실행 시에도 히스토그램 팝업이 뜨도록 로직을 수정합니다. (Post #17에서 만든 팝업을 재 사용합니다!) 아래의 코드 부분에 각 함수별로 수정 및 추가해야 하는 부분만 적었습니다. MainViewModel 생성자 함수, CreateParametersForAlgorithm() 함수에서 NormalizeParams 클래스 객체를 생성하도록 하고, ApplyAlgorithm () 함수에서는 히스토그램 그래프가 생성되어 보여 지는 조건을 Normalize 에서도 가능하도록 수정하였습니다. (주석을 잘 참조하여 주세요)
// 1. 생성자 목록 추가
AlgorithmList = new ObservableCollection<string>
{
// ... 기존 목록 ...
"Histogram",
"Normalize" // [추가]
};
// 2. 파라미터 생성 로직 (CreateParametersForAlgorithm)
case "Normalize":
CurrentParameters = new NormalizeParams();
break;
// 3. 팝업 띄우기 로직 (ApplyAlgorithm 함수 수정)
// [수정 전] if(SelectedAlgorithm == "Histogram" ...
// [수정 후] Histogram이거나 Normalize면 팝업을 띄운다.
if((SelectedAlgorithm == "Histogram" || SelectedAlgorithm == "Normalize") && _cvServices.LastHistogramData != null)
{
HistogramWindow histWin = new HistogramWindow(_cvServices.LastHistogramData, _cvServices.LastHistogramChannel);
histWin.Owner = Application.Current.MainWindow;
histWin.Show();
}
Step 3: UI 구성 (View)
MainWindow.xaml의 <Window.Resource> </Window.Resource> 섹션에 NormalizeParams를 위한 UI DataTemplate을 만들고, Alpha와 Beta 값을 슬라이더로 조절할 수 있도록 아래의 코드를 추가합니다.
<DataTemplate DataType="{x:Type local:NormalizeParams}">
<StackPanel>
<TextBlock Text="Normalize Settings" Margin="0,0,0,5" FontWeight="Bold"/>
<TextBlock Text="Normalization Type" Margin="0,5,0,0"/>
<ComboBox ItemsSource="{Binding NormTypeSource}" SelectedItem="{Binding NormType}" Margin="0,2,0,0" Height="25"/>
<TextBlock Text="Alpha (Min / Norm Value)" Margin="0,5,0,0"/>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="50"/>
</Grid.ColumnDefinitions>
<Slider Grid.Column="0" Minimum="0" Maximum="255" Value="{Binding Alpha}" VerticalAlignment="Center" />
<TextBox Grid.Column="1" Text="{Binding Alpha}" Margin="5,0,0,0" TextAlignment="Center" />
</Grid>
<TextBlock Text="Beta (Max Value for MinMax)" Margin="0,5,0,0"/>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="50"/>
</Grid.ColumnDefinitions>
<Slider Grid.Column="0" Minimum="0" Maximum="255" Value="{Binding Beta}" VerticalAlignment="Center" />
<TextBox Grid.Column="1" Text="{Binding Beta}" Margin="5,0,0,0" TextAlignment="Center" />
</Grid>
<TextBlock Text="* MinMax 모드: Alpha=최소값, Beta=최대값" Foreground="Gray" FontSize="11" Margin="0,5,0,0"/>
<TextBlock Text="* 적용 후 히스토그램 팝업이 뜹니다." Foreground="Blue" FontSize="11" Margin="0,5,0,0"/>
</StackPanel>
</DataTemplate>
Step 4: OpenCVService : Normalize
가장 중요한 OpenCVService.cs입니다. 여기서는 두 가지 작업을 하도록 하죠.
CalculateHistogramForPopup: 결과 이미지의 히스토그램을 다시 계산하는 도우미 함수 추가.ProcessImageAsync: 실제Cv2.Normalize실행.
// [추가] 팝업용 히스토그램 계산 도우미 함수
private void CalculateHistogramForPopup(Mat image)
{
if (image == null || image.IsDisposed) return;
// 편의상 0번 채널(Blue or Gray)만 계산해서 보여 줌
Mat[] channels = Cv2.Split(image);
Mat source = channels[0];
LastHistogramChannel = 0;
Mat hist = new Mat();
int[] histSize = { 256 };
Rangef[] ranges = { new Rangef(0, 256) };
// 히스토그램 계산
Cv2.CalcHist(new[] { source }, new[] { 0 }, null, hist, 1, histSize, ranges);
float[] rawData = new float[256];
hist.GetArray(out rawData);
LastHistogramData = rawData;
// 메모리 정리
hist.Dispose();
foreach (var c in channels) c.Dispose();
}
// [수정] ProcessImageAsync 내부 switch문
case "Normalize":
if (parameters is NormalizeParams normParams)
{
// 1. 정규화 실행 (핵심)
// Alpha: 구간의 시작(Min), Beta: 구간의 끝(Max) -> MinMax 모드일 때
Cv2.Normalize(_srcImage, _destImage,
normParams.Alpha,
normParams.Beta,
normParams.NormType);
// 2. 결과 이미지의 히스토그램 재계산 (변화 확인용)
CalculateHistogramForPopup(_destImage);
resultMessage += $": Normalize ({normParams.NormType}, A:{normParams.Alpha}, B:{normParams.Beta})";
}
break;
실행 결과 및 분석
이제 빌드 후 실행해 보도록 하겠습니다. 흐릿한(명암 대비가 낮은) 이미지를 불러와 히스토그램을 실행하여 분석한 그래프는 아래와 같이 실행 결과를 보여줍니다.


아래의 이미지는 바로 위 원본 이미지를 Nomalize 처리를 한 이미지와 Nomaliize 처리된 이미지의 히스토그램 분석 그래프입니다. 이때 조절한 파라미터는 MinMAX Type, MIN: 60, MAX:180 을 적용하여 실행하였습니다. 전체적으로 약간 어두워진 것 같지 않나요? 아주 어두운 부분(60보다 작은 픽셀 값)은 밝아지고, 아주 밝은 부분(180보다 밝은 픽셀 값)은 어두워지게 처리가 된거니까요. (이해하시죠?) 이 포스팅을 보고 계신 분들께서 보유한 이미지 중 흐릿한 (명암 대비가 낮은) 이미지를 한번 분석해 보세요. 아마도 음~ 이런 거구나 할 겁니다.
다음에는 이와 비슷하지만 조금 다른, 평탄화(Equalize)에 대해 다뤄보겠습니다.


참고 자료
[Post #17] 히스토그램: [WPF OpenCV Project #17] – 히스토그램 분석 및 그래프 그리기
Cv2.Normalize: OpenCV 공식 문서 (Normalize) – 수학적 수식과 파라미터 상세 설명
NormTypes: OpenCVSharp API – 정규화 타입 열거형 설명