python之筛选图像中是否存在黑白背景
紧接上篇文章的需求,需要进行功能增加
某些图片存在背景丢失问题,出现黑白背景现象,这种需要排查,同样交给了自动化处理。
这次不比上次了,我搜罗了一堆资料,全是什么人工智能领域的图像识别,AI识别之类的,没有能够符合我需求的,看来CV大法这次是失策了。
那如何找到突破口?毕竟这可是工作,还是我主动请缨,我原先思路也很简单,上篇文章中提到使用AirTest库中的cal_ccoeff_confidence
这个方法可以实现图片对比,那么我自己做一张纯黑和纯白的图片,拿目标图片和这两张图片进行对比,相似度越高,不就代表目标图片可能存在背景丢失问题吗?
理论可行,实践失败。
我曾经学了点UI,稍微知道一点,图像一个像素点由三个数值组成,如纯白色可以用(255,255,255)来表示,纯黑色可以用(0,0,0)来表示。RGB与十六进制颜色码转换 - 在线工具 (toolhelper.cn)
在搜集的资料中,图像对比处理都是采用的黑白化(灰度图)图片进行取值,我用比较通俗的话来讲:
提取一张图片中所有像素点的值,将这个值和纯黑或纯白像素点的值进行差值计算,
另外一张图片也是如此,
最后将这两张图片的所有点进行挨个计算,最后算出均值,从而判断两张图片是否相似。
专业领域称之为均值哈希算法
有兴趣的小伙伴可以去研究,均值哈希算法、差值哈希算法、感知哈希算法、灰度直方图算法
均值、差值、感知哈希算法三种算法值越小,则越相似,相同图片值为0
三直方图算法和单通道的直方图 0-1之间,值越大,越相似。 相同图片为1
这些东西对我来说,晦涩难懂,而且我看它们显得我就是个文盲,不过还是得理性分析一波,为什么实践失败了呢?
第一,我是要找黑白背景,而他们都是由哈希值来求取,黑白在两个极值点,我无法准确判断是否为黑或者白;
第二,我使用cal_ccoeff_confidence
方法求出来的值直接是负数,转手使用cal_rgb_confidence
彩图计算相似度也是负数,而且比前者更离谱,要么我不会用,要么我这需求人家根本没考虑过。
第三,出发点不一样,我需要的是极值点数据,查找的方法都是求整体对比。
如何解决?
前面有提到,每一个像素值都是由三个数字组成的元组,那么就有256*256*256
种颜色,在以前的公司还考虑过8位16位24位32位色,但现在都是由我截的图,都是统一的,所以不用去考虑。
而在正常的UI设计规范中,是不会允许出现纯黑纯白颜色出现的,也就是(255,255,255)(0,0,0)这两种。
想清楚这个,问题就相对来说走上了正轨,不会被所查找的资料给带跑偏了,我们开始一步步推导:
1、我们需要找丢失背景的图片,意味着这张图片的背景被纯黑色或者纯白色占据了大部分。
2、既然是纯黑或纯白占据大部分,那么我们可以提取一张图片上所有的像素点的值,并按数量从大到小取值。
3、取值只取前三,如果前三中,排名第一多的是纯黑或者纯白,那么我们判断该图片为背景缺失。
4、如果为(255,255,255)则记录该图片背景丢失,背景为白色
5、如果为(0,0,0)则记录该图片背景丢失,背景为黑色
在实际操作下来发现,白色并不一定完全是纯白,还有个范围差,于是我取值为三项都是大于251,判断为白色,三项都是小于10,且每项相等,为黑色。(通过多次实验数据分析得出的谨代表个人观点的结论)。
如果想要判断背景是红色、绿色之类的,可以自己去查找颜色范围,将取值范围和相关算法匹配写到代码里面就行了。
以上这些都是实际实践并有产出的,本着宁愿多判定两张,绝不漏掉一张的本质,白色的99%都能识别准确,黑色的识别准确度会低一点,黑色会多判定一些(有部分转场截屏是黑的也算进去了)
,最终也需要人工复核,但一般5000张图片,关于背景缺失问题,人工复核只需要5分钟不到。
以下为脱敏后代码,整体逻辑都在文章中了,不懂的地方自行百度吧,我写累了,懒子一个不想多写了:
def makeFolderResult(imgPath, logName):
logFloder = os.path.join(imgPath, f'背景缺失对比结果')
os.mkdir(logFloder)
logPath = os.path.join(imgPath, f'背景缺失对比结果/{logName}')
return logPath
def wirteLog(msg, logPath):
with open(logPath, "a+", encoding='utf-8') as f:
f.write(msg)
f.write("\n")
def get_dominant_colors(imagePath, logPath):
'''
:param imagePath: 图片存放的路径
:param logPath: 日志存放的路径
:return:
'''
for root, dirs, files in os.walk(imagePath):
for file in files:
if ".jpg" in file:
imgFile = os.path.join(root, file)
image = Image.open(imgFile)
# 缩小图片,减少运算压力
small_image = image.resize((80, 80))
result = small_image.convert("P", palette=Palette.ADAPTIVE, colors=10)
# 10个主要颜色的图像
# 找到主要的颜色
palette = result.getpalette()
color_counts = sorted(result.getcolors(), reverse=True)
colors = list()
for i in range(3):
try:
palette_index = color_counts[i][1]
dominant_color = palette[palette_index * 3: palette_index * 3 + 3]
colors.append(tuple(dominant_color))
except:
break
### 判定数量排名第一的颜色是否满足黑或白
firstColor = colors[0]
### 计算平均值
try:
firstColorAvg = numpy.average(firstColor)
if firstColorAvg > 251:
if firstColor[0] > 251 and firstColor[1] > 251 and firstColor[2] > 251:
writeMsg = f"【疑似】{file}背景为 【白色】"
wirteLog(writeMsg, logPath)
print(writeMsg, firstColor)
elif 0 =< firstColorAvg < 10:
if firstColor[0] == firstColor[1] == firstColor[2]:
writeMsg = f"【疑似】{file}背景为 【黑色】"
wirteLog(writeMsg, logPath)
print(writeMsg, colors)
except:
traceback.print_exc()
if __name__ == '__main__':
imagePath = ""
logName = str(imagePath.split("\\")[-1]) + ".txt"
logPath = makeFolderResult(imagePath, logName)
get_dominant_colors(imagePath, logPath)
最终会将结果写入到目标图片文件夹下的log中。