카메라 기능을 제외하고 나머지 기능을 먼저 구현한 후에 마지막에 카메라 기능을 추가하기로 했다.
카메라 기능을 나중에 추가하는 이유
WebcamService 클래스는 카메라 관련 기능만 담당하고, MainWindowViewModel은 이미지 처리 및 게임 로직에 집중할 수 있습니다.ImageProcessingService의 ProcessImage() 메서드를 테스트할 때, 실제 카메라 입력 없이 테스트 이미지를 사용하여 테스트할 수 있습니다.카메라 기능 제외 방법
MainWindowViewModel 수정: MainWindowViewModel에서 WebcamService를 사용하는 부분을 주석 처리하거나 제거합니다. StartCapture(), StopCapture(), UpdateFrame() 메서드를 수정해야 합니다.
// ViewModels/MainWindowViewModel.cs
public MainWindowViewModel()
{
// ...
// webcamService = new WebcamService(); // 주석 처리 또는 제거
// ...
}
private async void StartCapture()
{
// await webcamService.StartCaptureAsync(); // 주석 처리 또는 제거
// CompositionTarget.Rendering += UpdateFrame; // 주석 처리 또는 제거
}
private void StopCapture()
{
// webcamService.StopCapture(); // 주석 처리 또는 제거
// CompositionTarget.Rendering -= UpdateFrame; // 주석 처리 또는 제거
}
private async void UpdateFrame(object? sender, EventArgs e)
{
// Mat frame = await webcamService.GetFrameAsync(); // 주석 처리 또는 제거
// if (!frame.Empty()) // 주석 처리 또는 제거
// {
// ...
// }
}
테스트 이미지 사용: ImageProcessingService의 ProcessImage() 메서드를 테스트할 때, 실제 카메라 입력 대신 테스트 이미지를 사용합니다.
// Services/ImageProcessingService.cs
public GameInfo ProcessImage(Mat image)
{
// ...
// 테스트 이미지 로드
Mat testImage = Cv2.ImRead("test_image.png");
// 테스트 이미지를 사용하여 이미지 처리 로직을 수행합니다.
// ...
return gameInfo;
}
카메라 기능 추가
나머지 기능 구현을 완료한 후, 카메라 기능을 추가할 때는 주석 처리했던 코드를 다시 활성화하고, WebcamService를 사용하는 코드를 추가하면 됩니다.
주의 사항
CameraImage 속성과 GameWindowRect 속성이 제대로 업데이트되지 않을 수 있습니다.WebcamService 클래스가 제대로 동작하는지 확인해야 합니다.Image 컨트롤과 캡처 시작/중지 버튼을 제거웹캠에서 캡처한 이미지를 표시하는 Image 컨트롤과 캡처 시작/중지 버튼을 제거한 후에는, MainWindow.xaml에서 노트북 화면을 인식하고 있다는 것을 직접적으로 확인할 수 있는 UI 요소가 없습니다.
하지만, ImageProcessingService에서 맵 영역을 인식하면 GameWindowRect 속성에 맵 영역의 좌표와 크기 정보가 저장됩니다. 이 정보는 MainWindowViewModel의 GameWindowRect 속성에 바인딩되고, MainWindow.xaml에서 Rectangle 컨트롤을 사용하여 빨간색 테두리로 표시됩니다.
따라서, 맵 영역이 인식되면 MainWindow 창에 빨간색 테두리가 표시되어 미니맵 영역을 시각적으로 확인할 수 있습니다.
게임 플레이 시 확인
게임을 플레이하면서 미니맵 영역이 화면에 표시되는지 확인할 수 있습니다.
Rectangle 컨트롤이 미니맵 영역을 따라 이동하면서 빨간색 테두리로 표시될 것입니다. Rectangle 컨트롤이 표시되지 않거나 잘못된 위치에 표시될 것입니다.추가적인 확인 방법
GameWindowRect 속성 값을 확인하고, 미니맵 영역이 제대로 인식되는지 확인할 수 있습니다.ImageProcessingService에서 미니맵 영역 인식 결과를 로그로 출력하여 확인할 수 있습니다.결론
웹캠 기능을 제외하고 MainWindow.xaml에서 Image 컨트롤과 캡처 시작/중지 버튼을 제거한 경우, 미니맵 영역 인식 여부는 Rectangle 컨트롤의 빨간색 테두리를 통해 확인할 수 있습니다. 게임을 플레이하면서 미니맵 영역이 제대로 인식되는지 확인하고, 필요하다면 디버깅 또는 로그 출력을 통해 문제를 해결할 수 있습니다.
하나 하나 중단점 설정하고 디버깅하기가 힘들어서, 로그 출력을 설정하기로 했다.
로그 출력을 설정하면 맵 영역 인식 결과를 쉽게 확인하고 디버깅할 수 있다.
ImageProcessingService에서 맵 영역 인식 결과를 로그로 출력하여 확인하기 위한 작업은 다음과 같은 순서로 진행했다.
1. 로깅 라이브러리 추가
로그 출력을 위해 NLog 라이브러리를 사용. NuGet 패키지 관리자를 사용하여 NLog 패키지를 프로젝트에 추가할 수 있다.
2. NLog 설정 파일 추가
프로젝트에 NLog.config 파일을 추가하고, 로그 출력 형식, 출력 위치 등을 설정한다.
<?xml version="1.0" encoding="utf-8" ?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
autoReload="true"
throwExceptions="false"
internalLogLevel="Off"
internalLogFile="c:\temp\nlog-internal.log">
<targets>
<target xsi:type="File" name="logfile" fileName="logs/log.txt"
layout="${longdate}|${level:uppercase=true}|${logger}|${message}" />
<target xsi:type="Console" name="logconsole"
layout="${longdate}|${level:uppercase=true}|${logger}|${message}" />
</targets>
<rules>
<logger name="*" minlevel="Debug" writeTo="logfile, logconsole" />
</rules>
</nlog>
이 XML 코드는 NLog 라이브러리의 설정을 정의한다.
targets: 로그 출력 대상을 정의한다. 여기서는 파일 (logfile)과 콘솔 (logconsole) 두 개의 대상을 정의했다.rules: 로깅 규칙을 정의한다. 여기서는 모든 로거 (*)에서 Debug 레벨 이상의 로그를 logfile과 logconsole 대상에 출력하도록 설정했다.추가 정보
3. 코드 수정
ImageProcessingService 클래스의 ProcessImage() 메서드에서 NLog를 사용하여 미니맵 영역 인식 결과를 로그로 출력한다.
// Services/ImageProcessingService.cs
using NLog;
using OpenCvSharp;
using OpenCvSharpProjects.Models;
// ...
public class ImageProcessingService
{
private static readonly Logger logger = LogManager.GetCurrentClassLogger(); // Logger 객체 생성
// ...
public GameInfo ProcessImage(Mat image)
{
// ...
try
{
// ...
// 미니맵 영역 검출
if (matchPoints.Count == 4)
{
// ...
logger.Info("미니맵 영역 검출 성공"); // 로그 출력
}
else
{
logger.Warn("미니맵 영역 검출 실패"); // 로그 출력
}
// ...
}
// ...
}
}
코드 설명
private static readonly Logger logger = LogManager.GetCurrentClassLogger();: 현재 클래스의 Logger 객체를 생성한다.logger.Info("미니맵 영역 검출 성공");: 미니맵 영역 검출 성공 시 로그를 출력한다.logger.Warn("미니맵 영역 검출 실패");: 미니맵 영역 검출 실패 시 로그를 출력한다.로그 확인
프로그램을 실행하면 logs 폴더에 log.txt 파일이 생성되고, 로그 메시지가 파일에 기록된다. Visual Studio의 "출력" 창에서도 로그 메시지를 확인할 수 있다.
추가 정보
NLog는 다양한 로그 출력 방식 (파일, 콘솔, 데이터베이스 등)을 지원합니다.NLog.config 파일에서 로그 출력 형식, 출력 위치, 로그 레벨 등을 설정할 수 있습니다.NLog는 로그 메시지에 추가적인 정보 (예: 시간, 로그 레벨, 클래스 이름)를 포함할 수 있습니다.네, ImageProcessingService에서 미니맵 영역 인식 결과를 로그로 출력하여 확인하는 방법을 추천합니다.
로그 출력 장점
오류 발생
콘솔 창에 "미니맵 영역 검출 성공"나 "미니맵 영역 검출 실패"가 안뜬다.
시도
1. 출력 창 설정 확인
Console.WriteLine() 출력이 표시되지 않습니다.-> 이상 없음
2. 빌드 구성 확인
Console.WriteLine() 출력이 표시되지 않을 수 있습니다.-> 이상 없음
3. 코드 실행 확인
ImageProcessingService의 ProcessImage() 메서드가 실제로 실행되는지 확인합니다. ProcessImage() 메서드 안에 중단점을 설정하고 디버깅 모드로 실행하여 메서드가 호출되는지 확인합니다.ProcessImage() 메서드 안에 다른 Console.WriteLine() 문을 추가하여 테스트합니다.4. NLog 설정 확인
NLog.config 파일이 프로젝트의 루트 폴더에 있는지 확인합니다.NLog.config 파일의 내용이 올바른지 확인합니다. 특히, targets 섹션에서 logconsole 타겟이 제대로 설정되어 있는지 확인합니다.NLog.config 파일의 rules 섹션에서 minlevel 속성이 Debug 또는 더 낮은 레벨로 설정되어 있는지 확인합니다.-> 이상 없음
5. NLog 초기화 확인
ImageProcessingService 클래스에서 NLog를 사용하기 전에 LogManager.GetCurrentClassLogger() 메서드를 호출하여 Logger 객체를 초기화했는지 확인합니다.-> 이상 없음
6. Visual Studio 재시작
-> 이상 없음
시도
MainWindowViewModel.cs 코드 수정
// ViewModels/MainWindowViewModel.cs
// ...
private async void UpdateFrame(object? sender, EventArgs e)
{
// ...
await Task.Run(() =>
{
gameInfo = imageProcessingService.ProcessImage(frame.Clone());
GameWindowRect = gameInfo.GameWindowRect;
// 미니맵 영역 검출 결과 확인
if (GameWindowRect.Width > 0 && GameWindowRect.Height > 0)
{
Console.WriteLine("미니맵 영역 검출 성공");
}
else
{
Console.WriteLine("미니맵 영역 검출 실패");
}
});
// ...
}
// ...
시도
ImageProcessingService에서 로그를 출력할 때 사용하는 로그 레벨이 minlevel 설정보다 높은 레벨이 아닌지 확인합니다. 예를 들어, minlevel이 Debug로 설정되어 있는데 logger.Info() 메서드를 사용하여 로그를 출력하면 로그가 표시되지 않습니다.-> rules 섹션에서 minlevel 속성이 Debug 또는 더 낮은 레벨로 설정.
minlevel이 Debug로 설정되어 있으면 logger.Info() 메서드로 출력한 로그는 표시되지 않습니다. logger.Info() 메서드는 Info 레벨의 로그를 출력하는 메서드이고, minlevel이 Debug로 설정된 경우 Debug 레벨 이상의 로그만 출력되기 때문입니다.
Debug 레벨의 로그를 출력하려면 logger.Debug() 메서드를 사용해야 합니다.
// Services/ImageProcessingService.cs
// ...
private Rect DetectGameWindow(Mat image)
{
// ...
// 꼭짓점 좌표를 이용하여 게임 화면 영역 계산
if (matchPoints.Count == 4)
{
// ...
logger.Debug("미니맵 영역 검출 성공"); // Debug 레벨 로그 출력
return new OpenCvSharp.Rect(topLeft.X, topLeft.Y, width, height);
}
else
{
logger.Debug("미니맵 영역 검출 실패"); // Debug 레벨 로그 출력
return new OpenCvSharp.Rect(); // 빈 Rect 객체 반환
}
}
// ...
NLog 로그 레벨
NLog는 다음과 같은 로그 레벨을 제공합니다.
Trace: 가장 상세한 로그 레벨입니다.Debug: 디버깅 정보를 출력하는 로그 레벨입니다.Info: 일반적인 정보를 출력하는 로그 레벨입니다.Warn: 경고 메시지를 출력하는 로그 레벨입니다.Error: 오류 메시지를 출력하는 로그 레벨입니다.Fatal: 치명적인 오류 메시지를 출력하는 로그 레벨입니다.minlevel 속성에 설정된 로그 레벨 이상의 로그만 출력됩니다. 따라서 minlevel이 Debug로 설정된 경우 Debug, Info, Warn, Error, Fatal 레벨의 로그가 출력됩니다.
추가 정보
logger.Trace(), logger.Debug(), logger.Info(), logger.Warn(), logger.Error(), logger.Fatal())시도
gameInfo.GameWindowRect 값은 DetectGameWindow() 메서드 내에서 계산되므로, DetectGameWindow() 메서드 안에서 로그를 출력해야 합니다.
수정된 ImageProcessingService.cs 코드
// Services/ImageProcessingService.cs
// ...
private Rect DetectGameWindow(Mat image)
{
// ...
// 꼭짓점 좌표를 이용하여 게임 화면 영역 계산
if (matchPoints.Count == 4)
{
// ...
logger.Info("미니맵 영역 검출 성공"); // 로그 출력
return new OpenCvSharp.Rect(topLeft.X, topLeft.Y, width, height);
}
else
{
logger.Warn("미니맵 영역 검출 실패"); // 로그 출력
return new OpenCvSharp.Rect(); // 빈 Rect 객체 반환
}
}
// ...
코드 설명
DetectGameWindow() 메서드:if (matchPoints.Count == 4): 미니맵 영역 검출에 성공하면 logger.Info("미니맵 영역 검출 성공");을 실행하여 로그를 출력합니다.else: 미니맵 영역 검출에 실패하면 logger.Warn("미니맵 영역 검출 실패");를 실행하여 로그를 출력합니다.시도
UpdateFrame() 메서드 안에 아무런 코드도 없다면, ImageProcessingService.ProcessImage() 메서드가 호출되지 않아 미니맵 영역 검출이 이루어지지 않고, 따라서 "미니맵 영역 검출 성공" 또는 "미니맵 영역 검출 실패" 메시지가 출력되지 않는다.
UpdateFrame() 메서드는 웹캠에서 프레임을 가져와 이미지 처리를 수행하고, CameraImage 속성을 업데이트하는 역할을 합니다. 웹캠 기능을 제외했더라도, ImageProcessingService.ProcessImage() 메서드를 호출하여 미니맵 영역을 검출하고 GameWindowRect 속성을 업데이트해야 합니다.
MainWindowViewModel.cs 코드 수정
// ViewModels/MainWindowViewModel.cs
// ... (생략) ...
// 프레임 업데이트 메서드
private async void UpdateFrame(object? sender, EventArgs e)
{
// 테스트 이미지를 사용하여 이미지 처리를 수행합니다.
Mat frame = Cv2.ImRead("Resources/minimap_template.png", ImreadModes.Color); // 테스트 이미지 로드
if (!frame.Empty())
{
// 이미지 처리 및 게임 정보 업데이트
await Task.Run(() =>
{
gameInfo = imageProcessingService.ProcessImage(frame.Clone());
GameWindowRect = gameInfo.GameWindowRect;
IsMinimapDetected = gameInfo.IsMinimapDetected;
});
}
}
// ... (생략) ...
코드 설명
Mat frame = Cv2.ImRead("Resources/minimap_template.png", ImreadModes.Color);: Resources 폴더에 있는 minimap_template.png 파일을 컬러 이미지로 로드합니다.if (!frame.Empty()): 이미지가 제대로 로드되었는지 확인합니다.await Task.Run(() => ...);: Task.Run() 메서드를 사용하여 이미지 처리 및 게임 정보 업데이트 작업을 별도 스레드에서 비동기적으로 실행합니다.gameInfo = imageProcessingService.ProcessImage(frame.Clone());: ImageProcessingService의 ProcessImage() 메서드를 호출하여 이미지 처리를 수행하고, 결과를 gameInfo 객체에 저장합니다.GameWindowRect = gameInfo.GameWindowRect;: gameInfo 객체에서 게임 화면 영역 정보를 가져와 GameWindowRect 속성에 설정합니다.IsMinimapDetected = gameInfo.IsMinimapDetected;: gameInfo 객체에서 미니맵 인식 여부를 가져와 IsMinimapDetected 속성에 설정합니다.시도
DetectGameWindow() 메서드 안에 로그 출력 코드를 추가했지만, 여전히 로그가 출력되지 않는 문제가 발생.
ImageProcessingService.cs 코드를 살펴보니, 특징점 매칭 결과를 저장하는 matchPoints 리스트가 템플릿 이미지마다 초기화되고 있는 것을 발견했다.
즉, foreach 루프 안에서 matchPoints 리스트를 매번 새로 생성하고 있기 때문에, 두 개의 템플릿 이미지에서 매칭된 특징점을 모두 저장할 수 없다. 따라서 matchPoints.Count는 항상 2 이하가 되고, if (matchPoints.Count == 4) 조건문이 실행되지 않아 로그가 출력되지 않는다.
해결 방법
matchPoints 리스트를 foreach 루프 밖에서 한 번만 생성하고, 루프 안에서는 matchPoints 리스트에 특징점을 추가하는 방식으로 코드를 수정해야 한다.
// Services/ImageProcessingService.cs
// ...
private Rect DetectGameWindow(Mat image)
{
// 템플릿 이미지 파일 경로
string[] templatePaths = { "Resources/top_left.png", "Resources/bottom_right.png" };
// 각 템플릿 이미지에 대한 매칭 결과를 저장할 리스트
var matchPoints = new List<Point2f>(); // foreach 루프 밖에서 리스트 생성
// 각 템플릿 이미지에 대해 특징점 매칭 수행
foreach (var templatePath in templatePaths)
{
// ... (생략) ...
// 매칭 결과를 이용하여 템플릿 위치 계산
if (goodMatches.Count >= 4) // 최소 4개의 매칭점 필요
{
// ... (생략) ...
matchPoints.AddRange(imageCorners); // matchPoints 리스트에 특징점 추가
}
}
// 꼭짓점 좌표를 이용하여 게임 화면 영역 계산
if (matchPoints.Count == 4) // matchPoints 리스트의 크기가 4인지 확인
{
// ... (생략) ...
logger.Debug("미니맵 영역 검출 성공"); // 로그 출력
return new OpenCvSharp.Rect(topLeft.X, topLeft.Y, width, height);
}
else
{
logger.Debug("미니맵 영역 검출 실패"); // 로그 출력
return new OpenCvSharp.Rect(); // 빈 Rect 객체 반환
}
}
// ...
코드 설명
var matchPoints = new List<Point2f>();: foreach 루프 밖에서 matchPoints 리스트를 한 번만 생성합니다.matchPoints.AddRange(imageCorners);: foreach 루프 안에서 matchPoints 리스트에 특징점을 추가합니다.if (matchPoints.Count == 4): matchPoints 리스트의 크기가 4인지 확인합니다. 즉, 두 개의 템플릿 이미지에서 매칭된 특징점이 모두 저장되었는지 확인합니다.이렇게 수정하면 두 개의 템플릿 이미지에서 매칭된 특징점을 모두 matchPoints 리스트에 저장하고, 미니맵 영역 검출 결과에 따라 로그가 올바르게 출력될 수 있도록 수정된다.
UpdateFrame() 메서드 안의 코드를 제거하거나 주석 처리.
rivate async void UpdateFrame(object? sender, EventArgs e)
{
// 테스트 이미지를 사용하여 이미지 처리를 수행합니다.
Mat frame = Cv2.ImRead("Resources/minimap_template.png", ImreadModes.Color); // 테스트 이미지 로드
if (!frame.Empty())
{
// 이미지 처리 및 게임 정보 업데이트
await Task.Run(() =>
{
gameInfo = imageProcessingService.ProcessImage(frame.Clone());
GameWindowRect = gameInfo.GameWindowRect;
IsMinimapDetected = gameInfo.IsMinimapDetected;
});
}
}
UpdateFrame() 메서드는 웹캠에서 프레임을 가져와 이미지 처리를 수행하고, CameraImage 속성을 업데이트하는 역할을 합니다.
현재 웹캠 기능을 제외했으므로, UpdateFrame() 메서드 자체는 필요하지 않습니다. 따라서, 메서드 전체를 삭제하거나 주석 처리해도 괜찮습니다.
하지만, ImageProcessingService.ProcessImage() 메서드를 호출하여 미니맵 영역을 검출하고 GameWindowRect 속성과 IsMinimapDetected 속성을 업데이트하는 부분은 필요합니다. 따라서, 이 부분은 UpdateFrame() 메서드에서 제거하고, MainWindowViewModel의 다른 메서드 (예: 생성자 또는 StartGame() 메서드)에서 호출해야 합니다.
수정된 MainWindowViewModel.cs 코드
// ViewModels/MainWindowViewModel.cs
// ... (생략) ...
public MainWindowViewModel()
{
// ... (생략) ...
// 테스트 이미지를 사용하여 이미지 처리를 수행합니다.
Mat frame = Cv2.ImRead("Resources/minimap_template.png", ImreadModes.Color); // 테스트 이미지 로드
if (!frame.Empty())
{
// 이미지 처리 및 게임 정보 업데이트
Task.Run(() =>
{
gameInfo = imageProcessingService.ProcessImage(frame.Clone());
GameWindowRect = gameInfo.GameWindowRect;
IsMinimapDetected = gameInfo.IsMinimapDetected;
});
}
}
// ... (생략) ...
// 프레임 업데이트 메서드 (제거 또는 주석 처리)
// private async void UpdateFrame(object? sender, EventArgs e)
// {
// // ...
// }
// ... (생략) ...
코드 설명
UpdateFrame() 메서드를 제거하거나 주석 처리했습니다.MainWindowViewModel 생성자에서 테스트 이미지를 사용하여 이미지 처리를 수행하고, 게임 정보를 업데이트하는 코드를 추가했습니다.이렇게 수정하면 웹캠 기능 없이도 ImageProcessingService.ProcessImage() 메서드가 호출되어 미니맵 영역 검출이 이루어지고, GameWindowRect 속성과 IsMinimapDetected 속성이 업데이트됩니다.
해결
UpdateFrame() 메서드를 제거하고 MainWindowViewModel.cs 코드를 수정하니까 콘솔창에 메시지가 뜨지 않던 오류가 해결됐다. 그리고 디버깅으로 메서드 내부도 확인이 가능해졌다.
콘솔창에 아래와 같은 오류가 메시지를 확인할 수 있었다.
OpenCV 예외 발생: The operation is neither 'array op array' (where arrays have the same size and the same number of channels), nor 'array op scalar', nor 'scalar op array'
OpenCV 예외 발생: The operation is neither 'array op array' (where arrays have the same size and the same number of channels), nor 'array op scalar', nor 'scalar op array'
2025-02-01 19:18:02.1649|ERROR|OpenCvSharpProjects.Services.ImageProcessingService|OpenCV 예외 발생
2025-02-01 19:18:02.1649|ERROR|OpenCvSharpProjects.Services.ImageProcessingService|OpenCV 예외 발생

