C#/C# 리니지m/

C#으로 만드는 리니지m 매크로 (2-2)hp인식 - 픽셀서치

2019. 10. 3.

지난 시간에 이어 다른 방식의 hp 인식방법을 알아보겠습니다.

 

원리는 간단합니다

전체 hp바를 보았을때

피가 채워진부분은 진하게 빨간색, 빠진 부분은 투명하게 된다는 점을 이용

뒷픽셀부터 한픽셀씩 읽어서 진한빨간색을 찾고 전체비율을 구하면 됩니다.

 

우선 지난 시간에 만든 폼에 추가로

hp바 전체부분을 띄울 picturebox 1

수치를 보여줄 label 1

실행시킬 button 1개를 추가해줍니다

 

지난 시간과 다르게 버튼 명만 수정했습니다.

 

그리고 새로추가해준 button4의 이름을 보기좋게 수정하고

코드를 입력해봅시다.

 

private void Button4_Click(object sender, EventArgs e)
        {
            Rectangle rect = new Rectangle(75, 30, 199, 4);
            // x,y 시작좌표 x,y범위 생성 hp바의 맨밑줄만 떠오는것

            Bitmap cap = new Bitmap(pictureBox1.Image);
            // 버튼2 클릭 이벤트에서 screen값을 바꾸었기때문에 다시 picturebox1이미지 불러오기

            cap = cap.Clone(rect, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
            // 캡쳐뜬 이미지에서 위의 Rect 만큼 자르기

            cap = new Bitmap(cap, new Size(174, 10));
            // 보기좋게 picturebox3과 같은 크기로 리사이즈

            pictureBox3.Image = cap;
            // picturebox3에 이미지 띄우기

            int point = 0;

            for(int i = cap.Width-1; i > 0; i--)
            {
                Color c = cap.GetPixel(i, cap.Height-1);
                if (c.R > 200)
                {
                    point = i;
                    break;
                }
            }
            // for 반복문을 통해 뒷 픽셀부터 확인하여 red값이 200이넘는 픽셀위치찾기

            label3.Text = (100 * point / cap.Width).ToString("##.##");
            // 찾은 픽셀 위치를 전체 범위에서 몇퍼센트인지 label3에 출력
        }

 

너무 기본적인 코드라 딱히 설명할게 없네요

 

디버깅하고 테스트해봅시다

지난 시간 까지의 결과물, 여기서 새로추가한 버튼을 누르면

 

약 1% 정도의 오차가 나오네요 하지만 문제가 될만큼의 오차는 아니라고 생각되네요.

혹시모르니 한번더 테스트

거의 유사치가 나왔네요

여러번 테스트 결과 0.1~1.5 정도의 오차가 나오긴하나

실 수치보다 크게나오는 경우는 없어서 그냥 써도 될듯합니다.

 

tesseract와의 차이점이라면

반복으로 실행했을 때 비교도안되는 속도와

눈에띄게 적게먹는 리소스가 장점입니다.

무시할만한 오차, 눈에 띄는 장점.

픽셀서치가 더 좋아보이네요

 

전체코드

더보기
using System;
using System.Drawing;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using Tesseract;

namespace imagesearch
{
    public partial class Form1 : Form
    {
        [DllImport("User32", EntryPoint = "FindWindow")]
        private static extern IntPtr FindWindow(string IpClassName, string IpWindowName);

        [DllImport("user32")]
        private static extern IntPtr FindWindowEx(IntPtr hWnd1, int hWnd2, string lp1, string lp2);

        [DllImport("user32.dll")]
        internal static extern bool PrintWindow(IntPtr hwnd, IntPtr hdcblt, int nFlags);

        Bitmap screen;

        public Form1()
        {
            InitializeComponent();
        }
        
        private void Button1_Click(object sender, EventArgs e)
        {
            string a = textBox1.Text;

            IntPtr b = FindWindow(null, a);

            IntPtr c = FindWindowEx(b, 0, "RenderWindow", "TheRender");

            Graphics gdata = Graphics.FromHwnd(c);

            Rectangle rect = Rectangle.Round(gdata.VisibleClipBounds);

            Bitmap bmp = new Bitmap(rect.Width, rect.Height);

            using (Graphics g = Graphics.FromImage(bmp))
            {
                IntPtr hdc = g.GetHdc();
                PrintWindow(c, hdc, 0x2);
                g.ReleaseHdc(hdc);
            }

            screen = new Bitmap(bmp, new Size(1280, 720));

            pictureBox1.Image = screen;
        }

        private void Button2_Click(object sender, EventArgs e)
        {
            Rectangle rect = new Rectangle(130, 13, 85, 20);
            // x,y 시작좌표 x,y범위 생성

            Bitmap cap = screen.Clone(rect, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
            // 캡쳐뜬 이미지에서 위의 Rect 만큼 자르기

            cap = new Bitmap(cap, new Size(170, 40));
            // 인식률을 높이기위해 두배크기로 리사이즈

            for(int i = 0; i < cap.Width; i++)
            {
                for(int j = 0; j < cap.Height; j++)
                {
                    Color c = cap.GetPixel(i, j);
                    int binary = (c.R + c.G + c.B) / 3;

                    if (binary > 190)
                        cap.SetPixel(i, j, Color.White);
                    else
                        cap.SetPixel(i, j, Color.Black);
                }
            }
            // 간단한 for을 통한 Binary(이진화)

            screen = cap;

            pictureBox2.Image = screen;
            // picturebox2 에 자르고 키우고 이진화한 이미지 띄우기
        }

        private void Button3_Click(object sender, EventArgs e)
        {
            Pix pix = PixConverter.ToPix(screen);
            
            var engine = new TesseractEngine(@"./tessdata", "eng", EngineMode.TesseractOnly);
            // tesseractengine 생성
            string whitelist = "0123456789/";
            engine.SetVariable("tessedit_char_whitelist", whitelist);
            // 인식률을 높이기위한 숫자와 '/' 만 화이트리스트 적용

            var result = engine.Process(pix);

            string HP = result.GetText();

            HP = HP.Replace(" ", "");
            // 공백제거

            label1.Text = HP;

            string[] split = HP.Split('/');
            // 현재와 맥스수치를 나눠주기 구분점 '/'
            label2.Text = "HP : " + (Convert.ToSingle(split[0]) * 100 / Convert.ToSingle(split[1])).ToString("##.##") + "%";
        }

        private void Button4_Click(object sender, EventArgs e)
        {
            Rectangle rect = new Rectangle(75, 30, 199, 4);
            // x,y 시작좌표 x,y범위 생성 hp바의 맨밑줄만 떠오는것

            Bitmap cap = new Bitmap(pictureBox1.Image);
            // 버튼2 클릭 이벤트에서 screen값을 바꾸었기때문에 다시 picturebox1이미지 불러오기

            cap = cap.Clone(rect, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
            // 캡쳐뜬 이미지에서 위의 Rect 만큼 자르기

            cap = new Bitmap(cap, new Size(174, 10));
            // 보기좋게 picturebox3과 같은 크기로 리사이즈

            pictureBox3.Image = cap;
            // picturebox3에 이미지 띄우기

            int point = 0;

            for(int i = cap.Width-1; i > 0; i--)
            {
                Color c = cap.GetPixel(i, cap.Height-1);
                if (c.R > 200)
                {
                    point = i;
                    break;
                }
            }
            // for 반복문을 통해 뒷 픽셀부터 확인하여 red값이 200이넘는 픽셀위치찾기

            label3.Text = (100 * point / cap.Width).ToString("##.##");
            // 찾은 픽셀 위치를 전체 범위에서 몇퍼센트인지 label3에 출력
        }
    }
}