🍎

バーコードとQRコードのスキャン - Enterprise APIs for visionOS

2025/02/21に公開

Enterprise APIs for visionOS 解説シリーズ、今回は「バーコードとQRコードのスキャン」です。

Apple Vision Pro を活用することで、バーコードや QRコードの位置情報と内容を取得し、業務アプリや物流管理、スマートリテールの分野での活用が可能になります。例えば、倉庫での在庫管理や店舗での顧客向け情報提供に応用できます。

空間上のコード位置を利用したQRコードでのコンテンツの位置合わせも可能になります。

サンプルはバーコードの規格の一つである「code128」と「QRコード」を検出し空間上に位置を表示します。ここでは、サンプルの実装に加えて、値の表示と、他のバーコードとして日本のバーコード形式であるJAN/EAN13形式のバーコードも認識できるようにします。


バーコード(日本のバーコードを認識し、その値を表示する)


QRコード

サンプルプロジェクトはこちら。
https://developer.apple.com/documentation/visionos/locating-and-decoding-barcodes-in-3d-space

サンプルの動作動画はこちら。

サンプルの実行手順

先ほどのドキュメントページよりサンプルプロジェクトをダウンロードします。

zipファイルを展開して「LocatingAndDecodingBarcodes.xcodeproj」を開きます。

プロジェクトを開いたら「Enterprise.license」ファイルを差し替えます。
サンプルにはテキストファイルが入っているので、一度このファイルを削除して、取得した正規の「Enterprise.license」をプロジェクトにコピーします。

プロジェクト設定の「General」よりvisoinOSのバージョンを指定します。
Enterprise APIs for visionOSはvisonOS 2.0以上が対応するので「2.0」を指定します。

サンプルでは設定済みのためスキップしますが、「Capability」の追加として「Signing & Capabilities」より「+ Capability 」ボタンを押して「Spatial Barcode and QR code Scanning」を選択します。

ビルドして実機で実行します。
バーコード認識は Immersive Space のみ有効になるため、ボタンを押して切り替えると認識処理が開始されます。

認識したバーコードやQRコードの値を表示する

次に認識したバーコードやQRコード(以下、コードと略します)の値を表示してみましょう。

認識処理はImmersiveView.swiftに実装されています。コードの認識が開始されるとanchorに情報が格納され、コードの値はanchor.payloadStringから取得できます。
認識したコードの位置はplayAnimationで緑の板を表示させていますので、今回はplayAnimationにテキストメッシュとテキストエンティティを作成して追加する処理を追加します。
また、認識時に表示される緑の板は0.5秒で表示/非表示が切り替わるので、文字が読めるようフェードインとフェードアウトの間に数秒表示状態を維持するホールドを加えました。
ちなみに、このコードはChatGPTに出力してもらいました。

修正したplayAnimationは下記のようになります。

