mcpField
A
axiom-vision-diag

axiom-vision-diag

github.com/charleswiltgen開発1.2k178 回インストール今日更新
リポジトリ

axiom-vision-diag について

AppleのVisionフレームワークを使う開発者向けのトラブルシューティングガイドです。検出や認識、座標、スレッド、APIの利用可否、パフォーマンスの問題を診断する手順と対処法を示します。

主な機能

  1. 01Visionの検出問題を診断
  2. 02信頼度と処理スレッドを確認
  3. 03座標変換の問題を調査
  4. 04APIの利用可否の違いに対応
  5. 05リアルタイム処理を最適化

活用シーン

  1. 01手や人体のポーズが検出されない原因を調査
  2. 02UIオーバーレイの位置ずれを修正
  3. 03テキストやバーコード認識の失敗を調査
  4. 04リアルタイム画像処理の遅延を軽減

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/charleswiltgen/axiom --skill axiom-vision-diag

インストール後、Claude Code で/axiom-vision-diagを入力して読み込まれたか確認します。

詳細

Vision Framework の診断

Vision framework の問題を体系的にトラブルシューティングします。被写体が検出されない、ランドマークが見つからない、信頼度が低い、パフォーマンスに問題がある、座標が一致しない、テキスト認識に失敗する、バーコード検出に問題がある、書類スキャンに問題がある場合に対応します。

概要

基本原則:Vision が動作しない場合、通常は次のいずれかが原因です。

  1. 環境(照明、遮蔽、フレームの端)- 40%
  2. 信頼度のしきい値(信頼度の低いデータを無視している)- 30%
  3. スレッド処理(メインスレッドをブロックして UI がフリーズする)- 15%
  4. 座標(左下原点と左上原点を混在させている)- 10%
  5. API の利用可否(古いデバイスで iOS 17 以降の API を使用している)- 5%

コードをデバッグする前に、必ず環境と信頼度を確認してください。

注意すべき兆候

Vision 固有の問題を示す症状:

症状考えられる原因
被写体がまったく検出されないフレームの端、照明不足、被写体が非常に小さい
手のランドマークが断続的に nil になる手がフレームの端にある、カメラに対して平行、手袋/遮蔽
身体のポーズでフレームがスキップされる人物が前屈している、上下逆さま、ゆったりした服
処理中に UI がフリーズするメインスレッドで Vision を実行している
オーバーレイの位置がずれている座標変換(左下原点と左上原点)
古いデバイスでクラッシュする@available チェックなしで iOS 17 以降の API を使用している
人物セグメンテーションで人物が検出されないシーン内に人物が 4 人を超えている(インスタンスマスクの上限)
カメラ映像の FPS が低いmaximumHandCount が高すぎる、フレームを間引いていない
テキストがまったく認識されない画像がぼやけている、装飾フォント、認識レベルが不適切
テキストの読み取りを誤る(文字が違う)言語補正が無効、カスタム単語が不足している
バーコードが検出されないシンボル体系が不適切、コードが小さすぎる、まぶしさ/反射
DataScanner の画面が真っ白になるカメラへのアクセスが拒否されている、デバイスがサポートされていない
書類の端が検出されないコントラスト不足、矩形ではない、まぶしさ
リアルタイムスキャンが遅すぎるすべてのフレームを処理している、領域が大きすぎる

必須の初手

コードを調べる前に、次の診断を実行してください。

ステップ 1:診断コードで検出を確認する
let request = VNGenerateForegroundInstanceMaskRequest()  // Or hand/body pose
let handler = VNImageRequestHandler(cgImage: testImage)

do {
    try handler.perform([request])

    if let results = request.results {
        print("✅ Request succeeded")
        print("Result count: \(results.count)")

        if let observation = results.first as? VNInstanceMaskObservation {
            print("All instances: \(observation.allInstances)")
            print("Instance count: \(observation.allInstances.count)")
        }
    } else {
        print("⚠️ Request succeeded but no results")
    }
} catch {
    print("❌ Request failed: \(error)")
}

想定される出力:

  • ✅ リクエスト成功、インスタンス数 > 0 → 検出は動作しています
  • ⚠️ リクエスト成功、インスタンス数 = 0 → 何も検出されていません(判断ツリーを参照)
  • ❌ リクエスト失敗 → API の利用可否に問題があります
