게임 실시간 화면을 분석하여 낚시

C++, OpenCV로 만든 낚시 매크로,

다양한 응용이 가능하나 즉시 사용 가능한 게임은 비공개..

#include <iostream> 
#include <ctime>
#include <opencv2/opencv.hpp>
#include <windows.h>
#include <time.h>
#include <string>

using namespace std;
using namespace cv;

#ifdef _DEBUG
#pragma comment(lib, "opencv_world343d.lib")
#else
#pragma comment(lib, "opencv_world343.lib")
#endif


int objWinX, objWinY, objWinW, objWinH;
int scrWidth, scrHeight;
int dispLog;
int LowH, HighH, LowS, HighS, LowV, HighV, castAfterRelease;
int countFishs = 1;

Mat hwnd2mat(HWND hwnd)
{
	HDC hwindowDC, hwindowCompatibleDC;

	HBITMAP hbwindow;
	Mat src;
	BITMAPINFOHEADER  bi;

	hwindowDC = GetDC(hwnd);
	hwindowCompatibleDC = CreateCompatibleDC(hwindowDC);
	SetStretchBltMode(hwindowCompatibleDC, COLORONCOLOR);


	src.create(objWinH, objWinW, CV_8UC4);

	hbwindow = CreateCompatibleBitmap(hwindowDC, objWinW, objWinH);
	bi.biSize = sizeof(BITMAPINFOHEADER);
	bi.biWidth = objWinW;
	bi.biHeight = -objWinH;
	bi.biPlanes = 1;
	bi.biBitCount = 32;
	bi.biCompression = BI_RGB;
	bi.biSizeImage = 0;
	bi.biXPelsPerMeter = 0;
	bi.biYPelsPerMeter = 0;
	bi.biClrUsed = 0;
	bi.biClrImportant = 0;

	SelectObject(hwindowCompatibleDC, hbwindow);

	StretchBlt(hwindowCompatibleDC, 0, 0, objWinW, objWinH, hwindowDC, objWinX, objWinY, objWinW, objWinH, SRCCOPY);
	GetDIBits(hwindowCompatibleDC, hbwindow, 0, objWinH, src.data, (BITMAPINFO *)&bi, DIB_RGB_COLORS);

	DeleteObject(hbwindow);
	DeleteDC(hwindowCompatibleDC);
	ReleaseDC(hwnd, hwindowDC);
	return src;
}


void keyOutput(int realPosX, int realPosY) {

	int castDelay;
	castDelay = GetPrivateProfileInt("etcValues", "castDelay", 1000, "./config.ini");


	if (dispLog == 1) {
		cout << "실행 : 마우스" << realPosX << "/" << realPosY << endl;
	}
	
	if (realPosX && realPosY) {
		time_t now = time(0);
		char* dt = ctime(&now);
		cout << dt << " : Get!! " << endl;
		SetCursorPos(realPosX, realPosY);
		Sleep(100);
		mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0);
		Sleep(100);
		mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);
	}

	Sleep(castDelay * 1000);



	time_t now = time(0);
	char* dt = ctime(&now);
	cout << dt << "[" << countFishs << "] : Cast!! " << endl;
	countFishs++;

	keybd_event(0x30, 0, 0, 0);
	keybd_event(0x30, 0, KEYEVENTF_KEYUP, 0);
	Sleep(castAfterRelease * 1000);

}


void keySkill(int useKey) {

	int keyCode;

	switch (useKey) {
	case 1:
		keyCode = 0x31;
		break;
	case 2:
		keyCode = 0x32;
		break;
	case 3:
		keyCode = 0x33;
		break;
	case 4:
		keyCode = 0x34;
		break;
	case 5:
		keyCode = 0x35;
		break;
	case 6:
		keyCode = 0x36;
		break;
	case 7:
		keyCode = 0x37;
		break;
	case 8:
		keyCode = 0x38;
		break;
	case 9:
		keyCode = 0x39;
		break;

	}

	keybd_event(keyCode, 0, 0, 0);
	keybd_event(keyCode, 0, KEYEVENTF_KEYUP, 0);
	Sleep(100);

	keybd_event(0x30, 0, 0, 0);
	keybd_event(0x30, 0, KEYEVENTF_KEYUP, 0);
	Sleep(castAfterRelease * 1000);
}




void setRange(int setR) {
	string profile = "profile" + to_string(setR);
	LPCSTR _profile = profile.c_str();

	LowH = GetPrivateProfileInt(_profile, "LowH", 0, "./config.ini");
	HighH = GetPrivateProfileInt(_profile, "HighH", 0, "./config.ini");
	LowS = GetPrivateProfileInt(_profile, "LowS", 0, "./config.ini");
	HighS = GetPrivateProfileInt(_profile, "HighS", 0, "./config.ini");
	LowV = GetPrivateProfileInt(_profile, "LowV", 0, "./config.ini");
	HighV = GetPrivateProfileInt(_profile, "HighV", 0, "./config.ini");

}


