開発記録 / 画像認識 × 将棋のルール

将棋の対局動画・画面に映った動画から
棋譜(Kif)を自動生成するアプリの作成記録

盤の材質も、駒の書体も、撮影の角度も、画面に占める大きさも違います。それでも「実物用」「ゲーム用」と処理を分けることなく、ひとつの仕組みで棋譜になるものを作りたいと思いました。

スマホで撮った実物の対局

盤を真上から撮影した動画。指す人の手が盤に写り込みます。投了まで通しで読み取りました。

将棋ゲームの実況動画

画面録画。投了までの全手を読み取りました。

棋戦の中継

斜め上からの撮影。盤は画面の一部にしか写りません。

オンライン対局サイトの画面

ブラウザの録画。罫線がはっきり描かれた、最も条件の良い盤です。

実物の対局を真上から撮影した1コマ

実物の対局を録画した動画の例です。盤を真上から撮影した1コマです。

指す人の手が盤に入り込み、いくつものマスが隠れています。動画の大半はこの状態で、盤全体がきれいに見える瞬間は限られています。

写真の駒はプラスチック、盤は将棋マットです。実物の対局で使う道具はさまざまで、木の盤に木の駒なら、駒と盤はほとんど同じ色になります。道具によって色が変わる以上、色を手がかりにはできません。駒も盤も常に同じ色で描かれる画面上の対局とは、条件がまるで違います。

こちらは将棋ゲームの画面です。

将棋ゲームの対局画面
盤に演出の文字が重なり、画面の右半分はキャラクターが占めています。それでも、この画面を録画した動画から棋譜を作れます。盤の位置を見つけて、盤だけを見るからです。

将棋ゲームの画面を記事や配信に使うときは、そのゲームの利用規約に従ってください

これはオンライン将棋lishogiの対局をしている画面を録画したものです。

オンライン対局サイトの画面

作成したアプリの画面です。

棋譜を生成したアプリの画面
指定するのは動画ファイルだけです。対局の開始と終了は、空欄なら動画の全体を見ます。読み取った棋譜がそのまま画面に並び、将棋ソフトで開ける形式でも保存されます。

1 目標と、できあがった手順

指し終えた将棋を、あとから並べ直したくなることがあります。ところが実物の盤で指した将棋には、棋譜が残りません。対局しながら手を書き留めるのは大変ですし、終わってから思い出そうとしても、途中で分からなくなります。

盤を撮っておくことはできます。ただ、その動画から棋譜を起こすには、映像を見返しながら1手ずつ手で入力するしかありません。数十手ぶんとなると、それだけでかなりの時間がかかります。

撮った動画を渡すだけで棋譜ができれば、そのまま将棋ソフトで開いて検討にかけられます。それを作った記録です。

最初の構想

最初の方針は素直なものでした。次の4段階で処理します。

  1. 動画から、盤が写っているフレームを取り出す
  2. 盤を正方形に補正し、81マスに分ける
  3. 各マスの駒を画像認識する
  4. 前のフレームと見比べ、変わったマスから指し手を求める

つまずいた点

素直に作ったものは、動きませんでした。フレームの抽出から駒の認識まで、3段階目まではどうにか動きました。つまずいたのは最後の「変わったマスから指し手を求める」段階です。

画像認識だけの判定 : △22ka(22)
画像認識だけの判定 : ▲28hi(28)
画像認識だけの判定 : 変化を特定できず

1行目は「2二の角が2二に動いた」という意味です。移動元と移動先が同じで、将棋の手として成立していません。試した手はすべて失敗でした。

原因ははっきりしています。照明、影、駒の書体、盤の木目、カメラのわずかな揺れ。条件が少し変わるだけで認識は崩れます。そして「変化したマスを2つ選ぶ」という方式は、認識が1マスでも誤った瞬間に破綻します。閾値を調整しても、あるフレームで直せば別のフレームで壊れました。

前提を組み替える

行き詰まって気づいたのは、将棋にはルールがあるという点でした。ある局面で指せる手は限られています。平手の初手なら30通り。中盤でも100通り前後です。

【従来】 画像 → 認識 → 指し手(誤りが混入する)
 
【変更後】 局面 → ルールから、指せる手をすべて列挙
それぞれについて「盤がどう変わるか」を予測
実際の画像と一致するものを選ぶ

画像から答えを作るのではなく、先に候補を用意して画像で選ぶ。順序を入れ替えたわけです。ルール上ありえない手はそもそも候補に入らないため、先ほどのような答えは原理的に出なくなります。