ステップ 2:信頼度スコアを確認する
// For hand/body pose
if let observation = request.results?.first as? VNHumanHandPoseObservation {
    let allPoints = try observation.recognizedPoints(.all)

    for (key, point) in allPoints {
        print("\(key): confidence \(point.confidence)")

        if point.confidence < 0.3 {
            print("  ⚠️ LOW CONFIDENCE - unreliable")
        }
    }
}

想定される出力:

  • ほとんどのランドマークの信頼度が > 0.5 → 良好な検出
  • 多くのランドマークの信頼度が < 0.3 → 照明不足、遮蔽、またはフレームの端
ステップ 3:スレッド処理を確認する
print("🧵 Thread: \(Thread.current)")

if Thread.isMainThread {
    print("❌ Running on MAIN THREAD - will block UI!")
} else {
    print("✅ Running on background thread")
}

想定される出力:

  • ✅ バックグラウンドスレッド → 正常
  • ❌ メインスレッド → DispatchQueue.global() に移動してください

判断ツリー

Vision not working as expected?
│
├─ No results returned?
│  ├─ Check Step 1 output
│  │  ├─ "Request failed" → See Pattern 1a (API availability)
│  │  ├─ "No results" → See Pattern 1b (nothing detected)
│  │  └─ Results but count = 0 → See Pattern 1c (edge of frame)
│
├─ Landmarks have nil/low confidence?
│  ├─ Hand pose → See Pattern 2 (hand detection issues)
│  ├─ Body pose → See Pattern 3 (body detection issues)
│  └─ Face detection → See Pattern 4 (face detection issues)
│
├─ UI freezing/slow?
│  ├─ Check Step 3 (threading)
│  │  ├─ Main thread → See Pattern 5a (move to background)
│  │  └─ Background thread → See Pattern 5b (performance tuning)
│
├─ Overlays in wrong position?
│  └─ See Pattern 6 (coordinate conversion)
│
├─ Person segmentation missing people?
│  └─ See Pattern 7 (crowded scenes)
│
├─ VisionKit not working?
│  └─ See Pattern 8 (VisionKit specific)
│
├─ Text recognition issues?
│  ├─ No text detected → See Pattern 9a (image quality)
│  ├─ Wrong characters → See Pattern 9b (language/correction)
│  └─ Too slow → See Pattern 9c (recognition level)
│
├─ Barcode detection issues?
│  ├─ Barcode not detected → See Pattern 10a (symbology/size)
│  └─ Wrong payload → See Pattern 10b (barcode quality)
│
├─ DataScannerViewController issues?
│  ├─ Blank screen → See Pattern 11a (availability check)
│  └─ Items not detected → See Pattern 11b (data types)
│
└─ Document scanning issues?
   ├─ Edges not detected → See Pattern 12a (contrast/shape)
   └─ Perspective wrong → See Pattern 12b (corner points)

診断パターン

パターン 1a:リクエスト失敗(API の利用可否)

症状:try handler.perform([request]) がエラーをスローする

よくあるエラー:

"VNGenerateForegroundInstanceMaskRequest is only available on iOS 17.0 or newer"
"VNDetectHumanBodyPose3DRequest is only available on iOS 17.0 or newer"

根本原因:古いデプロイメントターゲットで iOS 17 以降の API を使用している

修正方法:

if #available(iOS 17.0, *) {
    let request = VNGenerateForegroundInstanceMaskRequest()
    // ...
} else {
    // Fallback for iOS 14-16
    let request = VNGeneratePersonSegmentationRequest()
    // ...
}

予防策:実装前に axiom-vision-ref で API の利用可否を確認してください

修正にかかる時間:10 分

パターン 1b:結果がない(何も検出されない)

症状:request.results == nil または results.isEmpty

診断:

// 1. Save debug image to Photos
UIImageWriteToSavedPhotosAlbum(debugImage, nil, nil, nil)

// 2. Inspect visually
// - Is subject too small? (< 10% of image)
// - Is subject blurry?
// - Poor contrast with background?

よくある原因:

  • 被写体が小さすぎる(サイズを変更するか、近くで切り抜く)
  • 被写体がぼやけすぎている(照明を強くする、カメラを固定する)
  • コントラストが低い(被写体と背景の色が同じ)

修正方法:

// Crop image to focus on region of interest
let croppedImage = cropImage(sourceImage, to: regionOfInterest)
let handler = VNImageRequestHandler(cgImage: croppedImage)

