文字起こしと発声時刻で動画に文字を表示してみた

WSL の debian で作業した。 apt install ffmpegFFmpegをインストールした。

音声ファイルを静止画の動画にする

ffmpeg
  -loop 1
  -r 30
  -i image.png
  -i audio.mp3
  -pix_fmg yuv420p
  movie.mp4

文字起こしと文節ごとの発声時刻データを作る

とりあえず、Google の Cloud Speech-to-Text を使った。他の音声認識サービスでも同じようなものだろう。Cloud Speech-to-Text では単語ごとの発声時刻も取得できる。文字を表示させるのは単語単位よりも文節単位のほうがよさそうに思った。文字起こしした文章を GiNZA を使って文節に分けた。

文字起こしと単語とごの発声時刻

gcloud beta ml speech recognize
  audio.mp3
  --language-code="ja-JP"
  --encoding="mp3"
  --sample-rate=44100
  --include-word-time-offsets

サンプルレートが分からないときはffmpeg -i audio.mp3で確認できる。

文節に分ける

GiNZA のインストールを手元でやると時間がかかるから、Google Colaboratory を使った。

pip install "https://github.com/megagonlabs/ginza/releases/download/latest/ginza-latest.tar.gz"
import spacy
nlp = spacy.load('ja_ginza')
doc = nlp('まずは心を鎮めます。目を閉じて。全身の力をすーっと抜いていきます')
for sent in doc.sents:
    for token in sent:
        print(token.i, token._.bunsetu_index, token.orth_)
    print('EOS')

token._.bunsetu_indexで文節のインデックスが取得できる。

動画に文字を書き込む

今回は手っ取り早く FFMpeg の drawtext を使った。

日本語フォントをインストールする

IPA フォントにした。

sudo apt install fonts-ipafont

フォントのパスの確認方法

fc-list

文字を書き込む位置と場所とタイミングを決める

今回は適当に手で書いた。表示するフレームは発声時刻のデータを使う。

drawtext
    =text='まずは'
    :fontfile='/usr/share/fonts/opentype/ipafont-gothic/ipagp.ttf'
    :fontsize=60
    :fontcolor='#FFF100'
    :enable='between(n, trunc(0 * 30), trunc((43/10) * 30))'
    :y=(th * 1)
,drawtext
    =text='心を'
    :fontfile='/usr/share/fonts/opentype/ipafont-gothic/ipagp.ttf'
    :fontsize=60
    :fontcolor='#FFF100'
    :enable='between(n, trunc((7/10) * 30), trunc((43/10) * 30))'
    :y=(th * 2) + 10
,drawtext
    =text='静めます'
    :fontfile='/usr/share/fonts/opentype/ipafont-gothic/ipagp.ttf'
    :fontsize=60
    :fontcolor='#FFF100'
    :enable='between(n, trunc((22/10) * 30), trunc((43/10) * 30))'
    :y=(th * 3) + 10


,drawtext
    =text='目を'
    :fontfile='/usr/share/fonts/opentype/ipafont-gothic/ipagp.ttf'
    :fontsize=60
    :fontcolor='#FFF100'
    :enable='between(n, trunc((43/10) * 30), trunc((83/10) * 30))'
    :y=(th * 1)
,drawtext
    =text='閉じて'
    :fontfile='/usr/share/fonts/opentype/ipafont-gothic/ipagp.ttf'
    :fontsize=60
    :fontcolor='#FFF100'
    :enable='between(n, trunc((49/10) * 30), trunc((83/10) * 30))'
    :y=(th * 2) + 10

......

書き込む

ffmpeg -i movie.mp4 -vf "`cat draw.txt`" result.mp4

その他

字幕を SVG アニメーションで作ったら Web の編集ツールとか工夫できそうで面白そう。

音の波形で文字の装飾とかできそう。

音声を字幕動画にするのに、たぶん一番面倒そうな発声タイミングと文字表示の同期が簡単にできることがわかった。