局面の管理には python-shogi を使いました。確定した手を1手ずつ進めていくので、ルール上ありえない盤面には決してなりません。一度の誤認識が後々まで尾を引くこともありません。

駒の種類も、見ないことにする

ルールと組み合わせてもなお、画像認識の精度に引きずられる部分が残っていました。候補が複数あるとき、最後は「移動先のマスが本当にその駒に見えるか」を採点して選んでいたからです。

実際に判定させると、正解の手と次点の手の差はごくわずかでした。採点項目のひとつに「移動元のマスが空になっているか」があり、この判定が反転するだけで、その差は消えてしまいます。照明や撮影角度が少し変われば、順位は簡単に入れ替わるということです。

そこで、画像に任せる役割をさらに減らすことにしました。

画像からは「どのマスが埋まっているか」だけを読む。何の駒かは判定しない。

駒の種類はルールが決めてくれます。7六に動ける先手の駒が歩しかないなら、そこにあるのは歩です。画像で確かめる必要がありません。

各合法手について「指したら配置がどう変わるか」を計算し、画像から読み取った配置と完全に一致する手だけを残します。

手の種類盤の変化
通常の移動駒が1マス消え、1マス現れる
駒を取る駒が1マス消える
持ち駒を打つ駒が1マス現れる
変化なしどこも変わらない

この方式に変えたところ、実測ではほとんどの手が候補1つに絞られました。見た目の微妙な差で決める必要がなくなったのです。

小さく写った駒の文字を読むのは困難ですが、「そこに何かあるか」なら誤りにくい。難易度の高い判断を機械に負わせず、確実にできることだけを任せて、残りはルールに解かせる。 今回もっとも効いた設計判断でした。

指されていない画像を弾く

ここで別の問題が出ます。初期の実装は「1手指された」ことを前提に最良の手を選ぶだけで、どんな画像を渡しても必ず何かの手を返していました。局面が変わっていない画像を並べたところ、19手ぶんの誤った棋譜が生成されました。

そこで、画像1枚を読んだ結果を5つに分けるようにしました。

状態意味動作
move1手(または2手)進んだ棋譜に追加
no_change配置が前と同じ読み飛ばす
stale少し前の局面が映っている読み飛ばす
occluded手などが盤に被っている読み飛ばす
unexplained手順で説明できない読み飛ばす

これでフレームを多めに抽出しても構わなくなりました。余分な画像は判定側が弾きます。抽出側で完璧を目指す必要がなくなったのは大きな利点でした。

できあがった手順

ここまでの組み替えを重ねた結果、画像1枚を読む手順は次のようになりました。最初に考えた4段階のうち、そのまま残ったのは前半だけです。

盤の四隅を検出する 候補を列挙し、罫線が縦横10本ずつ取れるものを選ぶ 四隅で正方形(900×900)に直す 81マスに切り分ける 1マス100px。1枚目だけ、駒40枚の見た目をここで覚える 各マスを「空」「駒」「どちらでもない」に読む いまの駒の配置を、前の局面と比べる 駒の種類は読まない ルールから、この配置になる手順をすべて挙げる 打った駒が先の画像にも残っているか確かめる 候補が複数なら、この順で絞る 1 打った駒の種類は、先の画像の見た目で 2 先の画像と辻褄が合うかで 3 それでも残れば、見た目と変化量の採点で 1手に確定 局面を1手進めて、棋譜に書き足す 初めて出た駒の見た目を、ここで覚える ここから、画像1枚ごとに繰り返す 配置が変わっている 候補がある 残っている 見送る(局面は進めない) 読めないマスが多い 盤に何かが被っている 配置が前と同じ まだ指されていない 少し前の局面と同じ 画面の更新待ち 手順がひとつも無い 1手では説明できない 先の画像に残っていない 駒の読み違い 次の画像へ
画像1枚から1手を取り出すまで。はじめの3段が盤を切り出す準備です。主線を最後まで通った画像だけが棋譜に1手を足します。右へ抜ける5つは、どれも局面を進めずに次の画像へ回ります。抽出の段階で余分な画像が混ざっても構わないのは、この5つが受け止めるからです。

2 盤を見つける

