Python + Opencv进行图片色彩分析

本文介绍了如何使用Python和OpenCV对照片进行HSV色彩分析,包括绘制HSV直方图、计算色彩占比,以及改进方法如处理暗淡图片和提高亮度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

 

问题背景

实现效果

代码思路及实现

小结


问题背景

前段时间遇到了一个需要进行色彩分析的项目,主要是针对文件夹内的照片进行HSV的分析,在网上寻找了一下资料,没看到什么这一方面的代码,于是记录一下实现过程

实现效果

代码主要运用到的Python和Opencv以及Matlab的库,绘制图片HSV的直方图,并对主要含有色彩进行占比分析,输出具体值并可视化

代码思路及实现

首先需要导入以下库

import os
import cv2 as cv
import matplotlib.pyplot as plt
from collections import Counter

使用os库的命令实现批量读取文件夹内的照片

def getfiles():
    file_path = '/Users/fbz/Desktop/City_Color/Photos'  # 文件夹路径
    file_names = os.listdir(file_path)
    print(file_names)
    return file_names, file_path

将图像格式从BGR转到HSV空间,对HSV的值统计绘制HSV直方图,并计算均值

def calchist_for_rgb(frame, num):
    h, s, v = cv.split(frame)  # 分离hsv三个通道
    hsv_H = cv.calcHist([h], [0], None, [256], [0, 255])
    hsv_S = cv.calcHist([s], [0], None, [256], [0, 255])
    hsv_V = cv.calcHist([v], [0], None, [256], [0, 255])

    # 绘制并保存色彩直方图
    plt.plot(hsv_H, color="r")
    plt.plot(hsv_S, color="g")
    plt.plot(hsv_V, color="b")
    plt.savefig("result_hsv" + str(num) + ".jpg")
    plt.show()
    # ---------------- hsv均值 -----------------------
    hsv_sum = 0
    h_c, h_r = h.shape
    for i in range(h_c):
        for j in range(h_r):
            hsv_sum = hsv_sum + h[i][j]
    hsv_sum = hsv_sum / (h_c * h_r)
    print('mean h =', hsv_sum)

    hsv_sum = 0
    s_c, s_r = s.shape
    for i in range(s_c):
        for j in range(s_r):
            hsv_sum = hsv_sum + s[i][j]
    hsv_sum = hsv_sum / (s_c * s_r)
    print('mean s =', hsv_sum)

    hsv_sum = 0
    v_c, v_r = v.shape
    for i in range(v_c):
        for j in range(v_r):
            hsv_sum = hsv_sum + v[i][j]
    hsv_sum = hsv_sum / (v_c * v_r)
    print('mean v =', hsv_sum)

直方图绘制完之后,我们就对图像的HSV空间分布有一个大体的了解了,但是要直观具体地了解一张图片的色彩占比,光直方图是远远不够的。我们还需要确定出一张图像的主要色彩以及各个主要色彩的占比,才能使数据有具体的作用。

那么就可以细分出两个小问题,第一个是如何从整个图像中区分出主要的颜色,第二个就是如何计算主要颜色的占比。

第一个问题的解决思路就是,遍历图像后统计像素值,提取出重复次数多的像素值,对这些像素值再进行一个区分统计,来划分出几个主要的色彩区间。这里采取的区分方法就是对三通道的差值计算均方根计算,小于阈值则说明区分度不够,大于一定的区分度阈值才能确定其色彩与另一色彩不同。

第二个问题就相对简单很多,只要统计出上述各色彩区间的色彩数量就可以了。

# 色彩占比分析
def color_sort(frame, num):
    c = []
    for x in range(frame.shape[0]):
        for y in range(frame.shape[1]):
            c.append(str(frame[x, y][0]) + ',' + str(frame[x, y][1]) + ',' + str(frame[x, y][2]))  # 遍历图片所有bgr值
    temp = Counter(c)
    most = temp.most_common(500)  # 按重复次数排序,抽取排在前500的元素
    count = 1  # 状态值,记录是否是第一个被加入的元素,1代表是
    color = []  # 储存主色彩
    color_num = []  # 储存各主色彩的数量
    for i in most:
        b_i, g_i, r_i = i[0].split(',')  # 待加入色彩
        if count == 1:
            # 第一个被加入的色彩
            color.append(str(b_i) + ',' + str(g_i) + ',' + str(r_i))
            count = 0
            color_num.append(1)
        state = False  # 状态量,若变更为Ture为新颜色,决定加入color内
        for j in color:
            b_o, g_o, r_o = j.split(',')  # 已加入色彩
            # 计算待加入色彩和已加入色彩的区分度,若过为区分度超过阈值则视为要加入的新色彩
            dis = (int(b_o) - int(b_i)) ** 2 + (int(g_o) - int(g_i)) ** 2 + (int(r_o) - int(r_i)) ** 2
            if dis <= threshold:  # 区分度过小,视为列表内已经存在该色彩
                color_num[color.index(j)] += 1
                break
            else:
                state = True  # 修改状态量加入新色彩
            if state and j == color[len(color) - 1]:  # 若所有已有颜色都遍历后state仍为True则加入该色彩
                color.append(str(b_i) + ',' + str(g_i) + ',' + str(r_i))
                color_num.append(1)

