WPF OpenCV 프로젝트 #15: Otsu Threshold (오츠 알고리즘) 구현

Otsu Thresold (오츠 알고리즘)을 WPF OpenCV 프로젝트에 구현하도록 하겠습니다.
앞선 포스팅들에서 길었다 생각하면 길 수 있는 UI의 터널을 지나왔습니다. ROI(관심 영역)를 자르고(Crop), 저장하고, 그 위에 도형을 그리고… 별로 한건 없는 것 같은데 몇 가지 기능들을 구현했죠. (1편부터 14편까지 대부분을 UI(View)만 붙들고 있었으니 지칠 만도 합니다.)

사실 UI 기능을 더 욕심내자면 끝도 없겠지만, 애초에 이 프로젝트를 시작한 목적은 “WPF 기반의 OpenCV 영상 처리“였습니다. 주객이 전도되기 전에 UI는 여기서 잠정 마무리하고(정말 필요하면 나중에 슬쩍 추가하죠 뭐), 오늘부터는 진짜 영상 처리 알고리즘을 하나씩 도장 깨기 하듯 추가해 나가겠습니다.

이번에 추가할 내용은 바로 오츠 알고리즘(Otsu’s Binarization)입니다.

Otsu Threshold: 자동으로 임계값을 찾아주는 녀석.

우리는 이전에 가장 기본적인 Threshold(이진화)를 구현했었습니다. 그런데 이 기능의 단점은 사용자가 슬라이더를 움직이며 “어디가 배경이고 어디가 물체인지” 직접 값을 정해줘야 한다는 것입니다. (귀찮죠.)

Otsu 알고리즘은 이 고민을 해결해 줍니다.

  • 원리: 이미지의 히스토그램(밝기 분포)을 분석해 배경과 전경(물체)을 가장 잘 구분할 수 있는 ‘최적의 임계값‘을 수학적으로 계산해서 자동으로 적용합니다.
  • 장점: 사용자가 값을 조절할 필요가 없습니다. (Auto!)
  • 단점(주의 사항): 만능은 아닙니다. 히스토그램상에서 배경과 물체의 밝기 차이가 명확해서 산봉우리가 2개(Bimodal)로 딱 갈라질 때 가장 잘 동작합니다. 조명이 불균일하거나 밝기 차이가 애매하면 엉뚱한 값을 잡기도 합니다.

우리는 잘 가져다 적절한 곳에 잘 쓰면 되는 사람들입니다. 따라서, 더 깊은 내용이 필요하다고 생각하지 않습니다. 그냥 우리의 MVVM 구조에 맞춰 코드를 넣어 보도록 하죠.

Model Parameter: OtsuParams class 생성

가장 먼저 할 일은 이 알고리즘이 사용할 “설정값(Parameter)“을 정의하는 것입니다. AlgorithmParameters.cs 파일에 OtsuParams 클래스를 새로 만들고 아래 코드를 추가합니다.


추가된 클래스를 살펴보죠. 먼저 AlgorithmParameters를 상속 받아 클래스를 생성했습니다. 이와 관련해서는 기존 구조(Post #3 참조)를 따르니까, 기억이 가물 가물 하다면, 앞선 포스팅된 부분을 한번 보고와 주세요. 바인딩하여 연결할 프로퍼티는 selectedType 하나 입니다. 그리고, UI(View) 에서 사용자가 선택할 수 있도록 ComobBox 로 제공하기 위해 List<Template> 을 사용하였습니다.

ThresholdTypes: 흑/백(Binary), 반전(BinaryInv), 자르기(Trunc) 중 어떤 형태로 결과를 볼지 선택합니다.

View: XAML에 UI 연결 (DataTemplate)

파라미터를 만들었으니, 화면(UI)에 보여줄 차례입니다. 우리는 DataTemplate을 사용해 파라미터 타입에 따라 UI가 자동으로 바뀌도록 설계했었죠? (Post #5 참조)

MainWindow.xaml<Window.Resources> 섹션에 아래 템플릿 코드를 추가합니다.

ViewModel: 알고리즘 목록 등록

이제 ViewModel에게 “나 Otsu 알고리즘 추가했어!“라고 알려줘야 합니다. MainViewModel.cs를 열어 두 군데를 수정합니다. 먼저 생성자 함수에서 “Otsu Threshold”를 추가합니다.
그리고, CreateParameterForAlgorithm() 함수에 파라미터 생성 로직을 추가합니다.

생성자 에서 목록 추가

파라미터 생성 로직 추가 (CreateParametersForAlgorithm)

여기까지 하고 빌드(Build) 후 실행해 보세요. 알고리즘 목록에 Otsu Threshold가 생겼고, 선택하면 콤보박스(ComboBox)가 뜨나요? 하지만 아직 기능은 동작하지 않을 겁니다. 서비스(Service) 로직을 구현하지 않았기 때문에 동작은 하지 않습니다.

Otsu Threshold UI 빌드 이미지

OpenCVService : Otsu Threshold

OpenCVService.cs ProcessImageAsync 함수에 실제 Otsu Thresold 영상 처리 코드를 아래와 같이 추가합니다. 특별히 어려운 코드가 없죠? 영상 처리 알고리즘은 어려울지 몰라도, 잘 만들어둔 함수를 가져다 쓰는것은 사실 어려울 게 별로 없죠. 이게 뭐 하는 녀석인지 그것만 딱 ~ 알면 되니까요.

위 코드에서 사실 핵심이라면 이 부분 입니다.
ThresholdTypes type = otsuParams.SelectedType | ThresholdTypes.Otsu;

OpenCVThreshold 함수는 하나의 인자에 여러 옵션을 섞어서 전달할 수 있습니다.

  • SelectedType: 결과 이미지를 어떻게 만들 것인가? (예: Binary – 흰색/검은색)
  • ThresholdTypes.Otsu: 값을 어떻게 구할 것인가? (자동 계산)

이 둘을 OR 연산(|)으로 합쳐서 전달하면, “Otsu 방식으로 값을 구해서, Binary 형태로 만들어라”라는 명령이 됩니다.

실행

자, 이제 모든 준비가 끝났습니다. 실행해 볼까요? 이미지를 불러오고 Otsu Threshold를 선택해 보세요.
아래의 이미지 결과처럼 잘 실행이 되나요?

상태 바(Status Bar)를 보면 Auto val: 161 처럼 자동으로 계산된 임계값이 표시되는 것을 확인할 수 있습니다. BinaryInv를 선택하면 반전된 이미지가 나오고, Trunc를 선택하면 밝은 부분만 눌러진 이미지가 나옵니다.

이제 UI의 늪에서 벗어나 알고리즘의 바다로 들어왔습니다. 다음 시간에는 조명이 일정하지 않은 이미지에서도 물체를 잘 찾아내는적응형 이진화(Adaptive Threshold)“를 구현해 보겠습니다.
(이건 Otsu보다 조금 더 똑똑합니다!)

참고 자료

Otsu’s Method (Wikipedia): 알고리즘의 수학적 원리 참조.
https://en.wikipedia.org/wiki/Otsu%27s_method
OpenCVSharp Threshold: 함수 원형과 사용법에 대한 공식 문서.
https://shimat.github.io/opencvsharp_docs/html/M_OpenCvSharp_Cv2_Threshold.htm
MVVM DataTemplate: 데이터 타입에 따라 UI를 바꾸는 WPF 기술입니다. (Post #5에서 다룸)
https://learn.microsoft.com/ko-kr/dotnet/desktop/wpf/data/data-templating-overview

댓글 남기기