Google Cloud Shellのカスタム環境をGoogle Cloud Shellでcloudshell envコマンドを使って用意する

Google Cloud Shell は Cloud Registory に登録した Dockerイメージ をシェルとして利用できる。これをカスタム環境 [en]と呼ぶ。

カスタム環境の開発には Google Cloud Shell に予めインストールされているcloudshell envコマンドが便利。 cloudshell env コマンドを使えば Cloud Shell 上で Dockerイメージをビルド、実行して試した後に GCP 上で自動ビルド、コンテナレジストリへのプッシュまでできる。

自動ビルド、デプロイには、Cloud Build と Cloud Source Repositories、Container Registory が使われる。

用意するもの

課金が有効になった Google Cloud Platform のプロジェクトが必要。

以下の API を有効にしておく。

  • Cloud Source Repositories
  • Cloud Build
  • Container Registory

開発環境を作る

以下は Google Cloud Shell での作業。

# 現在のセッションにGCPプロジェクトを設定する
$ gcloud config set project $PROJECT_ID

$ cloudshell env create-custom-image $REPO_NAME

$ cd $REPO_NAME

cloudshell env コマンドを実行するには、現在のセッションが GCPプロジェクトにログインしている必要がある。gcloud コマンドで現在のセッションを GCPプロジェクトにログインする。$REPO_NAMEは作成するカスタム環境の名前になる。

env create-custom-imageコマンドによって Git リポジトリが作成される

  • Cloud Source Repositories に新規 Git リポジトリが作成される
    • Cloud Shell の ~/$REPO_NAME に クローンされて origin として設定される
    • Dockerfile と Cloud Build のビルド構成ファイルが含まれる
  • Cloud Build に自動ビルドのトリガーが2つ作成される
    • このリポジトリが更新で発火
    • Goolgeが提供する Cloud Shell のベースイメージの更新で発火

作成されたことによって最初の自動ビルドが Cloud Build で実行される。ビルドが完了したら Container Registory にプッシュされる。ビルドの詳細は作成された cloudbuild.yaml に書かれている。

create-custom-image 以外の cloudshell env コマンドはこのリポジトリディレクトリで実行する必要がある。

Cloud Shellの「環境構成.イメージの場所」を変更する

cloudshell env update-default-imageで Cloud Shellインスタンスの Dockerイメージの取得元をカスタム環境イメージに設定する。作業ディレクトリのコードから自動ビルド、プッシュされる先の Container Registory のパスは cloudshell env get-image-nameで表示できる。

Dockerfileを編集する

Dockerfile を編集したらcloudshell env build-localしてビルドできるか確認する。ビルドできたらcloudshell env runでコンテナの中に入って内容を確認する、exit でコンテナからログアウトできる。

Dockerイメージを更新する

Cloud Source Repositories のリポジトリが origin として設定済みになっているので git push origin master すれば自動ビルドが実行されて Dockerイメージが更新される。

cloudshell env pushを使えば cloudshell env build-local でビルドした Dockerイメージを直接 Container Registory にプッシュすることもできる。

料金

Container Registory が使用する Cloud Storage でイメージサイズに応じた料金がかかる。Cloud Shell のベースイメージが現時点で4.5GBなので最低でも月に13円ぐらいかかる。

Cloud Build が120分/日までは無料で使える。参考として、monoのランタイムだけを追加でインストールした場合、1回のビルドが10分以内に終了した。

monoランタイムとPaketを実行できるだけのdllをインストールしたGoogle Cloud ShellのDockerイメージを作った

Google Cloud Shell で F# スクリプトを書きたかった。Google Cloud Shellには .NET Core SDK がインストールされているのでdotnet fsiコマンドで F# の REPL が使える。

nuget パッケージを使うときに Paket があると便利だが、Paket の実行には mono が必要。mono は apt でインストールができる。

Cloud Shell のインスタンスに毎回インストールしてもいいが時間がかかるので、Cloud Shell のカスタム環境として用意することにした。

