axiom-vision-ref について
画像のセグメンテーション、姿勢や顔の検出、OCR、バーコード検出、書類スキャンに使うVisionフレームワークのAPIリファレンスです。リクエストハンドラーの使い分けや、動画シーケンスでの状態処理も説明します。
主な機能
- 01前景・人物のセグメンテーションAPI
- 02手と身体の姿勢検出
- 03顔分析とテキスト認識
- 04バーコード検出と書類スキャン
- 05画像・シーケンス用ハンドラー
活用シーン
- 01画像から前景の被写体を抽出
- 02ジェスチャーやフィットネスアプリで姿勢を追跡
- 03画像内の文字やバーコードを認識
- 04ライブスキャナーや書類認識を構築
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ charleswiltgen/ axiom --skill axiom-vision-refインストール後、Claude Code で/axiom-vision-refを入力して読み込まれたか確認します。
詳細
Vision フレームワーク API リファレンス
Vision フレームワークのコンピュータービジョン機能に関する包括的なリファレンスです。対象のセグメンテーション、手や身体のポーズ検出、人物検出、顔分析、テキスト認識(OCR)、バーコード検出、書類スキャンを扱います。
このリファレンスを使用する場面
- VisionKit または Vision を使って対象の切り抜きを実装する
- ジェスチャー認識やフィットネスアプリ向けに手や身体のポーズを検出する
- 背景から人物をセグメント化する、または複数の人物を分離する
- AR エフェクトや認証向けに顔検出と顔の特徴点検出を行う
- 複雑なコンピュータービジョンの問題を解決するためにVision API を組み合わせる
- 特定の API シグネチャやパラメーターの意味を調べる
- VNRecognizeTextRequest を使って画像内のテキストを認識する(OCR)
- VNDetectBarcodesRequest を使ってバーコードや QR コードを検出する
- DataScannerViewController を使ってライブスキャナーを構築する
- VNDocumentCameraViewController を使って書類をスキャンする
- RecognizeDocumentsRequest を使って書類の構造化データを抽出する(iOS 26 以降)
関連スキル:判断ツリーとパターンについては axiom-vision、トラブルシューティングについては axiom-vision-diag を参照してください。
Vision フレームワークの概要
Vision は、静止画像とビデオ向けのコンピュータービジョンアルゴリズムを提供します。
基本的なワークフロー:
- リクエストを作成します(例:
VNDetectHumanHandPoseRequest()) - 画像を指定してハンドラーを作成します(
VNImageRequestHandler(cgImage: image)) - リクエストを実行します(
try handler.perform([request])) request.resultsから観測結果にアクセスします
座標系:左下が原点の正規化座標(0.0〜1.0)
パフォーマンス:バックグラウンドキューで実行してください。リソースを大量に消費するため、メインスレッドで実行すると UI がブロックされます。
リクエストハンドラー
Vision は、用途に応じて 2 種類のリクエストハンドラーを提供します。
VNImageRequestHandler
単一の画像を分析します。画像を指定して初期化し、その画像に対してリクエストを実行した後、破棄します。
let handler = VNImageRequestHandler(cgImage: image)
try handler.perform([request1, request2]) // Multiple requests, one image
初期化に使用できるもの:CGImage、CIImage、CVPixelBuffer、Data、または URL
ルール:画像ごとに 1 つのハンドラーを使用します。別の画像でハンドラーを再利用することはサポートされていません。
VNSequenceRequestHandler
フレームのシーケンス(ビデオ、カメラ映像)を分析します。空の状態で初期化し、各フレームを perform() に渡します。フレーム間の状態を保持し、時間的な平滑化を行います。
let sequenceHandler = VNSequenceRequestHandler()
// In your camera/video frame callback:
func processFrame(_ pixelBuffer: CVPixelBuffer) throws {
try sequenceHandler.perform([request], on: pixelBuffer)
}
ルール:一度作成したら、フレーム間で再利用します。ハンドラーは呼び出し間の状態を追跡します。
どちらを使用するか
| 用途 | ハンドラー |
|---|---|
| 単一の写真またはスクリーンショット | VNImageRequestHandler |
| ビデオストリームまたはカメラのフレーム | VNSequenceRequestHandler |
| 時間的な平滑化(ポーズ、セグメンテーション) | VNSequenceRequestHandler |
| CVPixelBuffer の単発分析 | VNImageRequestHandler |
シーケンス処理で効果が得られるリクエスト
以下のリクエストは、VNSequenceRequestHandler を通じて実行するとフレーム間の状態を利用します。
VNDetectHumanBodyPoseRequest— 関節の追跡がより滑らかになりますVNDetectHumanHandPoseRequest— 特徴点の追跡がより滑らかになりますVNGeneratePersonSegmentationRequest— 時間的に一貫したマスクを生成しますVNGeneratePersonInstanceMaskRequest— フレーム間で人物の識別を安定させますVNDetectDocumentSegmentationRequest— 書類の輪郭が安定しますVNStatefulRequestのサブクラスすべて — シーケンス向けに設計されています
よくある間違い
ビデオの各フレームで新しい VNImageRequestHandler を作成すると、時間的なコンテキストが失われます。ポーズの特徴点が揺れ、セグメンテーションマスクがちらつき、シーケンス処理による平滑化の効果も得られなくなります。
// Wrong — loses temporal context every frame
func processFrame(_ buffer: CVPixelBuffer) throws {
let handler = VNImageRequestHandler(cvPixelBuffer: buffer)
try handler.perform([poseRequest])
}
// Right — maintains inter-frame state
let sequenceHandler = VNSequenceRequestHandler()
func processFrame(_ buffer: CVPixelBuffer) throws {
try sequenceHandler.perform([poseRequest], on: buffer)
}
対象セグメンテーション API
VNGenerateForegroundInstanceMaskRequest
利用可能な OS:iOS 17 以降、macOS 14 以降、tvOS 17 以降、visionOS 1 以降
人物、ペット、建物、食べ物、靴など、前景のオブジェクトのクラスに依存しないインスタンスマスクを生成します。
基本的な使用方法
let request = VNGenerateForegroundInstanceMaskRequest()
let handler = VNImageRequestHandler(cgImage: image)
try handler.perform([request])
guard let observation = request.results?.first as? VNInstanceMaskObservation else {
return
}
InstanceMaskObservation
allInstances:前景にあるすべてのインスタンスインデックスを含む IndexSet(背景の 0 は除外)
instanceMask:UInt8 ラベルを持つ CVPixelBuffer(0 = 背景、1 以上 = インスタンスインデックス)
instanceAtPoint(_:):正規化座標の点にあるインスタンスインデックスを返します
let point = CGPoint(x: 0.5, y: 0.5) // Center of image
let instance = observation.instanceAtPoint(point)
if instance == 0 {
print("Background tapped")
} else {
print("Instance \(instance) tapped")
}
マスクの生成
createScaledMask(for:croppedToInstancesContent:)
パラメーター:
for:含めるインスタンスのIndexSetcroppedToInstancesContent:false= 入力と同じ解像度で出力します(合成用)true= 選択したインスタンスの周囲をぴったり切り抜きます
戻り値:単一チャンネルの浮動小数点CVPixelBuffer(ソフトセグメンテーションマスク)
// All instances, full resolution
let mask = try observation.createScaledMask(
for: observation.allInstances,
croppedToInstancesContent: false
)
// Single instance, cropped
let instances = IndexSet(integer: 1)
let croppedMask = try observation.createScaledMask(
for: instances,
croppedToInstancesContent: true
)
インスタンスマスクのヒットテスト
未加工のピクセルバッファにアクセスして、タップ座標をインスタンスラベルにマッピングします:
let instanceMask = observation.instanceMask
CVPixelBufferLockBaseAddress(instanceMask, .readOnly)
defer { CVPixelBufferUnlockBaseAddress(instanceMask, .readOnly) }
let baseAddress = CVPixelBufferGetBaseAddress(instanceMask)
let width = CVPixelBufferGetWidth(instanceMask)
let bytesPerRow = CVPixelBufferGetBytesPerRow(instanceMask)
// Convert normalized tap to pixel coordinates
let pixelPoint = VNImagePointForNormalizedPoint(
CGPoint(x: normalizedX, y: normalizedY),
width: imageWidth,
height: imageHeight
)
// Calculate byte offset
let offset = Int(pixelPoint.y) * bytesPerRow + Int(pixelPoint.x)
// Read instance label
let label = UnsafeRawPointer(baseAddress!).load(
fromByteOffset: offset,
as: UInt8.self
)
let instances = label == 0 ? observation.allInstances : IndexSet(integer: Int(label))
VisionKit の被写体切り抜き
ImageAnalysisInteraction(iOS)
利用可能な環境:iOS 16 以降、iPadOS 16 以降
ビューにシステムのような被写体切り抜き UI を追加します:
let interaction = ImageAnalysisInteraction()
interaction.preferredInteractionTypes = .imageSubject // Or .automatic
imageView.addInteraction(interaction)
インタラクションの種類:
.automatic:被写体切り抜き + Live Text + データ検出.imageSubject:被写体切り抜きのみ(操作可能なテキストなし)
ImageAnalysisOverlayView(macOS)
利用可能な環境:macOS 13 以降
let overlayView = ImageAnalysisOverlayView()
overlayView.preferredInteractionTypes = .imageSubject
nsView.addSubview(overlayView)
プログラムによるアクセス
ImageAnalyzer
let analyzer = ImageAnalyzer()
let configuration = ImageAnalyzer.Configuration([.text, .visualLookUp])
let analysis = try await analyzer.analyze(image, configuration: configuration)
ImageAnalysis
subjects:[Subject] - 画像内のすべての被写体
highlightedSubjects:Set<Subject> - 現在ハイライト表示されている被写体(ユーザーが長押ししたもの)
subject(at:):正規化された座標の被写体を非同期で検索します(該当する被写体がない場合はnilを返します)
// Get all subjects
let subjects = analysis.subjects
// Look up subject at tap
if let subject = try await analysis.subject(at: tapPoint) {
// Process subject
}
// Change highlight state
analysis.highlightedSubjects = Set([subjects[0], subjects[1]])
Subject 構造体
image:UIImage/NSImage - 透明部分を含む抽出済みの被写体
bounds:CGRect - 画像座標における被写体の境界
// Single subject image
let subjectImage = subject.image
// Composite multiple subjects
let compositeImage = try await analysis.image(for: [subject1, subject2])
プロセス外での処理:VisionKit の解析はプロセス外で実行されます(パフォーマンス上の利点がありますが、画像サイズには制限があります)
人物セグメンテーション API
VNGeneratePersonSegmentationRequest
利用可能な環境:iOS 15 以降、macOS 12 以降
画像内のすべての人物を含む単一のマスクを返します:
let request = VNGeneratePersonSegmentationRequest()
// Configure quality level if needed
try handler.perform([request])
guard let observation = request.results?.first as? VNPixelBufferObservation else {
return
}
let personMask = observation.pixelBuffer // CVPixelBuffer
VNGeneratePersonInstanceMaskRequest
利用可能な環境:iOS 17 以降、macOS 14 以降
最大 4 人分の個別マスクを返します:
let request = VNGeneratePersonInstanceMaskRequest()
try handler.perform([request])
guard let observation = request.results?.first as? VNInstanceMaskObservation else {
return
}
// Same InstanceMaskObservation API as foreground instance masks
let allPeople = observation.allInstances // Up to 4 people (1-4)
// Get mask for person 1
let person1Mask = try observation.createScaledMask(
for: IndexSet(integer: 1),
croppedToInstancesContent: false
)
制限事項:
- 最大 4 人までをセグメント化します
- 4 人を超える場合、人物を見落としたり、複数の人物をまとめたりすることがあります(通常は背景にいる人物)
- 混雑したシーンに対応する必要がある場合は、
VNDetectFaceRectanglesRequestを使って顔の数を数えます
手のポーズ検出
VNDetectHumanHandPoseRequest
利用可能な環境:iOS 14 以降、macOS 11 以降
手ごとに21 個の手のランドマークを検出します:
let request = VNDetectHumanHandPoseRequest()
request.maximumHandCount = 2 // Default: 2, increase if needed
let handler = VNImageRequestHandler(cgImage: image)
try handler.perform([request])
for observation in request.results as? [VNHumanHandPoseObservation] ?? [] {
// Process each hand
}
パフォーマンスに関する注意:maximumHandCountはレイテンシーに影響します。ポーズが計算されるのは、手の数が最大値以下の場合のみです。許容できる範囲で最も小さい値を設定してください。
手のランドマーク(21 点)
手首:1 個のランドマーク
親指(4 個のランドマーク):
.thumbTip.thumbIP(指節間関節).thumbMP(中手指節関節).thumbCMC(手根中手関節)
指(各 4 個のランドマーク):
- 先端(
.indexTip、.middleTip、.ringTip、.littleTip) - DIP(遠位指節間関節)
- PIP(近位指節間関節)
- MCP(中手指節関節)
グループキー
ランドマークのグループにアクセスします:
| グループキー | 点 |
|---|---|
.all | すべての 21 個のランドマーク |
.thumb | 親指の関節 4 個 |
.indexFinger | 人差し指の関節 4 個 |
.middleFinger | 中指の関節 4 個 |
.ringFinger | 薬指の関節 4 個 |
.littleFinger | 小指の関節 4 個 |
// Get all points
let allPoints = try observation.recognizedPoints(.all)
// Get index finger points only
let indexPoints = try observation.recognizedPoints(.indexFinger)
// Get specific point
let thumbTip = try observation.recognizedPoint(.thumbTip)
let indexTip = try observation.recognizedPoint(.indexTip)
// Check confidence
guard thumbTip.confidence > 0.5 else { return }
// Access location (normalized coordinates, lower-left origin)
let location = thumbTip.location // CGPoint
ジェスチャー認識の例(ピンチ)
let thumbTip = try observation.recognizedPoint(.thumbTip)
let indexTip = try observation.recognizedPoint(.indexTip)
guard thumbTip.confidence > 0.5, indexTip.confidence > 0.5 else {
return
}
let distance = hypot(
thumbTip.location.x - indexTip.location.x,
thumbTip.location.y - indexTip.location.y
)
let isPinching = distance < 0.05 // Normalized threshold
利き手(左右)
let chirality = observation.chirality // .left or .right or .unknown
身体のポーズ検出
VNDetectHumanBodyPoseRequest(2D)
利用可能な環境:iOS 14 以降、macOS 11 以降
18 個の身体ランドマーク(正規化された 2D 座標)を検出します:
let request = VNDetectHumanBodyPoseRequest()
try handler.perform([request])
for observation in request.results as? [VNHumanBodyPoseObservation] ?? [] {
// Process each person
}
身体のランドマーク(18 点)
顔(5 個のランドマーク):
.nose、.leftEye、.rightEye、.leftEar、.rightEar
腕(6 個のランドマーク):
- 左:
.leftShoulder、.leftElbow、.leftWrist - 右:
.rightShoulder、.rightElbow、.rightWrist
胴体(7 個のランドマーク):
.neck(肩の間).leftShoulder、.rightShoulder(腕のグループにも含まれます).leftHip、.rightHip.root(腰の間)
脚(6 個のランドマーク):
- 左:
.leftHip、.leftKnee、.leftAnkle - 右:
.rightHip、.rightKnee、.rightAnkle
注意:肩と腰は複数のグループに含まれます
グループキー(身体)
| グループキー | ポイント |
|---|---|
.all | 18 個すべてのランドマーク |
.face | 顔の 5 個のランドマーク |
.leftArm | 肩、肘、手首 |
.rightArm | 肩、肘、手首 |
.torso | 首、肩、腰、ルート |
.leftLeg | 股関節、膝、足首 |
.rightLeg | 股関節、膝、足首 |
// Get all body points
let allPoints = try observation.recognizedPoints(.all)
// Get left arm only
let leftArmPoints = try observation.recognizedPoints(.leftArm)
// Get specific joint
let leftWrist = try observation.recognizedPoint(.leftWrist)
VNDetectHumanBodyPose3DRequest(3D)
利用可能環境:iOS 17 以降、macOS 14 以降
メートル単位の**3D 骨格(17 個の関節)**を返します(実世界の座標):
let request = VNDetectHumanBodyPose3DRequest()
try handler.perform([request])
guard let observation = request.results?.first as? VNHumanBodyPose3DObservation else {
return
}
// Get 3D joint position
let leftWrist = try observation.recognizedPoint(.leftWrist)
let position = leftWrist.position // simd_float4x4 matrix
let localPosition = leftWrist.localPosition // Relative to parent joint
3D 身体ランドマーク(17 個のポイント):耳がない点を除き、2D と同じです(2D のランドマークは 18 個、3D は 15 個)
3D 観測プロパティ
bodyHeight:メートル単位の推定身長
- 深度データがある場合:測定された身長
- 深度データがない場合:基準身長(1.8m)
heightEstimation:.measured または .reference
cameraOriginMatrix:被写体に対するカメラの位置/向きを示す simd_float4x4
pointInImage(_:):3D 関節を 2D 画像座標に投影します
let wrist2D = try observation.pointInImage(leftWrist)
3D ポイントクラス
VNPoint3D:simd_float4x4 の位置行列を持つ基底クラス
VNRecognizedPoint3D:識別子(関節名)を追加します
VNHumanBodyRecognizedPoint3D:localPosition と parentJoint を追加します
// Position relative to skeleton root (center of hip)
let modelPosition = leftWrist.position
// Position relative to parent joint (left elbow)
let relativePosition = leftWrist.localPosition
深度入力
Vision は画像とともに深度データを受け付けます:
// From AVDepthData
let handler = VNImageRequestHandler(
cvPixelBuffer: imageBuffer,
depthData: depthData,
orientation: orientation
)
// From file (automatic depth extraction)
let handler = VNImageRequestHandler(url: imageURL) // Depth auto-fetched
深度形式:視差または深度(AVFoundation を介して相互に切り替え可能)
LiDAR:正確なスケール/測定には、ライブキャプチャセッションで使用します
顔検出とランドマーク
VNDetectFaceRectanglesRequest
利用可能環境:iOS 11 以降
顔のバウンディングボックスを検出します:
let request = VNDetectFaceRectanglesRequest()
try handler.perform([request])
for observation in request.results as? [VNFaceObservation] ?? [] {
let faceBounds = observation.boundingBox // Normalized rect
}
VNDetectFaceLandmarksRequest
利用可能環境:iOS 11 以降
詳細なランドマークを持つ顔を検出します:
let request = VNDetectFaceLandmarksRequest()
try handler.perform([request])
for observation in request.results as? [VNFaceObservation] ?? [] {
if let landmarks = observation.landmarks {
let leftEye = landmarks.leftEye
let nose = landmarks.nose
let leftPupil = landmarks.leftPupil // Revision 2+
}
}
リビジョン:
- リビジョン 1:基本的なランドマーク
- リビジョン 2:上下逆さまの顔を検出
- リビジョン 3 以降:瞳孔の位置
人物検出
VNDetectHumanRectanglesRequest
利用可能環境:iOS 13 以降
人物のバウンディングボックス(胴体の検出)を検出します:
let request = VNDetectHumanRectanglesRequest()
try handler.perform([request])
for observation in request.results as? [VNHumanObservation] ?? [] {
let humanBounds = observation.boundingBox // Normalized rect
}
ユースケース:位置の把握だけが必要な場合、ポーズ検出より高速です
CoreImage との統合
CIBlendWithMask フィルター
Vision のマスクを使って、被写体を新しい背景に合成します:
// 1. Get mask from Vision
let observation = request.results?.first as? VNInstanceMaskObservation
let visionMask = try observation.createScaledMask(
for: observation.allInstances,
croppedToInstancesContent: false
)
// 2. Convert to CIImage
let maskImage = CIImage(cvPixelBuffer: visionMask)
// 3. Apply filter
let filter = CIFilter(name: "CIBlendWithMask")!
filter.setValue(sourceImage, forKey: kCIInputImageKey)
filter.setValue(maskImage, forKey: kCIInputMaskImageKey)
filter.setValue(newBackground, forKey: kCIInputBackgroundImageKey)
let output = filter.outputImage // Composited result
パラメーター:
- 入力画像:マスクを適用する元の画像
- マスク画像:Vision のソフトセグメンテーションマスク
- 背景画像:新しい背景(または透明にする場合は空の画像)
HDR の保持:CoreImage は入力のハイダイナミックレンジを保持します(Vision/VisionKit の出力は SDR です)
テキスト認識 API
VNRecognizeTextRequest
利用可能環境:iOS 13 以降、macOS 10.15 以降
精度と速度のトレードオフを設定して、画像内のテキストを認識します。
基本的な使い方
let request = VNRecognizeTextRequest()
request.recognitionLevel = .accurate // Or .fast
request.recognitionLanguages = ["en-US", "de-DE"] // Order matters
request.usesLanguageCorrection = true
let handler = VNImageRequestHandler(cgImage: image)
try handler.perform([request])
for observation in request.results as? [VNRecognizedTextObservation] ?? [] {
// Get top candidates
let candidates = observation.topCandidates(3)
let bestText = candidates.first?.string ?? ""
}
認識レベル
| レベル | パフォーマンス | 精度 | 最適な用途 |
|---|---|---|---|
.fast | リアルタイム | 良好 | カメラ映像、大きなテキスト、標識 |
.accurate | 低速 | 非常に高い | 文書、レシート、手書き文字 |
高速パス:文字単位の認識(ニューラルネットワーク → 文字検出)
高精度パス:行全体の ML 認識(ニューラルネットワーク → 行/単語認識)
プロパティ
| プロパティ | 型 | 説明 |
|---|---|---|
recognitionLevel | VNRequestTextRecognitionLevel | .fast または .accurate |
recognitionLanguages | [String] | BCP 47 言語コード。順序は優先順位を表します |
usesLanguageCorrection | Bool | 言語モデルを使って補正するかどうか |
customWords | [String] | ドメイン固有の語彙 |
automaticallyDetectsLanguage | Bool | 言語を自動検出します(iOS 16 以降) |
minimumTextHeight | Float | 画像に対する最小テキスト高さの割合(0〜1) |
revision | Int | API バージョン(サポートされる言語に影響します) |
言語サポート
// Check supported languages for current settings
let languages = try VNRecognizeTextRequest.supportedRecognitionLanguages(
for: .accurate,
revision: VNRecognizeTextRequestRevision3
)
言語補正:精度が向上しますが、処理時間がかかります。コードやシリアル番号では無効にしてください。
カスタム単語:ドメイン固有の語彙(医療用語、製品コードなど)を追加すると、認識精度が向上します。
VNRecognizedTextObservation
boundingBox: 認識されたテキストを含む正規化された矩形
topCandidates(_:): 信頼度順に並んだ [VNRecognizedText] を返します
VNRecognizedText
| プロパティ | 型 | 説明 |
|---|---|---|
string | String | 認識されたテキスト |
confidence | VNConfidence | 0.0~1.0 |
boundingBox(for:) | VNRectangleObservation? | 部分文字列の範囲に対応する矩形 |
// Get bounding box for substring
let text = candidate.string
if let range = text.range(of: "invoice") {
let box = try candidate.boundingBox(for: range)
}
バーコード検出 API
VNDetectBarcodesRequest
利用可能なバージョン: iOS 11+、macOS 10.13+
バーコードと QR コードを検出してデコードします。
基本的な使い方
let request = VNDetectBarcodesRequest()
request.symbologies = [.qr, .ean13, .code128] // Specific codes
let handler = VNImageRequestHandler(cgImage: image)
try handler.perform([request])
for barcode in request.results as? [VNBarcodeObservation] ?? [] {
let payload = barcode.payloadStringValue
let type = barcode.symbology
let bounds = barcode.boundingBox
}
シンボロジー
1D バーコード:
.codabar(iOS 15+).code39、.code39Checksum、.code39FullASCII、.code39FullASCIIChecksum.code93、.code93i.code128.ean8、.ean13.gs1DataBar、.gs1DataBarExpanded、.gs1DataBarLimited(iOS 15+).i2of5、.i2of5Checksum.itf14.upce
2D コード:
.aztec.dataMatrix.microPDF417(iOS 15+).microQR(iOS 15+).pdf417.qr
パフォーマンス: 指定するシンボロジーを減らすほど検出が高速になります
リビジョン
| リビジョン | iOS | 機能 |
|---|---|---|
| 1 | 11+ | 基本的な検出、一度に 1 つのコード |
| 2 | 15+ | Codabar、GS1、MicroPDF、MicroQR、より優れた ROI |
| 3 | 16+ | ML ベース、複数のコード、より優れた境界ボックス |
VNBarcodeObservation
| プロパティ | 型 | 説明 |
|---|---|---|
payloadStringValue | String? | デコードされた内容 |
symbology | VNBarcodeSymbology | バーコードの種類 |
boundingBox | CGRect | 正規化された境界 |
topLeft/topRight/bottomLeft/bottomRight | CGPoint | 四隅の座標 |
VisionKit スキャナー API
DataScannerViewController
利用可能なバージョン: iOS 16+
テキストとバーコードに対応した、組み込み UI を備えるカメラベースのライブスキャナーです。
利用可否を確認する
// Hardware support
DataScannerViewController.isSupported
// Runtime availability (camera access, parental controls)
DataScannerViewController.isAvailable
設定
import VisionKit
let dataTypes: Set<DataScannerViewController.RecognizedDataType> = [
.barcode(symbologies: [.qr, .ean13]),
.text(textContentType: .URL), // Or nil for all text
// .text(languages: ["ja"]) // Filter by language
]
let scanner = DataScannerViewController(
recognizedDataTypes: dataTypes,
qualityLevel: .balanced, // .fast, .balanced, .accurate
recognizesMultipleItems: true,
isHighFrameRateTrackingEnabled: true,
isPinchToZoomEnabled: true,
isGuidanceEnabled: true,
isHighlightingEnabled: true
)
scanner.delegate = self
present(scanner, animated: true) {
try? scanner.startScanning()
}
RecognizedDataType
| 型 | 説明 |
|---|---|
.barcode(symbologies:) | 特定のバーコードの種類 |
.text() | すべてのテキスト |
.text(languages:) | 言語でフィルタリングされたテキスト |
.text(textContentType:) | 種類 (URL、電話番号、メール) でフィルタリングされたテキスト |
デリゲートプロトコル
protocol DataScannerViewControllerDelegate {
func dataScanner(_ dataScanner: DataScannerViewController,
didTapOn item: RecognizedItem)
func dataScanner(_ dataScanner: DataScannerViewController,
didAdd addedItems: [RecognizedItem],
allItems: [RecognizedItem])
func dataScanner(_ dataScanner: DataScannerViewController,
didUpdate updatedItems: [RecognizedItem],
allItems: [RecognizedItem])
func dataScanner(_ dataScanner: DataScannerViewController,
didRemove removedItems: [RecognizedItem],
allItems: [RecognizedItem])
func dataScanner(_ dataScanner: DataScannerViewController,
becameUnavailableWithError error: DataScannerViewController.ScanningUnavailable)
}
RecognizedItem
enum RecognizedItem {
case text(RecognizedItem.Text)
case barcode(RecognizedItem.Barcode)
var id: UUID { get }
var bounds: RecognizedItem.Bounds { get }
}
// Text item
struct Text {
let transcript: String
}
// Barcode item
struct Barcode {
let payloadStringValue: String?
let observation: VNBarcodeObservation
}
非同期ストリーム
// Alternative to delegate
for await items in scanner.recognizedItems {
// Current recognized items
}
カスタムハイライト
// Add custom views over recognized items
scanner.overlayContainerView.addSubview(customHighlight)
// Capture still photo
let photo = try await scanner.capturePhoto()
VNDocumentCameraViewController
利用可能なバージョン: iOS 13+
自動エッジ検出、遠近補正、明るさ調整を備えたドキュメントスキャンです。
基本的な使い方
import VisionKit
let camera = VNDocumentCameraViewController()
camera.delegate = self
present(camera, animated: true)
デリゲートプロトコル
protocol VNDocumentCameraViewControllerDelegate {
func documentCameraViewController(_ controller: VNDocumentCameraViewController,
didFinishWith scan: VNDocumentCameraScan)
func documentCameraViewControllerDidCancel(_ controller: VNDocumentCameraViewController)
func documentCameraViewController(_ controller: VNDocumentCameraViewController,
didFailWithError error: Error)
}
VNDocumentCameraScan
| プロパティ | 型 | 説明 |
|---|---|---|
pageCount | Int | スキャンされたページ数 |
imageOfPage(at:) | UIImage | 指定したインデックスのページ画像を取得します |
title | String | ユーザーが編集可能なタイトル |
func documentCameraViewController(_ controller: VNDocumentCameraViewController,
didFinishWith scan: VNDocumentCameraScan) {
controller.dismiss(animated: true)
for i in 0..<scan.pageCount {
let pageImage = scan.imageOfPage(at: i)
// Process with VNRecognizeTextRequest
}
}
ドキュメント解析 API
VNDetectDocumentSegmentationRequest
利用可能なバージョン: iOS 15+、macOS 12+
カスタムカメラ UI や後処理のためにドキュメントの境界を検出します。
let request = VNDetectDocumentSegmentationRequest()
let handler = VNImageRequestHandler(ciImage: image)
try handler.perform([request])
guard let observation = request.results?.first as? VNRectangleObservation else {
return // No document found
}
// Get corner points (normalized)
let corners = [
observation.topLeft,
observation.topRight,
observation.bottomLeft,
observation.bottomRight
]
VNDetectRectanglesRequest との違い:
- ドキュメント: ML ベースで、ドキュメント専用にトレーニングされています
- 矩形: エッジベースで、あらゆる四角形を検出します
RecognizeDocumentsRequest (iOS 26+)
利用可能なバージョン: iOS 26+、macOS 26+
セマンティック解析による構造化されたドキュメント理解です。
基本的な使い方
let request = RecognizeDocumentsRequest()
let observations = try await request.perform(on: imageData)
guard let document = observations.first?.document else {
return
}
DocumentObservation の階層
DocumentObservation
└── document: DocumentObservation.Document
├── text: TextObservation
├── tables: [Container.Table]
├── lists: [Container.List]
└── barcodes: [Container.Barcode]
テーブルの抽出
for table in document.tables {
for row in table.rows {
for cell in row {
let text = cell.content.text.transcript
let detectedData = cell.content.text.detectedData
}
}
}
検出されるデータ型
for data in document.text.detectedData {
switch data.match.details {
case .emailAddress(let email):
let address = email.emailAddress
case .phoneNumber(let phone):
let number = phone.phoneNumber
case .link(let url):
let link = url
case .address(let address):
let components = address
case .date(let date):
let dateValue = date
default:
break
}
}
TextObservation の階層
TextObservation
├── transcript: String
├── lines: [TextObservation.Line]
├── paragraphs: [TextObservation.Paragraph]
├── words: [TextObservation.Word]
└── detectedData: [DetectedDataObservation]
Visual Intelligence 連携
Visual Intelligence は、ユーザーがカメラを現実世界のオブジェクトに向けて、アプリをまたいで一致するコンテンツを見つけられるようにするシステムレベルの機能 (iOS 26+) です。これは上記で扱った Vision フレームワーク (VNRequest ベースの画像解析) とは異なります。Vision はアプリ内で画像を解析しますが、Visual Intelligence はユーザーがカメラやスクリーンショットで検索したときにシステムがアプリを呼び出せるようにします。
仕組み
- ユーザーが Visual Intelligence カメラを起動するか、スクリーンショットを撮影します
- システムがユーザーの見ているものを分析します
- システムが
IntentValueQueryを介して参加アプリに問い合わせます - アプリはラベルやピクセルデータを含む
SemanticContentDescriptorを受け取ります - アプリがコンテンツを検索し、一致する
AppEntityの結果を返します - 結果はアプリのブランド表示とともに Visual Intelligence の UI に表示されます
必須フレームワーク
import VisualIntelligence
import AppIntents
SemanticContentDescriptor
システムがユーザーの見ているものを説明するために提供する中核オブジェクトです。
| プロパティ | 型 | 説明 |
|---|---|---|
labels | [String] | 検出された項目の分類ラベル |
pixelBuffer | CVReadOnlyPixelBuffer? | 検出された項目の視覚データ |
ラベルを使用すると、コンテンツカタログ内のキーワードとすばやく照合できます。ラベルでは不十分な場合は、ピクセルバッファを画像類似性検索に使用します。
IntentValueQuery
Visual Intelligence がアプリと通信するためのエントリーポイントです。values(for:) を実装して検索リクエストを受け取り、一致するエンティティを返します。
struct LandmarkIntentValueQuery: IntentValueQuery {
@Dependency var modelData: ModelData
func values(for input: SemanticContentDescriptor) async throws -> [LandmarkEntity] {
if !input.labels.isEmpty {
return try await modelData.search(matching: input.labels)
}
guard let pixelBuffer = input.pixelBuffer else { return [] }
return try await modelData.search(matching: pixelBuffer)
}
}
複数の結果タイプを返す
1 回の検索で異なるエンティティタイプを返せるアプリでは、@UnionValue を使用します。
@UnionValue
enum VisualSearchResult {
case landmark(LandmarkEntity)
case collection(CollectionEntity)
}
表示表現
Visual Intelligence は、エンティティの DisplayRepresentation を使用して結果を表示します。各結果にタイトル、サブタイトル、画像を設定してください。
struct LandmarkEntity: AppEntity {
var id: String
var name: String
var location: String
static var typeDisplayRepresentation: TypeDisplayRepresentation {
TypeDisplayRepresentation(
name: LocalizedStringResource("Landmark", table: "AppIntents"),
numericFormat: "\(placeholder: .int) landmarks"
)
}
var displayRepresentation: DisplayRepresentation {
DisplayRepresentation(
title: "\(name)",
subtitle: "\(location)",
image: .init(named: thumbnailImageName)
)
}
}
結果からのディープリンク
ユーザーが結果をタップしたときに、アプリは関連するコンテンツを開く必要があります。エンティティに appLinkURL を指定してください。
var appLinkURL: URL? {
URL(string: "yourapp://landmark/\(id)")
}
「その他の結果」インテント
大量の検索結果がある場合は、アプリの完全な検索 UI を開く VisualIntelligenceSearchIntent を用意します。
struct ViewMoreLandmarksIntent: AppIntent, VisualIntelligenceSearchIntent {
static var title: LocalizedStringResource = "View More Landmarks"
@Parameter(title: "Semantic Content")
var semanticContent: SemanticContentDescriptor
func perform() async throws -> some IntentResult {
// Open your app's search view with the semantic content
return .result()
}
}
ベストプラクティス
- 結果をすばやく返す — Visual Intelligence では低遅延の応答が求められます。関連性の高い結果を 10~20 件に絞ってください
- まずラベルを使用する — ラベル照合はピクセルバッファの分析より高速です。ラベルが空、または不十分な場合はピクセルバッファを使用します
- すべてをローカライズする — 表示表現はシステム UI に表示されます。ユーザーに表示するすべてのテキストに
LocalizedStringResourceを使用してください - 画像を含める — サムネイル付きの結果は Visual Intelligence オーバーレイで見分けやすくなります
テスト
- 実機でビルドして実行します
- Visual Intelligence カメラを起動するか、関連するコンテンツのスクリーンショットを撮影します
- ビジュアル検索を実行し、アプリの結果が表示されることを確認します
- 結果をタップし、ディープリンクで正しいコンテンツが開くことを確認します
API クイックリファレンス
被写体のセグメンテーション
| API | プラットフォーム | 目的 |
|---|---|---|
VNGenerateForegroundInstanceMaskRequest | iOS 17+ | クラスに依存しない被写体インスタンス |
VNGeneratePersonInstanceMaskRequest | iOS 17+ | 最大 4 人を個別に検出 |
VNGeneratePersonSegmentationRequest | iOS 15+ | すべての人物(単一マスク) |
ImageAnalysisInteraction (VisionKit) | iOS 16+ | 被写体を切り抜くための UI |
ポーズ検出
| API | プラットフォーム | ランドマーク | 座標 |
|---|---|---|---|
VNDetectHumanHandPoseRequest | iOS 14+ | 手ごとに 21 個 | 2D 正規化座標 |
VNDetectHumanBodyPoseRequest | iOS 14+ | 体の関節 18 個 | 2D 正規化座標 |
VNDetectHumanBodyPose3DRequest | iOS 17+ | 体の関節 17 個 | 3D メートル単位 |
顔と人物の検出
| API | プラットフォーム | 目的 |
|---|---|---|
VNDetectFaceRectanglesRequest | iOS 11+ | 顔のバウンディングボックス |
VNDetectFaceLandmarksRequest | iOS 11+ | 詳細なランドマークを含む顔 |
VNDetectHumanRectanglesRequest | iOS 13+ | 人物の胴体のバウンディングボックス |
テキストとバーコード
| API | プラットフォーム | 用途 |
|---|---|---|
VNRecognizeTextRequest | iOS 13+ | テキスト認識(OCR) |
VNDetectBarcodesRequest | iOS 11+ | バーコード/QR コードの検出 |
DataScannerViewController | iOS 16+ | ライブカメラスキャナー(テキスト+バーコード) |
VNDocumentCameraViewController | iOS 13+ | パースペクティブ補正付きの書類スキャン |
VNDetectDocumentSegmentationRequest | iOS 15+ | プログラムによる書類のエッジ検出 |
RecognizeDocumentsRequest | iOS 26+ | 構造化された書類の抽出 |
ビジュアルインテリジェンス
| API | プラットフォーム | 用途 |
|---|---|---|
SemanticContentDescriptor | iOS 26+ | ユーザーが見ているものを記述します(ラベル+ピクセルバッファ) |
IntentValueQuery | iOS 26+ | ビジュアル検索リクエストを受け取るためのエントリーポイント |
VisualIntelligenceSearchIntent | iOS 26+ | アプリへの「その他の結果」ディープリンク |
オブザベーションの種類
| オブザベーション | 返される内容 |
|---|---|
VNInstanceMaskObservation | 前景/人物のインスタンスマスク |
VNPixelBufferObservation | 人物のセグメンテーション(単一マスク) |
VNHumanHandPoseObservation | 手のポーズ |
VNHumanBodyPoseObservation | 身体のポーズ(2D) |
VNHumanBodyPose3DObservation | 身体のポーズ(3D) |
VNFaceObservation | 顔の検出/ランドマーク |
VNHumanObservation | 人物の矩形 |
VNRecognizedTextObservation | テキスト認識 |
VNBarcodeObservation | バーコードの検出 |
VNRectangleObservation | 書類のセグメンテーション |
DocumentObservation | 構造化された書類(iOS 26+) |
リソース
WWDC: 2019-234, 2021-10041, 2022-10024, 2022-10025, 2025-272, 2023-10176, 2023-111241, 2023-10048, 2020-10653, 2020-10043, 2020-10099
ドキュメント: /vision, /visionkit, /visualintelligence, /visualintelligence/semanticcontentdescriptor, /vision/vnrecognizetextrequest, /vision/vndetectbarcodesrequest
スキル: axiom-vision, axiom-vision-diag