修正にかかる時間:30 分

パターン 1c:フレームの端に関する問題

症状:被写体がフレーム内を移動すると、検出されたりされなかったりする

根本原因:被写体が画像の端に触れたときに部分的に遮蔽される

診断:

// Check if subject is near edges
if let observation = results.first as? VNInstanceMaskObservation {
    let mask = try observation.createScaledMask(
        for: observation.allInstances,
        croppedToInstancesContent: true
    )

    let bounds = calculateMaskBounds(mask)

    if bounds.minX < 0.1 || bounds.maxX > 0.9 ||
       bounds.minY < 0.1 || bounds.maxY > 0.9 {
        print("⚠️ Subject too close to edge")
    }
}

修正方法:

// Add padding to capture area
let paddedRect = captureRect.insetBy(dx: -20, dy: -20)

// OR guide user with on-screen overlay
overlayView.addSubview(guideBox)  // Visual boundary

修正にかかる時間:20 分

パターン 2:手のポーズに関する問題

症状:VNDetectHumanHandPoseRequest が nil を返す、または信頼度の低いランドマークを返す

診断:

if let observation = request.results?.first as? VNHumanHandPoseObservation {
    let thumbTip = try? observation.recognizedPoint(.thumbTip)
    let wrist = try? observation.recognizedPoint(.wrist)

    print("Thumb confidence: \(thumbTip?.confidence ?? 0)")
    print("Wrist confidence: \(wrist?.confidence ?? 0)")

    // Check hand orientation
    if let thumb = thumbTip, let wristPoint = wrist {
        let angle = atan2(
            thumb.location.y - wristPoint.location.y,
            thumb.location.x - wristPoint.location.x
        )
        print("Hand angle: \(angle * 180 / .pi) degrees")

        if abs(angle) > 80 && abs(angle) < 100 {
            print("⚠️ Hand parallel to camera (hard to detect)")
        }
    }
}

一般的な原因:

原因信頼度のパターン対処法
手が端に近い指先の信頼度が低いフレーミングを調整する
手がカメラと平行すべてのランドマークの信頼度が低い手を回転させるようユーザーに促す
手袋/遮蔽指の信頼度が低く、手首の信頼度が高い手袋を外すか、照明を変更する
足を手として検出想定外の手を検出chirality チェックを追加するか、無視する

平行な手への対処法:

// Detect and warn user
if avgConfidence < 0.4 {
    showWarning("Rotate your hand toward the camera")
}

修正所要時間:45 分

パターン 3:身体のポーズに関する問題

症状:VNDetectHumanBodyPoseRequest がフレームをスキップする、または信頼度の低い結果を返す

診断:

if let observation = request.results?.first as? VNHumanBodyPoseObservation {
    let nose = try? observation.recognizedPoint(.nose)
    let root = try? observation.recognizedPoint(.root)

    if let nosePoint = nose, let rootPoint = root {
        let bodyAngle = atan2(
            nosePoint.location.y - rootPoint.location.y,
            nosePoint.location.x - rootPoint.location.x
        )

        let angleFromVertical = abs(bodyAngle - .pi / 2)

        if angleFromVertical > .pi / 4 {
            print("⚠️ Person bent over or upside down")
        }
    }
}

一般的な原因:

原因解決策
人物が前かがみ直立するようユーザーに促す
逆さま(逆立ち)代わりに ARKit を使用する(動的なポーズに適しています)
ひらひらした服コントラストを上げるか、体にフィットした服を使用する
複数の人物が重なっている人物インスタンスセグメンテーションを使用する

修正所要時間:1 時間

パターン 4:顔検出に関する問題

症状:VNDetectFaceRectanglesRequest が顔を見逃す、または誤った数の顔を返す

診断:

if let faces = request.results as? [VNFaceObservation] {
    print("Detected \(faces.count) faces")

    for face in faces {
        print("Face bounds: \(face.boundingBox)")
        print("Confidence: \(face.confidence)")

        if face.boundingBox.width < 0.1 {
            print("⚠️ Face too small")
        }
    }
}

一般的な原因:

  • 顔が画像の 10% 未満(近くに寄って撮影する)
  • 横顔(代わりに顔のランドマークリクエストを使用する)
  • 照明が不十分(露出を上げる)

修正所要時間:30 分

パターン 5a:UI のフリーズ(メインスレッド)