콘솔창에 아래와 같은 오류가 메시지를 확인할 수 있었다.
OpenCV 예외 발생: The operation is neither 'array op array' (where arrays have the same size and the same number of channels), nor 'array op scalar', nor 'scalar op array'
OpenCV 예외 발생: The operation is neither 'array op array' (where arrays have the same size and the same number of channels), nor 'array op scalar', nor 'scalar op array'
2025-02-01 19:18:02.1649|ERROR|OpenCvSharpProjects.Services.ImageProcessingService|OpenCV 예외 발생
2025-02-01 19:18:02.1649|ERROR|OpenCvSharpProjects.Services.ImageProcessingService|OpenCV 예외 발생
위 오류는 OpenCV 함수에서 입력 배열의 크기 또는 채널 수가 일치하지 않을 때 발생하는 일반적인 오류입니다.
이 오류는 Cv2.AddWeighted() 함수에서 발생하는 것으로 보입니다. Cv2.AddWeighted() 함수는 두 개의 이미지를 가중치를 적용하여 합성하는 함수인데, 두 이미지의 크기와 채널 수가 일치해야 합니다.
오류 원인 분석
Cv2.AddWeighted(testImage, 1.2, new Mat(), 0, 1.5, testImage); 코드를 보면, testImage는 minimap_template.png 파일을 로드한 이미지입니다. 이 이미지는 컬러 이미지이므로 3개의 채널 (B, G, R)을 가지고 있습니다.new Mat()는 빈 이미지를 생성하는데, 이 이미지는 채널 수가 지정되지 않았으므로 기본적으로 1개의 채널을 갖습니다.Cv2.AddWeighted() 함수를 호출할 때, 3개의 채널을 가진 이미지와 1개의 채널을 가진 이미지를 합성하려고 하기 때문에 오류가 발생합니다.해결 방법
Cv2.AddWeighted() 함수를 호출할 때, 두 이미지의 채널 수를 일치시켜야 합니다.
빈 이미지를 생성할 때 채널 수를 3으로 지정하거나, testImage를 그레이스케일 이미지로 변환하여 채널 수를 1로 만들 수 있습니다.
1. 빈 이미지 채널 수 변경
// 밝기/대비 조절 (예: 밝기 1.2배, 대비 1.5배)
Cv2.AddWeighted(testImage, 1.2, new Mat(testImage.Size(), testImage.Type()), 0, 1.5, testImage); // 빈 이미지의 크기와 타입을 testImage와 동일하게 설정
new Mat(testImage.Size(), testImage.Type()) 코드는 testImage와 크기와 타입이 같은 빈 이미지를 생성합니다. 이렇게 하면 두 이미지의 채널 수가 모두 3이 되어 오류가 해결됩니다.
2. testImage를 그레이스케일로 변환
// 밝기/대비 조절 (예: 밝기 1.2배, 대비 1.5배)
Mat grayTestImage = new Mat();
Cv2.CvtColor(testImage, grayTestImage, ColorConversionCodes.BGR2GRAY); // testImage를 그레이스케일로 변환
Cv2.AddWeighted(grayTestImage, 1.2, new Mat(), 0, 1.5, grayTestImage); // 그레이스케일 이미지 사용
Cv2.CvtColor() 함수를 사용하여 testImage를 그레이스케일 이미지 (grayTestImage)로 변환합니다. 이렇게 하면 두 이미지의 채널 수가 모두 1이 되어 오류가 해결됩니다.
어떤 방법을 선택해야 할까?
두 가지 방법 모두 오류를 해결할 수 있지만, 1번 방법이 더 간단하고 효율적입니다. 2번 방법은 이미지를 변환하는 과정이 추가되므로, 성능이 다소 떨어질 수 있습니다.
추가 정보
Cv2.AddWeighted() 함수는 두 이미지의 크기와 타입이 같아야 합니다.Mat 클래스는 OpenCV에서 이미지를 표현하는 클래스입니다.Mat.Size() 메서드는 이미지의 크기를 반환합니다.Mat.Type() 메서드는 이미지의 타입을 반환합니다.Cv2.CvtColor() 함수는 이미지의 색상 공간을 변환하는 함수입니다.ColorConversionCodes 열거형은 색상 공간 변환 코드를 정의합니다.
matchPoints.AddRange(imageCorners); 부분은 잘 수정되었지만, 여전히 "미니맵 영역 검출 실패" 로그가 출력되고 오류가 발생하는 것 같습니다.
문제 원인 분석
ImageProcessingService의 ProcessImage() 메서드에서 testImage 변수에 minimap_template.png 이미지를 로드하고 있습니다. 이 이미지는 미니맵의 테두리 부분만 캡처한 이미지이기 때문에, 특징점 매칭을 수행할 때 image와 template 이미지가 크게 다릅니다.
즉, image는 미니맵을 포함한 전체 게임 화면이고, template은 미니맵 테두리 부분만 있는 이미지이므로, 특징점 매칭이 제대로 이루어지지 않아 matchPoints 리스트에 4개의 꼭짓점 좌표가 저장되지 않는 것입니다.
해결 방법
DetectGameWindow() 메서드에서 특징점 매칭을 수행하기 전에, 입력 이미지 (image)에서 미니맵 영역을 추출해야 합니다. 미니맵 영역을 추출하려면 먼저 미니맵의 대략적인 위치를 알아야 합니다.
1. 미니맵 템플릿 매칭
미니맵 템플릿 이미지 (minimap_template.png)를 사용하여 템플릿 매칭을 수행하고, 미니맵의 대략적인 위치를 찾습니다.
// Services/ImageProcessingService.cs
// ...
private Rect DetectGameWindow(Mat image)
{
// ...
// 미니맵 템플릿 매칭
Mat result = new Mat();
Cv2.MatchTemplate(image, template, result, TemplateMatchModes.CCoeffNormed);
double minVal, maxVal;
Point minLoc, maxLoc;
Cv2.MinMaxLoc(result, out minVal, out maxVal, out minLoc, out maxLoc);
if (maxVal > 0.8) // 템플릿 매칭 성공
{
// 미니맵 영역 추출
OpenCvSharp.Rect minimapRect = new OpenCvSharp.Rect(maxLoc.X, maxLoc.Y, template.Width, template.Height);
Mat minimapImage = new Mat(image, minimapRect);
// 특징점 검출 및 기술 (minimapImage 사용)
KeyPoint[] keypoints1, keypoints2;
Mat descriptors1 = new Mat(), descriptors2 = new Mat();
orb.DetectAndCompute(minimapImage, null, out keypoints1, descriptors1);
orb.DetectAndCompute(template, null, out keypoints2, descriptors2);
// ... (나머지 특징점 매칭 로직) ...
}
else
{
logger.Debug("미니맵 템플릿 매칭 실패"); // 로그 출력
return new OpenCvSharp.Rect(); // 빈 Rect 객체 반환
}
// ...
}
코드 설명
Cv2.MatchTemplate(): 템플릿 매칭을 수행합니다.Cv2.MinMaxLoc(): 매칭 결과에서 최댓값과 그 위치를 찾습니다.if (maxVal > 0.8): 템플릿 매칭 성공 시 미니맵 영역을 추출합니다.OpenCvSharp.Rect minimapRect = new OpenCvSharp.Rect(maxLoc.X, maxLoc.Y, template.Width, template.Height);: 템플릿 매칭 결과를 기반으로 미니맵 영역을 계산합니다.Mat minimapImage = new Mat(image, minimapRect);: 미니맵 영역을 추출합니다.orb.DetectAndCompute(minimapImage, null, out keypoints1, descriptors1);: 추출된 미니맵 영역에서 특징점을 검출합니다.2. 특징점 매칭
추출된 미니맵 영역에서 특징점 매칭을 수행합니다. 이때, 템플릿 이미지는 미니맵 테두리 이미지를 그대로 사용합니다.
주의 사항
이렇게 템플릿 매칭과 특징점 매칭을 함께 사용하면 미니맵 인식률을 높일 수 있습니다.
testImage를 전달하도록 해놓은 상태다. 그래도 여전히 Index was outside the bounds of the array 오류가 발생하고있다.
코드 설명
DetectGameWindow() 메서드에서 템플릿 매칭을 수행하여 미니맵의 대략적인 위치를 찾습니다.이제 미니맵 템플릿 매칭과 특징점 매칭을 함께 사용하여 미니맵 영역을 검출한다.

