人工智能收听、观看并解读高尔夫名人赛活动 – 经过排名的精彩画面

栏目: 数据库 · 发布时间: 5年前

内容简介:IBM iX 和企业团队项目成员包括:Brian Adams、Aaron Baughman、Karen Eickemeyer、Monica Ellingson、Stephen Hammer、Eythan Holladay、John Kent、William Padget、David Provan、Karl Schaffer、Andy Wismar+ 此系列共有四个部分,本文是第三部分,介绍了高尔夫名人赛中 AI Highlights 的相关情况。高尔夫球场的特定区域从来不乏戏剧性的时刻。“阿门角”由第 1

IBM iX 和企业团队项目成员包括:Brian Adams、Aaron Baughman、Karen Eickemeyer、Monica Ellingson、Stephen Hammer、Eythan Holladay、John Kent、William Padget、David Provan、Karl Schaffer、Andy Wismar

+ 此系列共有四个部分,本文是第三部分,介绍了高尔夫名人赛中 AI Highlights 的相关情况。

多媒体准备

高尔夫球场的特定区域从来不乏戏剧性的时刻。“阿门角”由第 11 洞、第 12 洞和第 13 洞组成,球员谈之色变,球迷则赞赏不已。“阿门角”通常是一道分水岭,水平高超的角逐者由此从一众球员中脱颖而出。接下来,根据在第 15 洞和第 16 洞的表现,锦标赛角逐者就会赢得或输掉比赛。一些球员和竞争动态会合并到特色群组中,涵盖了赛场精彩瞬间。其他媒体导演产生的新闻报道通过多个频道进行联播,也会带来候选的赛场精彩画面。

高尔夫比赛的最后阶段包括“阿门角”、第 15 洞和第 16 洞,这些内容以及特色群组和联播报道会流入场景剪辑器,然后传输到 IBM Cloud 上的 AI Highlight Ranking 系统。制作的剪辑可以通过 Python Flask 应用程序输入该系统,也可以从 IBM Cloudant 先进先出 (FIFO) 数据存储区进行提取。

if (self.read_job_id and not retrieved_work):
try:
  job_files = data_utils.get_sorted_files(self.job_output, '*.json')
  if job_files is None:
    print("No work from API.")
  elif (len(job_files)>0):
    retrieved_work = True
    with open(os.path.join(self.job_output,job_files[0])) as fh:
      json_input = json.load(fh)
      job_id = json_input['jobid']
      os.makedirs(os.path.join(self.output_dir,job_id))        os.rename(os.path.join(self.job_output,job_files[0]),os.path.join(self.output_dir,job_id,'job.json'))
  else:
    print("No job files.")
except Exception as e:
  print("Error getting job id: "+str(e))

if (self.read_cloudant and not retrieved_work):
  try:
    doc = self.cloudant.update_document_with_query({"status": {"$exists": False}},'status','retrieved')
    if not doc is None:
      job_id = str(data_utils.random_digits(6))
      retrieved_work = True
      name_parts = doc["source-assets"]["storage-url"].split('/')
      name = str(job_id)+name_parts[len(name_parts)-1]
      url = self.netstorage_path+doc["source-assets"]["storage-url"]
      urllib.urlretrieve(url, os.path.join(self.output_dir,name))
      statinfo = os.stat(os.path.join(self.output_dir,name))
      if (statinfo.st_size>self.max_file_size_bytes):
        error_msg = "File "+str(name)+" is too big ("+str(statinfo.st_size)+")\n"+
        "File size threshold is "+str(self.max_file_size_bytes)
        print(error_msg)
        os.remove(os.path.join(self.output_dir,name))
        utc_datetime = datetime.datetime.utcnow()
        utc_formatted_time = utc_datetime.strftime("%Y-%m-%d %H:%M:%S UTC")
        try:
          self.cloudant_out.insert_document({"error":error_msg,"jobid":job_id,"filename":name,
          "datetime":utc_formatted_time})
        except Exception as e:
          print("Could not get the file name of error file.  Continuing")
          self.cloudant_out.insert_document({"error":error_msg,"jobid":job_id,
          "datetime":utc_formatted_time})
.
.
.

在这两种情况下,MP4 格式的视频文件可以分成每秒 1 帧的 jpg 文件和 6 秒的 wav 文件。在准备通过 Caffe 和 Lua 进行 Soundnet 特征提取时,每个 wav 文件都需要具有相同的持续时间。如果有任何 wav 文件少于 6 秒,那么就会由 sox 生成一个空的 wav 文件,并保存在磁盘上。

outputfile = str(time.time())+'.wav'
silencefile = 'silence'+str(time.time())+'.wav'

