任务背景:若干个文件夹中存放着采集到的行车图片,这些图片由行车视频按时间逐帧抽出,相当于一个行车场景。每个场景都有六个摄像头和激光雷达采集到的图片,现以对主视角的评判代表整个片段。这些场景中存在停车或是某些路段目标较少的情况,不利于后续处理,打分标准则是目标多,停车少的分数高。满分为100,低于60分的为不合格片段。将输出及格片段的文件夹名称与相应得分,以此挑选质量更好的数据。
实现方案:
1.采用相似度对比算法来检测前后帧的相似性以判别停车或缓行片段
2. 相似度对比+目标检测,根据目标数量以及重复帧数选出优秀片段
1.遍历文件夹方法
参考文章【python遍历文件夹中的所有图像(按名称顺序读取)、将生成的新图像存入本地文件夹】
文件夹结构:
路径/采集日期/before_sample/cam_front/jpg格式图片
所以此处只需获得采集日期即可补全路径
测试代码如下:
import os
import cv2
folder_path = r'F:/video/result'
save_path = r'F:/result'
if __name__ == "__main__":
img_folder = folder_path
img_list = [os.path.join(nm) for nm in os.listdir(img_folder) if nm[-3:] in ['jpg', 'png', 'gif']]
## print(img_list) 将所有图像遍历并存入一个列表
## ['test_14.jpg', 'test_15.jpg', 'test_9.jpg', 'test_17.jpg', 'test_16.jpg']
for img_name in img_list:
path = img_folder + '/' + img_name
print(path)
image = cv2.imread(path) ## 逐个读取
#############################################################
#对图片进行操作
##############################################################
cv2.imwrite(save_path + '/ ' + str(img_name) + '.jpg', image)
在本地图片文件夹做测试,将会得到图片文件夹内所有图片的地址,将图片存放在新地址当中,要对图片做的操作直接插入到for循环中即可
2.相似度对比
采用差值哈希算法,计算相似度,生成的值n1含义为差异度,若n1大于10,则可认为是两张不同的图像。评分标准可自行拟定,此处将小于10 的值直接不计入,认定为重复图片,累加n1得到的值再除以所有图像的数量,如果小于10,则认为图像集相似度较高,为不及格图像,反之,输出及格图像的分数。
在本地做测试代码如下:
import os
import cv2
import numpy as np
"""
测试给文件夹打分任务
读取文件夹,遍历其中主视角图片,计算差异度,给出分数
在本地文件夹测试
"""
def dHash(img):
# 差值哈希算法
# 缩放8*8
img = cv2.resize(img, (9, 8))
# 转换灰度图
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
hash_str = ''
# 每行前一个像素大于后一个像素为1,相反为0,生成哈希
for i in range(8):
for j in range(8):
if gray[i, j] > gray[i, j + 1]:
hash_str = hash_str + '1'
else:
hash_str = hash_str + '0'
return hash_str
def cmpHash(hash1, hash2):
# Hash值对比
# 算法中1和0顺序组合起来的即是图片的指纹hash。顺序不固定,但是比较的时候必须是相同的顺序。
# 对比两幅图的指纹,计算汉明距离,即两个64位的hash值有多少是不一样的,不同的位数越小,图片越相似
# 汉明距离:一组二进制数据变成另一组数据所需要的步骤,可以衡量两图的差异,汉明距离越小,则相似度越高。汉明距离为0,即两张图片完全一样
n = 0
# hash长度不同则返回-1代表传参出错
if len(hash1) != len(hash2):
return -1
# 遍历判断
for i in range(len(hash1)):
# 不相等则n计数+1,n最终为相似度
if hash1[i] != hash2[i]:
n = n + 1
return n
folder_path = r'F:/video/result'
save_path = r'F:/result'
if __name__ == "__main__":
img_folder = folder_path
img_list = [os.path.join(nm) for nm in os.listdir(img_folder) if nm[-3:] in ['jpg', 'png', 'gif']]
# 初始化
image0 = np.zeros((10, 10, 3), np.uint8)
image = np.zeros((10, 10, 3), np.uint8)
count = 0 # 计数器
score = 0 # 计分器
for img_name in img_list:
path = img_folder + '/' + img_name
print(path)
image = cv2.imread(path)
#############################################################
# 对图片进行操作
# 计算两图相似度
hash1 = dHash(image0)
hash2 = dHash(image)
n1 = cmpHash(hash1, hash2)
if n1 <= 10:
image = image0
# print('两图片相似,舍去后图')
else:
image0 = image
score = score + n1
# print('两图片不相似')
count = count + 1
##############################################################
cv2.imwrite(save_path + '/ ' + str(img_name) + '.jpg', image)
# 评分标准:如果均分小于10则存在大量重复图像,为不及格
score = score / count
if score <= 10:
print("不及格")
else:
print("及格,分数为:",score)
遇到的问题有读取中文路径时报错
cv2.error: OpenCV(4.8.1) D:\a\opencv-python\opencv-
python\opencv\modules\imgproc\src\resize.cpp:4062: error: (-215:Assertion failed)
!ssize.empty() in function 'cv::resize'
解决参考这篇Python读取路径下所有文件名
#做出如下修改
#原代码
image = cv2.imread(path)
#修改后
image = cv2.imdecode(np.fromfile(path, dtype=np.uint8), -1)
3.相似度对比+目标检测
增加了计算片段总标注框数量的代码
检测结果如下:
输出的为采集数据日期,片段名称,片段内目标总数,重复帧数,可以自行修改。
4.片段评分
采集到的数据存在txt文件夹中,评分就是基于最后两列,一个是片段内目标总数,另一个是重复帧数。目标数越多越好,因此为正相关,同理重复帧数为负相关。二者之间又有关联,如果一个片段重复帧数较多,但目标数也不低,这也是有意义的片段,因此目标数为更加重要的判断指标。而且在目标数较少时,差别越大对分数影响更大,这样能够更加精确的筛选出好的片段,此处采用了简单的分段函数的方式处理,代码如下:
# coding=gbk
import os
import numpy as np
import numpy
"""
给文件夹评分:按照目标数量和重复帧数给文件夹评分
输入:txt文件,每行包括日期,片段名,目标数量,重复帧数
思路:两个重要参数分别命名为NUM,RFR,一个正相关一个负相关,而且NUM权重更高
输出:日期,片段名,百分制分数
"""
# 输入目标数与重复帧数,返回分值
def score(n1, n2):
# 输入目标数与重复帧数,进行处理
# num占60% rfr占20% 二者比例占20%
if n1 <= 1500:
if 700 <= n1:
fen1 = 40 + 20 * n1 / 1500
else: # 较少,需拉开分值
fen1 = 40 * n1 / 700
else:
fen1 = 60
# rfr占20%
if n2 > 0:
if n2 <= 10:
fen2 = 10 + 10 * (1 - n2 / 60)
else:
fen2 = 10 * (1 - n2 / 60)
else:
fen2 = 20
if n2 != 0:
n3 = n1/n2
if n3 <= 163:
fen3 = 10 * n3 / 163 + 5 * (1 - n2 / 60)
else:
fen3 = 10 + 5 * (1 - n2 / 60) + 5 * (n3 - 163) / n3
else:
fen3 = 5 * fen1/60 + 15
fen = fen1 + fen2 + fen3
# print(fen1,fen2,fen3)
return int(fen)
# 计算均值,最大目标数
def mean(all_path):
n1 = 0
n2 = 0
n3 = 0
count = 1
for path in all_path:
date, ming, NUM, RFR = path.strip().split(" ")
# print(NUM, RFR) # 仅仅是为了测试
# 不为零时算出均值
if int(NUM) + int(RFR) != 0:
if n3 < int(NUM):
n3 = int(NUM)
n1 += int(NUM)
n2 += int(RFR)
count += 1
n1 = n1 / count
n2 = n2 / count
print(n1, n2, n3)
# 712.8321479374112 4.3584637268847795 1982 163
def key_function(x):
return int(x[0])
#读取打分后的文件,为其排序
def sort(filename,save):
s2 = 0
a = []
s = open(filename, "r")
count = 0
# 逐行读取
lines = s.readlines()
for i in lines:
s1, date, ming, NUM, RFR = i.strip().split(" ")
a.append([s1, date, ming, NUM, RFR])
a.sort(key=key_function)
#打印数组
"""
获取数组行列
print(len(X)) #行
print(len(X[0])) #列
"""
h = int(len(a))
for i in range(0,h):
print(a[i])
def main():
f = open(dir_txt, "r")
w = open(txt_path, "w")
all_path = f.readlines()
# mean(all_path)
for path in all_path:
date, ming, NUM, RFR = path.strip().split(" ")
# print(NUM, RFR) # 仅仅是为了测试
# 不为零时算出均值
n1 = int(NUM)
n2 = int(RFR)
# 计算分数
fen = score(n1, n2)
print(fen,date, ming, NUM, RFR)
dir_txt = r'D:\pythonproject\frequency\cs.txt' # \\10.128.3.137\算法\原始视频\全地形\videos_20231108'D:\pythonproject\frequency\video'
txt_path = r'D:/pythonproject/frequency/name/name1.txt' # 生成的图片列表清单txt文件名
filename = r'D:/pythonproject/frequency/name/name2.txt'
save = r'D:/pythonproject/frequency/name/name3.txt'
if __name__ == '__main__':
"""第一步,获取均值与最值,为计分调整权重
f = open(dir_txt, "r")
w = open(txt_path, "w")
all_path = f.readlines()
mean(all_path)
"""
"""第二步,计算分值
main()
"""
"""第三步,分值排序"""
sort(filename, save)
按照步骤运行后,评分输出的结果如下:
可以看到分数由低到高排列,分数高的片段就是目标多且重复帧少的。