到此为止,所需的数据已经基本获得到了,但光是数据仍然不够直观,还差一步画图,画饼图或直方图的话感觉和图片关联度不大,于是打算将色彩分布图和原图放在同一个窗口内,直观简洁。

def color_draw(frame, color, color_num, num):
    start = 0
    Height = frame.shape[0]
    Width = frame.shape[1]
    image = cv.resize(frame, (Width, int(Height * 1.125)))  # 创建新画布
    image[0:Height, 0:Width] = frame  # 在新画布上部分填充原图像
    for i in range(len(color)):
        # 遍历所有主色彩,
        print(color[i])
        print(float(color_num[i] / 5), '%')  # 计算百分比,因抽取500个颜色故此处除5便可计算百分比
        b, g, r = color[i].split(',')
        f = int(color_num[i]) * Width / 500  # 计算当前色彩应在画布上的宽度
        image[Height:int(Height * 1.125), start:start + int(f)] = [b, g, r]  # 上色
        start = start + int(f)
    image = cv.cvtColor(image, cv.COLOR_HSV2BGR)
    cv.imshow('result' + str(num), image)
    cv.imwrite('result' + str(num) + '.jpg', image)
    cv.waitKey(0)

小结

完整代码如下

import os
import cv2 as cv
import matplotlib.pyplot as plt
from collections import Counter

threshold = 80  # 区分度阈值


# 计算彩色图的直方图
def calchist_for_rgb(frame, num):
    h, s, v = cv.split(frame)  # 分离hsv三个通道
    hsv_H = cv.calcHist([h], [0], None, [256], [0, 255])
    hsv_S = cv.calcHist([s], [0], None, [256], [0, 255])
    hsv_V = cv.calcHist([v], [0], None, [256], [0, 255])

    # 绘制并保存色彩直方图
    plt.plot(hsv_H, color="r")
    plt.plot(hsv_S, color="g")
    plt.plot(hsv_V, color="b")
    plt.savefig("result_hsv" + str(num) + ".jpg")
    plt.show()
    # ---------------- hsv均值 -----------------------
    hsv_sum = 0
    h_c, h_r = h.shape
    for i in range(h_c):
        for j in range(h_r):
            hsv_sum = hsv_sum + h[i][j]
    hsv_sum = hsv_sum / (h_c * h_r)
    print('mean h =', hsv_sum)

    hsv_sum = 0
    s_c, s_r = s.shape
    for i in range(s_c):
        for j in range(s_r):
            hsv_sum = hsv_sum + s[i][j]
    hsv_sum = hsv_sum / (s_c * s_r)
    print('mean s =', hsv_sum)

    hsv_sum = 0
    v_c, v_r = v.shape
    for i in range(v_c):
        for j in range(v_r):
            hsv_sum = hsv_sum + v[i][j]
    hsv_sum = hsv_sum / (v_c * v_r)
    print('mean v =', hsv_sum)


# 色彩占比分析
def color_sort(frame, num):
    c = []
    for x in range(frame.shape[0]):
        for y in range(frame.shape[1]):
            c.append(str(frame[x, y][0]) + ',' + str(frame[x, y][1]) + ',' + str(frame[x, y][2]))  # 遍历图片所有bgr值
    temp = Counter(c)
    most = temp.most_common(500)  # 按重复次数排序,抽取排在前500的元素
    count = 1  # 状态值,记录是否是第一个被加入的元素,1代表是
    color = []  # 储存主色彩
    color_num = []  # 储存各主色彩的数量
    for i in most:
        b_i, g_i, r_i = i[0].split(',')  # 待加入色彩
        if count == 1:
            # 第一个被加入的色彩
            color.append(str(b_i) + ',' + str(g_i) + ',' + str(r_i))
            count = 0
            color_num.append(1)
        state = False  # 状态量,若变更为Ture为新颜色,决定加入color内
        for j in color:
            b_o, g_o, r_o = j.split(',')  # 已加入色彩
            # 计算待加入色彩和已加入色彩的区分度,若过为区分度超过阈值则视为要加入的新色彩
            dis = (int(b_o) - int(b_i)) ** 2 + (int(g_o) - int(g_i)) ** 2 + (int(r_o) - int(r_i)) ** 2
            if dis <= threshold:  # 区分度过小,视为列表内已经存在该色彩
                color_num[color.index(j)] += 1
                break
            else:
                state = True  # 修改状态量加入新色彩
            if state and j == color[len(color) - 1]:  # 若所有已有颜色都遍历后state仍为True则加入该色彩
                color.append(str(b_i) + ',' + str(g_i) + ',' + str(r_i))
                color_num.append(1)
                # print(color)
                # print(color_num)
    color_draw(frame, color, color_num, num)  # 绘制色彩占比图


