上一篇是做的视频镜头分割,虽然分割成功了,但是视频中有一些镜头中,包含一些含有logo的视频帧,这部分视频是我不想要的,所以就需要识别含有logo图像的,然后把这些帧标记出来。
看了很多其他的实现思路,提到了哈希算法,包括有均值哈希算法、差值哈希算法、感知哈希算法。开始的时候,这三种算法都试过,发现都有问题,准确率很低,这时仔细看了每一帧的logo图,才发现logo图的大小不一样,也就是帧之间,logo图可能放大或者缩小了,那么用哈希算法判断的结果就很难去评估这个准确性。因为需要用到阈值,而这个阈值很难设定,所以就放弃了这个方案。
具体哈希实现,大家可以参考以下地址:
哈希算法实现
接下来就是按照其他思路来计算
一、LOGO图片大小不一
由于每一帧的logo图大小不一样,那么在判断之前,必须将logo图片的大小统一,这样才能根据图像数据进行比较判断
二、灰度值
由于图片需要用判断,那么尽量减少颜色的干扰,所以在处理的时候,需要将图片中的颜色去除,所以统一采用灰度值进行处理
三、计算每一行的平均值
图片统一尺寸后,每一行的元素记录了这张图片的特征,那么用每一行的灰度值的均值标识这一行的特征
四、方差
将上一步计算的平均值,求方差,也就是得到了这张图的所有灰度值的方差,然后通过两张图的方差比较,进行相似度的判断。具体方差的判断,我们可以根据多张图的方差曲线图,来做具体的阈值处理。
接下来就是具体的代码实现
1、提取图片数据
#这是一张logo图,用于判断依据的图。由于所使用的logo图色彩差异很大,所以先进行了二值化,方便后期处理
default_img = cv2.imread(default_img_path, cv2.IMREAD_GRAYSCALE)
ret, default_binary = cv2.threshold(default_img, 200, 255, cv2.THRESH_BINARY)
有了上面的原图,下面就是提取一帧图像中的logo图
#这里只包含了部分代码,读取图像,然后找到logo图的位置,从原图中截下对应区域的原图数据
while success:
success, frame = cap.read()
if not success:
break
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) # 转换成灰度
shape = np.shape(gray)
height = shape[0]
width = shape[1]
'''
1、二值化
'''
ret, binary = cv2.threshold(gray, 200, 255, cv2.THRESH_BINARY)
'''
2、找到需要截取的中间区域的图片
从中间开始,往前/后找到第一个全部为0的一列,
'''
position = find_logo_position(binary, width, height)
if position is None:
print("找寻logo位置信息失败 : {0}".format(frame_index))
else:
print("找寻logo位置信息成功 : {0}".format(frame_index))
start_sub_row = position[0]
end_sub_row = position[1]
start_sub_col = position[2]
end_sub_col = position[3]
# print("{0},{1},{2},{3}".format(start_sub_row, end_sub_row, start_sub_col, end_sub_col))
frame_sub = frame[start_sub_row:end_sub_row, start_sub_col:end_sub_col]
is_logo = compare_sub(default_binary, frame_sub)
print("是否是logo图 : {0} = {1}".format(frame_index, is_logo))
frame_index += 1
二、计算和比较
def compare_sub(src_binary, sub_frame):
src_shape = np.shape(src_binary)
src_h = src_shape[0]
src_w = src_shape[1]
sub_shape = np.shape(sub_frame)
sub_h = sub_shape[0]
sub_w = sub_shape[1]
# 由于参考的源已经是最小像素,则新图如果尺寸过小,忽略
if (src_h - sub_h) > 20 or (src_w - sub_w) > 20:
return False
# 以sub的高为固定值,计算sub的理论宽度,比较实际宽度与理论宽度
theory_sub_width = src_w * sub_h // src_h
if abs(theory_sub_width - sub_w) > 6:
return False
# 对图片进行缩放处理
crop_size = (src_w, src_h)
sub_new = cv2.resize(sub_frame, crop_size, interpolation=cv2.INTER_CUBIC)
sub_gray = cv2.cvtColor(sub_new, cv2.COLOR_BGR2GRAY) # 转换成灰度
ret, sub_binary = cv2.threshold(sub_gray, 200, 255, cv2.THRESH_BINARY) # 二值化
# 计算每一行的平均值
src_sum_row = np.sum(src_binary, axis=1)
src_sum_row = src_sum_row / src_w # 计算每行的均值
sub_sum_row = np.sum(sub_binary, axis=1)
sub_sum_row = sub_sum_row / src_w
ss_src = get_diff(src_sum_row)
ss_sub = get_diff(sub_sum_row)
ss = ss_sub - ss_src
ss = np.fabs(ss)
n = 0
for value in ss:
if value <= 1.0:
n += 1
if (n / len(ss)) > 0.9:
x = range(src_h)
plt.figure("avg")
plt.plot(x, ss_src, marker="*", label="$walk01$")
plt.plot(x, ss_sub, marker="*", label="$walk03$")
plt.title("compare")
plt.legend()
plt.show()
return True
else:
return False
3、计算方差
def get_diff(source):
size = len(source)
ave_src = sum(source) / size # 总的平均值
s_src = source - ave_src # 计算每个值与平均值的差值
ss_src = s_src * s_src / size # 方差
return ss_src
说明
在上面比较的代码中,我对logo图的方差与原图logo方差的差值进行了取绝对值,这是因为我需要看两张图在同一个位置特征上的波动情况。然后根据波动情况的范围,来判断这个位置是否相近,当走势中准确度达到90%时,我就认为这张图是原图。
这个比较和判断结果,我是通过多张图的logo解析结果,方差变化情况决定的。以下是对应的参考情况。
通过上面两个图,我们就能大概的选择自己需要判断的范围了。
以上内容是我自己根据一些文章,然后结合自己的需求,改进的方案,有问题或者需要探讨的,欢迎留言讨论