(3) MVVM 패턴 적용하여 코드 개선_3

00·2025년 1월 21일

개요

폴더 구조 변경

OpenCvSharpProjects
├── Models
│   └── GameInfo.cs
├── ViewModels
│   └── MainWindowViewModel.cs
├── Views
│   ├── MainWindow.xaml
│      └── MainWindow.xaml.cs
├── Services
│   ├── WebcamService.cs
│   ├── ImageProcessingService.cs
│   └── KeyboardControlService.cs
├── Resources
│   ├── top_left.png
│   └── bottom_right.png
└── Converters
    └── BooleanToVisibilityConverter.cs

기존 폴더 구조 개선

  • Services 폴더 추가

    • 이미지 처리, 네트워킹, 데이터베이스 접근 등과 같이 여러 곳에서 공통적으로 사용되는 기능을 모아 관리하는 것이 좋다.
    • Services 폴더를 추가하여 해당 기능들을 별도의 클래스로 분리하면 코드의 재사용성을 높이고 유지보수를 용이하게 만들 수 있다.
  • Converters 폴더의 위치 변경

    • 따로 떼어 놓는 경우:

      • 장점: 컨버터를 여러 View에서 공유하기 용이하고, 코드 중복을 방지할 수 있습니다.
      • 단점: View와 컨버터가 물리적으로 떨어져 있어 코드 관리가 다소 불편할 수 있습니다.
    • Views 폴더에 넣는 경우:

      • 장점: View와 관련된 컨버터를 View와 가까운 곳에 위치시켜 코드 관리가 용이합니다.(View와 밀접한 관련이 있는 값 변환 로직을 관리하기 용이)
      • 단점: 컨버터가 여러 View에서 공유되는 경우, Views 폴더에 넣으면 코드 중복이 발생할 수 있습니다.
  • 컨버터가 특정 View에서만 사용되는 경우: Views 폴더 안에 넣습니다.
  • 컨버터가 여러 View에서 공유되는 경우: 별도의 Converters 폴더에 넣습니다.

결론:

현재 프로젝트에서는 BooleanToVisibilityConverter가 여러 View에서 사용될 가능성이 있으므로, Converters 폴더를 따로 떼어 놓기로 했습니다.

추가 고려 사항

  • MLModels 폴더: 머신러닝 모델을 사용하는 경우, 별도의 MLModels 폴더를 생성하여 관리하는 것이 좋다.
  • Utils 폴더: 유틸리티 함수나 확장 메서드를 모아 관리하는 Utils 폴더를 추가할 수 있다.

Services 폴더

Services 폴더에 추가해야 할 서비스 종류

Services 폴더에는 애플리케이션의 핵심 로직을 담당하는 다양한 서비스 클래스를 추가할 수 있다. 특히, 자동 사냥 프로그램의 경우 다음과 같은 서비스 클래스를 고려해 볼 수 있다.

  • 이미지 처리 서비스: OpenCVSharp를 이용하여 이미지 캡처, 전처리, 템플릿 매칭 등 이미지 관련 작업을 수행합니다.
  • 입력 시뮬레이션 서비스: 키보드, 마우스 입력을 시뮬레이션하여 게임 조작을 자동화합니다.
  • 네트워킹 서비스: 게임 서버와의 통신이 필요한 경우, 해당 작업을 처리하는 서비스를 구현할 수 있습니다.
  • 데이터베이스 서비스: 게임 데이터를 저장하고 관리하기 위한 데이터베이스와의 연동을 처리합니다.
  • 설정 서비스: 사용자 설정을 저장하고 관리합니다.
  • 로그 서비스: 프로그램 실행 중 발생하는 이벤트를 기록합니다.

예시:

public class ImageProcessingService
{
    public Mat CaptureScreen()
    {
        // 화면 캡처 로직
    }

    public bool FindTemplate(Mat image, Mat template)
    {
        // 템플릿 매칭 로직
    }
}

Converters 폴더

Converters 폴더에 추가할 수 있는 클래스 종류

Converters 폴더에는 View와 ViewModel 사이에서 데이터 형식을 변환하거나, 특정 조건에 따라 UI 요소를 변경하는 역할을 수행하는 클래스를 추가한다.

  • 값 변환 컨버터:
    • bool 값을 Visibility로 변환
    • 숫자 값을 문자열로 변환
  • 복합 값 변환 컨버터:
    • 여러 개의 값을 조합하여 새로운 값을 생성
  • 컬렉션 변환 컨버터:
    • 컬렉션을 특정 형식으로 변환