症状:Vision リクエストの実行時にアプリがフリーズする

診断(上記のステップ 3 でメインスレッド上にあることを確認)

対処法:

// BEFORE (wrong)
let request = VNGenerateForegroundInstanceMaskRequest()
try handler.perform([request])  // Blocks UI

// AFTER (correct)
DispatchQueue.global(qos: .userInitiated).async {
    let request = VNGenerateForegroundInstanceMaskRequest()
    try? handler.perform([request])

    DispatchQueue.main.async {
        // Update UI
    }
}

修正所要時間:15 分

パターン 5b:パフォーマンスの問題(バックグラウンドスレッド)

症状:すでにバックグラウンドスレッド上にあるが、処理が遅い/フレームがドロップする

診断:

let start = CFAbsoluteTimeGetCurrent()

try handler.perform([request])

let elapsed = CFAbsoluteTimeGetCurrent() - start
print("Request took \(elapsed * 1000)ms")

if elapsed > 0.2 {  // 200ms = too slow for real-time
    print("⚠️ Request too slow for real-time processing")
}

一般的な原因と対処法:

原因対処法短縮時間
maximumHandCount = 10必要な数(例:2)に設定する50~70%
すべてのフレームを処理フレームをスキップする(3 フレームごとに処理)66%
フル解像度の画像1280x720 に縮小する40~60%
1 フレームにつき複数のリクエストリクエストをまとめるか、交互に実行する30~50%

リアルタイムカメラへの対処法:

// Skip frames
frameCount += 1
guard frameCount % 3 == 0 else { return }

// OR downscale
let scaledImage = resizeImage(sourceImage, to: CGSize(width: 1280, height: 720))

// OR set lower hand count
request.maximumHandCount = 2  // Instead of default

修正所要時間:1 時間

パターン 6:座標変換

症状:UI オーバーレイが誤った位置に表示される

診断:

// Vision point (lower-left origin, normalized)
let visionPoint = recognizedPoint.location
print("Vision point: \(visionPoint)")  // e.g., (0.5, 0.8)

// Convert to UIKit
let uiX = visionPoint.x * imageWidth
let uiY = (1 - visionPoint.y) * imageHeight  // FLIP Y
print("UIKit point: (\(uiX), \(uiY))")

// Verify overlay
overlayView.center = CGPoint(x: uiX, y: uiY)

よくある間違い:

// ❌ WRONG (no Y flip)
let uiPoint = CGPoint(
    x: axiom-visionPoint.x * width,
    y: axiom-visionPoint.y * height
)

// ❌ WRONG (forgot to scale from normalized)
let uiPoint = CGPoint(
    x: axiom-visionPoint.x,
    y: 1 - visionPoint.y
)

// ✅ CORRECT
let uiPoint = CGPoint(
    x: axiom-visionPoint.x * width,
    y: (1 - visionPoint.y) * height
)

修正所要時間:20 分

パターン 7:混雑したシーン(4 人超)

症状:VNGeneratePersonInstanceMaskRequest が人物を見逃す、または人物をまとめて検出する

診断:

// Count faces
let faceRequest = VNDetectFaceRectanglesRequest()
try handler.perform([faceRequest])

let faceCount = faceRequest.results?.count ?? 0
print("Detected \(faceCount) faces")

// Person instance segmentation
let personRequest = VNGeneratePersonInstanceMaskRequest()
try handler.perform([personRequest])

let personCount = (personRequest.results?.first as? VNInstanceMaskObservation)?.allInstances.count ?? 0
print("Detected \(personCount) people")

if faceCount > 4 && personCount <= 4 {
    print("⚠️ Crowded scene - some people combined or missing")
}

対処法:

if faceCount > 4 {
    // Fallback: Use single mask for all people
    let singleMaskRequest = VNGeneratePersonSegmentationRequest()
    try handler.perform([singleMaskRequest])

    // OR guide user
    showWarning("Please reduce number of people in frame (max 4)")
}

修正所要時間:30 分

パターン 8:VisionKit 固有の問題

症状:ImageAnalysisInteraction で被写体の切り抜き UI が表示されない

診断:

// 1. Check interaction types
print("Interaction types: \(interaction.preferredInteractionTypes)")

// 2. Check if analysis is set
print("Analysis: \(interaction.analysis != nil ? "set" : "nil")")

