NGMsoftware

NGMsoftware
로그인 회원가입
  • 매뉴얼
  • 학습
  • 매뉴얼

    학습


    C# C# .NET 매크로 프로그램 만들기. (화면에서 같은 이미지 모두 찾기 1부)

    페이지 정보

    본문

    안녕하세요. 엔지엠소프트웨어입니다. 윈도우 화면 또는 프로그램에서 동일한 이미지를 모두 찾는 방법에 대해서 알아볼께요. 이 기능은 엔지엠 매크로 6에도 있는 액션입니다. 액션 이름은 이미지 전체 매치인데요. 엔지엠 7 버전에도 동일한 이름으로 액션을 추가할겁니다. 다만, 이전 버전에서 문제가 되는 부분들을 개선하고 좀 더 편리하게 사용할 수 있도록 옵션들이 많이 사라졌습니다.

     

    1부에서는 이미지 전체 매치 액션으로 화면에서 동일한 이미지를 모두 찾는 방법을 알아보고, 2부에서는 이미지 서치와 같이 반대로 찾기 기능을 구현해보겠습니다. 이미지 매치의 알고리즘을 수정할 수 있으면 좋겠지만, OpenCV 라이브라리를 참조해서 사용하다보니 내부 로직을 변경할 수 없습니다. 그래서, 반대로 찾기 기능은 이미지 전체 매치에서 약간의 꼼수를 이용해야 합니다. 아무튼, 몇가지 확장 기능들은 2부를 기대해주세요.

     

    이미지 전체 매치 모델은 아래와 같습니다. 이미지 매치를 상속 받아서 추가 속성만 만들면 될텐데요. 지금 구조로는 불가능한 부분이어서 대부분 동일한 코드를 재사용해야 합니다.

    public class ImageMatchsModel : ImageModel, ISearchArea, IImageConditionResultSave

     

    상속 받은 ImageModel은 Source 이미지와 Target 이미지를 자동으로 생성 해줍니다. Source 이미지는 찾고 싶은 이미지를 말합니다. 그리고, Target 이미지는 실시간으로 캡쳐하는 화면을 말하는데요. 이 2개의 이미지는 이미지 조건 모델들이 공통으로 처리하는 부분이라서 부모 클래스에서 처리하는 방향으로 디자인 되어 있습니다.

     

    ISearchArea 인터페이스를 상속 받으면 찾기 영역을 설정할 수 있는 속성을 하나 만들어야 합니다. ISearchArea 인터페이스를 구현하는 모든 모델은 매크로 엔진에서 자동으로 감지합니다. 매크로가 실행될 때 자동으로 찾기 영역만큼만 Target 이미지를 캡쳐해서 사용합니다.

     

    IImageConditionResultSave 인터페이스를 구현하는 모든 모델은 아래와 같이 결과 이미지를 저장할 수 있는 속성들을 만듭니다. 이 부분은 뒤에서 확인 해볼께요.

    Q8ptcHK.jpeg

     

     

    화면 또는 프로그램에서 유사한 이미지를 모두 찾기 때문에 일반적인 이미지 서치와 이미지 매치와는 다르게 목록으로 찾은 이미지의 영역과 클릭 좌표를 속성으로 보여줘야 합니다.

    [LocalizedCategory("Data")]
    [LocalizedDisplayName("FindImageRectangles")]
    [LocalizedDescription("FindImageRectangles")]
    [Browsable(true)]
    [ReadOnly(true)]
    [DefaultValue(null)]
    public Rectangle[]? FindImageRectangleDatas { get; set; }
    
    [LocalizedCategory("Data")]
    [LocalizedDisplayName("MousePositions")]
    [LocalizedDescription("MousePositions")]
    [Browsable(true)]
    [ReadOnly(true)]
    [DefaultValue(null)]
    public System.Drawing.Point[]? MousePositionDatas { get; set; }

     

    아래와 같이 대상(Target) 이미지를 가져옵니다.

    Image? target = Ai.Common.Screen.Capture(this.SearchArea, player.Manager.Option.CaptureMode, player.Manager.Option.UseMultiMonitor);


    이미지 전체 찾기도 OpenCV를 사용하는데요. Met으로 변환된 Source와 Target을 메모리에서 처리합니다. 그리고, 퍼포먼스 옵션에 따라서 이미지를 흑백으로 처리할지 여부를 선택해야 합니다. 분석해야 할 이미지가 클수록 흑백으로 변환한 후 비교하는게 퍼포먼스가 더 좋습니다. 아무래도 컬러 RGB값을 모두 사용하는 것 보다는 반으로 줄여서 비교하는게 속도면에서 더 빠르기 때문입니다.

    using (Mat ScreenMat = OpenCvSharp.Extensions.BitmapConverter.ToMat((Bitmap)target))
    {
        if (isGrayScale)
            Cv2.CvtColor(ScreenMat, ScreenMat, ColorConversionCodes.BGR2GRAY);
        else
            Cv2.CvtColor(ScreenMat, ScreenMat, ColorConversionCodes.BGR2RGB);
    
        using (Mat FindMat = OpenCvSharp.Extensions.BitmapConverter.ToMat((Bitmap)source))
        {
            if (isGrayScale)
                Cv2.CvtColor(FindMat, FindMat, ColorConversionCodes.BGR2GRAY);
            else
                Cv2.CvtColor(FindMat, FindMat, ColorConversionCodes.BGR2RGB);
    
            if (mask == null)
            {
                using (Mat res = ScreenMat.MatchTemplate(FindMat, templateMatchModes))
                {
                    OpenCvSharp.Point minloc, maxloc;
                    Cv2.MinMaxLoc(res, out double minval, out double maxval, out minloc, out maxloc);
    
                    for (int r = 0; r < res.Rows; r++)
                    {
                        for (int c = 0; c < res.Cols; c++)
                        {
                            if (res.At<float>(r, c) < similarity)
                                rects.Add(new Rectangle(c, r, source.Width, source.Height));
                        }
                    }
                }
            }
            else
            {
                using (Mat maskMat = OpenCvSharp.Extensions.BitmapConverter.ToMat((Bitmap)mask))
                {
                    Cv2.CvtColor(maskMat, maskMat, ColorConversionCodes.BGR2GRAY);
    
                    using (Mat res = ScreenMat.MatchTemplate(FindMat, templateMatchModes, maskMat))
                    {
                        OpenCvSharp.Point minloc, maxloc;
                        Cv2.MinMaxLoc(res, out double minval, out double maxval, out minloc, out maxloc);
    
                        for (int r = 0; r < res.Rows; r++)
                        {
                            for (int c = 0; c < res.Cols; c++)
                            {
                                if (res.At<float>(r, c) < similarity)
                                    rects.Add(new Rectangle(c, r, source.Width, source.Height));
                            }
                        }
                    }
                }
            }
        }
    }

     

    아래 코드는 OpenCV 알고리즘의 허점을 보완하기 위한 코드입니다. 찾은 모든 사각형의 거리에 따라서 중복을 제거해주는데요. 위 로직에서 X, Y 거리에 크기를 계산하지 않기 때문에 1Pixel 정도 중복 처리가 될 수 있습니다. 또는, 연속되는 패턴 문양의 경우 중복해서 같은 이미지로 처리되는 경우들이 있습니다. 이런 경우 하나의 이미지로 처리할지 또는 분할된 이미지로 처리할지를 결정해야 합니다.

     

    이런 문제를 방지하기 위해 아래와 같이 어느정도 거리에 따라서 유사한 이미지를 제거해야 합니다.

    var reduced = rects.Skip(1).Aggregate(rects.Take(1).ToList(), (a, b) =>
    {
        if ((Math.Abs(a.Last().X - b.X) + Math.Abs(a.Last().Y - b.Y)) / 2d > SimilarDistanceRemove)
            a.Add(b);
        return a;
    });

     

    위 코드도 아래 테스트에서 확인할 수 있으니 참고만 해주세요. 테스트에 사용할 이미지는 아래와 같습니다. 구글에서 슈퍼마리오를 검색한 후 코인이 여러개 등장하는 이미지를 선택했습니다. 윈도우 화면 또는 구글 크롬 브라우저와 같은 프로그램에서 동일한 이미지를 찾는거라서 이 예제가 가장 적절한듯 합니다.

    Mnwqd9L.jpeg

     

     

    찾을 이미지는 아래와 같이 캡쳐 해두었습니다. coin.png를 사용할거예요.

    MeLG8Yj.jpeg

     

     

    아래 동영상은 이미지 전체 매치를 테스트한 내용입니다. 설명이 같이 첨부되어 있으니 소리를 켜고, 들어보세요.

     

     

    위 동영상에서도 설명하고 있듯이 이미지 서치와 이미지 매치와는 다르게 유사성 옵션을 통해서 모든 이미지를 찾아내야 합니다. 환경적인 문제로 동일한 유사성인데도 5개를 찾을때도 있고, 6개를 찾을때도 있습니다. 대부분 이런 경우는 1픽셀정도 X 또는 Y 축으로 차이가 발생합니다. 이 문제를 해결하려면 유사 거리 삭제 옵션을 추가적으로 설정해야 합니다. 보통 1정도 설정하면 1~5픽셀정도 붙어있는 사각형들을 제거가 됩니다.

    Fc1LJsA.jpeg

     

     

    오늘은 간단하게 화면에서 동일한 이미지를 모두 찾는 방법을 알아봤습니다. 다음에는 속성을 좀 더 추가해서 기능을 확장시킬건데요. 반대로 찾는 방법과, 마우스 클릭 방법등등... 멀티 환경에 대응할 수 있는 다양한 옵션들을 추가하고 어떻게 처리해야 하는지 알아볼께요. 다음 내용도 기대해주세요^^

     

    개발자에게 후원하기

    MGtdv7r.png

     

    추천, 구독, 홍보 꼭~ 부탁드립니다.

    여러분의 후원이 빠른 귀농을 가능하게 해줍니다~ 답답한 도시를 벗어나 귀농하고 싶은 개발자~

    감사합니다~

    • 네이버 공유하기
    • 페이스북 공유하기
    • 트위터 공유하기
    • 카카오스토리 공유하기
    추천0 비추천0

    댓글목록

    등록된 댓글이 없습니다.