예시

public class BoolToVisibilityConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return (bool)value ? Visibility.Visible : Visibility.Collapsed;
    }
    // ...
}

ViewModels 폴더 vs Services 폴더

ViewModels 폴더와 Services 폴더는 비슷해 보이지만, 각각 다른 역할을 수행하며 분리하는 것이 일반적입니다.

ViewModels 폴더UI와 데이터를 연결하는 역할을 합니다. 즉, View에서 사용되는 데이터를 관리하고, 사용자의 입력에 대한 반응을 처리하는 로직을 담고 있습니다. ViewModel은 다음과 같은 역할을 수행합니다.

  • View와 Model 사이의 중개자 역할: View에서 필요한 데이터를 Model에서 가져와서 제공하고, View에서 발생한 이벤트를 Model에 전달합니다.
  • 데이터 바인딩: View와 데이터를 연결하여 UI를 자동으로 업데이트합니다.
  • 명령 처리: 사용자의 입력(버튼 클릭 등)에 대한 반응을 처리합니다.

Services 폴더특정 기능을 제공하는 서비스 클래스를 모아놓는 곳입니다. ViewModel에서 필요한 기능을 제공하는데, ViewModel의 역할을 명확하게 하기 위해 분리합니다. Services 폴더에 들어갈 수 있는 클래스의 예시는 다음과 같습니다.

  • 데이터베이스 서비스: 데이터베이스와의 연결 및 데이터 CRUD 작업을 처리합니다.
  • 네트워킹 서비스: 서버와의 통신을 처리합니다.
  • 이미지 처리 서비스: 이미지 처리 관련 로직을 처리합니다.
  • 파일 시스템 서비스: 파일 시스템 관련 작업을 처리합니다.

ViewModels와 Services를 분리하는 이유

  • 역할 분담 명확화: 각 폴더의 역할이 명확해져 코드 관리가 용이해집니다.
  • 재사용성 증가: Services 폴더에 있는 서비스 클래스는 다른 ViewModel에서도 재사용할 수 있습니다.
  • 테스트 용이성: 각 클래스의 역할이 명확하기 때문에 단위 테스트를 작성하기 쉽습니다.
  • 유지보수성 향상: 코드 변경 시 영향 범위를 줄일 수 있습니다.

예시

  • ViewModel: 사용자의 입력을 받아 서비스를 호출하고, 결과를 View에 반영합니다.
  • Service: 실제 데이터베이스에 접근하여 데이터를 저장하거나 조회하는 작업을 수행합니다.

카메라로 화면을 인식하는 기능 임시 제거

카메라로 화면을 인식하는 기능을 나중에 추가하기로 했습니다. 처음부터 모든 기능을 완벽하게 구현하려고 하기보다는, 단계별로 기능을 추가하며 개발하는 것이 더 효율적이라고 판단내렸기 때문입니다.

이렇게 지금 이 기능을 뺴버리고 나중에 다시 넣기로 결정한 이유는 다음과 같습니다.

  • MVVM 패턴의 유연성: MVVM 패턴은 UI, 비즈니스 로직, 데이터를 분리하여 관리하기 때문에, 각 부분을 독립적으로 개발하고 수정할 수 있습니다.
  • 모듈화: 각 기능을 별도의 클래스나 모듈로 구현하면, 다른 기능에 영향을 주지 않고 특정 기능만 변경할 수 있습니다.
  • 단계별 테스트: 기능별로 단위 테스트를 진행하면서 안정적인 시스템을 구축할 수 있습니다.

즉, 잘 설계된 MVVM 구조라면, 카메라 인식 기능을 추가하기 위해 기존 코드를 크게 변경할 필요가 없기 때문입니다. 나중에 추가할 때 아래와 같은 작업이 필요할 것으로 생각됩니다.

  • 새로운 ViewModel 추가: 카메라 관련 로직을 담당하는 새로운 ViewModel을 추가합니다.
  • 기존 ViewModel과 연동: 필요한 경우 기존 ViewModel과 데이터를 주고받도록 연결합니다.
  • View 수정: XAML에서 새로운 ViewModel과 연결하고, 카메라 미리보기를 위한 UI를 추가합니다.