ここからは、この図の順に見ていきます。まずは盤を見つけるところからです。盤がどこにあるかは、色ではなく罫線の本数で決めます。盤を正方形に補正するには四隅の座標が必要です。画面に格子状に49個の種点を置いて領域を広げ、盤らしい候補をいくつも作ります。そのすべてについて「いったん正方形に補正して、罫線が縦横10本ずつ等間隔で取れるか」を確かめます。将棋盤なら必ず10本ずつあるはずで、これは盤の色と無関係です。

左:実物の盤に検出された四隅を赤、81マスの分割を緑で描いたもの。右:正方形に補正して9×9の格子を重ねたもの。
実物の盤から。左は元の映像に、自動検出した四隅(赤)と81マスの分割(緑)を重ねたもの。候補92個のうち、罫線が縦横10本ずつ取れたのは1個だけでした。右は正方形に補正したもので、格子が実際の罫線にそのまま乗っています。盤は0.6度傾いていて、端から端では約10px──1マスの1割ほどずれています。

歪んだ元画像から罫線を直接探すのは不可能でした。 横線は取れるのに縦線が取れない。原因は透視歪みではなく駒の並びです。横方向は駒9個の縁が同じ座標に揃って信号を補強しますが、4・5・6段が空なので縦方向にはその補強が効きません。縦で拾えていたのは、罫線ではなく駒の輪郭でした。

つまり「歪みを取るには盤の位置が必要で、盤を見つけるには罫線が必要」という循環があります。粗い候補を作ってから正方形化して検証するという順序でなければ解けません。

ゲーム画面では罫線が薄すぎてこの方式が通らず、別のやり方(罫線の周期からマスの大きさを求め、駒の並びが平手と一致する位置を探す)を追加しました。ただし「実物かゲームか」で切り替えてはいません。 まず方式1を試し、失敗したときだけ方式2を試すという順序です。

盤が綺麗なほど見つからない

この仕組みは、思わぬところでつまずきました。罫線がはっきり描かれた盤ほど、盤が見つからなくなるのです。

候補を作る最初の段階で、種点から色の似た範囲へ塗り広げています。罫線が濃くはっきりしていると、この塗りが線を越えられません。オンライン対局の画面では、駒の無い中央3段の帯だけが埋まって止まり、画面のわずか2.4パーセントにしかなりませんでした。候補として扱う最低の大きさに届かず、盤の候補にすら入らなかったのです。条件が良いほど失敗するという逆転が起きていました。

対処は単純でした。盤の輪郭を探す段階では罫線も駒も邪魔なだけなので、先に潰してから塗ります。 暗い罫線は埋め、明るい駒は削る。どちらもマスより小さい構造なので、この操作で盤が平らな一枚の領域になります。候補は足すだけなので、それまで見つかっていた候補は失われません。

もうひとつ問題がありました。塗り広げで得られる四角形は盤の内側に寄ります。 一回り小さいため、いちばん外側の罫線が枠の外に出てしまい、10本のうち8本しか取れません。そこで、通らなかったときだけ枠を1マス分広げて切り出し直します。広げると盤の外の輪郭も混ざるので、本数はちょうど10本になりません。ここでも「9マス等間隔」という将棋盤の条件が使えます。拾った線の中から、等間隔に最も近い10本の組を選びます。

検出した四隅と81マスの分割
オンライン対局の画面から。赤が自動検出した罫線の枠、緑が81マスの分割線です。盤の位置も大きさも事前には与えていませんが、実際の罫線にそのまま重なっています。この盤は、上の対処を入れるまで候補にすら入りませんでした。

結果として、四隅の検出は3段構えになりました。先に通った段の結果を使うので、すでに動いていた映像の挙動は変わりません。

やり方これで決まる映像
1候補をそのまま正方形化して、罫線が10本ずつ取れるか実物の対局、中継
21マス分広げて、等間隔に並ぶ10本を選ぶオンライン対局
3罫線の周期からマスの大きさを求め、駒の並びが平手と一致する位置を探すゲーム画面

3段目は最後の手段です。駒はマスの大部分を占めるので、半マスずれても並びは一致してしまいます。 同点の候補が数百個並び、そこから正しいものを選ぶ方法は8種類試してどれも当たりませんでした。罫線が読める盤は、1段目か2段目で決めるべきです。

3 マスに駒があるかを読む

図の「各マスを『空』『駒』『どちらでもない』に読む」段です。「どのマスが埋まっているか」だけを読む方式にしたので、その判定精度がすべてになりました。4つの局面のすべてのマスで試しました。明るさの平均では多くのマスを誤りますが、彩度なら誤りは出ません。これで解決したと思いました。しかし、盤は木目が入ることがあり、色が安定しない場合があります。