OpenCV 예외 발생: (depth == CV_8U || depth == CV_32F) && type == _templ.type() && _img.dims() <= 2
OpenCV 예외 발생: (depth == CV_8U || depth == CV_32F) && type == _templ.type() && _img.dims() <= 2
2025-02-01 20:02:54.0987|ERROR|OpenCvSharpProjects.Services.ImageProcessingService|OpenCV 예외 발생
2025-02-01 20:02:54.0987|ERROR|OpenCvSharpProjects.Services.ImageProcessingService|OpenCV 예외 발생
OpenCV 예외 발생: (depth == CV_8U || depth == CV_32F) && type == _templ.type() && _img.dims() <= 2 오류는 Cv2.MatchTemplate() 함수를 호출할 때 입력 이미지와 템플릿 이미지의 깊이(depth) 또는 타입(type)이 일치하지 않거나, 입력 이미지의 차원(dims)이 2보다 클 때 발생합니다.
현재 코드에서 이 오류가 발생하는 원인은 DetectGameWindow() 메서드에서 Cv2.MatchTemplate() 함수를 호출할 때, image (전체 화면 이미지)와 template (미니맵 템플릿 이미지)의 타입이 일치하지 않기 때문입니다.
image는 Cv2.ImRead() 함수를 사용하여 컬러 이미지 (BGR) 로 로드됩니다.template은 Cv2.ImRead(templatePath, ImreadModes.Grayscale)를 사용하여 그레이스케일 이미지로 로드됩니다.따라서 Cv2.MatchTemplate() 함수를 호출하기 전에 image를 그레이스케일 이미지로 변환해야 합니다.
수정된 DetectGameWindow() 메서드
private Rect DetectGameWindow(Mat image)
{
// ...
// 미니맵 템플릿 매칭
Mat result = new Mat();
// image를 그레이스케일로 변환합니다.
Mat grayImage = new Mat();
Cv2.CvtColor(image, grayImage, ColorConversionCodes.BGR2GRAY);
Cv2.MatchTemplate(grayImage, template, result, TemplateMatchModes.CCoeffNormed); // grayImage를 사용합니다.
// ...
}
코드 설명
Mat grayImage = new Mat();: 그레이스케일 이미지를 저장할 Mat 객체를 생성합니다.Cv2.CvtColor(image, grayImage, ColorConversionCodes.BGR2GRAY);: image를 그레이스케일 이미지로 변환하여 grayImage에 저장합니다.Cv2.MatchTemplate(grayImage, template, result, TemplateMatchModes.CCoeffNormed);: Cv2.MatchTemplate() 함수를 호출할 때 grayImage를 사용합니다.이렇게 수정하면 image와 template의 타입이 일치하게 되어 Cv2.MatchTemplate() 함수에서 발생하는 오류를 해결할 수 있습니다.
템플릿 이미지 개수를 늘리면 미니맵 인식률을 높이는 데 도움이 될 수 있습니다.
따라서 템플릿 이미지 개수를 늘리기로 했다.
특히 현재 미니맵의 크기와 비율이 맵에 따라 다르다는 점을 고려하면 더욱 효과적일 수 있습니다.
여러 개의 템플릿 이미지를 사용하는 방법
Resources 폴더에 저장합니다. 이미지 이름은 minimap_template_1.png, minimap_template_2.png 등과 같이 구분하기 쉽도록 짓습니다.ImageProcessingService 수정: ImageProcessingService 클래스의 DetectGameWindow() 메서드를 수정하여 여러 개의 템플릿 이미지를 사용하도록 변경합니다.// Services/ImageProcessingService.cs
// ...
private Rect DetectGameWindow(Mat image)
{
// 템플릿 이미지 파일 경로
string[] templatePaths = { "Resources/minimap_template_1.png", "Resources/minimap_template_2.png", ... }; // 템플릿 이미지 파일 경로 배열
// 각 템플릿 이미지에 대한 매칭 결과를 저장할 리스트
var matchPoints = new List<Point2f>();
// 각 템플릿 이미지에 대해 특징점 매칭 수행
foreach (var templatePath in templatePaths)
{
// ... (템플릿 매칭 및 특징점 매칭 로직) ...
}
// ... (나머지 코드) ...
}
템플릿 이미지 개수 늘리기의 장점
주의 사항
미니맵이 인식되면 MainWindow.xaml에서 Rectangle 컨트롤을 사용하여 빨간색 테두리로 표시되도록 구현되어 있다. 그러나 현재 미니맵이 인식되지 않아 빨간색 테두리가 표시되지 않는 것으로 보인다.
미니맵 인식 문제 해결
미니맵 인식 문제를 해결하기 위해 다음과 같은 방법들을 시도해 볼 수 있다.
1. 특징점 매칭 개선
orb.DetectAndCompute() 메서드를 사용하여 특징점을 검출할 때, nfeatures 파라미터를 늘려서 더 많은 특징점을 검출해 볼 수 있습니다.BFMatcher 대신 FlannBasedMatcher를 사용하여 매칭 속도를 높일 수 있습니다.2. 이미지 전처리
3. 템플릿 이미지 개선
4. 디버깅
5. 추가적인 방법
어떤 방법부터 시도해볼까?
추가적인 정보
특징점 매칭 결과를 눈으로 확인할 수 있도록 기능을 추가했다.