구체적으로

  • OpenCVSharp 활용: OpenCVSharp 라이브러리를 사용하여 카메라를 제어하고 이미지를 처리할 수 있습니다.
  • ViewModel에 추가: CameraViewModel에 카메라 관련 속성과 메서드를 추가합니다. (예: CapturedImage, StartCaptureCommand)
  • View에 연결: XAML에서 Image 컨트롤을 통해 캡처된 이미지를 표시하고, 버튼을 통해 캡처 기능을 제어합니다.
  • 이미지 처리: 캡처된 이미지를 전처리하고, 템플릿 매칭 등을 통해 원하는 정보를 추출합니다.

폴더별 역할 설명

1. Models 폴더

  • 게임 관련 데이터를 저장하는 Model 클래스를 저장하는 폴더입니다.
  • 역할: 게임 내 객체나 데이터를 나타내는 Model 클래스를 정의하는 곳입니다.
  • 예시: GameInfo.cs 파일에는 게임 캐릭터의 정보, 몬스터 정보, 아이템 정보 등을 나타내는 클래스를 정의할 수 있습니다.
  • 예시 코드:
public class GameInfo
{
    public int PlayerLevel { get; set; }
    public int Gold { get; set; }
    // ... 기타 게임 정보
}

2. ViewModels 폴더

  • 프로그램 로직을 담당하는 ViewModel 클래스를 저장하는 폴더입니다.
  • 역할: View와 Model 사이의 데이터 바인딩을 담당하고, 사용자의 입력에 대한 반응을 처리하는 클래스를 정의하는 곳입니다.
  • 예시: MainWindowViewModel.cs 파일에는 메인 화면에서 사용되는 데이터와 명령을 정의합니다.
  • 예시 코드:
public class MainWindowViewModel : ObservableObject
{
    public ICommand StartCaptureCommand { get; }
    // ... 기타 명령 및 속성
}

3. Views 폴더

  • 사용자 인터페이스를 담당하는 View (WPF 창) 파일을 저장하는 폴더입니다.

  • 역할: 사용자 인터페이스를 정의하는 XAML 파일을 저장하는 곳입니다.

  • 예시: MainWindow.xaml 파일에는 메인 화면의 레이아웃과 디자인을 정의합니다.

4. Converters 폴더

  • 데이터 바인딩에 사용되는 컨버터 클래스를 저장하는 폴더입니다.

  • 역할: ViewModel의 데이터를 View에서 사용하기 위한 형식 변환을 수행하는 클래스를 정의하는 곳입니다. (데이터 바인딩에 사용되는 컨버터 클래스를 저장하는 폴더)

  • 예시: RectToVisibilityConverter.cs 파일에는 Rect 타입의 데이터를 Visibility 타입으로 변환하는 로직을 정의할 수 있습니다.

5. Services 폴더

  • 웹캠 캡처, 이미지 처리, 키보드 제어 등의 기능을 담당하는 서비스 클래스를 저장하는 폴더입니다.

  • 각 서비스는 특정 기능에 집중하여 코드의 응집도를 높이고 재사용성을 향상시킵니다.

  • 역할: 특정 기능을 제공하는 서비스 클래스를 정의하는 곳입니다.

  • 예시: ImageProcessingService.cs 파일에는 이미지 캡처, 처리, 분석 등의 기능을 제공하는 메서드를 정의합니다.

각 서비스 클래스의 역할

  • WebcamService: 웹캠 캡처 및 제어 기능을 제공합니다.
    • 웹캠 장치를 열고 닫습니다.
    • 웹캠에서 프레임을 캡처합니다.
    • 캡처된 프레임을 Mat 객체로 변환합니다.
  • ImageProcessingService: 이미지 처리, 객체 인식, 특징점 매칭 등의 기능을 제공합니다.
    • 템플릿 매칭, 특징점 매칭 등을 사용하여 객체를 인식합니다.
    • 이미지 전처리 (예: 밝기 조정, 노이즈 제거)를 수행합니다.
    • 인식 결과를 MainViewModel에 전달합니다.
  • KeyboardControlService: 키보드 입력 제어 기능을 제공합니다.
    • System.Windows.Forms.SendKeys 클래스 또는 InputSimulator 라이브러리를 사용하여 키보드 입력을 시뮬레이션합니다.
    • MainViewModel에서 전달받은 정보에 따라 키보드 입력을 제어합니다.

