動画からスライドを自動生成するアプリを作りました

この記事は [クソアプリアドベントカレンダー2](https://qiita.com/advent-calendar/2021/kuso-app) の2日目の記事です。今まで傍から見ているだけでしたがついに初参加できて嬉しいです。

## 課題： 動画よりもスライドでサクサク見たい

WWDCセッションの内容をチェックしては発信する、ということを毎年やっている [^wwdc] のですが、なぜか昨年（2020）あたりからセッションの**スライドPDFが公開されなく**なりました。

セッションの動画は例年通り公開されているのですが、

https://developer.apple.com/videos/

**動画よりもスライドのほうがシーク効率が圧倒的に良い**ので、やっぱりスライドが欲しい。[^want]

[^wwdc]: [OSS](https://github.com/shu223/iOS-10-Sampler)を作ったり、[本](https://shu223.booth.pm/items/1461791)を書いたり、[noteマガジン](https://note.com/shu223/m/m1432c132b9b4)を書いたり。

[^want]: しっかり調査する際には結局動画を見るのだとしても、どの動画を見るかどうか決めるにあたって、まずはスライドをパラパラと見てざっと概要を把握したい。

で、つくったのが、

## 動画からスライドを抽出するiOS/macOSアプリ「Slidify」

https://developer.apple.com/videos/play/wwdc2021/10037/

↑この動画をSlidifyに食わせて**全自動で生成**したスライドが、

https://speakerdeck.com/shu223/slidify-sample

↑こちら。

いかがでしょう？

荒削りな部分（後述）はあるものの、私は自分用ツールとしては十分実用に耐えうると思いました。


## しくみ

スライド抽出のしくみはざっくり以下のとおりです：

- 動画のフレームを一定間隔ごとに解析
- 以下の条件を満たすフレームだけを抽出
  - 文字がある
  - 文字領域が前フレームから一定以上変化している


これだけ。

上のロジックに該当する実装を抜粋すると、こんな感じです：

- 一定間隔ごとの動画解析

```swift
let processingOption = VNVideoProcessor.RequestProcessingOptions()
processingOption.cadence = VNVideoProcessor.TimeIntervalCadence(processingTimeInterval)

let videoProcessor = VNVideoProcessor(url: url)
// request は VNDetectTextRectanglesRequest
try! videoProcessor.addRequest(request, processingOptions: processingOption)

let timeRange = CMTimeRange(start: CMTime.zero, end: CMTime.indefinite)
try! videoProcessor.analyze(timeRange)
```

`VNVideoProcessor` についてはこちらの記事をご参照ください：

https://note.com/shu223/n/nebdb849e56ac

- 文字があるか？

```swift
// observations は VNTextObservation の配列
guard observations.count > 0 else { return false }
```

- 文字領域が前フレームから一定以上変化しているか？

```swift
let intersection = bbox1.intersection(bbox2)
if intersection.area / bbox1.area < threshold {
    return true
}
```

文字認識も、抽出したフレーム画像同士の類似度判定も行って**いません**。

こういう簡素な抽出ロジックのため、

- メガネに映り込んだアイコン画像の中の文字を拾ってしまっていたり
- 同じようなページが連続してしまっていたり

といった問題も散見されますが、
「スライドをざーっと見て概要を把握したい」という自分の用途としては重大な問題ではありませんでした。

## 実装はほぼ使いまわし

ほとんどの実装は、過去の個人開発アプリからの流用です。

個人開発でも**使い回しやすいパーツはSwift Package化**しておいて、別プロジェクトにサクッと横展開できるようにしてあります。

https://note.com/shu223/n/n4c0bd80c9981

- 動画選択UI
  - [Chopper](https://apps.apple.com/jp/app/id1560947520) で実装したSwift Packageを流用
  - `PHPickerViewController`と`UIDocumentPickerViewController`を利用
  - メソッド一つ呼べばアクションシートが出て、後はファイル選択時（あるいは失敗時に）completion handlerに指定した処理が実行される。
- 文字領域検出
  - [Somato](https://apps.apple.com/jp/app/id1566199421) で実装したSwift Packageを流用（[TOTOC](https://apps.apple.com/jp/app/id1583927275)でも同パッケージを使っている）
  - 文字領域検出だけでなく、Visionラッパーとして自分が使いやすいようにモジュール化してある

```swift
visionHanlder.perform(video: videoURL, requestTypes: [.text], processingTimeInterval: minimumTimeInterval, resultHandler: { observations in
    // フレームごとの結果
    ...
}) { result in
    // 動画全体の処理が完了
    ...
}
```

- 動画からのフレーム抽出
  - Somato, TOTOCからの流用


今回新たに実装した部分は上述の文字領域の差分判定のところぐらい。


## 今後の展望

以下対応しておくともっと便利に使えそう：

- 類似画像の除去
- コマンドラインツール化
- コンテキストメニュー対応
  - 動画ファイル選択 → 右クリックで実行できるようにしたい


## おわりに

作り終えて記事を書き始めてから思いましたが、クソアプリカレンダーには真面目すぎる題材だったかもしれません...

「作りたい！という衝動にまかせて、有用性や需要のことは考えずとにかく手を動かす」

というものづくりの姿勢から生まれたものをクソアプリと呼ぶ、と拡大解釈して投稿させていただきました。もし場違いでしたらすみません🙇🏻‍♂️こういうネタ要素の少ないものでもよろしければぜひまた参加させてください。