2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

YOLO26とは

2026年1月にUltralyticsからリリースされた最新の物体検出モデル。前世代のYOLO11と比べてCPU推論が最大43%高速化され、エッジデバイスでの実用性が大幅に向上しています。

最大の特徴はNMS不要のEnd-to-End推論。従来のYOLOでは必須だったNon-Maximum Suppression(NMS)の後処理ステップが不要になり、モデルから直接最終的な検出結果が出力されます。

モデル mAP CPU推論 パラメータ
YOLO26n 40.9 38.9ms 2.5M
YOLO26s 48.6 63.3ms 9.2M
YOLO26m 53.1 155ms 18.7M

なぜiPhoneで動かすのか

  • リアルタイム推論: Neural Engineを活用して30FPS以上で動作
  • プライバシー: データがデバイス外に出ない
  • オフライン: ネットワーク不要で動作
  • 低レイテンシ: サーバー往復がないので即座に結果が返る

CoreMLモデルの準備

方法1: 変換済みモデルをダウンロード

CoreML-Modelsリポジトリから変換済みのモデルをダウンロードできます。

YOLO26s (18MB)

方法2: 自分で変換

pip install ultralytics coremltools==8.1

python -c "
from ultralytics import YOLO
model = YOLO('yolo26s.pt')
model.export(format='coreml', nms=False)
"

注意: coremltools 9.0には_castのバグがあるため、8.1を推奨します。

iOSアプリの実装

モデルの読み込み

import CoreML
import Vision

let config = MLModelConfiguration()
config.computeUnits = .all  // Neural Engine + GPU + CPU
let mlModel = try MLModel(contentsOf: modelURL, configuration: config)
let vnModel = try VNCoreMLModel(for: mlModel)

カメラフレームの推論

func captureOutput(_ output: AVCaptureOutput,
                   didOutput sampleBuffer: CMSampleBuffer,
                   from connection: AVCaptureConnection) {
    guard let pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) else { return }

    let request = VNCoreMLRequest(model: vnModel) { request, _ in
        self.handleDetections(request)
    }
    request.imageCropAndScaleOption = .scaleFill

    try? VNImageRequestHandler(cvPixelBuffer: pixelBuffer, orientation: .up)
        .perform([request])
}

NMS不要の出力デコード

YOLO26の出力は [1, 300, 6] のテンソル。各行が [x1, y1, x2, y2, confidence, class_id] で、すでにフィルタリング済みの最終結果です。

func handleDetections(_ request: VNRequest) {
    guard let results = request.results as? [VNCoreMLFeatureValueObservation],
          let array = results.first?.featureValue.multiArrayValue else { return }

    let shape = array.shape.map { $0.intValue }  // [1, 300, 6]

    for i in 0..<shape[1] {
        let confidence = array[[0, i, 4] as [NSNumber]].floatValue
        guard confidence >= 0.25 else { continue }

        let x1 = CGFloat(array[[0, i, 0] as [NSNumber]].floatValue) / 640
        let y1 = CGFloat(array[[0, i, 1] as [NSNumber]].floatValue) / 640
        let x2 = CGFloat(array[[0, i, 2] as [NSNumber]].floatValue) / 640
        let y2 = CGFloat(array[[0, i, 3] as [NSNumber]].floatValue) / 640
        let classId = Int(array[[0, i, 5] as [NSNumber]].floatValue)

        // x1,y1,x2,y2 は正規化座標 [0,1]
        // これをそのまま画面座標に変換して描画
    }
}

従来のYOLO(v5, v8, v9, v11)では、この段階でNMSの処理が必要でした。YOLO26ではモデル内部でDual Assignmentによる重複排除が完了しているため、閾値でフィルタリングするだけで最終結果が得られます。

NMS付きYOLOとの比較

YOLO26 (NMS不要) YOLO11 (NMS必要)
出力 [1, 300, 6] — 直接結果 [1, 84, 8400] — 要デコード+NMS
後処理 閾値フィルタのみ box decode → NMS → フィルタ
CoreML変換 nms=False でシンプル nms=True でパイプライン構築が必要
推論速度 43%高速 (CPU) ベースライン

バウンディングボックスの描画

高速描画には CAShapeLayer を使います。SwiftUIの ForEach で描画すると毎フレーム再生成されて遅くなります。

class BoundingBoxView {
    let shapeLayer = CAShapeLayer()
    let textLayer = CATextLayer()

    func show(frame: CGRect, label: String, color: UIColor) {
        CATransaction.begin()
        CATransaction.setDisableActions(true)  // アニメーション無効化

        shapeLayer.path = UIBezierPath(roundedRect: frame, cornerRadius: 10).cgPath
        shapeLayer.strokeColor = color.cgColor
        textLayer.string = label

        CATransaction.commit()
    }
}

ポイント:

  • CATransaction.setDisableActions(true) で暗黙のアニメーションを無効化。これがないとラベルがフレーム遅れで追従する
  • 100個程度のレイヤーをプール化して再利用。毎フレームのalloc/deallocを回避

カメラプレビューとの座標合わせ

ここが一番ハマるポイント。

// カメラ出力のvideoOrientationを.portraitに設定
// これでpixelBufferがポートレート回転済みで届く
let connection = videoOutput.connection(with: .video)
connection?.videoOrientation = .portrait

// VNImageRequestHandlerには .up を渡す(既に回転済みなので)
VNImageRequestHandler(cvPixelBuffer: pb, orientation: .up)

プレビューが resizeAspectFill でクロップされるため、カメラのアスペクト比と画面のアスペクト比の差を補正する必要があります。

let cameraRatio = shortSide / longSide  // e.g., 1080/1920
let displayRatio = screenWidth / screenHeight
let ratio = (screenHeight / screenWidth) / (longSide / shortSide)

if ratio >= 1 {
    // 画面がカメラより縦長 → 横方向にスケール補正
    let offset = (1 - ratio) * (0.5 - rect.minX)
    // ... アフィン変換で補正
}

サンプルアプリ

CoreML-Models リポジトリに完全なサンプルアプリがあります。

  • YOLO26Demo (sample_apps/YOLO26Demo/) — NMS不要モデル用
    • カメラリアルタイム推論 + FPS/レイテンシ表示
    • 写真ライブラリからの画像推論
    • 動画のフレームごと推論

セットアップ:

  1. モデルをダウンロードして解凍
  2. .mlpackage をXcodeプロジェクトにドラッグ
  3. 実機でビルド&ラン

出力形状 [1, N, 6] を持つモデルなら、ファイル名に関わらず自動で読み込まれます。

変換時のTips

今回の変換で得た知見:

  • coremltools 9.0 + numpy 2.x_cast でクラッシュする → coremltools 8.1 + numpy<2 を使う
  • ultralytics 8.4.31nms=True CoreMLエクスポートは pipeline_coreml のバグで失敗する → NMS不要のYOLO26ならそもそも nms=False で問題なし
  • Python 3.14はcoremltools非対応 → Python 3.12を使う

まとめ

YOLO26はNMS不要の設計により、CoreML変換もアプリ実装もシンプルになりました。従来のYOLOではNMSパイプラインの構築やデコード処理が必要でしたが、YOLO26では閾値フィルタリングだけで済みます。

iPhoneのNeural Engineを活用すれば30FPS以上のリアルタイム検出が可能。エッジAIの実用化がまた一歩近づいた感じがします。

参考リンク

🐣


最新のAI機能を使ったアプリやサービスを最速で試作したい。
そんなご要望にお応えします。
ご相談はこちらまで。
rockyshikoku@gmail.com

Twitter
Medium
GitHub

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?