cv找图色算法

发布时间 2023-06-20 23:34:46作者: zwnsyw
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)