6. Resources 폴더

  • 역할: 이미지, 사운드 등의 리소스 파일을 저장하는 곳입니다.

.NET Community Toolkit 활용

  • ObservableObject: MainViewModel 클래스에서 ObservableObject 클래스를 상속받아 속성 변경 알림을 구현합니다.
  • RelayCommand: MainViewModel 클래스에서 RelayCommand 클래스를 사용하여 Command를 구현합니다.

각 폴더의 상호 작용

  • ViewModelModel의 데이터를 참조하고, View에 데이터를 제공합니다.
  • ViewViewModel의 속성에 바인딩되어 사용자의 입력을 ViewModel에 전달하고, ViewModel의 변경 사항을 반영합니다.
  • ServicesViewModel에서 호출되어 필요한 기능을 제공합니다.
  • 각 폴더에 맞는 클래스를 추가하여 기능을 확장할 수 있습니다.

예를 들어, 게임 캐릭터 정보를 관리하는 Character 클래스를 Models 폴더에 추가하고, 캐릭터 정보를 표시하는 기능을 ViewModel에 추가할 수 있습니다.


MainWindow.xaml vs MainWindow.xaml.cs

MainWindow.xaml.cs는 MainWindow.xaml 파일과는 별개의 파일입니다. 하지만 둘은 긴밀하게 연결되어 함께 작동합니다.

MainWindow.xaml.csMainWindow.xaml코드 비하인드 (Code-Behind) 파일입니다. 즉, MainWindow.xaml에서 정의된 UI 요소들과 상호 작용하는 C# 코드를 담고 있는 파일입니다.

WPF에서 XAML 파일과 코드 비하인드 파일은 부분 클래스 (Partial Class)를 사용하여 하나의 클래스로 연결됩니다.

  • MainWindow.xaml: UI 요소를 XAML로 정의합니다.
  • MainWindow.xaml.cs: MainWindow.xaml의 UI 요소와 상호 작용하는 C# 코드를 작성합니다.

이 두 파일은 컴파일될 때 하나의 MainWindow 클래스로 합쳐집니다.

MainWindow.xaml 파일은 XAML(eXtensible Application Markup Language)을 사용하여 사용자 인터페이스(UI)를 정의하는 파일입니다. 즉, 버튼, 텍스트 상자, 이미지 등 화면에 보이는 요소들을 디자인하는 역할을 합니다.

MainWindow.xaml.cs 파일은 C# 코드로 작성된 파일로, MainWindow.xaml 파일에서 정의된 UI 요소와 상호 작용하는 코드를 작성하는 곳입니다. 즉, 사용자가 버튼을 클릭했을 때 어떤 동작을 할지, 데이터를 어떻게 표시할지 등의 로직을 구현합니다.

두 파일의 관계

  • X:Class 속성: MainWindow.xaml 파일의 최상위 요소에 x:Class 속성을 통해 MainWindow.xaml.cs 파일의 클래스를 지정합니다. 이를 통해 두 파일이 연결됩니다.
  • 코드 비하인드: MainWindow.xaml.cs 파일은 MainWindow.xaml 파일의 코드 비하인드 파일이라고 부릅니다. 즉, XAML에서 정의된 UI 요소에 대한 코드를 작성하는 곳입니다.
  • 이벤트 핸들러: 사용자가 UI 요소와 상호 작용할 때 발생하는 이벤트(버튼 클릭, 텍스트 변경 등)를 처리하는 함수를 MainWindow.xaml.cs 파일에 작성합니다.

간단히 말해서, MainWindow.xaml 파일은 건물의 설계도와 같고, MainWindow.xaml.cs 파일은 그 설계도를 바탕으로 건물을 짓는 과정에서 필요한 기능을 구현하는 코드라고 생각할 수 있습니다.

예를 들어, MainWindow.xaml 파일에서 버튼을 하나 정의하고, MainWindow.xaml.cs 파일에서 해당 버튼을 클릭했을 때 메시지 박스를 띄우는 코드를 작성할 수 있습니다.

정리:

  • MainWindow.xaml: UI 디자인
  • MainWindow.xaml.cs: UI와 상호 작용하는 로직 구현