// 3. Check if view supports interaction
if let view = interaction.view {
    print("View: \(view)")
} else {
    print("❌ View not set")
}

一般的な原因:

症状原因対処法
UI が表示されないanalysis が設定されていないanalyzer.analyze() を呼び出し、結果を設定する
UI は表示されるが、被写体の切り抜きができないインタラクションの種類が誤っている.imageSubject または .automatic を設定する
インタラクション時にクラッシュするインタラクション前にビューが削除されているビューをメモリ上に保持する

対処法:

// Ensure analysis is set
let analyzer = ImageAnalyzer()
let analysis = try await analyzer.analyze(image, configuration: config)

interaction.analysis = analysis  // Required!
interaction.preferredInteractionTypes = .imageSubject

修正所要時間:20 分

パターン 9a:テキストが検出されない(画像品質)

症状:VNRecognizeTextRequest が結果を返さない、または空の文字列を返す

診断:

let request = VNRecognizeTextRequest()
request.recognitionLevel = .accurate

try handler.perform([request])

if request.results?.isEmpty ?? true {
    print("❌ No text detected")

    // Check image quality
    print("Image size: \(image.size)")
    print("Minimum text height: \(request.minimumTextHeight)")
}

for obs in request.results as? [VNRecognizedTextObservation] ?? [] {
    let top = obs.topCandidates(3)
    for candidate in top {
        print("'\(candidate.string)' confidence: \(candidate.confidence)")
    }
}

一般的な原因:

原因症状対処法
画像がぼやけている結果が返らない照明を改善し、カメラを安定させる
テキストが小さすぎる結果が返らないminimumTextHeight を小さくするか、近くに寄って撮影する
装飾的なフォント誤読または結果が返らない.accurate 認識レベルを試す
コントラストが低い結果が部分的照明を改善し、画像のコントラストを上げる
回転したテキスト.fast では結果が返らない.accurate を使用する(回転に対応)

小さなテキストへの対処法:

// Lower minimum text height (default ignores very small text)
request.minimumTextHeight = 0.02  // 2% of image height

修正所要時間:30 分

パターン 9b:文字が誤って認識される(言語/補正)

症状:テキストは検出されますが、文字が誤っています(例:「C001」→「COOL」)

診断:

// Check all candidates, not just first
for observation in results {
    let candidates = observation.topCandidates(5)
    for (i, candidate) in candidates.enumerated() {
        print("Candidate \(i): '\(candidate.string)' (\(candidate.confidence))")
    }
}

よくある原因:

入力タイプ問題対処方法
シリアル番号言語補正によって「修正」されるusesLanguageCorrection を無効にします
技術コード単語として誤読されるcustomWords に追加します
英語以外の言語ML モデルが誤っている正しい recognitionLanguages を設定します
番地装飾された文字が誤読される上位候補だけでなく、すべての候補を確認します

コード/シリアル番号の対処方法:

let request = VNRecognizeTextRequest()
request.usesLanguageCorrection = false  // Don't "fix" codes

// Post-process with domain knowledge
func correctSerialNumber(_ text: String) -> String {
    text.replacingOccurrences(of: "O", with: "0")
        .replacingOccurrences(of: "l", with: "1")
        .replacingOccurrences(of: "S", with: "5")
}

修正時間:30 分

パターン 9c:テキスト認識が遅すぎる

症状:テキスト認識に 500ms 以上かかり、リアルタイムカメラでフレームがドロップする

診断:

let start = CFAbsoluteTimeGetCurrent()
try handler.perform([request])
let elapsed = CFAbsoluteTimeGetCurrent() - start

print("Recognition took \(elapsed * 1000)ms")
print("Recognition level: \(request.recognitionLevel == .fast ? "fast" : "accurate")")
print("Language correction: \(request.usesLanguageCorrection)")

よくある原因と対処方法:

原因対処方法速度向上
リアルタイム処理で .accurate を使用している.fast に切り替えます3~5 倍
言語補正が有効になっているコードの場合は無効にします20~30%
画像全体を処理しているregionOfInterest を使用します2~4 倍
すべてのフレームを処理しているフレームをスキップします50~70%

リアルタイム処理の対処方法:

request.recognitionLevel = .fast
request.usesLanguageCorrection = false
request.regionOfInterest = CGRect(x: 0.1, y: 0.3, width: 0.8, height: 0.4)

// Skip frames
frameCount += 1
guard frameCount % 3 == 0 else { return }