彩度で判定できたのは、たまたま手元の盤が布製のベージュで、駒が白かった時でした。木製の盤に木製の駒なら、盤と駒の色はほとんど同じになります。誤動作を防ぐために、色を使う判定はすべて止めることにしました。

代わりに使ったのが「ざらつき」です。駒には文字と輪郭があるのでマスの中の明るさがばらつき、空のマスは比較的のっぺりしています。測っているのは、明るさのばらつきと、輪郭の多さ(エッジの割合)の2つです。これは色に依存せず、誤りも彩度と同じく出ませんでした。

ただし絶対値では判定できません。木目の強い盤なら空のマスもざらつくからです。そこで次のようにしました。

その盤の空きマスを実際に測って、基準にする。

どのマスが空かは局面から分かります。ルールが与えてくれる情報です。初期配置なら4・5・6段の27マスは必ず空。そこを測って基準を作り、その2倍を超えたら駒とみなします。木目が強い盤なら基準も自動的に上がります。

実物とゲーム画面は見た目がまるで違うので、動画がどちらかを判別して処理を切り替える案も考えました。採用していません。分ければ片方の不具合がもう片方に隠れますし、きれいに撮った実物の盤と写実的なゲーム画面は見分けが付きにくいので、判別を誤れば全体が止まります。そもそも目標が「等しく扱えること」なので、分けた時点で遠ざかります。

代わりに徹底したのが、この節の方針です。実際に測って決めている量を挙げます。

何から決めているか
空きマスの見え方その盤の空きマスを測った値
カーソルの大きさその盤の駒の輪郭の広がりとの比
使えるフレームの条件その動画の「読めないマス数」の分布

ひとつの基準で足りないとき

動画から使えるフレームを選ぶとき、最初は「読めないマスが少ないもの」という一本の基準で選んでいました。これが中継の映像で行き詰まります。

盤が小さく写る映像では、端の駒が潰れて読めません。 しかもそれは一瞬ではなく、その局面が続く数秒のあいだずっとです。基準をどこに置いてもその局面だけが丸ごと抜け落ち、手順のつながりが切れて、そこから先が総崩れになります。

基準を緩めれば拾えるかというと、そう単純ではありませんでした。少し緩めるだけで拾える枚数はわずかしか増えないのに、読み取れる手数は0手から全手まで変わります。枚数の問題ではなく、特定の局面が拾えているかどうかで決まっているからです。この性質のため、分布から一本の基準を決める方法はどれも成功しませんでした。

解決したのは、基準を増やすことでした。

読めないマスが少ない。
または、その配置が合計0.5秒以上映っていた。

どちらかを満たせば使います。後者は何マス読めないかを問いません。指した後の局面は次の手まで盤に残りますが、駒が動いている最中や画面が切り替わる瞬間は一瞬しか映らないので、時間で分けられます。

秒数を選んだのには理由があります。「読めないマスの数」は盤の大きさや画質で意味が変わりますが、「指した後の局面はしばらく盤に残る」は、対局という行為の性質で、映像の作りに左右されません。

ただし後者だけでも足りません。実物の対局は指す人の手が絶えず動いていて、0.5秒そのまま映る瞬間がないからです。2つの条件は、性質の違う映像をそれぞれ受け持っています。どちらを使うかも動画が決めます。ほとんどのフレームが完璧に読める映像では、時間の条件を加えません。加えると、演出や選択画面の静止した状態まで局面として拾ってしまうからです。

駒を取る動作は、2段階だった

実物の対局を長く通して読ませたとき、途中から棋譜が崩れました。存在しない手が2つ挿入され、そこから先が別の手順に化けています。

原因は、画面上の対局には無い現象でした。実物の盤で駒を取るとき、人は「相手の駒を外す」「自分の駒を進める」の2つの動作をします。 その間に、取られた駒だけが消えて、取る側の駒はまだ元の位置にある、という状態が生まれます。手が盤から離れているので、盤はきれいに写ります。

これが厄介なのは、駒の配置だけを見ると本物の「取る」手と区別が付かないことです。どちらも「あるマスが空になり、新しく駒が現れたマスは無い」という同じ形をしています。区別するにはルールの知識が必要で、それはフレームを選ぶ段階にはありません。