쉽게 말해, MainWindow.xaml 파일은 XAML로 디자인된 UI를 정의하고, MainWindow.xaml.cs 파일은 C# 코드로 UI의 동작을 구현하는데, x:Class 속성을 통해 두 파일을 연결하여 하나의 완전한 윈도우를 만드는 것입니다.

<Window x:Class="OpenCvSharpProjects.MainWindow"
...>
  • Window: WPF에서 사용되는 기본적인 창을 의미합니다. 즉, 사용자가 보게 될 애플리케이션의 주 창을 나타냅니다.
  • x:Class: XAML에서 해당 윈도우와 연결될 C# 클래스를 지정하는 속성입니다.
  • OpenCvSharpProjects.MainWindow: 이 부분은 C# 프로젝트의 네임스페이스와 클래스 이름을 나타냅니다. 즉, MainWindow.xaml 파일은 OpenCvSharpProjects 네임스페이스에 속하는 MainWindow 클래스와 연결되어 있다는 의미입니다.

솔루션 탐색기에서 확인

솔루션 탐색기에서 MainWindow.xaml 파일 옆에 MainWindow.xaml.cs 파일이 함께 있는 것을 볼 수 있습니다. 이는 두 파일이 서로 연결되어 있음을 나타냅니다.

코드 비하인드 파일의 역할

  • 이벤트 처리: UI 요소에서 발생하는 이벤트 (예: 버튼 클릭)를 처리하는 코드를 작성합니다.
  • 데이터 바인딩: UI 요소와 ViewModel의 데이터를 연결하는 코드를 작성합니다.
  • UI 로직 구현: UI 요소의 동작을 제어하는 코드를 작성합니다.

중요:

  • MainWindow.xaml.cs 파일은 MainWindow.xaml과 같은 폴더에 있어야 합니다.
  • MainWindow.xaml.cs 파일의 클래스 이름은 MainWindow.xaml 파일의 x:Class 속성에 지정된 클래스 이름과 일치해야 합니다.

예시:

MainWindow.xaml:

<Window x:Class="OpenCvSharpProjects.Views.MainWindow" ...>
    <Grid>
        <Button Content="Click Me" Click="Button_Click" />
    </Grid>
</Window>

MainWindow.xaml.cs:

using System.Windows;

namespace OpenCvSharpProjects.Views
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            MessageBox.Show("Button Clicked!");
        }
    }
}

이처럼 MainWindow.xamlMainWindow.xaml.cs 파일은 서로 연결되어 WPF 창을 구성합니다.

데이터 바인딩

데이터 바인딩은 UI 요소와 데이터를 자동으로 연결하여, 데이터가 변경될 때 UI가 자동으로 업데이트되는 것을 의미합니다. WPF에서는 XAML에서 데이터 바인딩을 쉽게 설정할 수 있습니다.

데이터 바인딩의 장점:

  • 코드 줄이기: 코드에서 UI를 직접 조작하는 대신 데이터 바인딩을 사용하면 코드량을 줄일 수 있습니다.
  • 유지보수성 향상: 코드와 UI가 분리되어 있어 코드 변경 시 UI에도 영향을 미치는 부분을 최소화할 수 있습니다.
  • 데이터와 UI의 동기화: 데이터가 변경되면 UI가 자동으로 업데이트되어 항상 최신 상태를 유지합니다.

데이터 바인딩 예시:

<TextBlock Text="{Binding MyProperty}" />

위 코드는 TextBlock 컨트롤의 Text 속성을 ViewModel의 MyProperty 속성에 바인딩합니다. ViewModel에서 MyProperty 값이 변경되면 TextBlock의 Text가 자동으로 업데이트됩니다.

데이터 바인딩의 종류:

  • OneWay: 소스에서 대상으로 단방향으로 데이터를 전달합니다.
  • TwoWay: 소스와 대상 간에 양방향으로 데이터를 전달합니다.
  • OneTime: 처음 한 번만 데이터를 전달합니다.

데이터 바인딩의 사용법:

  • {Binding Path=propertyName}: 지정된 속성에 바인딩합니다.
  • {Binding ElementName=elementName, Path=propertyName}: 다른 요소의 속성에 바인딩합니다.
  • {Binding Converter={StaticResource converterName}}: 값 변환을 위한 컨버터를 사용합니다.