カスタム環境は Google Container Registory に置いた Docker イメージを Cloud Shell として利用する。Google によって提供される Docker イメージをベースにする必要がある。提供されるベースイメージは debian 9 (strech)。

カスタム環境用の Docker イメージをビルドする Dockerfile を書いた。 mono のパッケージリポジトリと鍵を追加して、apt install で mono-runtime と必要なライブラリをインストールする。

FROM gcr.io/cloudshell-images/cloudshell:latest

RUN apt-get update \
     && apt-get install -y --no-install-recommends gnupg dirmngr \
     && export GNUPGHOME="$(mktemp -d)" \
     && gpg --batch --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF \
     && gpg --batch --export --armor 3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF > /etc/apt/trusted.gpg.d/mono.gpg.asc \
     && gpgconf --kill all \
     && rm -rf "$GNUPGHOME" \
     && APT_KEY_DONT_WARN_ON_DANGEROUS_USAGE=DontWarn apt-key list | grep Xamarin \
     && apt-get purge -y --auto-remove gnupg dirmngr \
     && apt-get clean \
     && rm -rf /var/lib/apt/lists/*

RUN echo "deb http://download.mono-project.com/repo/debian stable-stretch main" > /etc/apt/sources.list.d/mono-official-stable.list \
     && apt-get update \
     && apt-get install -y \
       mono-runtime \
       libmono-system-data4.0-cil \
       libmono-system-io-compression-filesystem4.0-cil \
       libmono-system-net-http4.0-cil \
       libmono-system-runtime-serialization4.0-cil \
       libmono-system-xml-linq4.0-cil \
     && apt-get clean \
     && rm -rf /var/lib/apt/lists/* /tmp/*

Paketを実行するためのライブラリ

Paket.exe を実行するには mono-runtime だけでは足りない。mono-complete パッケージであれば十分だけど使わないライブラリもインストールされる。mono-runtime で Paket を実行してみてエラー表示から足りないdllを確認した。以下はとりあえずパッケージダウンロードを試してエラーになったものだけ。これらのライブラリがあれば nuget からライブラリのダウンロードはできるようになる。

  • System.Data.dll
  • System.IO.Compression.FileSystem.dll
  • System.Net.Http.dll
  • System.Runtime.Serialization.dll
  • System.Xml.Linq.dll

必要になったmonoプロジェクトの情報

鍵IDとパッケージリポジトリURL
https://www.mono-project.com/download/stable/#download-lin-debian
パッケージリポジトリの追加のしかた
https://github.com/mono/docker/blob/1d31220a290b2b4d19654a8cdb4ba13888e29717/6.4.0.198/slim/Dockerfile
dllとパッケージ名の対応
https://github.com/mono/linux-packaging-mono/blob/master/debian/control

Dockerfileのベストプラクティ

docs.docker.jp

apt update, install, clean を同じ RUN で実行する。

RUN apt-get update \
     && apt-get install ... \
     ...
     && apt-get clean \
     && rm -rf /var/lib/apt/lists/*

vue-language-serverをvim-lspから使う設定をした

Vue Language Server

  • Vue Language Serverはnpmのパッケージvue-language-serverとして公開されている
    • vlsコマンドとして提供される
  • Vue Language ServerはLanguage Server Protocolのサーバー実装
    • vim-lspをクライアントとしてvimから利用することができる
  • Vue Language ServerはVeturが使用する言語サーバー

Vetur - Visual Studio Marketplace

vim-lspにvue-language-serverを登録する

let s:vls_exe=expand('~/node_modules/.bin/vls')
  
if executable(s:vls_exe)
    au User lsp_setup call lsp#register_server({
        \ 'name': 'vue-language-server',
        \ 'cmd': {server_info->[s:vls_exe]},
        \ 'whitelist': ['vue'],
        \ 'initialization_options': { 'config': {
        \     'html': {}, 'vetur': {'validation': {}, 'completion': {'scaffoldSnippetSources': {}}}
        \     }}
        \ })
else
    echohl ErrorMsg
    echom '`vue-language-server` is not installed.'
    echohl
endif

configが無いと言語サーバーで Cannot read property 'ScaffoldSnippetSources' of undefined などのエラーが発生する

上記のconfigでいくつかの機能を無効にしてるっぽいけど具体的に何するときの何の機能かなのかは確認していない。とりあえずエラーなく起動させるまでやった。

参考にした Vim8でvim-lspとasyncomplete関連でvue-language-serverを使う(細かいオプションは省略) · GitHub

Google Cloud ShellでClangのビルドをしようとしたけど時間がかかりそうだからやめた

2019年4月29日の時点で、google cloud shellにはclang-8がインストール済みだった。 知らずにソースコードからビルドしようとしたけどものすごく時間がかかりそうだったから断念した。 インストール済みだけどパスは通ってない。 export PATH=/usr/lib/llvm-8/bin:$PATHした。

$ sudo apt update
$ sudo apt full-upgrade
$ sudo apt install cmake

$ git clone --depth 1 -b llvmorg-7.1.0 --single-branch https://github.com/llvm/llvm-project.git
$ cd llvm-project
$ mkdir build
$ cd build

$ cmake ^
-G "Unix Makefiles" ^
-DLLVM_ENABLE_PROJECTS=clang ^
-DLLVM_TARGETS_TO_BUILD=x86 ^
-DCMAKE_BUILD_TYPE=Release ^
-DCMAKE_INSTALL_PREFIX "インストールしたいpath"

$ make

ビルドしたいだけだから、git cloneには、depth 1 -b タグ --single-branchのオプションをつけて必要な分だけコピーした。

JavaScriptのフォーマッターPrettierのVimプラグインをインストールした

  • vim-prettierは、デフォルトの設定ではprettierのデフォルトルールとは違う整形ルールを持つ。
  • vim-prettierは、デフォルトの設定では.prettierrcがあればその設定を優先する。

prettier

prettier.io

prettierのインストール

プロジェクトローカルにprettierをインストールする。

npm init
npm install --save-dev prettier

使い方

npx prettier --write index.js
  • --writeはファイルを上書きする
  • --checkはdiffではなく、整形済みかどうかのみを出力する

vim-prettier

github.com

インストール

~/.vim/pack/<pkgname>/start/にgit cloneする。 vimrcに何も指定しなくても:Prettierコマンドを実行すれば使える。

  • g:prettier#exec_cmd_path

    • 指定しなくても、パッケージルートのnode_modules/.bin/prettierが使用された
    • コマンドを探す順は、READMEに書いてあった
    • 現在のパスは :PretierCliPathで確認できる
  • g:prettier#exec_cmd_async default:0

    • デフォルトでは非同期実行ではない
  • <Leader>p:Prettierコマンドがマップされている

Prettierの整形オプション

CLIでオプションなしで実行した場合と、vim-prettierでオプションを既定値で実行した場合とで結果が違う。 引用符の一重と二重とか他にも。 READMEに書いてあった、意図的にcliのdefaultとは違うオプション指定にしてあるそうな。

github.com

prettierrc

.prettierrcを置けば、vim-prettierもそれを読む。

  • g:prettier#config#config_precedence default: prefer-file
    • cli-override
      • 設定ファイルよりもcliフラグが優先される
    • file-override
      • cliフラグが設定ファイルとマージされる
    • prefer-file
      • 設定ファイルがあったらcliフラグは無視される
    • たぶんここでいうcliフラグは、vim-prettierがcliをコールするときに設定するg:prettier#configの各設定値のこと

{}だけを書いた.prettierrcをプロジェクトルートに置けば、prettier#config#config_precedence = prefer-fileのときにvim-prettierの整形ルールがデフォルトのCLIの整形ルールと同一になる。