import os import cv2, numpy as np from functools import reduce class FindPicColor: def __init__(self): self.path = None # 判断字符串是否为中文 def is_chinese(self,string): """ 检查整个字符串是否包含中文 :param string: 需要检查的字符串 :return: bool """ for ch in string: if u'\u4e00' <= ch <= u'\u9fff': return True return False def create_random_img(self, width, height, item=3): img = np.random.randint(0, 255, (width, height, item)) img = img.astype(np.uint8) return img # 转换大漠格式RGB "ffffff-303030" 为 BGR遮罩范围(100,100,100),(255,255,255) def __color_to_range(self, color, sim): if sim <= 1: if len(color) == 6: c = color weight = "000000" elif "-" in color: c, weight = color.split("-") else: raise Exception("参数错误") else: raise Exception("参数错误") color = int(c[4:], 16), int(c[2:4], 16), int(c[:2], 16) weight = int(weight[4:], 16), int(weight[2:4], 16), int(weight[:2], 16) sim = int((1 - sim) * 255) lower = tuple(map(lambda c, w: max(0, c - w - sim), color, weight)) upper = tuple(map(lambda c, w: min(c + w + sim, 255), color, weight)) return lower, upper def __imread(self, path): # 读取图片 if self.is_chinese(path): img = cv2.imdecode(np.fromfile(path, dtype=np.uint8), -1) # 避免路径有中文 else: img = cv2.imread(path) return img def __inRange(self, img, lower, upper): mask = cv2.inRange(img, np.array(lower), np.array(upper)) img = cv2.bitwise_and(img, img, mask=mask) return img def __imgshow(self, img): windows_name = "img" cv2.imshow(windows_name, img) cv2.waitKey() cv2.destroyWindow(windows_name) def __ps_to_img(self, img, ps): """ :param img: cv图像 :param ps: 偏色 :return: 偏色后的cv图像 """ # 判断是RGB偏色还是HSV偏色,对应使用遮罩过滤 if not ps: return img elif type(ps) == str: lower, upper = self.__color_to_range(ps, 1) img = self.__inRange(img, lower, upper) elif type(ps) == tuple: lower, upper = ps img_hsv1 = cv2.cvtColor(img, cv2.COLOR_BGR2HSV) img = self.__inRange(img_hsv1, lower, upper) return img def __FindPic(self, img, pic_name, delta_color, sim, method, drag=None): # 读取图片 img1 = img # img2 = self.__imread(self.path + os.path.sep + pic_name) img2 = cv2.imread(pic_name) # 判断是RGB偏色还是HSV偏色,对应使用遮罩过滤 img1 = self.__ps_to_img(img1, delta_color) img2 = self.__ps_to_img(img2, delta_color) # 利用cv的模板匹配找图 result = cv2.matchTemplate(img1, img2, method) min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result) yloc, xloc = np.where(result >= sim) height, width = img2.shape[:2] return result, min_val, max_val, min_loc, max_loc, yloc, xloc, height, width # 单点比色 def CmpColor(self, img, x, y, color, sim=1): """ :param x: 坐标x :param y: 坐标y :param color: 颜色字符串,可以支持偏色,"ffffff-202020",最多支持一个 :param sim:相似度(0.1-1.0) (0,255) :return:bool """ lower, upper = self.__color_to_range(color, sim) if not lower is None: new_color = img[y, x] for i in [0, 1, 2]: if new_color[i] < lower[i] or new_color[i] > upper[i]: return False return True return False # 范围找色 def FindColor(self, img, x1, y1, color, sim, dir=None): lower, upper = self.__color_to_range(color, sim) height, width = img.shape[:2] b, g, r = cv2.split(img) b = b.reshape(1, height * width) g = g.reshape(1, height * width) r = r.reshape(1, height * width) key1 = np.where(lower[0] <= b) key2 = np.where(lower[1] <= g) key3 = np.where(lower[2] <= r) key4 = np.where(upper[0] >= b) key5 = np.where(upper[1] >= g) key6 = np.where(upper[2] >= r) if len(key1[0]) and len(key2[0]) and len(key3[0]) and len(key4[0]) and len(key5[0]) and len(key6[0]): keys = reduce(np.intersect1d, [key1, key2, key3, key4, key5, key6]) # 相似度越小,交集数据越多,找的慢,相似度越大,找的越快,主要耗时的地方 if len(keys): x, y = divmod(keys[1], width) return 0, x + x1, y + y1 return -1, -1, -1 # 找图 def FindPic(self, img, findPicPath, delta_color, sim, method=5, drag=None): """ :param x1:区域的左上X坐标 :param y1:区域的左上Y坐标 :param x2:区域的右下X坐标 :param y2:区域的右下Y坐标 :param pic_name:图片名,只能单个图片 :param delta_color:偏色,可以是RGB偏色,格式"FFFFFF-202020",也可以是HSV偏色,格式((0,0,0),(180,255,255)) :param sim:相似度,和算法相关 :param dir:仿大漠,总共有6总 :param drag:是否在找到的位置画图并显示,默认不画 方差匹配方法:匹配度越高,值越接近于0。 归一化方差匹配方法:完全匹配结果为0。 相关性匹配方法:完全匹配会得到很大值,不匹配会得到一个很小值或0。 归一化的互相关匹配方法:完全匹配会得到1, 完全不匹配会得到0。 相关系数匹配方法:完全匹配会得到一个很大值,完全不匹配会得到0,完全负相关会得到很大的负数。 (此处与书籍以及大部分分享的资料所认为不同,研究公式发现,只有归一化的相关系数才会有[-1,1]的值域) 归一化的相关系数匹配方法:完全匹配会得到1,完全负相关匹配会得到-1,完全不匹配会得到0。 :return: """ result, min_val, max_val, min_loc, max_loc, yloc, xloc, height, width = self.__FindPic(img, findPicPath, delta_color, sim, method=5, drag=None) if len(xloc): x, y = max_loc[0], max_loc[1] if drag: img = cv2.rectangle(img, (x, y), (x + width, y + height), (255, 0, 0), thickness=2) self.__imgshow(img) return 0, x, y return -1, -1, -1 if __name__ == '__main__': dm = FindPicColor() # (img, filePath, "b5b2af-333333", 0.9) img_path = "img/img_1.png" img = cv2.imread(img_path) monster_skill = ['s2', 's3', 's4', 's5', 's6', 's7', 's8', 's9','s10'] # ok_skill = [] for skill in monster_skill: filePath = os.path.join("img/", skill + ".bmp") res = dm.FindPic(img, filePath, "b5b2af-333333", 0.9) print(skill,res)