基于OpenCV进行图片主要颜色识别

0、前言

        对于一张非纯色的图片,如果想要识别其颜色,可能没法直接识别。本文提供了一种利用颜色直方图来识别主要颜色的方法。

1、基础知识

        本文采用的方法适用于对象主体颜色占总区域面积比例较大的情况,不适合哪些颜色较为分散、杂乱的情况。

        基本步骤步骤如下:

  1. 图像crop, 降低周围背景的影响;
  2. 统计H、S、V三个分量的直方图;
  3. 分别取H、S、V的主要成分,判断落在哪种颜色区间内;

        本文主要参考的颜色区间见表:

绿
hmin000015611263578100125
hmax180180180101802534;7799124155
smin00043434343434343
smax2554330255255255255255255255
vmin04622146464646464646
vmax46220255255255255255255255255

2、实现代码

        颜色识别分类器代码如下:

import cv2
import numpy as np


class ColorClassify(object):
    def __init__(self, debug=True):
        self.debug = debug
        # 颜色范围,主要参考HSV颜色分量分布表:
        # 其中,gray和white进行了平衡中线调整,使白色占比更大些
        self.hsv_color = {
            "Black": [(0, 180), (0, 255), (0, 46)],
            "Gray": [(0, 180), (0, 43), (46, 150)],
            "White": [(0, 180), (0, 30), (151, 255)],
            "Red": [[(0, 10), (156, 180)], (43, 255), (46, 255)],
            "Orange": [(11, 25), (43, 255), (46, 255)],
            "Yellow": [(26, 34), (43, 255), (46, 255)],
            "Green": [(35, 77), (43, 255), (46, 255)],
            "CyanBlue": [(78, 99), (43, 255), (46, 255)],
            "Blue": [(100, 124), (43, 255), (46, 255)],
            "Purple": [(125, 155), (43, 255), (46, 255)],
        }

    def get_hsv_hist(self, img_hsv):
        h, s, v = img_hsv[:, :, 0], img_hsv[:, :, 1], img_hsv[:, :, 2]

        h_bins = [0, 11, 26, 35, 78, 100, 125, 156, 180]
        h_hist = np.histogram(h, h_bins)
        s_bins = [0, 30, 43, 255]
        s_hist = np.histogram(s, s_bins)
        v_bins = [0, 46, 151, 255]
        v_hist = np.histogram(v, v_bins)

        return [h_hist, s_hist, v_hist]

    def get_hsv_info(self, h_hist, s_hist, v_hist):
        infos = {
            "h": {
                "hist": h_hist,
                "argsort": None,
                "sort_normal": None,
                "arg_values": [],
            },
            "s": {
                "hist": s_hist,
                "argsort": None,
                "sort_normal": None,
                "arg_values": [],
            },
            "v": {
                "hist": v_hist,
                "argsort": None,
                "sort_normal": None,
                "arg_values": [],
            }
        }
        for k in infos:
            hist = infos[k]['hist']
            argsort = np.argsort(hist[0])[::-1][:2]  # 逆序排列, 取前面最大的两个
            infos[k]['argsort'] = argsort
            infos[k]['sort_normal'] = hist[0][argsort] / (sum(hist[0]) * 3)
            for idx in argsort:
                value_mean = round(np.mean([hist[1][idx], hist[1][idx + 1]]))
                infos[k]['arg_values'].append(value_mean)
        return infos

    def get_hsv_main_info(self, h_hist, s_hist, v_hist):
        h_main_idx = np.argmax(h_hist[0])
        h_main = [h_hist[1][h_main_idx], h_hist[1][h_main_idx + 1]]

        s_weights = np.array([1, 1, 1])
        s_array = s_hist[0] * s_weights
        s_main_idx = np.argmax(s_array)
        s_main = [s_hist[1][s_main_idx], s_hist[1][s_main_idx + 1]]

        v_weights = np.array([1, 1, 1])
        v_array = v_hist[0] * v_weights
        v_main_idx = np.argmax(v_array)
        v_main = [v_hist[1][v_main_idx], v_hist[1][v_main_idx + 1]]

        if self.debug:
            print("h_hist: {}\ns_hist: {}\nv_hist: {}".format(h_hist, s_hist, v_hist))
            print("h_main: {}, s_main: {}, v_main: {}".format(h_main, s_main, v_main))
        return np.mean(h_main), np.mean(s_main), np.mean(v_main)

    def hsv2color(self, infos):
        # print(infos)
        h_info = infos['h']
        s_info = infos['s']
        v_info = infos['v']
        result = {}
        for snh, avh in zip(h_info['sort_normal'], h_info['arg_values']):
            for sns, avs in zip(s_info['sort_normal'], s_info['arg_values']):
                for snv, avv in zip(v_info['sort_normal'], v_info['arg_values']):
                    cls = self.hsv2color_one(avh, avs, avv)
                    if cls is None:
                        print(avh, avs, avv)
                        continue
                    score = snh + sns + snv
                    if cls in result.keys():
                        result[cls] = max(score, result[cls])
                    else:
                        result[cls] = score
        print(result)
        return sorted(result.items(), key=lambda kv: (kv[1], kv[0]))[::-1]

    def hsv2color_one(self, h_mean, s_mean, v_mean):
        for cls, value in self.hsv_color.items():
            if isinstance(value[0], list):
                h_flag = value[0][0][0] <= h_mean <= value[0][0][1] or value[0][1][0] <= h_mean <= value[0][1][1]
            else:
                h_flag = value[0][0] <= h_mean <= value[0][1]
            s_flag = value[1][0] <= s_mean <= value[1][1]
            v_flag = value[2][0] <= v_mean <= value[2][1]
            if h_flag and s_flag and v_flag:
                return cls
        return None

    def classify(self, img):
        img_hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
        h_hist, s_hist, v_hist = self.get_hsv_hist(img_hsv)

        # 仅用hsv每个分量的最大值
        # h_mean, s_mean, v_mean = self.get_hsv_main_info(h_hist, s_hist, v_hist)
        # return self.hsv2color_one(h_mean, s_mean, v_mean)

        infos = self.get_hsv_info(h_hist, s_hist, v_hist)
        return self.hsv2color(infos)

用法也很简单,先实例化一个分类器,然后调用classify传入图片即可得到颜色类别:

classifier = ColorClassify(debug=True)
img = cv2.imread(img_path)
result = classifier.classify(img)
cls, score = result[0]

参考:

HSV颜色模型_百度百科

HSV基本颜色分量范围 - 百度文库

色域值各个标准对照表 - 百度文库

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

AICVHub

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值