ImmersiveView.swift
func playAnimation(for anchor: BarcodeAnchor) {
    guard let scene = root.scene else { return }
    
    // Create a plane and size it to match the barcode.
    let extent = anchor.extent
    let entity = ModelEntity(mesh: .generatePlane(width: extent.x, depth: extent.z), materials: [UnlitMaterial(color: .green)])
    entity.components.set(OpacityComponent(opacity: 0))
    
    // Position the plane over the barcode.
    entity.transform = Transform(matrix: anchor.originFromAnchorTransform)
    root.addChild(entity)
    
    // payloadStringが存在する場合、テキストエンティティを生成してプレーンに追加する
    if let payloadString = anchor.payloadString, !payloadString.isEmpty {
        let textMesh = MeshResource.generateText(
            payloadString,
            extrusionDepth: 0.001,
            font: .systemFont(ofSize: 0.01),
            containerFrame: .zero,
            alignment: .center,
            lineBreakMode: .byWordWrapping
        )
        let textEntity = ModelEntity(mesh: textMesh, materials: [UnlitMaterial(color: .white)])
        textEntity.transform.rotation = simd_quatf(angle: -.pi/2, axis: SIMD3<Float>(1, 0, 0))
        // テキストの位置をプレーンの上に配置(必要に応じて調整)
        textEntity.position = [0, 0.01, 0]
        // プレーンの子として追加
        entity.addChild(textEntity)
    }
    
    // Fade the plane in and out.
    do {
        let duration = 0.5
        let holdDuration = 2.0      // 表示を保持する時間
        
        let fadeIn = try AnimationResource.generate(with: FromToByAnimation<Float>(
            from: 0,
            to: 1.0,
            duration: duration,
            isAdditive: true,
            bindTarget: .opacity)
        )
        
        // ホールドのアニメーション(opacity: 1 -> 1 で一定時間保持)
        let hold = try AnimationResource.generate(with: FromToByAnimation<Float>(
            from: 1.0,
            to: 1.0,
            duration: holdDuration,
            isAdditive: true,
            bindTarget: .opacity
        ))
        
        let fadeOut = try AnimationResource.generate(with: FromToByAnimation<Float>(
            from: 1.0,
            to: 0,
            duration: duration,
            isAdditive: true,
            bindTarget: .opacity))
        
        let fadeAnimation = try AnimationResource.sequence(with: [fadeIn, hold, fadeOut])
        
        _ = scene.subscribe(to: AnimationEvents.PlaybackCompleted.self, on: entity, { _ in
            // Remove the plane after the animation completes.
            entity.removeFromParent()
        }).store(in: &fadeCompleteSubscriptions)
        
        entity.playAnimation(fadeAnimation)
    } catch {
        // Handle the error.
    }
}

ここまでで、コードに認識した値が表示されるようになります。

バーコード


QRコード

日本のバーコードを認識できるようにする

今の状態では日本のバーコードを読むことができませんので、設定を追加します。

日本のバーコードはJANコードと呼ばれ、海外ではEANと呼ばれます。
Enterprise APIs for visionOSでは8桁の「EAN-8」と13桁の「EAN-13」がサポートされます。

認識するコードの種類は「BarcodeDetectionProvider」で設定しますので、サンプルコードに「ean13」を加えます。

ImmersiveView.swift
let barcodeDetection = BarcodeDetectionProvider(symbologies: [.code128, .ean13, .qr])

これで日本のバーコードも読めるようになります。

認識できるコードの種類とコードの最小サイズ

ドキュメントでは「通常の照明条件 (100 ルクス) で腕の長さの距離 (約 40 cm) でバーコードを読み取るために必要な最小幅」が規定されています。
コードのサイズはこちらを参考にしてください。

コード種別 最小の幅 (cm)
Aztec Code 2.0
Codabar 5.5
Code 39 6.5
Code 39 Checksum 6.5
Code 39 Full ASCII 4.5
Code 39 Full ASCII Checksum 3.5
Code 93 5.0
Code 93i 5.0
Code 128 3.5
Data Matrix 1.0
EAN-8 2.0
EAN-13 4.0
GS1 DataBar 3.0
GS1 DataBar Expanded 6.5
GS1 DataBar Limited 3.0
ITF 3.5
ITF-14 5.0
ITF Checksum 3.5
MicroPDF417 6.5
Micro QR Code 2.0
MSI Plessey 4.5
PDF417 3.5
QR Code 1.5
UPC-E 2.5

まとめ

QRコードの位置や値の取得ができるようになりました。これにより、QRコードを活用したコンテンツの位置合わせや、倉庫や工場でのモノの認識、商品管理、ARコンテンツの配置といった業務用途に応用できます。例えば、小売店舗では商品情報の提供や在庫管理、製造現場ではパーツの識別や生産ラインの最適化に役立てることができます。

Enterprise APIs for visionOS ではバーコードも読み取れますが、これに対応したデバイスは少ないため、Vision Pro を活用することで新たなユースケースが生まれる可能性があります。例えば、工場や倉庫でのリアルタイム在庫管理、イベント会場でのデジタルチケット確認、医療現場での患者情報管理など、様々な分野での活用が期待されます。

ぜひ試してみてください。

ホロラボのテックブログ

Discussion