개요
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 폴더 추가
Converters 폴더의 위치 변경
따로 떼어 놓는 경우:
Views 폴더에 넣는 경우:
결론:
현재 프로젝트에서는 BooleanToVisibilityConverter가 여러 View에서 사용될 가능성이 있으므로, Converters 폴더를 따로 떼어 놓기로 했습니다.
추가 고려 사항
Services 폴더에 추가해야 할 서비스 종류
Services 폴더에는 애플리케이션의 핵심 로직을 담당하는 다양한 서비스 클래스를 추가할 수 있다. 특히, 자동 사냥 프로그램의 경우 다음과 같은 서비스 클래스를 고려해 볼 수 있다.
예시:
public class ImageProcessingService
{
public Mat CaptureScreen()
{
// 화면 캡처 로직
}
public bool FindTemplate(Mat image, Mat template)
{
// 템플릿 매칭 로직
}
}
Converters 폴더에 추가할 수 있는 클래스 종류
Converters 폴더에는 View와 ViewModel 사이에서 데이터 형식을 변환하거나, 특정 조건에 따라 UI 요소를 변경하는 역할을 수행하는 클래스를 추가한다.
예시
public class BoolToVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return (bool)value ? Visibility.Visible : Visibility.Collapsed;
}
// ...
}
ViewModels 폴더와 Services 폴더는 비슷해 보이지만, 각각 다른 역할을 수행하며 분리하는 것이 일반적입니다.
ViewModels 폴더는 UI와 데이터를 연결하는 역할을 합니다. 즉, View에서 사용되는 데이터를 관리하고, 사용자의 입력에 대한 반응을 처리하는 로직을 담고 있습니다. ViewModel은 다음과 같은 역할을 수행합니다.
Services 폴더는 특정 기능을 제공하는 서비스 클래스를 모아놓는 곳입니다. ViewModel에서 필요한 기능을 제공하는데, ViewModel의 역할을 명확하게 하기 위해 분리합니다. Services 폴더에 들어갈 수 있는 클래스의 예시는 다음과 같습니다.
ViewModels와 Services를 분리하는 이유
예시
카메라로 화면을 인식하는 기능을 나중에 추가하기로 했습니다. 처음부터 모든 기능을 완벽하게 구현하려고 하기보다는, 단계별로 기능을 추가하며 개발하는 것이 더 효율적이라고 판단내렸기 때문입니다.
이렇게 지금 이 기능을 뺴버리고 나중에 다시 넣기로 결정한 이유는 다음과 같습니다.
즉, 잘 설계된 MVVM 구조라면, 카메라 인식 기능을 추가하기 위해 기존 코드를 크게 변경할 필요가 없기 때문입니다. 나중에 추가할 때 아래와 같은 작업이 필요할 것으로 생각됩니다.
구체적으로
public class GameInfo
{
public int PlayerLevel { get; set; }
public int Gold { get; set; }
// ... 기타 게임 정보
}
public class MainWindowViewModel : ObservableObject
{
public ICommand StartCaptureCommand { get; }
// ... 기타 명령 및 속성
}
사용자 인터페이스를 담당하는 View (WPF 창) 파일을 저장하는 폴더입니다.
역할: 사용자 인터페이스를 정의하는 XAML 파일을 저장하는 곳입니다.
예시: MainWindow.xaml 파일에는 메인 화면의 레이아웃과 디자인을 정의합니다.
데이터 바인딩에 사용되는 컨버터 클래스를 저장하는 폴더입니다.
역할: ViewModel의 데이터를 View에서 사용하기 위한 형식 변환을 수행하는 클래스를 정의하는 곳입니다. (데이터 바인딩에 사용되는 컨버터 클래스를 저장하는 폴더)
예시: RectToVisibilityConverter.cs 파일에는 Rect 타입의 데이터를 Visibility 타입으로 변환하는 로직을 정의할 수 있습니다.
웹캠 캡처, 이미지 처리, 키보드 제어 등의 기능을 담당하는 서비스 클래스를 저장하는 폴더입니다.
각 서비스는 특정 기능에 집중하여 코드의 응집도를 높이고 재사용성을 향상시킵니다.
역할: 특정 기능을 제공하는 서비스 클래스를 정의하는 곳입니다.
예시: ImageProcessingService.cs 파일에는 이미지 캡처, 처리, 분석 등의 기능을 제공하는 메서드를 정의합니다.
각 서비스 클래스의 역할
WebcamService: 웹캠 캡처 및 제어 기능을 제공합니다.Mat 객체로 변환합니다.ImageProcessingService: 이미지 처리, 객체 인식, 특징점 매칭 등의 기능을 제공합니다.MainViewModel에 전달합니다.KeyboardControlService: 키보드 입력 제어 기능을 제공합니다.System.Windows.Forms.SendKeys 클래스 또는 InputSimulator 라이브러리를 사용하여 키보드 입력을 시뮬레이션합니다.MainViewModel에서 전달받은 정보에 따라 키보드 입력을 제어합니다.ObservableObject: MainViewModel 클래스에서 ObservableObject 클래스를 상속받아 속성 변경 알림을 구현합니다.RelayCommand: MainViewModel 클래스에서 RelayCommand 클래스를 사용하여 Command를 구현합니다.예를 들어, 게임 캐릭터 정보를 관리하는 Character 클래스를 Models 폴더에 추가하고, 캐릭터 정보를 표시하는 기능을 ViewModel에 추가할 수 있습니다.
MainWindow.xaml.cs는 MainWindow.xaml 파일과는 별개의 파일입니다. 하지만 둘은 긴밀하게 연결되어 함께 작동합니다.
MainWindow.xaml.cs는 MainWindow.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.cs 파일의 클래스를 지정합니다. 이를 통해 두 파일이 연결됩니다.간단히 말해서, MainWindow.xaml 파일은 건물의 설계도와 같고, MainWindow.xaml.cs 파일은 그 설계도를 바탕으로 건물을 짓는 과정에서 필요한 기능을 구현하는 코드라고 생각할 수 있습니다.
예를 들어, MainWindow.xaml 파일에서 버튼을 하나 정의하고, MainWindow.xaml.cs 파일에서 해당 버튼을 클릭했을 때 메시지 박스를 띄우는 코드를 작성할 수 있습니다.
정리:
쉽게 말해, MainWindow.xaml 파일은 XAML로 디자인된 UI를 정의하고, MainWindow.xaml.cs 파일은 C# 코드로 UI의 동작을 구현하는데, x:Class 속성을 통해 두 파일을 연결하여 하나의 완전한 윈도우를 만드는 것입니다.
<Window x:Class="OpenCvSharpProjects.MainWindow"
...>
솔루션 탐색기에서 확인
솔루션 탐색기에서 MainWindow.xaml 파일 옆에 MainWindow.xaml.cs 파일이 함께 있는 것을 볼 수 있습니다. 이는 두 파일이 서로 연결되어 있음을 나타냅니다.
코드 비하인드 파일의 역할
중요:
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.xaml과 MainWindow.xaml.cs 파일은 서로 연결되어 WPF 창을 구성합니다.
데이터 바인딩은 UI 요소와 데이터를 자동으로 연결하여, 데이터가 변경될 때 UI가 자동으로 업데이트되는 것을 의미합니다. WPF에서는 XAML에서 데이터 바인딩을 쉽게 설정할 수 있습니다.
데이터 바인딩의 장점:
데이터 바인딩 예시:
<TextBlock Text="{Binding MyProperty}" />
위 코드는 TextBlock 컨트롤의 Text 속성을 ViewModel의 MyProperty 속성에 바인딩합니다. ViewModel에서 MyProperty 값이 변경되면 TextBlock의 Text가 자동으로 업데이트됩니다.
데이터 바인딩의 종류:
데이터 바인딩의 사용법:
데이터 바인딩을 사용하기 위한 필수 요소:
데이터 바인딩에서 Converter를 사용하는 이유는 View에서 바로 사용하기 어려운 형식의 데이터를 변환하여 사용하기 위해서입니다.
Converter가 필요한 이유
Converter의 장점
Converter의 종류
예시
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 패턴에서 데이터 바인딩의 유연성을 높이고, 코드의 재사용성을 향상시키는 중요한 역할을 합니다.
public class GameInfo
{
public double PlayerHp { get; set; }
public Point PlayerPosition { get; set; }
public bool IsHpLow { get; set; }
// ... 기타 필요한 프로퍼티 추가
}
<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>
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는 WPF 애플리케이션에서 메인 윈도우의 데이터를 관리하고, UI와의 데이터 바인딩을 담당하는 클래스입니다.
기본적인 구성
GameScreenImage, MiniMapImage)Initialize, UpdateGameScreen)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();
}
}
}
ProcessImage 메서드에서 소스 이미지를 받아 미니맵과 체력 게이지 영역을 자릅니다.GameInfo 객체를 업데이트하여 ViewModel에 전달합니다.Cv2.Imread 함수를 사용하여 이미지 파일을 메모리에 로드합니다.Mat 객체를 BitmapSource로 변환하기 위해 ToBitmapSource() 확장 메서드를 사용했습니다.ImageProcessingService 클래스에서 미니맵 이미지를 처리하고, MiniMapImage 속성에 저장한 후, ToBitmapSource()를 통해 BitmapSource로 변환합니다.