赞
踩
目录
前段时间遇到了一个需要进行色彩分析的项目,主要是针对文件夹内的照片进行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通道设定范围,筛选掉低明度的色彩。当然也可以根据直方图自适应筛选,还可以直接提取用修图软件提高整体的亮度。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。