데이터 바인딩을 사용하기 위한 필수 요소:

  • DataContext: 각 UI 요소는 DataContext 속성을 통해 데이터 소스에 연결됩니다. 일반적으로 ViewModel이 DataContext로 설정됩니다.
  • INotifyPropertyChanged 인터페이스: ViewModel 클래스는 INotifyPropertyChanged 인터페이스를 구현하여 속성 값이 변경될 때 UI에 알려줍니다.

데이터 바인딩에서 Converter를 사용하는 이유

데이터 바인딩에서 Converter를 사용하는 이유는 View에서 바로 사용하기 어려운 형식의 데이터를 변환하여 사용하기 위해서입니다.

Converter가 필요한 이유

  • 데이터 형식 불일치: ViewModel에서 사용하는 데이터 형식과 View에서 표현해야 하는 형식이 다를 때, Converter를 통해 형식을 변환해줍니다. 예를 들어, bool 값을 Visibility로 변환하거나, 날짜 형식을 특정 문자열 형식으로 변환하는 경우가 있습니다.
  • 복합적인 로직: 단순한 데이터 변환뿐만 아니라, 여러 개의 데이터를 조합하여 새로운 값을 계산하거나, 특정 조건에 따라 값을 변경하는 등의 복잡한 로직을 구현할 수 있습니다.
  • UI 요소의 동적 변경: 데이터에 따라 UI 요소의 상태를 동적으로 변경해야 할 때 사용됩니다. 예를 들어, 특정 조건에 따라 버튼의 활성화 여부를 변경하거나, 리스트 박스의 항목을 필터링하는 경우에 사용됩니다.

Converter의 장점

  • 코드 재사용성: 한 번 작성한 Converter를 여러 곳에서 재사용할 수 있습니다.
  • 유지보수성: ViewModel과 View를 분리하여 각각의 역할에 집중할 수 있도록 도와줍니다.
  • 확장성: 다양한 종류의 Converter를 만들어 복잡한 데이터 변환 로직을 구현할 수 있습니다.

Converter의 종류

  • IValueConverter: 단일 값을 변환하는 데 사용됩니다.
  • IMultiValueConverter: 여러 개의 값을 조합하여 하나의 값으로 변환하는 데 사용됩니다.

예시

public class BoolToVisibilityConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return (bool)value ? Visibility.Visible : Visibility.Collapsed;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

위 예시는 bool 값을 Visibility 값으로 변환하는 Converter입니다. 이 Converter를 사용하면 ViewModel의 bool 속성을 View의 Visibility 속성에 바인딩하여, bool 값에 따라 UI 요소의 보이는 여부를 제어할 수 있습니다.

결론적으로 Converter는 MVVM 패턴에서 데이터 바인딩의 유연성을 높이고, 코드의 재사용성을 향상시키는 중요한 역할을 합니다.


1

GameInfo.cs

  • 목적: 게임 관련 정보를 저장하고 관리하는 클래스입니다.
  • 내용:
    • 플레이어의 현재 체력
    • 플레이어의 위치 (미니맵 상 좌표)
    • 체력이 낮은 상태인지 여부
    • 기타 게임 상태 정보 (필요에 따라 추가)
public class GameInfo
{
    public double PlayerHp { get; set; }
    public Point PlayerPosition { get; set; }
    public bool IsHpLow { get; set; }
    // ... 기타 필요한 프로퍼티 추가
}

MainWindow.xaml

  • 목적: 메인 윈도우의 UI를 정의합니다.
  • 내용:
    • 이미지 컨트롤: 게임 화면을 표시할 이미지 컨트롤
    • 미니맵 이미지 컨트롤: 미니맵을 표시할 이미지 컨트롤
    • 기타 UI 요소 (필요에 따라 추가)
<Window x:Class="OpenCvSharpProjects.Views.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2007/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2007/xaml"
        Title="MainWindow" Height="450" Width="800">
    <Grid>
        <Image x:Name="GameScreenImage" />
        <Image x:Name="MinimapImage" />
        </Grid>
</Window>

2 이미지 처리 및 UI 업데이트

ImageProcessingService.cs

using OpenCvSharp;
using System.Drawing;

namespace OpenCvSharpProjects.Services
{
    public class ImageProcessingService
    {
        private Mat _sourceImage;
        private Mat _miniMap;
        private Rect _playerHpRoi;
        private Rect _miniMapRoi;

