def cal_shadow_mask(self): ''' 获取阴影部分的罩层 ''' # 根据颜色的统计分布 阴影部分的颜色取值 # 实际是背景颜色的平移, 所以我们在背景色的阈值上, 左移 self.shd_lcolor = vutils.justify_rgb_value(self.bg_lcolor - 60) self.shd_ucolor = vutils.justify_rgb_value(self.bg_ucolor - 50) # 获取阴影的罩层 self.shd_mask = cv2.inRange(self.img, self.shd_lcolor, self.shd_ucolor) # 对获取的阴影罩层进行数学形态运算, 去除噪声 # 使用 9*9的核进行闭运算 kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (9, 9)) # open close self.shd_mask = cv2.morphologyEx(self.shd_mask, cv2.MORPH_OPEN, kernel)
def getChessFootByColor(img): ''' 利用颜色检索棋子位置 ''' MIN_CHESS_WIDTH = 65 MAX_CHESS_WIDTH = 80 MIN_CHESS_HEIGHT = 200 MAX_CHESS_HEIGHT = 230 chess_mask = getChessFootMask(img) image, contours, hier = cv2.findContours(chess_mask, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) contours = vutils.contours_filter(contours, minHeight=MIN_CHESS_HEIGHT, maxHeight=MAX_CHESS_HEIGHT, minWidth=MIN_CHESS_WIDTH, maxWidth=MAX_CHESS_WIDTH) if len(contours) == 1: # 刚好匹配到目标棋子 (x, y, w, h) = cv2.boundingRect(contours[0]) # 返回棋子坐标 return (int(x + w / 2), int(y + h)) else: return None
def getLittleWhitePointCenter(img, offset=(0,0), debug=False): ''' 找到下一跳的中心白点 ''' lowerb = (245, 245, 245) upperb = (245, 245, 245) mask = cv2.inRange(img, lowerb, upperb) image, contours, hierarchy = cv2.findContours(image=mask, mode=cv2.RETR_EXTERNAL, method=cv2.CHAIN_APPROX_SIMPLE) contours = vutils.contours_filter(contours, minWidth=35, maxWidth=45, minHeight=20, maxHeight=30) if len(contours) != 1: return None (x, y, w, h) = cv2.boundingRect(contours[0]) cx = int(x+w/2+offset[0]) cy = int(y+h/2+offset[1]) if debug == False: return (cx, cy) else: canvas = img.copy() print("w: {}, h: {}".format(w, h)) cv2.rectangle(canvas, (x,y), (x+w, y+h), (0,0,255), thickness=4) return (cx, cy), canvas
def getLittleWhitePointCenter(img, offset=(0,0), debug=False): ''' 找到下一跳的中心白点 ''' lowerb = (245, 245, 245) upperb = (245, 245, 245) mask = cv2.inRange(img, lowerb, upperb) image, contours, hierarchy = cv2.findContours(image=mask, mode=cv2.RETR_EXTERNAL, method=cv2.CHAIN_APPROX_SIMPLE) contours = vutils.contours_filter(contours, minWidth=35, maxWidth=45, minHeight=20, maxHeight=30) if len(contours) != 1: if debug: # 没有发现小白块 或者发现了多个 return (None, np.hstack((img, cv2.cvtColor(mask, cv2.COLOR_GRAY2BGR)))) return None # 确定白点的中心点 (x, y, w, h) = cv2.boundingRect(contours[0]) cx = int(x+w/2+offset[0]) cy = int(y+h/2+offset[1]) if debug == False: return (cx, cy) else: canvas = img.copy() cv2.rectangle(canvas, (x,y), (x+w, y+h), (0,0,255), thickness=4) return (cx, cy), canvas
def cal_background_mask(self): ''' 计算图像背景罩层 ''' # 采集图像顶部 高度为100像素点的区域,对背景颜色进行统计 # 你也可以根据你的画面自行修改 bg_sample = self.img[200:400, 0:200] # 分析背景样本图片, 得到背景颜色的阈值 (self.bg_lcolor, self.bg_ucolor) = vutils.cal_rgb_margin(bg_sample) # 因为摄像头采集的时候, 光照分布, 导致图片不同区域的背景颜色差异. # 背景色以顶部为准, 并适当放大一下背景色的取值范围, 我们这里取20 self.bg_lcolor = vutils.justify_rgb_value(self.bg_lcolor - 20) self.bg_ucolor = vutils.justify_rgb_value(self.bg_ucolor + 20) # 获取背景罩层 self.bg_mask = cv2.inRange(self.img, self.bg_lcolor, self.bg_ucolor) # 对背景罩层进行处理 数学形态学运算, 去除噪声 # 用7*7 的核对背景图片进行闭运算 kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (7, 7)) self.bg_mask = cv2.morphologyEx(self.bg_mask, cv2.MORPH_CLOSE, kernel)
def getChessFootPosi(img): MIN_CHESS_WIDTH = 65 MAX_CHESS_WIDTH = 80 MIN_CHESS_HEIGHT = 200 MAX_CHESS_HEIGHT = 230 chess_mask = getChessBinImg(img) image, contours, hier = cv2.findContours(chess_mask, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) contours = vutils.contours_filter(contours, minHeight=MIN_CHESS_HEIGHT, maxHeight=MAX_CHESS_HEIGHT, minWidth=MIN_CHESS_WIDTH, maxWidth=MAX_CHESS_WIDTH) rects = [] for c in contours: (x, y, w, h) = cv2.boundingRect(c) rects.append((x, y, w, h)) return rects, contours
def cal_top_box_center(self): ''' 获取最顶层盒子的中心 ''' canvas = np.copy(self.img) image, contours, hier = cv2.findContours(self.box_mask, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) contours = vutils.contours_filter(contours, minHeight=50, minWidth=50) boxes = [] for c in contours: x, y, w, h = cv2.boundingRect(c) boxes.append((x, y, w, h)) top_box = boxes[0] top_contour = contours[0] for i in range(1, len(contours)): # 对比box的y坐标的值, 获取最小 也就是最靠上方的盒子 box = boxes[i] if top_box[1] > box[1]: top_box = box top_contour = contours[i] # print("box : {}".format(top_box)) (x, y, w, h) = top_box # 这里有个问题, 如果两个box离的很近 # 当前的盒子跟下一跳的盒子的区域会联通, 所以需要裁减 # 通过判断chess是否在box中, 来判断是否相邻的两个盒子被当成一个。 if vutils.isPointInRectangle(top_box, self.chs_fposi): # 检测到盒子连体, 采用备用方案, 获取下一个盒子的中心。 miny = 100000 ptop = None for points in top_contour: (px, py) = points[0] if py < miny: miny = py ptop = (px, py) # 把最顶上的坐标中x点作为box中心点。 cx = ptop[0] dh = int(abs(cx - self.chs_fposi[0]) / math.sqrt(3)) cy = self.chs_fposi[1] - dh radius = abs(cy - ptop[1]) (x, y, w, h) = (cx - radius, ptop[1], 2 * radius, 2 * radius) # 再次判断chess底部是否落在矩形区域内 # 进行对应的放缩 # 下面的这段代码纯属靠凡哥发挥 实验有效。 if vutils.isPointInRectangle((x, y, w, h), self.chs_fposi): # 这里移动值, 我选的是chess矩形区域的宽度 delta = self.chs_rect[2] if self.chs_fposi[0] < x: x -= delta else: x += delta w -= delta h -= delta self.nbox_rect = (x, y, w, h) # 重新调整中心 self.nbox_center = (int(x + w / 2), int(y + h / 3)) else: self.nbox_rect = (x, y, w, h) (x, y, w, h) = self.nbox_rect # 计算中心点, 立体盒子的高的1/3处大概就是中心点y坐标的位置 # 中心点的x左边标定为宽度的1/2处. self.nbox_center = (int(x + w / 2), int(y + h / 3)) (x22, y22, w22, h22) = self.nbox_rect x33, y33 = self.chs_fposi x44, y44 = self.nbox_center cv2.rectangle(canvas, (x22, y22), (x22 + w22, y22 + h22), (255, 0, 0), 5) cv2.circle(canvas, (x33, y33), 5, (0, 0, 255), -1) cv2.circle(canvas, (x44, y44), 5, (0, 0, 255), -1) cv2.imshow('canvas', canvas)
while True: img = ADBHelper.getScreenShotByADB() # 获取棋子的位置 chess_posi = getChessFootPosi(img) # 获取下一跳中心的位置 center_posi,canvas = getNextJumpPlatCenter(img) if chess_posi is None or center_posi is None: break cv2.imshow('NextCenterFinder', canvas) # 计算距离 distance = vutil.cal_distance(chess_posi, center_posi) # 折算延迟 delay = distance2time(distance) rc = ADBHelper.pressOnScreen((500, 500), delay=delay) if rc: print("成功点击 并延时 3s") if debug == True: # 保存日志 注意需要创建文件路径 img_name = f"{datetime.datetime.now():%Y-%m-%d-%H-%M-%S-%f.png}" cv2.imwrite('./output/AutoJump/screenshot/'+img_name, img) cv2.imwrite('./output/AutoJump/log/'+img_name, canvas) key = cv2.waitKey(3000)
# -*- coding: utf-8 -*- from FGJumperMaster import FGJumperMaster from ADBHelper import ADBHelper from FGVisonUtil import FGVisionUtil as vutil import cv2 import numpy as np import time import datetime # 初次读入图片 img = ADBHelper.getScreenShotByADB() vutil.printImgInfo(img) adb = ADBHelper(1080, 1920) cv2.namedWindow('image', flags=cv2.WINDOW_NORMAL | cv2.WINDOW_FREERATIO) keyPressed = -1 def distance2time(distance): ratio = 1.53 # 事件必须是整数类型 return int(distance * ratio) def saveSampleImg(jmaster, img, tag=True): img_name = f"{datetime.datetime.now():%Y-%m-%d-%H-%M-%S.png}" if tag: cv2.imwrite("./samples/right/" + img_name, img)
def getNextJumpPlatCenter(img, debug=False): ''' 获取下一跳平台的中心 ''' # 声明画布 canvas = img.copy() edge = getCannyEdge(img) # 获取边缘信息 image, contours, hierarchy = cv2.findContours(image=edge, mode=cv2.RETR_EXTERNAL, method=cv2.CHAIN_APPROX_SIMPLE) contours = vutils.contours_filter(contours, minWidth=10, minHeight=10) # 找到最大 next_box_cnt = min(contours, key=lambda cnt: tuple(cnt[cnt[:,:,1].argmin()][0])[1]) # 顶点序号 top_point_idx = next_box_cnt[:,:,1].argmin() # 顶点 top_point = tuple(next_box_cnt[top_point_idx,0,:2]) # 背景色取样 img_hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV) refer_back_color = img_hsv[top_point[1], top_point[0]] left_idx_itr = 1 right_idx_itr = -1 if next_box_cnt[top_point_idx+left_idx_itr,0, 0] > top_point[0]: left_idx_itr, right_idx_itr = right_idx_itr, left_idx_itr right_pt_idx = top_point_idx+right_idx_itr left_pt_idx = top_point_idx+left_idx_itr right_point = None left_point = None # 寻找右边的边缘点 while True: # 当前right point right_point = tuple(next_box_cnt[right_pt_idx,0,:2]) next = tuple(next_box_cnt[right_pt_idx+right_idx_itr,0,:2]) if not isNextPoint(right_point, next, x_direction=1): break elif isInShadow(refer_back_color, img_hsv[next[1]+5, next[0]]): # 判断边缘下方的五个像素是不是阴影 break right_pt_idx += right_idx_itr # 寻找左边的边缘点 while True: left_point = tuple(next_box_cnt[left_pt_idx,0,:2]) next = tuple(next_box_cnt[left_pt_idx+left_idx_itr,0,:2]) if not isNextPoint(left_point, next, x_direction=-1): break elif isInShadow(refer_back_color, img_hsv[next[1]+5, next[0]]): break left_pt_idx += left_idx_itr # 调整三个点的位置 (top_point, left_point, right_point) = adjust_points(top_point, left_point, right_point) down_point = (left_point[0]+right_point[0]-top_point[0],left_point[1]+right_point[1]-top_point[1]) # 生成下一跳平台的搜索区域 # TODO 检索椭圆形 四边形 contour = np.array([ [list(top_point)], [list(right_point)], [list(down_point)], [list(left_point)]]) (x, y, w, h) = cv2.boundingRect(contour) # 在矩形区域内检索小白点提示 res_pt = getLittleWhitePointCenter(img[y:y+h, x:x+w], offset=(x,y)) center_point = None if res_pt is not None: # print("find white point") # print(res_pt) center_point = res_pt else: # 取left_point与right_point 中间处作为中心点 cx = int((left_point[0]+right_point[0])/2) cy = int((left_point[1]+right_point[1])/2) center_point = (cx, cy) if debug == True: cv2.drawContours(image=canvas, contours=[next_box_cnt], contourIdx=-1, color=(125,125,125), thickness=1) canvas[top_point[1],top_point[0]] = [0,0,255] canvas[left_point[1],left_point[0]] = [0, 255, 0] canvas[right_point[1], right_point[0]] = [255, 0, 0] next_right = tuple(next_box_cnt[right_pt_idx+right_idx_itr,0,:2]) canvas[next_right[1], next_right[0]] = [0,0,0] next_left = tuple(next_box_cnt[left_pt_idx+left_idx_itr,0,:2]) canvas[next_left[1], next_left[0]] = [0,0,0] print('LEFT: {}, TOP: {}, RIGHT: {}'.format(left_point, top_point, right_point)) else: # 设定圆圈半径 pt_radius = 10 # 绘制轮廓 cv2.drawContours(image=canvas, contours=[next_box_cnt], contourIdx=-1, color=(0,0,255), thickness=3) cv2.circle(canvas, top_point, pt_radius, (0, 255, 0), thickness=-1) cv2.circle(canvas, left_point, pt_radius, (0, 255, 255), thickness=-1) cv2.circle(canvas, right_point, pt_radius, (255, 0, 0), thickness=-1) cv2.circle(canvas, down_point, pt_radius, (255,255,0), thickness=-1) # 绘制检索框 # x,y,w,h = next_plat_rect # cv2.rectangle(canvas, (x,y), (x+w, y+h), (255,255,255), thickness=2) cv2.circle(canvas, center_point, pt_radius, (45,100,255), thickness=-1) return center_point,canvas
def getNextJumpPlatCenter(img, debug=False): ''' 获取下一跳平台的中心 ''' # 声明画布 canvas = img.copy() # 获取边缘图像 edge = getCannyEdge(img) # 获取边缘信息 image, contours, hierarchy = cv2.findContours(image=edge, mode=cv2.RETR_EXTERNAL, method=cv2.CHAIN_APPROX_SIMPLE) # 对轮廓点集进行过滤 contours = vutils.contours_filter(contours, minWidth=50, minHeight=50) # 找到最高顶点所在轮廓 next_box_cnt = min(contours, key=lambda cnt: tuple(cnt[cnt[:,:,1].argmin()][0])[1]) # 获取顶点序号 top_point_idx = next_box_cnt[:,:,1].argmin() # 顶点 top_point = tuple(next_box_cnt[top_point_idx,0,:2]) # 背景色取样 img_hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV) refer_back_color = img_hsv[top_point[1], top_point[0]] # 分别向左向右找到边界点 left_idx_itr = 1 right_idx_itr = -1 # 校正方向对应的序号叠加 if next_box_cnt[top_point_idx+left_idx_itr,0, 0] > top_point[0]: left_idx_itr, right_idx_itr = right_idx_itr, left_idx_itr right_pt_idx = top_point_idx+right_idx_itr left_pt_idx = top_point_idx+left_idx_itr right_point = None left_point = None # 寻找右边的边缘点 while True: # 当前right point right_point = tuple(next_box_cnt[right_pt_idx,0,:2]) # 获取下一个右边的点 next = tuple(next_box_cnt[right_pt_idx+right_idx_itr,0,:2]) # 判断这个点是否可以延伸 if not isNextPoint(right_point, next, x_direction=1): break elif isInShadow(refer_back_color, img_hsv[next[1]+5, next[0]]): # 判断边缘下方的五个像素是不是阴影 break right_pt_idx += right_idx_itr # 寻找左边的边缘点 while True: left_point = tuple(next_box_cnt[left_pt_idx,0,:2]) next = tuple(next_box_cnt[left_pt_idx+left_idx_itr,0,:2]) if not isNextPoint(left_point, next, x_direction=-1): break elif isInShadow(refer_back_color, img_hsv[next[1]+5, next[0]]): break left_pt_idx += left_idx_itr # 调整三个点的位置 (top_point, left_point, right_point) = adjust_points(top_point, left_point, right_point) # 通过平行四边形的定理 获取下方的点 down_point = (left_point[0]+right_point[0]-top_point[0],left_point[1]+right_point[1]-top_point[1]) # 生成下一跳平台的搜索区域 contour = np.array([ [list(top_point)], [list(right_point)], [list(down_point)], [list(left_point)]]) (x, y, w, h) = cv2.boundingRect(contour) # 在矩形区域内检索小白点提示 res_pt = getLittleWhitePointCenter(img[y:y+h, x:x+w], offset=(x,y)) center_point = None if res_pt is not None: # 如果存在小白点 就直接作为中心点 center_point = res_pt else: # 取left_point与right_point 中间处作为中心点 cx = int((left_point[0]+right_point[0])/2) cy = int((left_point[1]+right_point[1])/2) center_point = (cx, cy) # 设定圆圈半径 pt_radius = 10 # 绘制下一跳中心点 cv2.circle(canvas, center_point, pt_radius, (45,100,255), thickness=-1) if debug == True: # 绘制轮廓 cv2.drawContours(image=canvas, contours=[next_box_cnt], contourIdx=-1, color=(0,0,255), thickness=3) # 绘制平行四边形的四个点 cv2.circle(canvas, top_point, pt_radius, (0, 255, 0), thickness=-1) cv2.circle(canvas, left_point, pt_radius, (0, 255, 255), thickness=-1) cv2.circle(canvas, right_point, pt_radius, (255, 0, 0), thickness=-1) cv2.circle(canvas, down_point, pt_radius, (255,255,0), thickness=-1) return center_point,canvas