Bug 수정(Zoom 측정 시 ROI 좌표 틀어짐 해결) 및 직선 거리 측정을 WPF OpenCV 프로젝트에 구현하겠습니다. 지난 13편까지 우리는 도형 그리기에 ROI 설정까지 정말 많은 기능을 숨 가쁘게 구현해 왔습니다. 그런데 혹시… 뭔가 이상한 점 못 느끼셨나요? (12편부터 계속되던 문제였는데 말이죠.)
ROI 사각형을 예쁘게 그려 놓고, 마우스 휠을 돌려 이미지를 확대/축소(Zoom In/Out) 했을 때, 빨간색 ROI 사각형이 얌전히 있던가요? 아니면 배경 이미지만 확대 축소 되고, ROI 사각형은 전혀 변경되지 않는 문제와 ROI 사각형이 엉뚱한 곳으로 날아가 버리지 않던가요?
“어라? 이런 미친 ROI 사각형이 이미지를 안 따라가네?”
사실 이미지 줌/이동을 먼저 구현하고 ROI를 나중에 얹다 보니, 줌 이벤트 발생 시 ROI 좌표를 재 계산하는 로직을 빠뜨렸습니다. (개발하다 보면 흔히 있는 일이죠? 제가 찾았으니 다행입니다. 하하.)
오늘은 이 치명적인 버그를 수정하고, 기왕 손대는 김에 직선을 그렸을 때 길이(Distance)를 측정해서 보여주는 멋진 기능까지 추가해 보겠습니다.
Bug 수정: 확대/축소 시 ROI가 따라오지 않는 문제
원인 분석
위에 첨부된 동작 영상을 보면 작업하고 있는 WPF OpenCV 프로젝트의 ZoomBorder에서 마우스 휠 이벤트를 받아 이미지를 확대(ScaleTransform)하고 있습니다. 하지만 OverlayCanvas위에 그려진 ROI 사각형(RoiRect)은 “화면상(Screen)의 절대 좌표“를 가지고 있습니다.
이미지는 커졌는데 사각형의 위치와 크기는 그대로라면? 당연히 사각형이 이미지의 엉뚱한 위치를 가리키게 되겠죠. 따라서 휠 을 돌릴 때마다 ROI 사각형의 위치와 크기도 배율에 맞춰 다시 계산(Update) 해줘야 합니다.
ZoomBorder_MouseWheel 수정
MainWindow.xaml.cs의 ZoomBorder_MouseWheel 함수를 찾아가서, 마지막 부분에 아래 코드를 추가하여 정상적인 동작을 하지 못하던 버그를 수정하도록 하죠.
private void ZoomBorder_MouseWheel(object sender, MouseWheelEventArgs e)
{
if(ImgView.Source == null) return;
// 1. 현재 마우스 위치를 기준으로 줌(Zoom) 수행 (기존 로직)
Point p = e.GetPosition(ZoomBorder);
double zoom = e.Delta > 0 ? 1.2 : (1.0 / 1.2);
imgScale.ScaleX *= zoom;
imgScale.ScaleY *= zoom;
// 2. 위치 보정 (기존 로직: 마우스 포인터 중심 줌)
imgTranslate.X = p.X - (p.X - imgTranslate.X) * zoom;
imgTranslate.Y = p.Y - (p.Y - imgTranslate.Y) * zoom;
// ---------------------------------------------------------
// 줌 상태가 변경되면 ROI 사각형 위치도 갱신해야 함!
// ---------------------------------------------------------
if (RoiRect.Visibility == Visibility.Visible)
{
// 현재 논리적 ROI 좌표(_currentRoiRect)를 이용해
// 변경된 배율(Scale)과 이동(Translate)을 적용하여 다시 그립니다.
UpdateRoiVisual(new Point(_currentRoiRect.X, _currentRoiRect.Y),
new Point(_currentRoiRect.X + _currentRoiRect.Width,
_currentRoiRect.Y + _currentRoiRect.Height));
}
}
Bug 수정 내용
UpdateRoiVisual(...) 호출: 이 함수 기억나시죠? “이미지 기준 좌표”를 입력 받아 현재의 줌/이동 상태를 반영해 “화면 좌표”로 변환해 주는 핵심 함수입니다. 마우스 휠(Mouse Wheel)을 돌려 imgScale과 imgTranslate값이 바뀌었으니, 이 바뀐 값을 기준으로 UpdateRoiVisual을 강제로 한 번 더 호출해 주는 것입니다. 이렇게 하면 이미지가 커지는 만큼 ROI 사각형도 정확히 커지고 이동이 가능하게 됩니다.
이제 빌드 해서 실행해 보세요. 앞선 실행 영상의 문제들이 깔끔하게 해결이 됐음을 보게 될 거에요 ^^; 이미지를 아무리 확대하고 축소해도 ROI 사각형이 껌딱지처럼 이미지에 딱 붙어있는 놀라운 경험을 하시게 될 겁니다.
Line Distance (직선 거리 측정 및 값 표시)
영상 처리를 하다 보면 두 지점 사이의 거리를 측정할 때가 많습니다. 지난 시간에 Draw Line 기능을 만들었으니, 이번엔 선을 다 그렸을 때 “이 선의 길이는 OO px입니다”라고 알려주는 그다지 특별하지 않는 기능을 넣어 보겠습니다.
구현 위치 (ZoomBorder_MouseUp)
도형 그리기가 끝나는 시점은 마우스 버튼을 뗄 때, 즉 MainWindow.xaml.cs 파일에서 ZoomBorder_MouseUp이벤트입니다. 해당 이벤트에 다음의 코드를 추가하도록 하죠.
// MainWindow.xaml.cs > ZoomBorder_MouseUp 내부
else if(_currentDrawMode != DrawingMode.None && _tempShape != null)
{
ZoomBorder.ReleaseMouseCapture();
// [추가] 만약 그린 도형이 '직선(Line)'이라면 길이를 계산한다.
if(_currentDrawMode == DrawingMode.Line && _tempShape is Line line)
{
// 1. 유클리드 거리 공식 (피타고라스 정리)
// 거리 = sqrt((x2 - x1)^2 + (y2 - y1)^2)
double width = line.X2 - line.X1;
double height = line.Y2 - line.Y1;
double dist = Math.Sqrt(Math.Pow(width, 2) + Math.Pow(height, 2));
// 2. 텍스트블록(TextBlock) 동적 생성
TextBlock tb = new TextBlock()
{
Text = $"{dist:F1}px", // 소수점 첫째 자리까지 표시
Foreground = Brushes.Yellow,
Background = new SolidColorBrush(Color.FromArgb(128, 0, 0, 0)), // 반투명 검은 배경
Padding = new Thickness(2),
FontSize = 14,
FontWeight = FontWeights.Bold
};
// 3. 텍스트 위치 지정 (선의 끝점)
Canvas.SetLeft(tb, line.X2);
Canvas.SetTop(tb, line.Y2);
// 4. OverlayCanvas에 텍스트 추가
OverlayCanvas.Children.Add(tb);
}
_currentDrawMode = DrawingMode.None;
_tempShape = null;
Cursor = Cursors.Arrow;
}
특별히 어려운 내용은 없지만, 주석에 사용된 용어들에 대해 한번 짚어 보고 가도록 하죠 .
유클리드 거리 (Euclidean Distance):
중학교 수학 시간에 배운 피타고라스 정리를 사용합니다. Math.Sqrt (루트)와 Math.Pow (제곱)를 이용해 두 점 사이의 최단 거리를 구합니다.
영상처리에서 픽셀 간 거리를 잴 때 가장 기본이 되는 공식입니다.
동적 컨트롤 생성 (Dynamic Control):
XAML에 미리 만들어두지 않고, C# 코드에서 new TextBlock()을 통해 실시간으로 글자 상자를 만듭니다.
가독성 확보: 글자가 잘 보이도록 노란색 글씨(Foreground)에 반투명 검은색 배경(Background)을 깔아주었습니다. Color.FromArgb(128,0,0,0)에서 128이 투명도(Alpha)입니다.
오버레이 추가:
OverlayCanvas.Children.Add(tb)를 통해 방금 만든 텍스트를 화면에 띄웁니다.
실행
자, 이제 프로그램을 실행하고 직선을 그어보세요. 마우스를 놓는 순간, 선 끝 부분에 노란색 글씨로 거리(px)가 짠! 하고 나타날 겁니다. 확대/축소 버그도 잡고, 거리 측정 기능도 넣으니 이제 제법 그럴싸한 계측 프로그램 같지 않나요?