YOLO 온디바이스 모바일 구현 — iOS/Android 심층 가이드
조사 날짜: 2026-05-16
대상: iOS (A14 Bionic 이상), Android (Snapdragon 778G 이상)
모델: YOLO11n, YOLO11s (Detection + Classification)
1. 요약 (TL;DR)
섹션 제목: “1. 요약 (TL;DR)”결론: YOLO 온디바이스 모바일 구현은 충분히 가능하다. 올바른 버전·크기 선택이 핵심이다.
- YOLO11n / YOLO11s — 식물 앱 온디바이스에 현실적으로 권장되는 크기
- iOS: Core ML 변환 시 21 FPS(PyTorch) → 85 FPS(Core ML + Neural Engine)로 4배 이상 가속, 60+ FPS 달성 가능
- Android: Snapdragon 8 Gen 2 NPU + TFLite INT8에서 YOLOv8n 기준 ~5-8 ms/frame
- 공식 Flutter SDK 존재:
ultralytics_yolo패키지, iOS(Core ML)·Android(TFLite) 동시 지원 - 식물 병 진단 목적: 단순 잎 전체 분류 → YOLO-Classify 태스크. 병변 위치 검출 → YOLO Detection 태스크
2. YOLO 버전별 온디바이스 가능성 비교표
섹션 제목: “2. YOLO 버전별 온디바이스 가능성 비교표”Detection 모델 (COCO 기준)
섹션 제목: “Detection 모델 (COCO 기준)”| 모델 | 파라미터 | FLOPs | mAP50-95 | CPU ONNX(ms) | 권장 모바일 사용 |
|---|---|---|---|---|---|
| YOLO11n | 2.6M | 6.5B | 39.5 | 56.1 | 강력 권장 (최경량) |
| YOLO11s | 9.4M | 21.5B | 47.0 | 90.0 | 권장 (균형형) |
| YOLO11m | 20.1M | 68.0B | 51.5 | 183.2 | 플래그십 기기만 |
| YOLO11l | 25.3M | 86.9B | 53.4 | 238.6 | 모바일 부적합 |
| YOLO11x | 56.9M | 194.9B | 54.7 | 462.8 | 모바일 불가 |
CPU ONNX 속도는 서버 CPU 기준. 모바일 NPU 가속 시 수십 배 빠름.
Classification 모델 (ImageNet 기준)
섹션 제목: “Classification 모델 (ImageNet 기준)”| 모델 | 크기 | Top-1 정확도 | 모바일 적합성 |
|---|---|---|---|
| yolo11n-cls | 6 MB | 70.0% | 최적 |
| yolo11s-cls | 13 MB | 75.4% | 권장 |
| yolo11m-cls | 22 MB | 77.3% | 가능 |
| yolo11l-cls | 27 MB | 78.3% | 제한적 |
| yolo11x-cls | 57 MB | 79.5% | 부적합 |
YOLO 버전 비교 (모바일 관점)
섹션 제목: “YOLO 버전 비교 (모바일 관점)”| 버전 | Core ML | TFLite | Flutter SDK | 권장 여부 |
|---|---|---|---|---|
| YOLOv5 | 지원 | 지원 | 비공식 | 비권장 |
| YOLOv8 | 지원 | 지원(GPU 제한) | 비공식 | 가능 |
| YOLO11 | 권장 | 지원 | 공식 | 강력 권장 |
YOLO11 권장 이유: YOLOv8m 대비 22% 파라미터 감소 + 1.3% mAP 향상. Core ML NMS 내장 지원. 공식 Flutter SDK 제공.
3. iOS 구현 방법
섹션 제목: “3. iOS 구현 방법”3.1 사전 요구사항
섹션 제목: “3.1 사전 요구사항”pip install ultralytics coremltools# macOS 또는 x86 Linux에서 실행 (Apple Silicon Mac 권장)# coremltools 7.x 이상3.2 모델 내보내기
섹션 제목: “3.2 모델 내보내기”기본 내보내기:
from ultralytics import YOLO
# Detection 모델model = YOLO("yolo11n.pt")model.export(format="coreml", nms=True)# 출력: yolo11n.mlpackage
# Classification 모델model = YOLO("yolo11n-cls.pt")model.export(format="coreml")FP16 양자화 (권장 — 크기 50% 감소):
model = YOLO("yolo11n.pt")model.export(format="coreml", nms=True, half=True)INT8 양자화 (최대 압축 — 크기 75% 감소):
model = YOLO("yolo11n.pt")model.export( format="coreml", nms=True, int8=True, data="coco8.yaml" # 캘리브레이션용 데이터셋 필수)CLI:
yolo export model=yolo11n.pt format=coreml nms=True half=Trueyolo export model=plant_disease_yolo11n.pt format=coreml nms=True half=True주의:
nms=True는 Detection 모델에만 적용. 예상 파일 크기: yolo11n FP32 ~12 MB → FP16 ~6 MB → INT8 ~3 MB
3.3 Swift 통합 — Ultralytics Swift Package (가장 빠름)
섹션 제목: “3.3 Swift 통합 — Ultralytics Swift Package (가장 빠름)”- Xcode → File → Add Package Dependencies
- URL:
https://github.com/ultralytics/yolo-ios-app
import YOLO
// 카메라 실시간 추론struct PlantDiagnosisView: View { var body: some View { YOLOCamera( url: URL(string: "https://example.com/plant_yolo11n.mlpackage.zip")!, task: .detect, // 또는 .classify cameraPosition: .back ) .ignoresSafeArea() }}
// 단일 이미지 추론let model = YOLO("plant_yolo11n", task: .detect)let result = model(uiImage)for detection in result.boxes ?? [] { print("\(detection.cls): \(detection.conf)")}3.4 Swift — Core ML + Vision 프레임워크 직접 통합
섹션 제목: “3.4 Swift — Core ML + Vision 프레임워크 직접 통합”import CoreMLimport Visionimport UIKit
class PlantDiseaseDetector { private var request: VNCoreMLRequest?
init() { guard let modelURL = Bundle.main.url( forResource: "plant_yolo11n", withExtension: "mlpackage" ) else { return }
do { let mlModel = try MLModel(contentsOf: modelURL) let vnModel = try VNCoreMLModel(for: mlModel) request = VNCoreMLRequest(model: vnModel) { [weak self] req, _ in self?.handleResults(req.results) } request?.imageCropAndScaleOption = .scaleFit } catch { print("Model load error: \(error)") } }
func analyze(image: UIImage) { guard let cgImage = image.cgImage, let request = request else { return } let handler = VNImageRequestHandler(cgImage: cgImage, options: [:]) DispatchQueue.global(qos: .userInitiated).async { try? handler.perform([request]) } }
private func handleResults(_ results: [VNObservation]?) { guard let observations = results as? [VNRecognizedObjectObservation] else { return } DispatchQueue.main.async { for obs in observations where obs.confidence > 0.5 { let label = obs.labels.first?.identifier ?? "unknown" let box = obs.boundingBox // 정규화 좌표 (0~1) print("병명: \(label), 신뢰도: \(obs.confidence), 위치: \(box)") } } }}
// AVFoundation 카메라 라이브 피드 연동extension PlantDiseaseDetector: AVCaptureVideoDataOutputSampleBufferDelegate { func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) { guard let pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer), let request = request else { return } let handler = VNImageRequestHandler(cvPixelBuffer: pixelBuffer, options: [:]) try? handler.perform([request]) }}3.5 iOS 기기별 성능 벤치마크
섹션 제목: “3.5 iOS 기기별 성능 벤치마크”| 기기 | 칩셋 | Neural Engine | YOLO11n Core ML 속도 |
|---|---|---|---|
| iPhone 12 | A14 Bionic | 11.8 TOPS | ~20-25 ms (~40-50 FPS) |
| iPhone 14 | A15 Bionic | 15.8 TOPS | ~15-18 ms (~55-65 FPS) |
| iPhone 15 | A16 Bionic | 17 TOPS | ~12-15 ms (~67-83 FPS) |
| iPhone 15 Pro | A17 Pro | 35 TOPS | ~8-12 ms (85+ FPS) |
| iPhone 16 | A18 | 38 TOPS | ~6-10 ms (100+ FPS) |
PyTorch on-device → Core ML 변환 시 4배 가속 확인 사례 (21 FPS → 85 FPS).
4. Android 구현 방법
섹션 제목: “4. Android 구현 방법”4.1 모델 내보내기
섹션 제목: “4.1 모델 내보내기”TFLite (권장):
from ultralytics import YOLO
model = YOLO("yolo11n.pt")
model.export(format="tflite") # FP32model.export(format="tflite", half=True) # FP16model.export(format="tflite", int8=True, data="coco8.yaml") # INT8 (NPU 최대 활용)ONNX → TFLite 변환 경로 (대안):
# 1단계: ONNX 내보내기yolo export model=yolo11n.pt format=onnx
# 2단계: onnx2tf로 변환pip install onnx2tfonnx2tf -i yolo11n.onnx -o yolo11n_tf -oiqt# 출력: float32, float16, integer_quant .tflite 3종4.2 Kotlin + TFLite 통합
섹션 제목: “4.2 Kotlin + TFLite 통합”dependencies { implementation("org.tensorflow:tensorflow-lite:2.14.0") implementation("org.tensorflow:tensorflow-lite-gpu:2.14.0") implementation("org.tensorflow:tensorflow-lite-support:0.4.4")}import org.tensorflow.lite.Interpreterimport org.tensorflow.lite.gpu.GpuDelegate
class PlantDiseaseDetector(context: Context) { private lateinit var interpreter: Interpreter private val inputSize = 640
init { val modelBytes = context.assets.open("yolo11n_float16.tflite").readBytes() val buffer = java.nio.ByteBuffer.wrap(modelBytes)
val options = Interpreter.Options().apply { addDelegate(GpuDelegate()) // GPU delegate setUseNNAPI(true) // NPU (Hexagon/APU) 활용 setNumThreads(4) } interpreter = Interpreter(buffer, options) }
fun analyze(bitmap: Bitmap): List<Detection> { val resized = Bitmap.createScaledBitmap(bitmap, inputSize, inputSize, true) val inputBuffer = preprocessBitmap(resized)
// YOLO11n 출력: 1 x 84 x 8400 (4 bbox + 80 class) // 파인튜닝 모델은 클래스 수에 맞게 조정 val outputBuffer = Array(1) { Array(84) { FloatArray(8400) } }
interpreter.run(inputBuffer, outputBuffer) return postprocess(outputBuffer[0]) }
private fun preprocessBitmap(bitmap: Bitmap): java.nio.ByteBuffer { val buffer = java.nio.ByteBuffer.allocateDirect(4 * 3 * inputSize * inputSize) buffer.order(java.nio.ByteOrder.nativeOrder()) val pixels = IntArray(inputSize * inputSize) bitmap.getPixels(pixels, 0, inputSize, 0, 0, inputSize, inputSize) for (pixel in pixels) { buffer.putFloat(((pixel shr 16) and 0xFF) / 255.0f) buffer.putFloat(((pixel shr 8) and 0xFF) / 255.0f) buffer.putFloat((pixel and 0xFF) / 255.0f) } return buffer }
data class Detection(val label: String, val confidence: Float, val box: android.graphics.RectF) private fun postprocess(output: Array<FloatArray>): List<Detection> = emptyList()}주의: TFLite GPU delegate가 일부 YOLO 연산(SILU 등)을 미지원할 경우 CPU fallback 자동 처리. NNAPI(Android 8.1+)로 NPU 직접 활용 권장.
4.3 ONNX Runtime 대안 (Android)
섹션 제목: “4.3 ONNX Runtime 대안 (Android)”implementation("com.microsoft.onnxruntime:onnxruntime-android:1.18.0")import ai.onnxruntime.*
class YOLOOnnxDetector(context: Context) { private val env = OrtEnvironment.getEnvironment() private val session: OrtSession
init { val modelBytes = context.assets.open("yolo11n.onnx").readBytes() val options = OrtSession.SessionOptions().apply { addNnapi() // NPU 가속 } session = env.createSession(modelBytes, options) }
fun detect(bitmap: Bitmap): OrtSession.Result { val inputArray = preprocessToFloatArray(bitmap) val tensor = OnnxTensor.createTensor(env, inputArray, longArrayOf(1, 3, 640, 640)) return session.run(mapOf("images" to tensor)) }
private fun preprocessToFloatArray(bitmap: Bitmap): Array<Array<Array<FloatArray>>> { // 640x640 리사이즈 + 정규화 구현 return Array(1) { Array(3) { Array(640) { FloatArray(640) } } } }}4.4 Android 기기별 벤치마크
섹션 제목: “4.4 Android 기기별 벤치마크”| SoC | 기기 예시 | 프레임워크 | 양자화 | 추론속도 |
|---|---|---|---|---|
| Snapdragon 8 Gen 2 | Galaxy S23 | TFLite + NPU | INT8 | ~5-8 ms (125-200 FPS) |
| Snapdragon 8 Gen 2 | Galaxy S23 | TFLite + CPU | FP32 | ~120 ms (8 FPS) |
| Snapdragon 888 | Galaxy S21 | TFLite + NPU | INT8 | ~12-18 ms (55-83 FPS) |
| Snapdragon 778G | Galaxy A52s | TFLite + CPU | FP16 | ~60-80 ms (12-17 FPS) |
| Dimensity 9000 | OPPO Find X5 | TFLite + APU | INT8 | ~15-25 ms (40-67 FPS) |
출처: Samsung Galaxy Tab S9 (Snapdragon 8 Gen 2) 실측. NPU = CPU 대비 최대 298배 가속.
5. 식물 병 진단 특화 전략 — 분류 vs 검출 선택 가이드
섹션 제목: “5. 식물 병 진단 특화 전략 — 분류 vs 검출 선택 가이드”판단 기준
섹션 제목: “판단 기준”"이 잎이 어떤 병인가?" → YOLO Classification (yolo11n-cls)"이 잎의 어느 부분에 병이 있나?" → YOLO Detection (yolo11n)상세 비교
섹션 제목: “상세 비교”| 항목 | YOLO Classify (yolo11n-cls) | YOLO Detect (yolo11n) |
|---|---|---|
| 목적 | 잎 전체 병명 분류 | 병변 위치 + 병명 검출 |
| 출력 | 클래스별 확률 | 바운딩박스 + 클래스 + 신뢰도 |
| 학습 데이터 | PlantVillage (이미지+레이블) | PlantDoc (바운딩박스 필요) |
| 모델 크기 | 6 MB (YOLO11n-cls) | ~12 MB (YOLO11n) |
| 어노테이션 | 간단 | 바운딩박스 필요 (복잡) |
| UX | ”병명: 흰가루병 94%“ | 병변 부위 시각적 하이라이트 |
권장 로드맵
섹션 제목: “권장 로드맵”Phase 1 (MVP): YOLO11n-cls Classification
- PlantVillage 54,000장 이미지 바로 활용
- 바운딩박스 어노테이션 불필요
- 관엽식물 병 분류에 충분
Phase 2 (고도화): YOLO11n Detection
- 병변 위치를 앱 UI에 표시 (차별화)
- 한 잎에 복수 병 동시 감지
- PlantDoc 또는 커스텀 어노테이션 필요
데이터셋 비교
섹션 제목: “데이터셋 비교”| 항목 | PlantVillage | PlantDoc |
|---|---|---|
| 이미지 수 | 54,000+ | 2,569 |
| 식물 종 | 14종 | 13종 |
| 클래스 수 | 38 | 30 |
| 환경 | 실험실 (배경 제거) | 현실 환경 |
| 어노테이션 | 분류 레이블 | 바운딩박스 |
| 라이선스 | CC BY-SA 4.0 | CC BY 4.0 |
| Roboflow | 이용 가능 | YOLO 형식 다운로드 |
6. 현실적 권장사항
섹션 제목: “6. 현실적 권장사항”6.1 모델 선택
섹션 제목: “6.1 모델 선택”| 상황 | 권장 모델 | 이유 |
|---|---|---|
| MVP, 빠른 검증 | yolo11n-cls | 6 MB, 파인튜닝 용이 |
| 정확도-속도 균형 | yolo11s-cls | 13 MB, 75.4% 정확도 |
| 병변 위치 표시 | yolo11n (detect) | 실시간 가능, ~12 MB |
| 최고 정확도 | yolo11s (detect) | 47.0 mAP, 21.5 MB |
6.2 파인튜닝 코드
섹션 제목: “6.2 파인튜닝 코드”PlantVillage로 분류 모델 학습:
from ultralytics import YOLO
model = YOLO("yolo11n-cls.pt")
model.train( data="plantvillage/", # train/, val/ 서브디렉토리 epochs=50, imgsz=224, batch=32, lr0=0.001, freeze=10, # 초반 레이어 동결 (전이학습) device="mps" # Apple Silicon Mac)
# iOS 내보내기model = YOLO("runs/classify/train/weights/best.pt")model.export(format="coreml", half=True)
# Android 내보내기model.export(format="tflite", half=True)PlantDoc으로 검출 모델 학습:
model = YOLO("yolo11n.pt")model.train( data="plantdoc.yaml", epochs=100, imgsz=640, batch=16, patience=20, device="mps")PlantDoc Roboflow 다운로드:
from roboflow import Roboflowrf = Roboflow(api_key="YOUR_API_KEY")project = rf.workspace("joseph-nelson").project("plantdoc")dataset = project.version(1).download("yolov8")6.3 Flutter 크로스플랫폼 통합
섹션 제목: “6.3 Flutter 크로스플랫폼 통합”dependencies: ultralytics_yolo: ^0.3.4import 'package:ultralytics_yolo/ultralytics_yolo.dart';
class PlantDiagnosisScreen extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( body: YOLOView( // iOS: .mlpackage 자동 사용 // Android: .tflite 자동 사용 modelPath: 'assets/models/plant_yolo11n', task: YOLOTask.detect, // 또는 YOLOTask.classify onResult: (List<YOLOResult> results) { for (final result in results) { print('병명: ${result.className}'); print('신뢰도: ${result.confidence}'); print('위치: ${result.boundingBox}'); } }, confidenceThreshold: 0.5, iouThreshold: 0.45, ), ); }}Flutter 플러그인이 iOS =
.mlpackage, Android =.tflite를 자동 선택.
assets/models/에 두 파일 모두 배치하면 됨.
6.4 최소 지원 기기
섹션 제목: “6.4 최소 지원 기기”iOS:
- 최소: iPhone 12 (A14) — YOLO11n-cls 40+ FPS
- 권장: iPhone 14 (A15) — YOLO11n detect 실시간
- 최적: iPhone 15 Pro 이상 (A17 Pro)
Android:
- 최소: Snapdragon 778G (2021년 이후 중급) — FP16 CPU 15-20 FPS
- 권장: Snapdragon 888 이상 — NPU 60+ FPS
- 최적: Snapdragon 8 Gen 2 이상 — NPU INT8 100+ FPS
6.5 MobileNet vs YOLO 비교
섹션 제목: “6.5 MobileNet vs YOLO 비교”| 항목 | MobileNetV3 | YOLO11n-cls | YOLO11n detect |
|---|---|---|---|
| 크기 | 3-5 MB | 6 MB | ~12 MB |
| 병변 위치 | 불가 | 불가 | 가능 |
| 공식 Flutter SDK | 없음 | 있음 | 있음 |
| 상업 라이선스 | Apache 2.0 | AGPL-3.0* | AGPL-3.0* |
*상업적 앱 배포 시 Ultralytics Enterprise 라이선스 구매 필요.
7. 알려진 이슈
섹션 제목: “7. 알려진 이슈”| 이슈 | 플랫폼 | 대응 |
|---|---|---|
| TFLite GPU delegate SILU 미지원 | Android | NNAPI 또는 CPU fallback 자동 처리 |
| INT8 mAP ~6.5p 하락 | Android | Dynamic quantization 사용 (0.1p 하락) |
| INT8 CoreML 캘리브레이션 필요 | iOS | data 파라미터로 데이터셋 지정 |
| 상업 배포 라이선스 | 공통 | Enterprise 라이선스 구매 |
.mlmodel 구형 → .mlpackage | iOS | coremltools 7.x 이상 사용 |
8. 출처
섹션 제목: “8. 출처”- Ultralytics YOLO11 공식 문서
- CoreML Export 가이드
- TFLite Export 가이드
- YOLO Export 모드 전체 옵션
- Ultralytics YOLO iOS App (GitHub)
- ultralytics_yolo Flutter 패키지
- Hardware optimization on Android for AI inference (arXiv 2511.13453)
- Best iOS Object Detection Models — Roboflow
- PlantDoc Dataset — Roboflow
- Android YOLOv8 ONNX Runtime — WaynesTalk
- ONNX Runtime Mobile iOS Deployment
- Leveraging YOLO for plant disease — Scientific Reports 2025
- Advancing crop health with YOLOv11 classification — Springer 2025