From 021be6fa01929fbf9f9559fd6c591ad0f1e9a6e1 Mon Sep 17 00:00:00 2001 From: andy <1414772332@qq.com> Date: Sat, 1 Mar 2025 23:49:41 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=9E=E7=8E=B0=E8=87=AA=E5=8A=A8=E8=BE=93?= =?UTF-8?q?=E5=85=A5=E5=AF=86=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 4 + ReadMe.txt | 5 ++ cv2_test.py | 42 +++++++++ index.json | 3 + log.py | 41 +++++++++ moter.py | 53 ++++++++++++ ocr.py | 47 +++++++++++ password.py | 10 +++ touch.py | 120 ++++++++++++++++++++++++++ touch2.py | 239 ++++++++++++++++++++++++++++++++++++++++++++++++++++ win32.py | 22 +++++ 11 files changed, 586 insertions(+) create mode 100644 .gitignore create mode 100644 ReadMe.txt create mode 100644 cv2_test.py create mode 100644 index.json create mode 100644 log.py create mode 100644 moter.py create mode 100644 ocr.py create mode 100644 password.py create mode 100644 touch.py create mode 100644 touch2.py create mode 100644 win32.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c2ce902 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +__pycache__/ +scr_path/ +*.png +log.txt \ No newline at end of file diff --git a/ReadMe.txt b/ReadMe.txt new file mode 100644 index 0000000..81aede5 --- /dev/null +++ b/ReadMe.txt @@ -0,0 +1,5 @@ + + + +2025.3.1 + 自动识别 多屏协同 窗口 抓取截图 \ No newline at end of file diff --git a/cv2_test.py b/cv2_test.py new file mode 100644 index 0000000..12fe988 --- /dev/null +++ b/cv2_test.py @@ -0,0 +1,42 @@ +import cv2 +import numpy as np + + + + + +#定义形状检测函数 +def ShapeDetection(img:cv2.typing.MatLike): + contours,hierarchy = cv2.findContours(img,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_NONE) #寻找轮廓点 + all_cont=len(contours) + # print(f"all_cont:{all_cont}") + boll_count=0 + for obj in contours: + perimeter = cv2.arcLength(obj,True) #计算轮廓周长 + approx = cv2.approxPolyDP(obj,0.02*perimeter,True) #获取轮廓角点坐标 + # CornerNum = len(approx) #轮廓角点的数量 + # area = cv2.contourArea(obj) #计算轮廓内区域的面积 + # print(f"area:{area}, perimeter:{perimeter}, CornerNum:{CornerNum}") + x, y, w, h = cv2.boundingRect(approx) #获取坐标值和宽度、高度 + # print(f"{x},{y},{x+w},{y+h}") + img_obj=img[y:y+h,x:x+w] + # print(img_obj) + nonzero_num=np.count_nonzero(img_obj) + percentage=nonzero_num/img_obj.size + # print(f"nonzero_num:{nonzero_num},size={img_obj.size}, pos:{percentage}") + # 通过计算图像白值的比例来判断实心还是空心 + if(percentage>0.55): + boll_count+=1 + return (all_cont,boll_count) + +def calc_boll_count(path:str): + img = cv2.imread(path) + imgGray = cv2.cvtColor(img,cv2.COLOR_RGB2GRAY) # 转为灰度图 + ret, binary = cv2.threshold(imgGray,60,255,cv2.THRESH_BINARY) # 转为二值图 + return ShapeDetection(binary) #形状检测 + +if __name__ == "__main__": + ret=calc_boll_count("1.png") + print(ret) + ret=calc_boll_count("1644-440 172x27.png") + print(ret) diff --git a/index.json b/index.json new file mode 100644 index 0000000..c9903f3 --- /dev/null +++ b/index.json @@ -0,0 +1,3 @@ +{ + "index": 1681 +} \ No newline at end of file diff --git a/log.py b/log.py new file mode 100644 index 0000000..c7f2a36 --- /dev/null +++ b/log.py @@ -0,0 +1,41 @@ +import time +import sys + + +# 同一个进程中所有调用这个文件的 .py 文件都使用这个变量 +_log_fp=None + +def _time(): + return '['+time.strftime("%Y-%m-%d %H:%M:%S")+']' + +def myprint_dec(func): + def wrapper(*args, **kwargs): + # 在这里添加额外的功能 + print(*args, **kwargs) + if(_log_fp is not None): + _log_fp.write(_time()) + kwargs["file"]=_log_fp + result = func(*args, **kwargs) + _log_fp.flush() + else: + result=None + return result + return wrapper + +myprint=myprint_dec(print) + +def mywrite(data:str): + if(_log_fp is not None): + txt=data.replace('\r','\n') + txt=txt.replace('\n\n','\n') + txt=txt.replace('\n\n','\n') + _log_fp.write(txt) + _log_fp.flush() + sys.stdout.write(data) + sys.stdout.flush() + +def log_init(file_name:str): + global _log_fp + if _log_fp is None: + _log_fp=open(file_name,mode="a+",encoding="utf-8") + diff --git a/moter.py b/moter.py new file mode 100644 index 0000000..3638b25 --- /dev/null +++ b/moter.py @@ -0,0 +1,53 @@ +#pip install pyserial + +#查看可用的端口 + +# coding:utf-8 + +import time + +import serial.tools.list_ports + +plist = list(serial.tools.list_ports.comports()) + +print("端口数>>>",len(plist)) + +if len(plist) <= 0: + + print("没有发现端口!") + +else: + plist_0 = list(plist[0]) #在第一个端口的情况 + + #plist_0 = list(plist[1]) #在第二个端口的情况 + + serialName = "com8" + + serialFd = serial.Serial(serialName, 93450, timeout=60) + + print("端口名>>>", serialFd.name) + + while True: + try: + time.sleep(2) #等待机器准备好,重要!!!!! + n = serialFd.write("X100Y130\r".encode()) #移动到50*50毫米位置 + time.sleep(1) + n = serialFd.write("X50Y0\r".encode()) #移动到50*50毫米位置 + time.sleep(1) + n = serialFd.write("X50Y51.5\r".encode()) #移动到50*50毫米位置 + time.sleep(1) + for i in range(10): + n = serialFd.write("Z6\r".encode()) #按下 + time.sleep(0.05) + n = serialFd.write("Z0\r".encode()) #抬起 + time.sleep(1) + # time.sleep(1) + n = serialFd.write("X0Y0\r".encode()) #回原点 + time.sleep(1) + except KeyboardInterrupt as k: + time.sleep(1) + n = serialFd.write("Z0\r".encode()) #抬起 + time.sleep(0.1) + n = serialFd.write("X0Y0\r".encode()) #回原点 + break + serialFd.close() diff --git a/ocr.py b/ocr.py new file mode 100644 index 0000000..de8c5c7 --- /dev/null +++ b/ocr.py @@ -0,0 +1,47 @@ +import os +import sys +import time +import easyocr +import pyautogui + +from cv2_test import calc_boll_count + + +reader = easyocr.Reader(['ch_sim','en'], gpu = True) # need to run only once to load model into memory +tmp_path=os.getenv("TEMP") + +if not os.path.exists("scr_path"): + os.mkdir("scr_path") + +def get_text(left:int,top:int,wid:int,hit:int)->list[str]: + img = pyautogui.screenshot(region=[left, top, wid, hit]) + img.save(f"{tmp_path}\\screenshot.png") + result = reader.readtext(f"{tmp_path}\\screenshot.png") + ret=[] + for item in result: + ret.append(item[1]) + return ret + +def get_scr(left:int,top:int,wid:int,hit:int,text:str): + img = pyautogui.screenshot(region=[left, top, wid, hit]) + img.save(f"scr_path\\{time.strftime('%Y%m%d_%H%M%S')}_{text}.png") + +def get_boll_count(left:int,top:int,wid:int,hit:int): + path=f"{tmp_path}\\boll_scr.png" + img = pyautogui.screenshot(region=[left, top, wid, hit]) + img.save(path) + return calc_boll_count(path) + + +def mouse_touch(left:int,top:int,wid:int,hit:int): + x,y=pyautogui.position() + if(x>left and xtop and ydict: + data_file="index.json" + if os.path.exists(data_file): + with open(data_file,mode='r') as f: + return json.loads(f.read()) + return {"index":0} + +if __name__ == "__main__": + s=ser() + s.open("com6") + password=data_get().get("index",0) + log_init("log.txt") + myprint(f"start_index:{password}") + while(True): + try: + if(find_text("手机存储")): + myprint("enter") + s.enter() + if(find_text("请输入解锁密码")): + myprint(f"try:{password:06d}") + s.passsword(f"{password:06d}") + except Exception as e: + myprint(e) + data_save({"index":password}) + break + except KeyboardInterrupt as k: + myprint(k) + data_save({"index":password}) + break + if password>999999: + myprint("try end") + break + myprint(f"end_index:{password}") + s.close() diff --git a/touch2.py b/touch2.py new file mode 100644 index 0000000..6986295 --- /dev/null +++ b/touch2.py @@ -0,0 +1,239 @@ +import os +import sys +import time +import json +import serial + +from ocr import get_text, mouse_touch, get_scr, get_boll_count +from log import log_init, myprint +from win32 import get_wind_pos + +# 识别文字的截图区域 +_ocr_pos=[1545,157,367,384] + +# 识别小圆点的截图区域 +_ocr_boll=[1644,440+3,172,27] + +# 自动点击屏幕的区域 +_mouse_touch=[1550,760,140,70] + +# 获取最新的窗口位置 +def flush_wind_pos(): + global _ocr_pos,_ocr_boll,_mouse_touch + not_find=False + while(True): + pos=get_wind_pos() + if(pos[0]<=0 and pos[1]<=0 and pos[2]<=0 and pos[3]<=0): + if not_find==False: + myprint("未找到相机窗口") + not_find=True + time.sleep(1) + continue + _ocr_pos=[pos[0]+0,pos[1]+180,375,296] + _ocr_boll=[pos[0]+99,pos[1]+440,172,27] + _mouse_touch=[pos[0]+0,pos[1]+603,145,70] + return + +# 识别屏幕文字 +def identify_text(): + flush_wind_pos() + mouse_touch(_mouse_touch[0],_mouse_touch[1],_mouse_touch[2],_mouse_touch[3]) + text=get_text(_ocr_pos[0],_ocr_pos[1],_ocr_pos[2],_ocr_pos[3]) + print(text) + return text +def find_text(text:str,key:str): + for item in text: + if(item.find(key)>=0): + return True + return False + +# 识别已输入数量 +def find_input_count(): + flush_wind_pos() + ret=get_boll_count(_ocr_boll[0],_ocr_boll[1],_ocr_boll[2],_ocr_boll[3]) + if(ret[0]==6): + return ret[1] + myprint("未识别到已输入个数") + return 0 + +def data_save(data:dict): + data_file="index.json" + with open(data_file,mode='w+') as f: + f.write(json.dumps(data,sort_keys=True, indent=2, separators=(',', ': '))) + + + +class ser(object): + # 1 x_step 18 y_step 15 + step_x=16 + step_y=14 + touch_table={ + "1":(53,70), + "2":(53+step_x,70), + "3":(53+step_x*2,70), + "4":(53,70+step_y), + "5":(53+step_x,70+step_y), + "6":(53+step_x*2,70+step_y), + "7":(53,70+step_y*2), + "8":(53+step_x,70+step_y*2), + "9":(53+step_x*2,70+step_y*2), + "0":(53+step_x,70+step_y*3), + } + def __init__(self): + self.ser=None + self.x=0 + self.y=0 + def open(self,com:str): + try: + self.ser = serial.Serial(port=com, baudrate=93450, timeout=60) + except Exception: + print(f"serial com:{com} open failed.") + sys.exit(-1) + time.sleep(2) + def close(self): + if not (self.ser is None): + self.ser.close() + self.ser=None + def write(self,t:str): + # print("send:",t) + if not (self.ser is None): + self.ser.write(t.encode(encoding="utf-8")) + def togo(self,x:int,y:int): + if(x<0 or x>100 or y<0 or y>150): + print(f"位置超出范围 x={x}, y={y}") + return + self.write(f"X{x}Y{y}\r") + # 电机运行速度160mm/s + disx=abs(self.x-x) + disy=abs(self.y-y) + time.sleep((disx+disy)/160+0.1) + self.x=x + self.y=y + def touch(self): + self.write(f"Z6\r") + time.sleep(0.1) + self.write(f"Z0\r") + time.sleep(0.05) + def reset(self): + self.write(f"Z0\r") + time.sleep(0.05) + self.togo(0,0) + def enter(self): + self.togo(70+20,27) + self.touch() + def back(self): + self.togo(70,132) + self.touch() + # 避让摄像头 也是在取消按钮的位置 + def avoid(self): + self.togo(70,132) + # 按键位置测试 + def password_test(self,p:str): + for i in p: + pos=self.touch_table[i] + myprint(f"点击 {i}:{pos}") + self.togo(pos[0],pos[1]) + time.sleep(2) + def password(self,p:str): + index=0 + p_len=len(p) + while indexdict: + data_file="index.json" + if os.path.exists(data_file): + with open(data_file,mode='r') as f: + return json.loads(f.read()) + return {"index":0} + + +if __name__ == "__main1__": + s=ser() + s.open("com8") + password=data_get().get("index",0) + log_init("log.txt") + myprint(f"起始密码:{password}") + s.avoid() + unknown_scr=False + while(True): + try: + text=identify_text() + if(find_text(text,"安装系统软件")): + unknown_scr=False + myprint("进入密码界面") + s.enter() + # s.avoid() + elif(find_text(text,"请输入解锁密码")): + unknown_scr=False + pw=f"{password:06d}" + myprint(f"开始尝试:{pw}") + if(s.password(pw)): + myprint(f"密码已输入") + password+=1 + s.avoid() + flush_wind_pos() + get_scr(_ocr_pos[0],_ocr_pos[1],_ocr_pos[2],_ocr_pos[3],f"{pw}") + elif(find_text(text,"手机已锁定")): + time.sleep(1) + else: + if(unknown_scr==False): + myprint("进入未知界面") + myprint(f"界面信息为;{text}") + unknown_scr=True + time.sleep(1) + except Exception as e: + myprint(e) + data_save({"index":password}) + print("终止运行") + break + except KeyboardInterrupt as k: + myprint(k) + data_save({"index":password}) + print("终止运行") + break + myprint(f"终止密码:{password}") + s.reset() + s.close() + + +if __name__ == "__main__": + # identify_text() + # get_scr(_ocr_pos[0],_ocr_pos[1],_ocr_pos[2],_ocr_pos[3],f"test") + # find_input_count() + s=ser() + s.open("com8") + s.avoid() + while True: + try: + s.password_test("1234567890") + except Exception as e: + myprint(e) + break + except KeyboardInterrupt as k: + myprint(k) + break + s.reset() + s.close() diff --git a/win32.py b/win32.py new file mode 100644 index 0000000..7976114 --- /dev/null +++ b/win32.py @@ -0,0 +1,22 @@ +import os +import sys +import time +import win32gui + + + +def get_wind_pos(): + hwnd = win32gui.FindWindow(None, "多屏协同") + if hwnd == 0: + return (0,0,0,0) + else: + # print("找到相机窗口") + # print("hwnd:",hwnd) + # print("title:",win32gui.GetWindowText(hwnd)) + # print("class:",win32gui.GetClassName(hwnd)) + # print("pos:",win32gui.GetWindowRect(hwnd)) + return win32gui.GetWindowRect(hwnd) + + +if __name__=="__main__": + print(get_wind_pos())