修正時間:30 分

パターン 10a:バーコードが検出されない(シンボル体系/サイズ)

症状:VNDetectBarcodesRequest が結果を返さない

診断:

let request = VNDetectBarcodesRequest()
// Don't specify symbologies to detect all types
try handler.perform([request])

if let results = request.results as? [VNBarcodeObservation] {
    print("Found \(results.count) barcodes")
    for barcode in results {
        print("Type: \(barcode.symbology)")
        print("Payload: \(barcode.payloadStringValue ?? "nil")")
        print("Bounds: \(barcode.boundingBox)")
    }
} else {
    print("❌ No barcodes detected")
}

よくある原因:

原因症状対処方法
シンボル体系が誤っている検出されないフィルターを指定しないか、正しいタイプを追加します
バーコードが小さすぎる検出されないカメラを近づけるか、画像をトリミングします
まぶしさ/反射検出されない角度を変えるか、照明を改善します
バーコードが破損している部分的に検出される/検出されないバーコードをきれいにするか、画像を改善します
リビジョン 1 を使用しているコードが 1 つしか検出されない複数検出にはリビジョン 2 以降を使用します

小さなバーコードの対処方法:

// Crop to barcode region for better detection
let croppedHandler = VNImageRequestHandler(
    cgImage: croppedImage,
    options: [:]
)

修正時間:20 分

パターン 10b:バーコードのペイロードが誤っている

症状:バーコードは検出されますが、payloadStringValue が誤っているか nil になっている

診断:

if let barcode = results.first {
    print("String payload: \(barcode.payloadStringValue ?? "nil")")
    print("Raw payload: \(barcode.payloadData ?? Data())")
    print("Symbology: \(barcode.symbology)")
    print("Confidence: Implicit (always 1.0 for barcodes)")
}

よくある原因:

原因対処方法
バイナリバーコード(文字列ではない)代わりに payloadData を使用します
コードが破損している再スキャンするか、バーコードをきれいにします
想定したシンボル体系が誤っている実際の symbology 値を確認します

修正時間:15 分

パターン 11a:DataScanner の画面が真っ暗

症状:表示すると DataScannerViewController が黒い画面または空白の画面になる

診断:

// Check support first
print("isSupported: \(DataScannerViewController.isSupported)")
print("isAvailable: \(DataScannerViewController.isAvailable)")

// Check camera permission
let status = AVCaptureDevice.authorizationStatus(for: .video)
print("Camera access: \(status.rawValue)")

よくある原因:

症状原因対処方法
isSupported = falseデバイスにカメラまたはチップがない表示前に確認します
isAvailable = falseペアレンタルコントロールまたはアクセス拒否カメラの使用許可をリクエストします
黒い画面別のアプリがカメラを使用中カメラを独占的に使用できるようにします
表示時にクラッシュするエンタイトルメントが不足しているカメラ使用目的の説明を追加します

対処方法:

guard DataScannerViewController.isSupported else {
    showError("Scanning not supported on this device")
    return
}

guard DataScannerViewController.isAvailable else {
    // Request camera access
    AVCaptureDevice.requestAccess(for: .video) { granted in
        // Retry after access granted
    }
    return
}

修正時間:15 分

パターン 11b:DataScanner で項目が検出されない

症状:DataScanner にカメラは表示されますが、項目が認識されない

診断:

// Check recognized data types
print("Data types: \(scanner.recognizedDataTypes)")

// Add delegate to see what's happening
func dataScanner(_ scanner: DataScannerViewController,
                 didAdd items: [RecognizedItem],
                 allItems: [RecognizedItem]) {
    print("Added \(items.count) items, total: \(allItems.count)")
    for item in items {
        switch item {
        case .text(let text): print("Text: \(text.transcript)")
        case .barcode(let barcode): print("Barcode: \(barcode.payloadStringValue ?? "")")
        @unknown default: break
        }
    }
}

よくある原因:

原因対処方法
データタイプが誤っている正しい .barcode(symbologies:) または .text() を追加します
テキスト内容タイプのフィルターフィルターを削除するか、正しいタイプを使用します
カメラが近すぎる/遠すぎる距離を調整します
照明が不十分照明を改善します

修正時間:20 分

パターン 12a:書類の端が検出されない

症状:VNDetectDocumentSegmentationRequest が結果を返さない

診断:

