详细算法主要原理核心请见:足球记分牌提取项目(解释版) - 飞桨AI Studio (baidu.com)
一键运行Aistudio位置:PPSIG:PaddleSports 足球记分牌识别(封装版) - 飞桨AI Studio (baidu.com)
任务简要:输入一段足球比赛视频和预设的队伍的txt,识别记分牌的信息,得到视频每一秒的比赛时间,比分,比赛队伍名称。实现视频时间与比赛时间相对应(因为视频刚开始往往会有与比赛无关的信息,并且视频中间可能存在比赛的跳跃,比如在视频2分30秒时比赛时间为1分40秒,但是视频2分31秒时比赛时间为3分11秒),输出的txt文件包含比赛跳跃的时刻和比分变动的时刻,这样就可以输入比赛时间得到对应视频时间。
该视频是我自己制作的,就是人工把完整的一个视频中一些片段抽取出来,从而让视频出现跳跃,然后我一共制作了两个视频进行测试放在了test_soccervideo,分别为myvideo.mp4和myvideo1.mp4。
game_begin的意思为第一次记分牌上出现比赛时间
video_time | game_time | reason-label |
---|---|---|
12秒 | 12s | game_begin |
4分25秒(265s) | 7分10秒 | 视频跳跃 |
7分7秒(427s) | 11分49 | 视频跳跃 |
10分25(625s) | 15分34 | 视频跳跃 |
8分32秒(512s) | 13分15 | 比分变动 |
video_time | game_time | reason-label |
---|---|---|
9秒 | 9s | game_begin |
2分12秒(132s) | 4分6秒 | 视频跳跃 |
7分41秒(461s) | 14分38 | 视频跳跃 |
11分20(680s) | 21分40 | 视频跳跃 |
18分4秒(1084s) | 29分27 | 视频跳跃 |
17分21秒(1041s) | 27分41 | 比分变动 |
18分12秒(1092s) | 29分36 | 比分变动 |
(假设output_dir为默认,video_file为myvideo.mp4)
-
首先将视频使用"ffmpeg"进行抽帧(默认2帧每秒),输出中间文件夹 ./myvideo
-
2个json文件,分别为 ./myvideo.json 和 ./myvideo.raw.json ,./myvideo.json 输出的就是第一次识别到记分牌比赛时间信息和视频跳跃的信息和比分变动的信息,./myvideo.raw.json 输出的是每一帧的信息(方便进行中间检查)。
'''
整个json就一个key-value {"video_processed_infomation":list}
然后这个list有几个元素,每个元素为一个dict,就代表几个事件。然后这个每个元素的dict的key ,game_teams代表比赛双方队伍,game_time代表比赛时间,game_scores代表比赛比分,image_filename代表图片的文件路径名,processed_event有三个状态game_begin,time_jump和score_change 分别代表比赛开始,时间跳跃和比分变动,然后如果状态为time_jump,那么dict就会多一个key-value,key为time_jump,value为List(此刻如果不时间跳跃应该的比赛时间和此刻的比赛时间,都是按秒算的),如果状态为score_change,那么dict就会多一个key-value,key为score_change,value为List(刚才的比分和此刻的比分)
'''
- parser.add_argument('--video_file', type=str,default="myvideo.mp4")
video_file参数代表需要操作的比赛视频的文件路径
- parser.add_argument('--team_list_file', type=str, help = "List of team names in text",default="team_name.txt")
team_list_file为预设全部足球比赛的队伍名称信息
- parser.add_argument('--output_dir', type=str,default="")
如果不填参数则2个json和抽帧文件夹为video_file同路径。 如果out_dir为./12/video1 那么2个txt文件路径为./12/video1.json 和 ./12/video1.raw.json 那么抽帧图片文件夹为./12/video1。
- parser.add_argument("--extract_frames_fps帧",type=int,default=2)
ffmpeg一秒extract_frames_fps帧
- parser.add_argument("--seq_second",type=int,default=10,help="粗粒度间隔seq_second秒判别视频中记分牌出现时间")
-
因为PaddleOCR提取记分牌文字并不能每帧都很好提取,所以我进行了很多的处理
-
比如比赛时间从25秒突然变成1分钟,有可能是OCR识别错了,也有可能是出现比赛跳跃现象,那么我会判断后面3秒(默认及6帧)内是否比赛时间为正常进行。如果后3秒内的图片有没有game_time的,那么就往后依次顺延,直到判断有game_time的6帧图片比赛时间都正常进行。
if game_time:
new_sec = self.str2sec(game_time)
if abs(new_sec-before_sec) >3:
try:
temp_sec = 3*self.extract_frames_fps
x = 0
flag = 0
# flag1 = 0
while True:
x += 1
if video_raw_infomations[step+x]["game_time"]:
step_sec = self.str2sec(video_raw_infomations[step+x]["game_time"])
if abs(step_sec - new_sec) >= x/self.extract_frames_fps+4:
flag = 1
break
else:
temp_sec +=1
if x == temp_sec:
break
# while True:
# if video_raw_infomations[step+x][1]["game_time"]:
# step_sec = self.str2sec(video_raw_infomations[step+x][1]["game_time"])
# if abs(step_sec - before_sec) <= x/self.extract_frames_fps+4:
# flag1 = 1
# break
# else:
# temp_sec +=1
# x += 1
# if x == temp_sec:
# break
if flag ==0:
# one_info.append(["time reason",before_sec,new_sec])
one_info["processed_event"] = "time_jump"
one_info["time_jump"] = [str(before_sec),str(new_sec)]
video_processed_infomation.append(one_info)
before_sec = new_sec
else:
# before_sec += 1/self.extract_frames_fps
pass
except (IndexError):
# before_sec += 1/self.extract_frames_fps
pass
else:
# before_sec += 1/self.extract_frames_fps
pass
- 如果比分变动,为了防止是OCR识别错误,我会判断接下来2秒内比分是否一致,若有帧无比分,则顺延。同时判断总比分是否每次相较于上次加1
if game_scores:
if game_scores != before_score:
game_score0 = 0
for i in game_scores:
if is_number(i):
game_score0 += is_number(i)
before_score0 = 0
for i in before_score:
if is_number(i):
before_score0 += is_number(i)
if abs(game_score0-before_score0) == 1:
temp_sec = 2*self.extract_frames_fps
x = 0
score_flag = 0
try:
while True:
x += 1
if video_raw_infomations[step+x]["game_scores"]:
step_score = video_raw_infomations[step+x]["game_scores"]
if step_score != game_scores:
score_flag = 1
break
else:
temp_sec +=1
if x == temp_sec:
break
if score_flag ==0:
# one_info.append("score reason")
one_info["processed_event"] = "score_change"
one_info["score_change"] = [before_score,game_scores]
video_processed_infomation.append(one_info)
before_score = game_scores
except IndexError:
pass
- 如果比分变动时候,无法识别比赛时间,则根据视频时间算差,计算得到。
for step,i in enumerate(video_processed_infomation):
i["game_teams"] = soccer_teams[0]
image_path = i["image_filename"]
video_time_fps = int(image_path.split("/")[-1].split(".")[0])
i["video_time(s)"] = str(video_time_fps/self.extract_frames_fps)
if i["game_scores"] =="":
i["game_scores"] = video_processed_infomation[step-1]["game_scores"]
if i["game_time"] == "":
before_game_time = self.str2sec(video_processed_infomation[step-1]["game_time"])
x = float(i["video_time(s)"]) - float(video_processed_infomation[step-1]["video_time(s)"])
i["game_time"] = self.sec2str(int(before_game_time+x))
!pip install -r ./PaddleOCR2_5/requirements.txt
#请先创建空文件夹./output 和下载测试的mp4数据集
!python main.py --video_file ./myvideo1.mp4 --output_dir output/video1 --seq_second 5
from main import get_video_time_from_game_time
'''
输入比赛时间得到对应的视频时间
'''
get_video_time_from_game_time("output/video1.json","00:02")
由于我判断比赛跳跃和比分变动的条件比较苛刻,所以识别出来的比分变动往往比实际的比分变动后几秒,视频跳跃倒是前后相差往往小于2s.