Pythonによるゲームプレイ動画への自動タグ付け
しばらく前に、ゴルフゲームのプレイ動画から、1ホール毎に切り出しするPythonコードを紹介しました。今回は、分割したファイルへタグ付けするコードを紹介したいと思います。
出来栄え
こんな感じにタグ付けされます。検索で、コースとかホールとか入れると、サクっと抽出。同じホールをパパパっと見ていって比較すると、高低差どのぐらいみたほうがいいのか?、とか、カーブどのくらいがいいか、とか判断しやすくっていいです。あとは、腕が伴えばねぇ。。。
またも、C#からPythonへの移植でハマる
以前に書いてたコードはC#。今回もPythonへ書き換えていて、困ってしまいました。タグ付け方法がわ・か・ら・な・いー! C#の時は、WindowsAPICodePackを使ってました。Pythonでは使えないけど、なんてったってPythonだから、ググればすぐにわかるでしょ、って思ったけどなかなかヒットしない。読み込みはあるけど、書き方がわからん。
ググってわからない時は、じじぃに聞け!
じじぃ(もとい、経験豊富な年長者(;^_^A)は、大抵、聞けば何かしら知ってる。ちょっとヘルプ投げて、おだてておいたら、早速教えてくれました。ブログに書いてくれてましたので、リンク貼っときます。ちなみに、このブログもテンプレとか真似させてもらってます。
たのじぃの書き捨てノート
PythonでWindows ファイルの拡張プロパティを読み書きする
Pythonで、Windows拡張タグを読み書きするサンプルコードの紹介。GetDetailsOfで読むことはできる。書き込みは、propsys
を使う。
dummy
コードはこちら
画像判定用の比較方法は、昔、C#で使ってた時のものに変えました。RGB各要素の差分が100以上の要素数をカウントして判断する方法。こちらのほうがコードもシンプルだし、早くて安定して判定できる。後は、タグ付け用の比較画像のリストを追加していけば、いろいろ判定してタグ付けしていってくれます。実際は沢山の参照画像を登録してますが、下のコードでは適度に省いておきました。
import os
import glob
import cv2
import ffmpeg
import numpy as np
import pythoncom
from win32com.propsys import propsys
from win32com.shell import shellcon
# タグの読み込み
def readTags(filename):
# get PROPERTYKEY for "System.Keywords"
pk = propsys.PSGetPropertyKeyFromName("System.Keywords")
# get property store for a given shell item (here a file)
ps = propsys.SHGetPropertyStoreFromParsingName(filename, None, shellcon.GPS_READWRITE, propsys.IID_IPropertyStore)
# read & print existing (or not) property value, System.Keywords type is an array of string
keywords = ps.GetValue(pk).GetValue()
print(f"Read Tags:{filename}:{keywords}")
return keywords
# タグの書き込み
def writeTags(filename,keywords):
# get PROPERTYKEY for "System.Keywords"
pk = propsys.PSGetPropertyKeyFromName("System.Keywords")
# get property store for a given shell item (here a file)
ps = propsys.SHGetPropertyStoreFromParsingName(filename, None, shellcon.GPS_READWRITE, propsys.IID_IPropertyStore)
# build an array of string type PROPVARIANT
newValue = propsys.PROPVARIANTType(keywords, pythoncom.VT_VECTOR | pythoncom.VT_BSTR)
# write property
ps.SetValue(pk, newValue)
ps.Commit()
print(f"Write Tags:{filename}:{keywords}")
# 比較画像クラス
class comp_image:
def __init__(self,t,img,x,y,w,h):
self.tag = t
self.x1 = x
self.y1 = y
self.x2 = x+w
self.y2 = y+h
self.sq = w*h
self.fr = 0.0
self.cmpimage = img[y:y+h,x:x+w]
self.res = False
# 参照画像と比較してみる
def comp(self,frame,threshold):
# 差分を取る
im_diff = np.abs(frame[self.y1:self.y2,self.x1:self.x2].astype(int) - self.cmpimage.astype(int))
# 100以上の差を数える
judge=im_diff>100
self.fr = judge.sum()
if self.fr < threshold:
self.res = True
return True
else:
self.res = False
return False
# テスト表示
def testview(self):
cv2.imshow("test",self.cmpimage)
while True:
if cv2.waitKey(25) & 0xFF == ord('q'):
break
cv2.destroyAllWindows()
def AddTag(mvfile,mabiki,comp_images,overwrite):
# 既についてるKeywordsを読み込む
keywords = readTags(mvfile)
# 5個なかったら、基本タグをセットしておく
if keywords == None or len(keywords)<5:
keywords=['Tour**','Course**','Hole**','Par**','***']
# 上書きモード
FixedTag = [False,False,False,False,False]
if not overwrite:
if keywords[0]!='Tour**':
FixedTag[0] = True
if keywords[1]!='Course**':
FixedTag[1] = True
if keywords[2]!='Hole**':
FixedTag[2] = True
if keywords[3]!='Par**':
FixedTag[3] = True
if keywords[4]!='***':
FixedTag[4] = True
# VideoCapture
cap = cv2.VideoCapture(mvfile)
if (cap.isOpened()== False):
print("ビデオファイルを開くとエラーが発生しました")
while(cap.isOpened()):
# 読み飛ばし
for i in range(mabiki):
ret, frame = cap.read()
# False なら終了
if ret == True:
# View (見ながらするならコメント外す)
# miniframe = cv2.resize(frame,dsize=None, fx=0.5 , fy=0.5)
# cv2.imshow("Video", miniframe)
# if cv2.waitKey(25) & 0xFF == ord('q'):
# break
if not FixedTag[0]:
# Tour Check
for ci in comp_images['Tour']:
if ci.comp(frame,20):
# みつけたのでタグ上書き
print(f"Find:{ci.tag}, {ci.fr:.1f}")
keywords[0] = ci.tag
FixedTag[0] = True
break
if not FixedTag[1]:
# Course Check
for ci in comp_images['Course']:
if ci.comp(frame,20):
# みつけたのでタグ上書き
print(f"Find:{ci.tag}, {ci.fr:.1f}")
keywords[1] = ci.tag
FixedTag[1] = True
break
if not FixedTag[2] or not FixedTag[3]:
# Hole&Par
for ci in comp_images['HolePar']:
if ci.comp(frame,20):
# みつけたのでタグ上書き
print(f"Find:{ci.tag}, {ci.fr:.1f}")
keywords[2] = ci.tag.split(',')[0]
keywords[3] = ci.tag.split(',')[1]
FixedTag[2] = FixedTag[3] = True
break
if not FixedTag[4]:
# OtherInfo Check
for ci in comp_images['Others']:
if ci.comp(frame,20):
# みつけたのでタグ上書き
keywords[4] = ci.tag
FixedTag[4] = True
break
else:
break
if FixedTag[0] and FixedTag[1] and FixedTag[2] and FixedTag[3] and FixedTag[4]:
break
# 終了処理
cap.release()
cv2.destroyAllWindows()
writeTags(mvfile,keywords)
# タグ付け用、比較画像のリスト
Tour2 = comp_image('Tour2' ,cv2.imread(r"D:\GolfClash\RefImage\StdSize\Tour2_JuniperPoint_Hole2_Par4.jpg"), 190,180,370,90)
Tour3 = comp_image('Tour3' ,cv2.imread(r"D:\GolfClash\RefImage\StdSize\Tour3_NamhaeCliffs_Hole8_Par3.jpg"), 190,180,370,90)
Tour4 = comp_image('Tour4' ,cv2.imread(r"D:\GolfClash\RefImage\StdSize\Tour4_TheMilano_Hole9_Par5.jpg"), 130,190,485,50)
PorthelloCove = comp_image('PorthelloCove' ,cv2.imread(r"D:\GolfClash\RefImage\StdSize\Tour10_PorthelloCove_Hole9_Par5.jpg"), 220,310,310,40)
MapleBay = comp_image('MapleBay' ,cv2.imread(r"D:\GolfClash\RefImage\StdSize\Tour10_MapleBay_Hole6_Par3.jpg"), 220,310,310,40)
TheOasis = comp_image('TheOasis' ,cv2.imread(r"D:\GolfClash\RefImage\StdSize\Tour10_TheOasis_Hole9_Par5.jpg"), 220,310,310,40)
GokashoBay = comp_image('GokashoBay' ,cv2.imread(r"D:\GolfClash\RefImage\StdSize\Tour10_GokashoBay_Hole9_Par5.jpg"), 220,310,310,40)
CityPark = comp_image('CityPark' ,cv2.imread(r"D:\GolfClash\RefImage\StdSize\Tour6_CityPark_Hole7_Par4.jpg"), 220,310,310,40)
Hole9Par5 = comp_image('Hole9,Par5' ,cv2.imread(r"D:\GolfClash\RefImage\StdSize\Tour10_PorthelloCove_Hole9_Par5.jpg"), 220,358,310,30)
Hole8Par4 = comp_image('Hole8,Par4' ,cv2.imread(r"D:\GolfClash\RefImage\StdSize\Tour11_ChateauLavande_Hole8_Par4.jpg"), 220,358,310,30)
Hole8Par3 = comp_image('Hole8,Par3' ,cv2.imread(r"D:\GolfClash\RefImage\StdSize\Tour10_MapleBay_Hole8_Par3.jpg"), 220,358,310,30)
Hole7Par4 = comp_image('Hole7,Par4' ,cv2.imread(r"D:\GolfClash\RefImage\StdSize\Tour10_MapleBay_Hole7_Par4.jpg"), 220,358,310,30)
Hole7Par3 = comp_image('Hole7,Par3' ,cv2.imread(r"D:\GolfClash\RefImage\StdSize\Tour11_GrunbergSlopes_Hole7_Par3.jpg"), 220,358,310,30)
Coin400 = comp_image('400' ,cv2.imread(r"D:\GolfClash\RefImage\StdSize\400.jpg"), 290,115,150,45)
Coin1_6K = comp_image('1.6K',cv2.imread(r"D:\GolfClash\RefImage\StdSize\1_6K.jpg"), 290,115,150,45)
Coin6K = comp_image('6K' ,cv2.imread(r"D:\GolfClash\RefImage\StdSize\6K.jpg"), 290,115,150,45)
Coin200K = comp_image('200K',cv2.imread(r"D:\GolfClash\RefImage\StdSize\200K.jpg"), 290,115,150,45)
TourImages = [Tour2,Tour3,Tour4]
CourseImages = [PorthelloCove,MapleBay,TheOasis]
HoleParImages = [Hole9Par5,Hole8Par4,Hole8Par3,Hole7Par4,Hole7Par3]
Others = [Coin400,Coin1_6K,Coin6K,Coin200K]
comp_images = {'Tour':TourImages,'Course':CourseImages,'HolePar':HoleParImages,'Others':Others}
# 指定フォルダ内のmp4ファイルを順次処理する
top_level_dir_path = r'D:\GolfClash\CutMovie'
#top_level_dir_path = r'E:\CutTemp'
# サブディレクトリ、ファイルの絶対パスのリストを得る
paths = map(os.path.abspath, glob.glob(top_level_dir_path + '/**/*.mp4', recursive=True))
# ファイルのみフィルタリングする
paths = filter(os.path.isfile, paths)
# ソートされていないので昇順にソートする
paths = sorted(paths)
# 1つずつ処理する
for i, p in enumerate(paths):
print('[%s / %s] Start processing %s' % (i + 1, len(paths), p))
AddTag(p,5,comp_images,False)
コメント
0 件のコメント :
コメントを投稿