✂️
動画の無音部分を自動でカットする 〜 MLと波形解析のハイブリッド音声処理 〜
本記事では、ChopperというiOSアプリで実現している動画の無音部分を自動でカットする処理のiOSでの実装方法について解説します。
人の声だけを抽出して無音区間を除去するアプリ、Chopper
無音部分のカット?
- 人が話している部分「以外」をカットしたい
- いわゆるジェットカット
さっそくデモ
Chopper: AIで人の声を抽出して動画の無音を自動でカットするアプリ
- 2021年リリース
- 2023年に有料化
- そこそこ売れている
Before: 300秒
After: 145秒(52%カット)
動画の無音カット処理全体の流れ
入力
→ 音声抽出
→ 発話区間検出
→ 入力動画の発話区間以外をカット
→ 出力
「発話区間検出」をどう実現するか?
「音のありなし」で判定すればいい?
→ 周りがガヤガヤしてたり車の音が入ってたりしてるだけでもう発話と区別できない
→ MLで「人の声」を判定する
Sound Analysisフレームワーク
- MLベースの音声分類
音声分類(Sound Classification)
- iOS 13 / WWDC19 で登場
当時書いた記事:
実装
// アナライザを初期化
let audioFileAnalyzer = try SNAudioFileAnalyzer(url: url)
// MLModelオブジェクトを渡してリクエストを作成
let request = try SNClassifySoundRequest(mlModel: mlmodel)
// リクエストをアナライザに追加
try audioFileAnalyzer.add(request, withObserver: self)
// 解析開始
audioFileAnalyzer.analyze()
デモ: Sound Analysisを用いた話者認識
- WWDC19のキャッチアップイベント用につくったデモ
- 人物ごとの音声を学習したモデルをCreate MLで作成
Sound Analysisを用いた発話区間検出 ver. 1
- Human / Background の音声分類を行うカスタムSound Classificationモデルを使用
- 分類性能はそこそこ
Sound Analysisを用いた発話区間検出 ver. 2
- iOS 15 / WWDC21でビルトイン(システム組み込み)の音声分類器が利用可能に
- それまではモデルを自作するしかなかった
- 約300種類の音声を判別可能
- その中に "speech" も
- 分類性能は以前のカスタムモデルと比較して**大幅に向上**
解説記事:
実装方法
以前とほぼ同じ
let audioFileAnalyzer = try SNAudioFileAnalyzer(url: url)
// ここだけが新しい
let request = try SNClassifySoundRequest(classifierIdentifier: .version1)
try audioFileAnalyzer.add(request, withObserver: self)
audioFileAnalyzer.analyze()
Chopperにおける発話区間検出の細かい話
- "speech" 以外にも、「人の発話」として検出したいクラスを拾っている
- 笑い声系 "laughter", "belly_laugh", "giggling", ...
- 表現系 ”singing”, "humming", "whispering", "breathing", ...
- etc...
- 1位にspeechが来てたら発話区間、みたいな単純な話でもなく、精度向上のため秘伝のタレ的なロジック調整を色々と行っている
- 順位関係なく、発話クラスのConfidence(確信度)ベースで判定している
- Confidenceのヒストグラムから、カットレベルLow, Mid, Highの閾値を動的に決定
- 発話が断片的にならないよう、連続性も見ている
- 順位関係なく、発話クラスのConfidence(確信度)ベースで判定している
MLは完璧じゃない
普通に無音なところも検出できないことがある
→ 波形処理ベースの無音区間判定も併用
iOSにおける音声波形処理
- 音声はAVFoudationで扱う(
AVAudioPCMBuffer
) - 音声波形処理 ⇒ デジタル信号処理には Accelerateフレームワークの vDSP を使用する
解説記事:
RMS(root mean square, 二乗平均平方根)
- 音圧を表す指標
- 単位時間ごとに計測された音量を計算する
RMS計算の実装
絶対値の平均を計算してもいいが、Accelerateフレームワークの vDSP_measqv
関数を利用して一行で書ける
static func rms(data: UnsafeMutablePointer<Float>, frameLength: UInt) -> Float {
var val : Float = 0
vDSP_measqv(data, 1, &val, frameLength)
return val
}
RMSを用いてどのように無音カットするか?
Logic Proと同等のパラメータ構成で実装(単純な閾値処理ではない)
public struct Configuration: Equatable, Hashable {
public init() {}
/// 閾値(デシベル)
public var threshold: Int = ...
/// 無音として扱う最低限の時間
public var minDuration: Double = ...
/// カットし始める部分の長さに余裕を持たせる
public var preAttackTime: Double = ...
/// カットし終わる部分の長さに余裕を持たせる
public var postReleaseTime: Double = ...
}
まとめ
ChopperというiOSアプリにおける動画の自動無音カットの実現手法とその実装について解説した。
以下2つの方法をハイブリッドで使用:
- MLベースの発話区間検出
- Sound Analysis のビルトイン音声分類器を利用
- 音声波形処理ベースの無音区間検出
- AVFoundation / Accelerate を利用
Discussion