generate_silence_length = 6 - sec_length
cmnd = ['sox','-n','-r','16000',os.path.join(dir_path,silencefile),'trim','0.0',str(generate_silence_length)]
p = subprocess.Popen(cmnd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
result=p.communicate()

空文件会附加在原始 wav 文件的末尾,确保该文件的持续时间为 6 秒。每个 wav 文件都会经历这个预处理过程。

cmnd = ['sox',os.path.join(dir_path,file),os.path.join(dir_path,silencefile),os.path.join(dir_path,outputfile)]
p = subprocess.Popen(cmnd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
result=p.communicate()

如果从 Cloudant FIFO 提取 MP4 文件,那么就会更新基于文档的记录,这样其他系统分派器就不会拣取已经处理过的工作。更新将立即执行,最大限度降低重复执行的可能性。

doc = self.cloudant.update_document_with_query({"status": {"$exists": False}},'status','retrieved')

def update_document_with_query(self,query,field,value):
doc_result = None
query = cloudQuery.query.Query(self._db,selector=query)
for doc in query(limit=1)['docs']:
doc_result = doc
if (not doc_result is None):
  doc = self._db[(doc_result['_id'])]
  doc.fetch()
  doc[field]=value
  doc.save()
  return doc_result
return None

Cloudant 查询会找到状态字段不存在的所有文档。状态字段用于保存一项工作的状态,如“retrieved”。

作业 JSON 文件可编写到分派器基于磁盘的队列中。作业 JSON 文件引用原始 MP4 文件的位置,并划分 jpeg 文件和 wav 文件。分派器会在循环中迭代,在作业队列中寻找工作,并维护每个作业的状态。分派器会确保在特定时间内完成一项作业,并且会编写错误消息以发送给 Cloudant 接收器。

self.cloudant_out.insert_document({"error":error_msg,"jobid":job_id,"file_name":name})

在每次迭代期间,分派器都会对每个启动的作业执行维护。最大并发作业数可保护 AI Highlight Ranker,避免过度使用 CPU、GPU 以及内存。同时,通过套接字执行 4 个单独的进程来解释声音、动作、语音和图形。

for component_name in self.components:
  if component_name == 'soundnet' and self.soundnet_msg:
    result = self.send_request(job_id, component_name, audios)
  elif component_name == 'graphics' and self.graphics_msg:
    result = self.send_request(job_id, component_name, frames)
  elif component_name == 'speech2text' and self.speech2text_msg:
    result = self.send_request(job_id, component_name, audios)
  elif component_name == 'actions' and self.actions_msg:
    result = self.send_request(job_id, component_name, frames)

通过套接字发送每个请求,最大限度减少任何网络和协议开销。并行进程使用 SocketServer.TCPServer 类来启动每个服务器。

server = TCPServer((HOST, PORT), SoundnetRequestHandler, componentHandler)
server.serve_forever()
人工智能收听、观看并解读高尔夫名人赛活动 – 经过排名的精彩画面

收听高尔夫赛事

高尔夫赛事的戏剧性时刻不时会被球迷、球员和解说员的声音所打断。如果球迷发出激动的呐喊声,这种声音就能有力地说明这是一个非常有看头的精彩瞬间。球员和解说员发出的欢呼声也会增加激动程度。每个对高尔夫赛事期间的声音进行编码的 wav 文件都会被转换成 MP3 格式。通过名为 SoundNet 的深度一维卷积神经网络对有关声音的数字理解进行解码 — SoundNet 对将近 200 万个视频的声音表示方式进行了编码。为每个输入声音导出来自第五卷积层的特征。导出的特征向量大小为 17152,将其发送到两个不同的支持向量机 (SVM) 模型。

torch.manualSeed(0)
torch.setnumthreads(1)
torch.setdefaulttensortype('torch.FloatTensor')
cutorch.setDevice(1)
print('Loading network: ' .. opt.model)
local net = torch.load(opt.model)
net:cuda()
net:evaluate()
local sound = audio.load(path)
if sound:size(2) > 1 then sound = sound:select(2,1):clone() end 
sound:mul(2^-23)                                        
sound = sound:view(1, 1, -1, 1)                        
sound = sound:cuda()                                    
net:forward(sound)
local feat = net.modules[opt.layer].output:float()
local result = feat:squeeze()

欢呼程度检测器已使用 Python 的 sklearn 进行训练。欢呼程度已知的特征向量用地面实况标记,用于训练 SVM 的最优边界。SVM 的输出结果可以判断出欢呼程度。第二个 SVM 用于判断球赛期间语音的整体激动程度。将 SoundNet 中大小为 17152 的特征向量输入到激动程度模型 SVM 中。SVM 的输出结果是高质量精彩画面的另一个指示器。SVM 的两次结果都会发送到证据融合组件。

听懂解说员

高尔夫赛事期间的解说可提供这一领域的相关知识,揭示某一记击球的重要意义。解说员的声调语音表明了比赛的戏剧性场面和重要性。一般句子结构和情绪表达都会体现解说员的基本激动程度。例如,Watson 语音转文本的输出结果会发送到 Python 自然语言 工具 包的 Vader 极性记分器。将计算并增减每个 wav 文件转录文本的极性。

if self.user_vader:
  polarity=self.vader.polarity_scores(' '.join(words))
  polarity_scale = ((polarity['compound']-(-1))*(0-1))/(1-(-1))+1

接下来,解说员口中特定的插入词汇,比如“amazing”、“beautiful shot”、“great”等等,会提高激动程度。高尔夫主题专家 (SME) 已经规定了这些插入词汇的相对权重和重要程度。所有插入词汇的权重总计最大为 1。文本语义得分与插入词汇得分取平均值。

if score > 1:
  score = 1
  average_positive=0.0
if len(positive_scores)>0:
  average_positive = np.mean(np.array(positive_scores))
  weighted_score = score*0.5 + average_positive*0.5
else:
  weighted_score = score

Python 适配器封装将语音转换为文本的逻辑和服务调用,通过反射获得支持。系统会加载特定的 Python 模块,比如 Watson,用于将语音转换为文本。如有任何 wav 文件,使用相应的时间将其转换为文本文件。将文本发送到 Python 自然语言工具包的 Vader 模型,检索有关整个文本的通用极性测量值。

if (self.config['business_logic'].lower() == 'watson'):
  targetClass = getattr(SpeechComponentHandlerWatsonBusinessLogic,   
  'SpeechComponentHandlerWatsonBusinessLogic')
  self.business_logic = targetClass(self.config)

def speech_to_text_helper(self,job_id, input_dir, files, output_dir):
  try:
    return self.business_logic.speech_to_text(job_id, input_dir, files, output_dir)
  except Exception as e:
    print(str(e))

观看高尔夫赛事

在击球之前、期间或之后的特定手势会表明赛事出现高潮。例如,挥舞拳头或高举双手通常会表明出现重要结果和球员的欢呼庆祝。在从 MP4 分离出来的一系列 jpeg 文件中,呈现了身体移动的动画。VGG-16 深度学习模型在 ImageNet 上经过预先训练,然后在自定义数据上进行迭代训练。IBM 研究院基于多年累积的名人赛视频对该模型进行迭代训练,这些视频包括 6744 个没有重要手势的示例和 2906 个含有重要手势的示例。分类器处理一个测试集合,其中包括 858 张负像和 460 张正像,图像内包含重要人类动作,处理的准确性达到 88%。

self.net = caffe.Net(self.prototxt_path, self.model_path, caffe.TEST)
file = os.path.join(input_dir, dir_name, filename)
image = caffe.io.load_image(file)
results = self.predict_image(image)

def predict_image(self, img):
  num = self.net.blobs['data'].shape[0]
  orig_img = self.preprocess_image(img)
  self.net.blobs['data'].data[...] = self.generate_augmentation(orig_img, num)
  out = self.net.forward()
  result = out['prob'][0][0]
  return result * 1000

每张图像经过预先处理,以创建标准化大小、通道数量和图像背景。将产生的图像发送到数据增强器,以生成新的图像。对图像进行随机裁剪,并镜像成一组新图像。将这组图像输入到 CNN 中,用于识别动作。每个 jpg 得分都被写入到文件存储中,并向证据融合套接字侦听器发送一条消息。

读取广播图形

与剪辑器中名人赛的徽标和图形检测器非常相似,我们会搜索相同的图形来确定是否可以继续划分候选剪辑。

人工智能收听、观看并解读高尔夫名人赛活动 – 经过排名的精彩画面

如果找到图形,则将感兴趣区域从用于 OCR 处理的帧中分割出来。

img = cv2.imread(img_path, 3)
if (len(img) < xy[0] or len(img) < xy2[0]):
  print("Graphic images are too small, continuing")
  return results
if (len(img[0]) < xy[1] or len(img[0]) < xy2[1]):
  print("Graphic images are too small, continuing")
  return results
px = img[xy[1], xy[0]]
px2 = img[xy2[1], xy2[0]]


dR = abs(px[0] - rgbc[2])
dG = abs(px[1] - rgbc[1])
dB = abs(px[2] - rgbc[0])
dR2 = abs(px2[0] - rgbc[2])
dG2 = abs(px2[1] - rgbc[1])
dB2 = abs(px2[2] - rgbc[0])
 
r = 0
# detected graphics
if dR < tol and dG < tol and dB < tol:
  r = 1
else:
  if dR2 < tol and dG2 < tol and dB2 < tol:
    r = 2
    if r > 0:
      for k in range(0, 4-r):
        textRegion = img[self.yt[k]:self.yt[k] + h[k], self.xt[k]:self.xt[k] + w[k]]

通过使用以下代码,从命令行调用开源 Tesseract。

p = subprocess.Popen(["tesseract", imgName2, "stdout", "-l", "eng", "-psm", "3"],stdout=subprocess.PIPE)

然后处理这个过程的输出结果,查找提到的高尔夫球员、洞号以及一般文本。结果会发送到证据融合套接字侦听器。

精彩画面排名

每个剪辑以及来自声音、语音、手势和图形的相关预示信息,都会提供有据可循的排名依据。从并行特征提取器对剪辑进行分析,完成剪辑之后,融合套接字会确定是否所有提取器都已完成。

if not (job_id in self.result_dict):
  array = dict()
  array[sender] = result
  self.result_dict[job_id] = array
else:
  array = self.result_dict[job_id]
  array[sender] = result
  self.result_dict[job_id] = array
if len(self.result_dict[job_id]) == self.component_num:
  #run fusion

如果所有多媒体算法都已完成,那么会从剪辑提取动作。根据封装的高尔夫业务逻辑,可以从原始剪辑切分出任意数量的动作。例如,一个剪辑中的多个镜头应当分别记分,这样它们才不会对模型输出结果产生直接影响。电视图形用于将剪辑切分成不同的动作。迭代通过检测到的所有欢呼次数,查找最近的电视图形出现时间(如有)。开始时间为最近的电视图形出现时间前 5 秒。通过检查每个相邻帧之间的颜色直方图差值来检测场景变更,由此确定结束时间。

每个动作分段的计时用于查找语音、声音和动作的特定预示信息。下列代码给出了一个示例。

if fAction:
  SNScoresSegsN, ActionScoresSegs = self.processAction(SNScoresSegs, fAction)
#processing speaker excitement
if fdetExc:
  SpExcScoresSegs = self.processSpExc(SNScoresSegsN, fdetExc)
#processing speech2text
if s2txtflag:
  speech_results = os.path.join(self.job_dir,job_id,'results')
  TextScoresSegs, Sp2TxtHash = self.processSp2Txt(SNScoresSegs, speech_results)

手势逻辑在动作开始和结束时间之内查找 VGG-16 最高得分。同样地,也会在动作的时间段内检索来自 SoundNet 的解说员激动程度的最高得分。语音转文本组件会对语音转文本记录中每个句子的语义含义取平均值,然后使用插入词汇检测提高分值。最后,返回在场景过渡和电视图形出现期间的人群激动程度得分。

将多媒体证据以线性方式融合起来,对剪辑中的每个动作进行排名。整体激动程度得分会保存相对于其他精彩画面的排名。

FinalScoresSegs[t] = float(self.SNWt)*SNScoresSegs[t] + float(self.ActionWt)*ActionScoresSegs[t] + float(self.SpExcWt)*SpExcScoresSegs[t] + float(self.TextWt)*TextScoresSegs[t]

产生的 JSON 文件会发送至 Content Management System,并上传到 Cloudant 精彩画面数据库。

"cognitiveInsights": {
  "commentatorTone": 0,
  "overallMetric": 0.14845520432486656,
  "gestureMetric": 9.800137664347137e-7,
  "crowdMetric": 0.18737307692307692,
  "commentatorText": 0.26275,
  "scoringMetric": 0
}

人工智能收听、观看并解读高尔夫名人赛活动

+ 此系列共有四个部分,本文是第三部分,介绍了高尔夫名人赛中 AI Highlights 的相关情况。

本文翻译自: AI Listens, Watches and Reads the Masters Golf Tournament Action – Ranked Highlights (2018-04-08)


以上所述就是小编给大家介绍的《人工智能收听、观看并解读高尔夫名人赛活动 – 经过排名的精彩画面》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

计算机动画算法与编程基础

计算机动画算法与编程基础

雍俊海 / 清华大学出版社 / 2008-7 / 29.00元

《计算机动画算法与编程基础》整理了现有动画算法和编程的资料,提取其中基础的部分,结合作者及同事和学生的各种实践经验,力求使得所介绍的动画算法和编程方法更加容易理解,从而让更多的人能够了解计算机动画,并进行计算机动画算法设计和编程实践。《计算机动画算法与编程基础》共8章,内容包括:计算机动画图形和数学基础知识,OpenGL动画编程方法,关键帧动画和变体技术,自由变形方法,粒子系统和关节动画等。一起来看看 《计算机动画算法与编程基础》 这本书的介绍吧!

HTML 编码/解码
HTML 编码/解码

HTML 编码/解码

XML、JSON 在线转换
XML、JSON 在线转换

在线XML、JSON转换工具

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具