int main(int argc, char **argv) {

	time_t run_start, run_finish;
	time(&run_start);

	int centerX, centerY;

	// 환경변수를 config.ini 에서 불러옴
	// etcValues
	int posRange, counterPer, counterMax, mouseFocus, startPause;
	posRange = GetPrivateProfileInt("etcValues", "posRange", 50, "./config.ini");
	dispLog = GetPrivateProfileInt("etcValues", "dispLog", 1, "./config.ini");
	counterPer = GetPrivateProfileInt("etcValues", "counterPer", 5, "./config.ini");
	counterMax = GetPrivateProfileInt("etcValues", "counterMax", 20, "./config.ini");
	mouseFocus = GetPrivateProfileInt("etcValues", "mouseFocus", 0, "./config.ini");
	startPause = GetPrivateProfileInt("etcValues", "startPause", 3000, "./config.ini");
	castAfterRelease = GetPrivateProfileInt("etcValues", "castAfterRelease", 3, "./config.ini");

	int gamePlay = 0;
	int output_source = 0;
	int key = 0;
	int counter = 0;
	int prev_pos, this_pos = 0;
	int set_range = 1;
	int set_range_max = 10;
	int freeSet = 0;

	int useSkill = 0;
	int useSkillKey = 5;
	int useSkillKeyMin = 5;
	int useSkillKeyMax = 5;
	int useSkillKeyTemp = 0;
	int useSkillKeyDelay = 300;
	
	time_t skill_start, skill_end;
	time(&skill_start);

	// 기본 설정값이 없다면 실행
	setRange(set_range);
	
	
	HWND hwndDesktop = GetDesktopWindow();

	RECT windowsize;    // get the height and width of the screen
	GetClientRect(hwndDesktop, &windowsize);

	scrWidth = windowsize.right;
	scrHeight = windowsize.bottom;


	objWinW = scrWidth / 3;
	objWinH = scrHeight / 3;
	objWinX = scrWidth / 3;
	objWinY = scrHeight / 5;

	int realPosX = objWinX;
	int realPosY = objWinY;


	if (scrWidth > 2000) {
		namedWindow("설정프로그램");
		resizeWindow("설정프로그램", objWinW / 2, objWinH / 2);
		moveWindow("설정프로그램", 0, 0);

	}
	else {
		namedWindow("설정프로그램");
		resizeWindow("설정프로그램", 400, 800);
		moveWindow("설정프로그램", 0, 0);

		
		namedWindow("영상소스");
		resizeWindow("영상소스", objWinW / 2, objWinH / 2);
		moveWindow("영상소스", 300, 0);
	}

	while (key != 27)
	{
		// 프리셋 상태가 아닐경우에만
		if (freeSet == 0) {
			setRange(set_range);
		}
		
		Mat src = hwnd2mat(hwndDesktop);
		key = waitKey(60); // you can change wait time


		if (counter % counterPer == 0) {

			Mat img_all, img_input, img_hsv, img_binary, img_result, img_concat;

			/* 이미지 크롭
			desktop = src;
			Rect rect(objWinX, objWinY, objWinW, objWinH);
			img_input = desktop(rect);
			*/

			img_input = src;


			//HSV로 변환
			cvtColor(img_input, img_hsv, COLOR_BGR2HSV);


			//지정한 HSV 범위를 이용하여 영상을 이진화
			inRange(img_hsv, Scalar(LowH, LowS, LowV), Scalar(HighH, HighS, HighV), img_binary);


			//morphological opening 작은 점들을 제거 
			erode(img_binary, img_binary, getStructuringElement(MORPH_ELLIPSE, Size(5, 5)));
			dilate(img_binary, img_binary, getStructuringElement(MORPH_ELLIPSE, Size(5, 5)));


			//morphological closing 영역의 구멍 메우기 
			dilate(img_binary, img_binary, getStructuringElement(MORPH_ELLIPSE, Size(5, 5)));
			erode(img_binary, img_binary, getStructuringElement(MORPH_ELLIPSE, Size(5, 5)));


			//라벨링 
			Mat img_labels, stats, centroids;
			int numOfLables = connectedComponentsWithStats(img_binary, img_labels, stats, centroids, 8, CV_32S);


			//영역박스 그리기
			int max = -1, idx = 0;
			for (int j = 1; j < numOfLables; j++) {
				int area = stats.at<int>(j, CC_STAT_AREA);
				if (max < area)
				{
					max = area;
					idx = j;
				}
			}



			int left = stats.at<int>(idx, CC_STAT_LEFT);
			int top = stats.at<int>(idx, CC_STAT_TOP);
			int width = stats.at<int>(idx, CC_STAT_WIDTH);
			int height = stats.at<int>(idx, CC_STAT_HEIGHT);
			

			if (output_source == 0) {
				//cvtColor(img_binary, img_result, CV_GRAY2BGR);
				img_result = img_binary;
				rectangle(img_result, Point(left, top), Point(left + width, top + height), Scalar(255, 255, 255), 1);
			}
			else {
				img_result = img_input;
				rectangle(img_result, Point(left, top), Point(left + width, top + height), Scalar(0, 0, 255), 1);
			}


			//트랙바 생성 


			cvCreateTrackbar("LowH", "설정프로그램", &LowH, 179); //Hue (0 - 179)
			cvCreateTrackbar("HighH", "설정프로그램", &HighH, 179);
			cvCreateTrackbar("LowS", "설정프로그램", &LowS, 255); //Saturation (0 - 255)
			cvCreateTrackbar("HighS", "설정프로그램", &HighS, 255);
			cvCreateTrackbar("LowV", "설정프로그램", &LowV, 255); //Value (0 - 255)
			cvCreateTrackbar("HighV", "설정프로그램", &HighV, 255);
			cvCreateTrackbar("프리셋", "설정프로그램", &freeSet, 1);

			
			cvCreateTrackbar("LowH", "설정프로그램", &LowH, 179); //Hue (0 - 179)
			cvCreateTrackbar("LowS", "설정프로그램", &LowS, 255); //Saturation (0 - 255)
			cvCreateTrackbar("LowV", "설정프로그램", &LowV, 255); //Value (0 - 255)
			cvCreateTrackbar("HighH", "설정프로그램", &HighH, 179);
			cvCreateTrackbar("HighS", "설정프로그램", &HighS, 255);
			cvCreateTrackbar("HighV", "설정프로그램", &HighV, 255);
			cvCreateTrackbar("동작", "설정프로그램", &gamePlay, 1);
			cvCreateTrackbar("영상소스", "설정프로그램", &output_source, 1);
			cvCreateTrackbar("로그", "설정프로그램", &dispLog, 1);
			cvCreateTrackbar("프로필", "설정프로그램", &set_range, set_range_max);
			cvCreateTrackbar("변화높이", "설정프로그램", &posRange, 100);
			cvCreateTrackbar("스킵프레임", "설정프로그램", &counterPer, 10);
			cvCreateTrackbar("자동캐스팅", "설정프로그램", &counterMax, 20);
			cvCreateTrackbar("지연시간", "설정프로그램", &castAfterRelease, 10);
			cvCreateTrackbar("포커스", "설정프로그램", &mouseFocus, 1);


			cvCreateTrackbar("스킬사용", "설정프로그램", &useSkill, 1);
			cvCreateTrackbar("단축시작키", "설정프로그램", &useSkillKeyMin, 9);
			cvCreateTrackbar("단축종료키", "설정프로그램", &useSkillKeyMax, 9);
			cvCreateTrackbar("간격(초)", "설정프로그램", &useSkillKeyDelay, 600);


			if (scrWidth > 2000) {

				imshow("설정프로그램", img_result);
			}
			else {

				imshow("영상소스", img_result);
			}

			// 스킬 키 사용 관련
			if (useSkillKeyMax > useSkillKeyMin) {
				if(useSkillKeyTemp == 0 || useSkillKeyTemp < useSkillKeyMin || useSkillKeyTemp > useSkillKeyMax) {
					useSkillKeyTemp = useSkillKeyMin;
				}
				useSkillKey = useSkillKeyTemp;
			}
			else {
				useSkillKey = useSkillKeyMin;
			}
			

			time(&run_finish);

			if (dispLog == 1) {
				cout << difftime(run_finish, run_start) << " centerX:" << centerX << " centerY:" << centerY << " prev_pos:" << prev_pos << " this_pos:" << this_pos << endl;
			}

			if (left > 0 && top > 0) {
				centerX = left + width / 2;
				centerY = top + height / 2;

				realPosX = centerX + objWinX;
				realPosY = centerY + objWinY;

				this_pos = centerY;

				if (gamePlay == 1 && prev_pos != 0 && abs(prev_pos - this_pos) > posRange) { // 이전 픽셀 포지션과 posRange 이상 차이가 난다면


					keyOutput(realPosX, realPosY);

					// 스킬 키 사용
					
					if (useSkill == 1 && useSkillKey > 0) {
						time(&skill_end);
						if (difftime(skill_end, skill_start) > useSkillKeyDelay) {
							cout << "스킬사용: " << useSkillKey << endl;
							keySkill(useSkillKey);
							time(&skill_start);
							if (useSkillKeyTemp > 0) {
								useSkillKeyTemp++;
							}

						}
					}
					
					counter = 1;
					prev_pos = 0;
					time(&run_start);

				}
				else {
					prev_pos = centerY;

				}

				if (gamePlay == 1) {
					if (mouseFocus == 1) {
						SetCursorPos(realPosX, realPosY);
					}					

					if (difftime(run_finish, run_start) > counterMax) {
						keyOutput(realPosX, realPosY);
						counter = 1;
						prev_pos = 0;
						time(&run_start);

					}
				}
			}
		}

		counter++;
	}
	cvDestroyAllWindows();
}