選ぶ段階が持っている情報は、時間だけでした。本物の局面は数秒から10秒あまり盤に残るのに対し、駒を持ち替える間は1秒足らずです。

ただし秒数をそのまま条件にはできません。中継の映像ではカメラが棋士に切り替わるため、正しい局面でも0.3秒しか映らず、この中間状態より短くなります。同じ「時間」でも、映像の作りによって意味が変わってしまいます。

そこで、前後の局面と比べることにしました。前の局面も次の局面も自分より何倍も長く映っているなら、それは指し手の途中である、と判断します。比であれば、切り替わりの多い映像でも意味が保たれます。

もうひとつ、実際に手こずった点があります。中間状態は続けて2つ現れることがあります。 相手の駒を外した瞬間と、自分の駒を置いている途中です。1つずつ見ると、隣にもう一方の中間状態がいるせいで「前後とも十分長い」を満たせず、素通りしてしまいます。中間状態どうしが互いをかばう形です。そこで、続けて現れた短い状態はひとまとまりとして見ることにしました。前後の本物の局面が数秒ずつ映るのに対し、あいだの2つはどちらも1秒足らずです。まとめれば、前後との差はふたたびはっきりします。

これで、実物の対局が投了まで通して読めるようになりました。

盤に重なるものを見分ける

駒でないものが、駒に見えることがあります。ゲーム画面の動画では、実際には指されていない手が棋譜に混ざりました。原因は3種類で、いずれも盤に何かが重なって「そこに駒がある」ように見せていたものです。

  • マウスの矢印 駒テンプレートに一致していました。矢印はマスの一部にしか輪郭がないので、その盤の駒との比で見分けられます。固定値では動画ごとに通用しません
  • 演出の表示 画面を横切る帯が空のマスを覆っていました。厄介なのは、その誤りが駒の配置だけでは矛盾しないことです。駒の向きも手がかりにしましたが、向きを信じてよい確からしさが動画ごとに違いすぎて、照合の条件にはできませんでした。いまは「向きを決められないマス=何かが被っている疑いがある」という用途にだけ使っています

いちばん手強かったのは成り/不成の選択ダイアログです。ダイアログには本物の駒の絵が、ほぼマスと同じ大きさで描かれています。見分けられる特徴を2つ実測しましたが、どちらも失敗しました。

  • 罫線が隠れるか → この盤の罫線は薄すぎて、測っていたのは駒の輪郭だった
  • 中心からのずれ → 本物の駒とほとんど変わらなかった

1マスだけを見る特徴では、原理的に区別できないと分かりました。

共通点から解く

3つの誤りを並べたところ、共通点がありました。マウスの矢印も、演出の表示も、選択ダイアログも、作り出した誤った手はすべて「持ち駒を打った」形だったのです。理由を考えると当然でした。

駒を何もないところから生み出せるのは「打ち」だけ。

通常の移動も駒を取る手も、移動元のマスが空になっていなければ成立しません。重なっただけの要素では、移動元の変化を作れない。だから盤に重なるものは、必ず「打ち」の形でしか入り込めないのです。

ここで前提をひとつ。アプリは動画をそのまま見ているのではなく、盤がよく見えている瞬間だけを静止画として抜き出し、それを1枚ずつ順に読んでいます。少しあとの盤を確かめたければ、次に抜き出された画像を見ればよいわけです。

対策は一点に絞れます。打ったと判断したら、その駒があとの画像にも残っているか確かめる。

打った直後あとの画像判定
本物の打った駒あるある採用
カーソル / 演出 / ダイアログあるない却下

重なる要素の見た目を一切知らずに済むので、別の将棋ゲームで演出の形が変わっても通用します。

4 打った駒の種類を決める

ここだけは、画像の見た目に頼るしかありません。図の絞り込みの1段目、打った駒の種類を決めるところです。持ち駒に歩と銀があるとき、どちらを打っても「そのマスに駒が現れる」だけです。盤の変化はまったく同じで、配置を見ている限り原理的に区別できません。

しかもこの誤りは、その場では表面化しません。打った駒が数手後に動くとき、歩なら真っすぐ前へ、銀なら斜め前へ進みます。行き先は違いますが、どちらも「打ったマスが空になる」だけで、盤の変化は一致します。誤ったまま何手も進み、ずっと後になって破綻します。

さらに、打った直後の駒はもっとも読みにくい状態です。ゲーム画面では白く光る演出が入るためです。