# 绘制色彩占比图
def color_draw(frame, color, color_num, num):
    start = 0
    Height = frame.shape[0]
    Width = frame.shape[1]
    image = cv.resize(frame, (Width, int(Height * 1.125)))  # 创建新画布
    image[0:Height, 0:Width] = frame  # 在新画布上部分填充原图像
    for i in range(len(color)):
        # 遍历所有主色彩,
        print(color[i])
        print(float(color_num[i] / 5), '%')  # 计算百分比,因抽取500个颜色故此处除5便可计算百分比
        b, g, r = color[i].split(',')
        f = int(color_num[i]) * Width / 500  # 计算当前色彩应在画布上的宽度
        image[Height:int(Height * 1.125), start:start + int(f)] = [b, g, r]  # 上色
        start = start + int(f)
    image = cv.cvtColor(image, cv.COLOR_HSV2BGR)
    cv.imshow('result' + str(num), image)
    cv.imwrite('result' + str(num) + '.jpg', image)
    cv.waitKey(0)


def getfiles():
    file_path = '/Users/fbz/Desktop/City_Color/Photos'  # 文件夹路径
    file_names = os.listdir(file_path)
    print(file_names)
    return file_names, file_path


if __name__ == '__main__':
    filenames, path = getfiles()  # 获取文件路径及该路径下的所有照片
    photos_num = 0  # 图片总数量
    for name in filenames:
        if name == '.DS_Store':  # mac电脑在处理文件时需要去除.DS_Store
            continue
        photos_num = photos_num + 1
        file = os.path.join(path, name)
        print(file)
        img = cv.imread(file)
        hsv_img = cv.cvtColor(img, cv.COLOR_BGR2HSV)
        # cv.imshow('frame', frame)
        # cv.waitKey(0)
        calchist_for_rgb(hsv_img, photos_num)  # 第一个参数改为img可以获取BGR直方图
        color_sort(hsv_img, photos_num)  # 第一个参数改为img可以获取BGR直方图

代码仍有很多优化空间,写完之后,和朋友简单讨论了一下,当前的代码对于明度过低和彩度过高的照片效果不够好,大白话就是太暗的图片难以区分出较多的色彩,在区分度计算那里简单的三通道均方根并不能很好的处理黑暗的图片,最好对V通道设定范围,筛选掉低明度的色彩。当然也可以根据直方图自适应筛选,还可以直接提取用修图软件提高整体的亮度。

Python中结合OpenCV库检查图片是否存在花屏(通常是由于像素不一致或色度溢出导致的颜色异常)通常涉及到图像处理的基本步骤。以下是原理概述: 1. **读取图片**:首先使用`cv2.imread()`函数从硬盘或内存中加载图片。 ```python import cv2 img = cv2.imread('image.jpg') ``` 2. **预处理**:对图片进行灰度转换或者色彩空间调整,如BGR转HSV(hue-saturation-value),因为某些颜色问题在特定颜色空间下更容易检测。 ```python if img is not None: gray_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 对于灰度检查 hsv_img = cv2.cvtColor(img, cv2.COLOR_BGR2HSV) # 对于颜色检查 ``` 3. **计算直方图或统计信息**:使用`cv2.calcHist()`或类似方法,分析图像的像素分布,比如对比度、饱和度等。如果某部分像素偏离正常范围,可能会提示存在花屏现象。 ```python hist = cv2.calcHist([hsv_img], [0, 1], None, [180, 256], [0, 179, 0, 255]) ``` 4. **比较阈值或检测异常**:通过设定合适的阈值,检查直方图或其他统计信息是否超过预期范围。如果某个通道(如Hue)的平均值或标准差较大,就可能是花屏。 ```python mean_value = np.mean(hist) std_dev = np.std(hist) if std_dev > threshold or mean_value > expected_mean: print("图片可能存在花屏") ``` 5. **可视化结果**:有时可以将图像的直方图或处理后的结果显示出来,帮助理解检查过程。 ```python plt.hist(hsv_img[:, :, 0].ravel(), bins=256, range=(0, 256)) plt.show() ```
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

爱兜圈

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

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

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

打赏作者

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

抵扣说明:

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

余额充值