ストリーミングはほとんどのブラウザと
Developerアプリで視聴できます。
-
VisionKitで機械可読コードやテキストをキャプチャする
VisionKitのData Scannerとは:このフレームワークは、AVCaptureとVisionを組み合わせて、シンプルなSwift APIで機械可読コードやテキストをライブキャプチャできるようにするものです。ここでは、バーコードの種類や言語選択を指定して、Appがキャプチャできるコンテンツの種類を制御する方法を紹介します。また、Appのガイダンスを有効化したり、項目のハイライト表示や関心領域をカスタマイズしたり、Appで項目が検出されたときにインタラクションを処理したりする方法も紹介します。 静止画または一時停止したビデオフレームでのLive Textとのインタラクションの詳細については、WWDC22の「Live TextのインタラクションをAppに追加する」をご覧ください。
リソース
関連ビデオ
Tech Talks
WWDC22
-
ダウンロード
(軽快な音楽)
こんにちは Ron Santos です input engineer をしています 本日 お話するのは機械可読コードや文字を ビデオフィードからキャプチャすること つまりデータスキャンについてです では データスキャンとは何なのでしょうか? それはカメラなどのセンサーを使い データを読み取る方法です
通常 データは文字の形で出てきます 例えば 人の関心を引く情報で例を挙げると 電話番号や日付や値段ですね
あるいは機械可読コードは どこでも見られるようになった QR コードなどです 皆さん データスキャナは すでにご存じでしょうか カメラ App や iOS15 で導入された Live Text 機能などで使われています ご自身のスキャン方法で 日常生活で独自のスキャン機能を持つ App を使われたことと思います ですが 自分でデータスキャナを 作成しなければならなかったら? どのようにやりますか? iOS SDKなら要求に応じて ソリューションがいくつかあります AVFoundationフレームワークを使って カメラグラフを設定し 入力出力を繋げ バーコードなどの AVMetadataObjects を生成します 文字をキャプチャしたい場合は AVFoundation と Vision を組み合わせます この図ではメタデータの出力の代わりに ビデオデータ出力を作成します 出力にはサンプルバッファが生成され それが Vision で文字やバーコードの 認識リクエストに使われます Vision のデータスキャンに関する詳細は 下に出ているセッションをご覧ください AVFoundation と Vision を使った データスキャンのお話でした iOS16 ではそれを全て統合した 新しいオプションがあります DataScannerViewController です AVFoundation と Vision の機能を合わせ持ち データスキャンの目的に最適だと言えます DataScannerViewControllerが 提供する機能のには ライブカメラのプレビューや 有用なガイダンスのラベル表示 アイテムのハイライトに加え 対象を選択するタップフォーカスや ピンチアウトで拡大する機能ですね
デベロッパ向けの機能としては まず DataScannerViewController は UIViewController のサブクラスでなので 自由に表示できます 認識されたアイテムの座標は 常にビュー座標なので 画像スペースからVision座標に変換したり ビュー座標に変換する必要はありません 対象エリアを指定することで ビューの有効範囲も制限でき これもまたビュー座標が使えます 文字認識に関しては コンテンツタイプを特定して 見つけたい文字のタイプを制限できます 機械可読コードの場合は どのシンボル体系が対象か正確に特定できます App を使う中でデータスキャンは App の機能のほんの一部ですよね ですが実装にはかなりのコードを要します DataScannerViewController なら 一般的なタスクを代わりにやってくれるので 皆さんは他の部分に 時間をかけられるようなります 次は App への追加方法をお話していきます プライバシーの記述から始めましょう App がビデオのキャプチャを試みると iOS がユーザーに カメラへのアクセスの明示的許可を求めます 許可の必要性を正当化する記述を 提供しましょう そのためには “privacy camera usage description” を App の Info.plist ファイルに加えてください ユーザーに分かりやすいよう できるだけ明確にしましょう 次はコードです データスキャナを表示したい場合は VisionKit のインポートから始めましょう
データスキャンは全てのデバイスで サポートされているわけではないので isSupported が false の場合は 機能を表示するボタンやメニューを全て隠し 使用できないものは ユーザーに見えないようにしましょう
2018年以降の iPhone や iPad で Apple Neural Engine 搭載なら データスキャンをサポートしています 可用性のチェックも必要です 先ほどのプライバシーの記述でありましたね スキャンが使えるのは ユーザーがカメラへのアクセスを許可し この画面にあるような スクリーンタイムのコンテンツ およびプライバシー制限での カメラアクセス制限が無い場合だけです ではインスタンスの作成です 対象としたいデータタイプを 指定することから始めます 例えばこの指定なら QRコードと文字の 両方のスキャンが可能です
また 文字認識に言語リストを指定すると テキスト認識エンジンで言語修正など 様々な処理でヒントとなります どの言語か予測可能であれば リストを作りましょう 特に 2つの言語の外見が 似ている場合は有用です 何の言語も記載しなければ ユーザー設定の言語がデフォルトで使われます 特定のテキストコンテンツの タイプもリクエスト可能です この例では スキャナの対象は URL になっています 認識したいデータのタイプが 記述されたところで DataScanner のインスタンスを作成します 前の例では バーコードのシンボル体系や認識言語 そしてテキストコンテンツのタイプを 指定しました これらの他の選択肢について少し説明します バーコードのシンボル体系は Vision のバーコード検出器と同じ 全てのシンボル体系をサポートしています 言語の観点から言うと DataScannerViewController は LiveText 機能の一部なので 全く同じ言語をサポートします iOS 16 では 嬉しいことに 日本語と韓国語が追加されました もちろん また将来変わるでしょう supportedTextRecognitionLanguages の クラスプロパティから最新のリストを 取得してください 特定の意味を持つテキストをスキャンする時は DataScannerViewController は 次の7種類を見つけ出します これで Data Scannerをユーザーに表示できます 他のビューコントローラと同様に フルスクリーンやシートでの使用 別のビューの階層に使うなど色々できます ご自由に決めてください 表示が完了したら startScanning() を呼び出し データ探索を開始します では1つ前の段階に戻り Data Scanner の 初期化パラメータについて少しお話します ここで使ったのは recognizedDataTypes ですが 他にもカスタムマイズに使えるものがあります 1つずつ見ていきましょう recognizedDataTypes は 認識するデータの種類を指定します 文字や機械可読コード そして それぞれのタイプの指定です qualityLevel は balanced fast accurate のいずれかです fast は 標識のテキストのような 大きくて読みやすいものを想定していて 速度を優先して解像度を犠牲にします accurate 正確さはマイクロ QR コードや 極小のシリアル番号でも最高の精度です balanced バランスから始めると 大半のケースで上手くいきます recognizesMultipleItems は 一度に複数の バーコードをスキャンする場合など フレーム内の1つまたは複数のアイテムを 探すためのオプションです false の場合は ユーザーが他の場所をタップするまで 中央のアイテムが認識されるのが デフォルトです ハイライトを描画する時はハイフレームレート トラッキングを有効にします カメラが動いたりシーンが変わっても ハイライト表示が可能な限り アイテムに追従するようになります ピンチアウトでズームも 有効無効を設定できます ズームのレベルを指定する方法もあります ガイダンスを有効にすると 画面上部のラベルで うまくスキャンできる様 ユーザーを導いてくれます システムのハイライト表示を無効にして 自分でハイライトを描画することもできます データスキャナの表示法が分かった所で 今度は認識したアイテムの取り込み方と 自分でカスタム化した ハイライト表示について見ましょう
まずデータスキャナにデリゲートを設定します デリゲートができたので dataScanner didTapOnメソッドを実装します これはユーザーがアイテムをタップした時に 呼び出されます 新らしく定義された RecognizedItem の インスタンスを受け取ります RecognizedItem は文字やバーコードを associated value として保持する enum です 認識されたものが 文字列なら transcript で バーコードならペイロードに文字列があれば payloadStringValue で取得できます RecognizedItem についてもう2つ 知っておきましょう まず 認識されたアイテムには それぞれ独自の識別子があり 存続中はそのアイテムの追跡に使えます 存続期間は アイテムが最初に見えた時から 見えなくなった時までです また RecognizedItem にはそれぞれの bounds 境界プロパティがあります 境界は長方形ではなく各コーナーの 4つの座標で構成されます 次に話すのは3つのデリゲートメソッドで シーンで認識されたアイテムの状況が 変化した時に呼び出されます 最初は didAdd で アイテムが新たに認識された時に 呼び出されます 独自のハイライト表示を描画したい場合は ここで新らしいアイテムが加わる度に 1つ作成することになります アイテムのIDを使って ハイライト表示の識別が可能です DataScanner のビュー階層に 新たなビューとして追加する時は overlayContainerView に追加すれば カメラプレビューの上かつ 他の UI の下に表示されます
次のデリゲートメソッドは didUpdate で アイテムまたはカメラが動くと呼び出されます 認識された文字列が変わった時も 呼び出されます 認識された文字列が変わるのは スキャナが文字を長く見ていると 正確さが増していきそれが反映されるからです 更新されたアイテムのIDを使って 保存しておいた Dictionary から 該当のハイライト表示を取得して 更新された bounds にビューを動かします 最後に didRemove デリゲートメソッドで アイテムがシーンから見えなくなると 呼び出されます 見えなくなったアイテムの ハイライト表示のビューを ビュー階層から取り除きます まとめると 対象アイテムに 独自のハイライト表示をする場合 シーンにハイライトを登場させ 動かしたり削除したりするのに この3つのデリゲートメソッドが必要です この3つのデリゲートメソッドには 現在認識されている全てのアイテムが 配列として与えられます 認識された順で 渡されますので 自然な読み順となり これは文字認識で便利です DataScannerViewController の 使用方法の概要でした まとめに入る前に いくつか 他の機能を簡単に紹介します 例えば 写真のキャプチャです capturePhoto 方法を呼び出し 高品質の UIImage を非同期で取得できます
独自のハイライト表示をしないなら これら3つのデリゲートメソッドは 不要かもしれません 代わりに使えるのが recognizedItems プロパティで シーンの変化に合わせて更新され続ける AsyncStream です
お話は以上です iOS SDK は AVFoundation と Vision フレームワークを使って コンピュータビジョンの ワークフローを作成するのに 様々なオプションを 与えてくれる事をお忘れなく ライブビデオのプレビューで 文字や機械可読コードをスキャンする App を作っているかもしれませんね 集荷梱包 App か 倉庫業務や POS の App でしょうか その場合は VisionKit の DataScannerViewController を 試してみてください 本日 お話したように App のスタイルや需要に合った 顧客体験を提供するための 初期化パラメータやデリゲートメソッドが 数多くあります
最後にお知らせしたいのは 下に出ているセッションに参加して VisionKit の静止画用 Live Text 機能をご覧ください
それでは また次回 (軽快な音楽)
-
-
4:40 - Creating a data scanner instance and present it
import VisionKit // Specify the types of data to recognize let recognizedDataTypes:Set<DataScannerViewController.RecognizedDataType> = [ .barcode(symbologies: [.qr]), // uncomment to filter on specific languages (e.g., Japanese) // .text(languages: ["ja"]) // uncomment to filter on specific content types (e.g., URLs) // .text(textContentType: .URL) ] // Create the data scanner, present it, and start scanning! let dataScanner = DataScannerViewController(recognizedDataTypes: recognizedDataTypes) present(dataScanner, animated: true) { try? dataScanner.startScanning() }
-
8:11 - Set a delegate
// Specify the types of data to recognize let recognizedDataTypes:Set<DataScannerViewController.RecognizedDataType> = [ .barcode(symbologies: [.qr]), .text(textContentType: .URL) ] // Create the data scanner, present it, and start scanning! let dataScanner = DataScannerViewController(recognizedDataTypes: recognizedDataTypes) dataScanner.delegate = self.present(dataScanner, animated: true) { try? dataScanner.startScanning() }
-
8:19 - Handling tap interactions
func dataScanner(_ dataScanner: DataScannerViewController, didTapOn item: RecognizedItem) { switch item { case .text(let text): print("text: \(text.transcript)") case .barcode(let barcode): print("barcode: \(barcode.payloadStringValue ?? "unknown")") default: print("unexpected item") } }
-
9:11 - Adding custom highlights via the didAdd delegate method
// Dictionary to store our custom highlights keyed by their associated item ID. var itemHighlightViews: [RecognizedItem.ID: HighlightView] = [:] // For each new item, create a new highlight view and add it to the view hierarchy. func dataScanner(_ dataScanner: DataScannerViewController, didAdd addItems: [RecognizedItem], allItems: [RecognizedItem]) { for item in addedItems { let newView = newHighlightView(forItem: item) itemHighlightViews[item.id] = newView dataScanner.overlayContainerView.addSubview(newView) } }
-
9:37 - Animating custom highlights during the didUpdate delegate method
// Animate highlight views to their new bounds func dataScanner(_ dataScanner: DataScannerViewController, didUpdate updatedItems: [RecognizedItem], allItems: [RecognizedItem]) { for item in updatedItems { if let view = itemHighlightViews[item.id] { animate(view: view, toNewBounds: item.bounds) } } }
-
10:03 - Removing custom highlights during the didRemove delegate callback
// Remove highlights when their associated items are removed. func dataScanner(_ dataScanner: DataScannerViewController, didRemove removedItems: [RecognizedItem], allItems: [RecognizedItem]) { for item in removedItems { if let view = itemHighlightViews[item.id] { itemHighlightViews.removeValue(forKey: item.id) view.removeFromSuperview() } } }
-
10:54 - Take a still photo and save it to the camera roll
// Take a still photo and save to the camera roll if let image = try? await dataScanner.capturePhoto() { UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil) }
-
11:10 - Using the recognizedItems async stream to keep track of items
// Send a notification when the recognized items change. var currentItems: [RecognizedItem] = [] func updateViaAsyncStream() async { guard let scanner = dataScannerViewController else { return } let stream = scanner.recognizedItems for await newItems: [RecognizedItem] in stream { let diff = newItems.difference(from: currentItems) { a, b in return a.id == b.id } if !diff.isEmpty { currentItems = newItems sendDidChangeNotification() } } }
-
-
特定のトピックをお探しの場合は、上にトピックを入力すると、関連するトピックにすばやく移動できます。
クエリの送信中にエラーが発生しました。インターネット接続を確認して、もう一度お試しください。