ここでもあとの画像が効いてきます。「1枚あと」とは、次に抜き出された画像、つまり対局が少し進んだ時点の盤のことです。光が消えたあとなら、駒の絵をはっきり照合できます。歩を打った場面では、打った直後こそ歩と銀がほとんど並びますが、1枚あとの画像では歩がはっきり勝ちました。

ただし、あとの画像を見れば済むという単純な話ではありませんでした。別の場面では、どの画像を見るかで答えが変わります。桂を打った場面では、1枚あとでも2枚あとでも香が1位で、正解の桂が勝つのは3枚あとの画像だけでした。

そこで、4つの規則を採用しました。

規則1 — 駒がもうそこに無い画像は使わない

打った駒は、いずれ動くか取られます。そのあとのマスはどの駒にも似ておらず、駒の種類について何も語りません。ところが候補どうしの差だけは、偶然大きくなることがあります。

ある場面では、駒が残っている4枚すべてで正解の香が1位でした。ところが差がもっとも開いたのは、駒が消えたあとの画像です。そこだけを見て角と誤りました。差が大きいことと、判断材料があることは別です。 1位でさえ駒に見えない画像は、使いません。

規則2 — もっとも鮮明に写った1枚で決める

残った画像のうち、1位の一致度がもっとも高い1枚を選びます。差が最大の1枚ではありません。 画像ごとの一致度を平均すると候補どうしが打ち消し合ってしまうので1枚に絞りますが、選ぶ基準は「駒がはっきり写っていること」です。

差の測り方にも注意が要ります。当初は「最高値と最低値の差」で判断していました。しかしこれでは、明らかに異なる駒が候補に混じっているだけで差が水増しされます。ごく僅差でしかない1位が、はっきり勝ったものとして選ばれていました。比較するのは1位と2位です。

規則3 — 僅差なら、全会一致のときだけ決める

もっとも鮮明な画像でも差が小さいことがあります。そのときは、使える画像すべてで同じ駒が1位になっている場合にかぎって決めます。過半数では足りません。実際に過半数で判定させたところ、本当に見分けが付かない場面で誤りました。決めないことも、正しい判断のひとつです。

規則4 — 決めきれなくても、明らかに劣る駒は除外する

判定を見送ると、処理は採点に回されます。ところが採点は打った直後の画像を参照します。そこでは光の影響で誤った駒が1位になっていました。「決められないときに、より信頼性の低い証拠に戻る」という誤った構造でした。

あとの画像は、決めきれなくても「明らかに異なる駒を除外する」ことはできます。これが効いた理由は後になって判明しました。香を使ったことにすると持ち駒が1枚減り、その先の局面で正しい手が候補に入らなくなってしまうのです。

5 使い方と、できないこと

画面はPython標準のtkinterだけで作りました。画像処理はOpenCV、数値の計算はNumPy、局面の管理はpython-shogiを使っています。

将棋 棋譜自動生成
動画ファイル対局動画.mp4
対局開始(対局が始まる時刻)
対局終了(対局が終わる時刻)
先手 / 後手(対局者名・任意)
勝敗先手の勝ち / 投了
棋譜を作成中止盤の検出結果を見る棋譜を開く棋譜を保存

使い方

  1. 動画ファイルを選ぶ。「参照」から選びます
  2. 対局が始まる時刻と終わる時刻を入れる。秒数でも「分:秒」でも指定できます。空欄なら動画全体を対局とみなします
  3. 対局者名と勝敗を入れる。どちらも任意です
  4. 「棋譜を作成」を押す。進み具合が画面に表示されます
  5. 「棋譜を保存」で書き出す。好きな場所にKIFファイルとして保存できます

盤をうまく認識できているか不安なときは、「盤の検出結果を見る」を押すと、四隅と9×9の格子を描き込んだ画像が開きます。ここがずれていれば、以降の判定もすべてずれます。最初に確かめておくと確実です。

入力で迷いやすいのは対局の開始時刻です。駒がまだ初期配置に並んでいて、指す手が盤に写っていない瞬間を指定してください。アプリはその1枚から駒の見た目を覚えるので、ここを外すとうまく動きません。

勝敗を手で入力してもらう理由もあります。投了・時間切れ・反則負けは駒が動かないため、画像から判定できないからです。詰みだけは機械的に判定できますが、実際の対局は投了後そのまま感想戦に入って盤面が動き続けるので、自動判定は見送りました。

出力は将棋ソフトでそのまま開けるKIF形式です。