let request = VNDetectDocumentSegmentationRequest()
try handler.perform([request])

if let observation = request.results?.first {
    print("Document found at: \(observation.boundingBox)")
    print("Corners: TL=\(observation.topLeft), TR=\(observation.topRight)")
} else {
    print("❌ No document detected")
}

よくある原因:

原因対処方法
コントラストが低い背景とのコントラストを高くします
長方形ではないML は長方形の書類を想定しています
まぶしさ/反射照明の角度を変えます
書類がフレームいっぱいに写っている背景が少し見えるようにする必要があります

対処方法:ライブフィードバック付きのガイド付きユーザー体験を提供するには、VNDocumentCameraViewController を使用します。

修正時間:15 分

パターン 12b:遠近補正が正しくない

症状:ドキュメントは抽出されるものの、歪んでいます

診断:

// Verify corner order
print("TopLeft: \(observation.topLeft)")
print("TopRight: \(observation.topRight)")
print("BottomLeft: \(observation.bottomLeft)")
print("BottomRight: \(observation.bottomRight)")

// Check if corners are in expected positions
// TopLeft should have larger Y than BottomLeft (Vision uses lower-left origin)

よくある原因:

原因修正方法
角の順序が正しくないVision では左上から反時計回りに指定します
座標系正規化座標をピクセル座標に変換します
フィルターパラメーターが正しくないCIPerspectiveCorrection のパラメーターを確認します

修正方法:

// Scale normalized to image coordinates
func scaled(_ point: CGPoint, to size: CGSize) -> CGPoint {
    CGPoint(x: point.x * size.width, y: point.y * size.height)
}

修正にかかる時間:20 分

本番環境での危機的状況

状況:「分析ボタンをタップするとアプリがフリーズする」として App Store の審査で却下されました

トリアージ(5 分):

  1. Vision がメインスレッドで実行されていることを確認 → パターン 5a
  2. 古いデバイス(iPhone 12)で確認 → フリーズします
  3. プロファイリングを確認:メインスレッドで 800ms

修正(15 分):

@IBAction func analyzeTapped(_ sender: UIButton) {
    showLoadingIndicator()

    DispatchQueue.global(qos: .userInitiated).async { [weak self] in
        let request = VNGenerateForegroundInstanceMaskRequest()
        // ... perform request

        DispatchQueue.main.async {
            self?.hideLoadingIndicator()
            self?.updateUI(with: results)
        }
    }
}

PM への連絡: 「メインスレッドでの Vision 処理が原因で App Store の審査に却下されました。バックグラウンドキューに移すことで修正済みです(業界標準)。iPhone 12 でのテストで修正を確認しました。再提出して問題ありません。」

クイックリファレンステーブル

症状考えられる原因最初に確認する項目パターン推定時間
結果がない何も検出されていないステップ 1 の出力1b/1c30 分
検出が不安定フレームの端にある被写体の位置1c20 分
手のランドマークが欠落信頼度が低いステップ 2(信頼度)245 分
人体のポーズがスキップされる人が前かがみになっている体の角度31 時間
UI がフリーズするメインスレッドステップ 3(スレッド処理)5a15 分
処理が遅いパフォーマンスの調整リクエストのタイミング5b1 時間
オーバーレイの位置が正しくない座標ポイントを出力する620 分
人物が欠落(4 人超)混雑した場面顔の数730 分
VisionKit に UI が表示されない分析が設定されていないインタラクションの状態820 分
テキストが検出されない画像品質結果の数9a30 分
文字が正しくない言語設定候補の一覧9b30 分
テキスト認識が遅い認識レベルタイミング9c30 分
バーコードが検出されないシンボル体系/サイズ結果のダンプ10a20 分
バーコードのペイロードが正しくない破損/バイナリペイロードデータ10b15 分
DataScanner に何も表示されない利用可能性isSupported/isAvailable11a15 分
DataScanner で項目が見つからないデータタイプrecognizedDataTypes11b20 分
ドキュメントのエッジが欠落コントラスト/形状結果の確認12a15 分
遠近補正が正しくない角の順序角の位置12b20 分

リソース

WWDC:2019-234, 2021-10041, 2022-10024, 2022-10025, 2025-272, 2023-10176, 2020-10653

ドキュメント:/vision, /vision/vnrecognizetextrequest, /visionkit

スキル:axiom-vision, axiom-vision-ref

コメント