        // ... 기타 필요한 변수들

        public void ProcessImage(Mat sourceImage)
        {
            _sourceImage = sourceImage;

            // 미니맵 영역 자르기
            OpenCvSharp.Rect miniMapRect = new Rect(_miniMapRoi.X, _miniMapRoi.Y, _miniMapRoi.Width, _miniMapRoi.Height);
            _miniMap = new Mat(_sourceImage, miniMapRect);

            // 플레이어 체력 게이지 영역 자르기
            OpenCvSharp.Rect hpRect = new Rect(_playerHpRoi.X, _playerHpRoi.Y, _playerHpRoi.Width, _playerHpRoi.Height);
            Mat hpImage = new Mat(_sourceImage, hpRect);

            // 플레이어 위치 찾기 (템플릿 매칭 등 사용)
            // ...

            // 플레이어 체력 계산 (색상 분석 등 사용)
            // ...

            // GameInfo 객체 업데이트
            // ...
        }

        // ... 기타 이미지 처리 함수들
    }
}

MainWindowViewModel.cs

MainWindowViewModel.cs는 WPF 애플리케이션에서 메인 윈도우의 데이터를 관리하고, UI와의 데이터 바인딩을 담당하는 클래스입니다.

기본적인 구성

  • 프로퍼티: UI에 표시될 데이터를 저장하는 프로퍼티를 선언합니다. (예: GameScreenImage, MiniMapImage)
  • 메서드: 데이터를 초기화하거나 업데이트하는 메서드를 정의합니다. (예: Initialize, UpdateGameScreen)
  • 이벤트: UI 이벤트에 대한 처리 로직을 구현합니다. (예: 버튼 클릭 이벤트)

OpenCVSharp를 이용한 이미지 처리

  • OpenCvSharp 라이브러리를 사용하여 카메라에서 이미지를 캡처하고, 필요한 영역을 추출합니다.
  • 추출된 이미지를 BitmapSource로 변환하여 WPF UI에 표시합니다.
  • CommunityToolkit.Mvvm.ComponentModel 라이브러리를 사용하여 프로퍼티 변경 시 UI를 자동으로 업데이트합니다.

코드

using OpenCvSharp;
using OpenCvSharp.WpfExtensions;
using CommunityToolkit.Mvvm.ComponentModel;
using OpenCvSharpProjects.Services;
using System.Windows.Media.Imaging;
using System.Windows.Media;


namespace OpenCvSharpProjects.ViewModels
{
    internal partial class MainWindowViewModel : ObservableObject
    {
        private readonly ImageProcessingService _imageProcessingService;

        [ObservableProperty]
        private BitmapSource _gameScreenImage;

        [ObservableProperty]
        private BitmapSource _miniMapImage;

        public MainWindowViewModel()
        {
            _imageProcessingService = new ImageProcessingService();

            // 이미지 파일 로드 (예시)
            Mat sourceImage = Cv2.ImRead("your_image_path.jpg");

            // 이미지 처리
            _imageProcessingService.ProcessImage(sourceImage);

            // 이미지 업데이트
            GameScreenImage = sourceImage.ToBitmapSource();
            MiniMapImage = _imageProcessingService.MiniMapImage.ToBitmapSource();
        }
    }
}

설명

  • ImageProcessingService.cs:
    • ProcessImage 메서드에서 소스 이미지를 받아 미니맵과 체력 게이지 영역을 자릅니다.
    • 템플릿 매칭 등을 이용하여 플레이어 위치를 찾고, 색상 분석을 통해 체력을 계산합니다.
    • GameInfo 객체를 업데이트하여 ViewModel에 전달합니다.
  • MainWindowViewModel.cs:
    • 이미지 파일 로드: Cv2.Imread 함수를 사용하여 이미지 파일을 메모리에 로드합니다.
    • OpenCvSharp.WpfExtensions 활용: Mat 객체를 BitmapSource로 변환하기 위해 ToBitmapSource() 확장 메서드를 사용했습니다.
    • Minimap 이미지 처리: ImageProcessingService 클래스에서 미니맵 이미지를 처리하고, MiniMapImage 속성에 저장한 후, ToBitmapSource()를 통해 BitmapSource로 변환합니다.

참조

https://clamp-coding.tistory.com/366

0개의 댓글