#KIF version=2.0 encoding=Shift_JIS
手合割:平手
先手:
後手:
手数----指手---------消費時間--
   1 7六歩(77)   ( 0:00/00:00:00)
  27 同 歩(87)   ( 0:00/00:00:00)
  85 3六香打   ( 0:00/00:00:00)
  86 投了   ( 0:00/00:00:00)
まで85手で先手の勝ち

KIFには細かい決まりがあります。手番記号(▲△)は書かない、直前と同じマスに指したときは「同 」と全角空白を入れる、文字コードはShift_JIS、消費時間の欄は省くと読めないソフトがある。このあたりを踏まえて実装しました。

処理中は、盤を切り分けたマス画像が大量に生成されます。そのままでは溜まり続けるので、処理の前と後の2回、片付けるようにしました。前に消すのは、動画を切り替えたときに前回のファイルが混ざるのを防ぐため。後に消すのは容量のためです。処理を終えると、棋譜と確認用の画像だけが残ります。

処理にかかる時間

処理は2段階です。まず動画から盤がよく見えているフレームを抜き出し、次にそれを1枚ずつ読んで棋譜にします。

かかる時間を決めるのは動画全体の長さではなく、対局部分の長さです。開始と終了の時刻を指定すると、その範囲だけを調べるためです。

ゲーム実況の動画で測った実例です。

時間
動画全体41分57秒解説や複数の対局を含む
うち対局部分15分34秒開始・終了の時刻で指定した範囲
処理時間9分52秒フレーム抽出6分38秒 + 解析3分14秒
フレーム抽出6分38秒
解析3分14秒

対局時間の6割ほどで終わる計算です。15分半の対局なら10分弱。動画を見返しながら手で棋譜を起こすことを考えれば、待っていられる範囲だと思います。

時間の3分の2はフレームの抽出に使われています。動画を細かく調べて「盤がよく見えている瞬間」を探す工程で、ここが処理の主役です。読み取り自体は、抽出さえ済んでいれば速く終わります。

なお、盤が画面に大きく写っている動画ほど1マスあたりの画素が多くなり、照合に時間がかかります。上の数字はあくまで目安です。

できないこと

理由
カット編集された動画指し手が映っていない
駒落ち・途中局面から平手を前提に駒を学習している
1本に複数局(自動分割)時間指定で足りると判断
対局中に盤やカメラが動く四隅を1枚目で確定して使い回す設計

いちばん上の行について補足します。中継やダイジェストでは、対局の一部が編集で切り落とされ、数手ぶんの映像がまるごと無くなっていることがあります。

これはアプリの精度とは関係のない問題です。指されていない手は画像のどこにも写っていないので、どれだけ読み取りを工夫しても復元できません。情報がそもそも存在しないからです。なおカットとカットの間は、それぞれ正しく読めます。通しの棋譜にはなりませんが、区間ごとの記録としてなら使えます。

編集されているかどうかは、隣り合うフレームで「見た目が大きく変わったマスの数」を数えれば分かります。1手で変わるのは2マス前後で、10マスを超える変化は盤の上で起きたことでは説明が付きません。対局時計が映っていれば、切れ目の前後で時計が何分も進んでいることでも分かります。

2行目の平手を前提にしているのは、1枚目の画像から駒の見た目を覚えるためです。中継の映像では、盤に切り替わってから最初の手が指されるまでの窓が1秒未満しかなく、そこを外すと解析そのものが成立しませんでした。

ソースコード

この記事で説明した仕組みは、GitHubで公開しています。

github.com/onipoo/shogi-kifu-from-video-and-pc-screen

いまのところ閲覧のみで、複製や改変は許可していません。内容が固まった段階で、あらためて決めるつもりです。

謝辞

本記事で使用した将棋ゲームの画面は、とつげき東北氏にご提供いただきました。仕組みを検討する過程でも、技術的な助言をいただいています。記して感謝いたします。

出典

本文で「オンライン対局サイト」としている画面は lishogi.org のものです。lishogi は AGPLv3 以降のフリーソフトウェアで、盤の画像は the lishogi authors・nexxogen・CouchTomato87 による AGPLv3+、駒の画像(Ryoko_1Kanji)は nexxogen 氏による CC BY-SA 4.0 です。画面は筆者自身のアカウントで対局したものです。

実物の盤の写真、アプリの画面、図表はいずれも筆者が撮影・作成したものです。