diff --git a/README.md b/README.md
index 8ed70ce..46ef444 100644
--- a/README.md
+++ b/README.md
@@ -4,7 +4,7 @@
[![Ultralytics Actions](https://github.com/ultralytics/yolo-ios-app/actions/workflows/format.yml/badge.svg)](https://github.com/ultralytics/yolo-ios-app/actions/workflows/format.yml)
-Welcome to the [Ultralytics YOLO iOS App](https://apps.apple.com/us/app/idetection/id1452689527) GitHub repository! 📖 Leveraging Ultralytics' advanced [YOLOv8 object detection models](https://github.com/ultralytics/ultralytics), this app transforms your iOS device into an intelligent detection tool. Explore our guide to get started with the Ultralytics YOLO iOS App and discover the world in a new and exciting way.
+Welcome to the [Ultralytics YOLO iOS App](https://apps.apple.com/us/app/idetection/id1452689527) GitHub repository! 📖 Leveraging Ultralytics' advanced [YOLO11 object detection models](https://github.com/ultralytics/ultralytics), this app transforms your iOS device into an intelligent detection tool. Explore our guide to get started with the Ultralytics YOLO iOS App and discover the world in a new and exciting way.
@@ -60,17 +60,17 @@ Ensure you have the following before you start:
In Xcode, go to the project's target settings and choose your Apple Developer account under the "Signing & Capabilities" tab.
-3. **Add YOLOv8 Models to the Project:**
+3. **Add YOLO11 Models to the Project:**
- Export CoreML INT8 models using the `ultralytics` Python package (with `pip install ultralytics`), or download them from our [GitHub release assets](https://github.com/ultralytics/yolo-ios-app/releases). You should have 5 YOLOv8 models in total. Place these in the `YOLO/Models` directory as seen in the Xcode screenshot below.
+ Export CoreML INT8 models using the `ultralytics` Python package (with `pip install ultralytics`), or download them from our [GitHub release assets](https://github.com/ultralytics/yolo-ios-app/releases). You should have 5 YOLO11 models in total. Place these in the `YOLO/Models` directory as seen in the Xcode screenshot below.
```python
from ultralytics import YOLO
- # Loop through all YOLOv8 model sizes
+ # Loop through all YOLO11 model sizes
for size in ("n", "s", "m", "l", "x"):
- # Load a YOLOv8 PyTorch model
- model = YOLO(f"yolov8{size}.pt")
+ # Load a YOLO11 PyTorch model
+ model = YOLO(f"yolo11{size}.pt")
# Export the PyTorch model to CoreML INT8 format with NMS layers
model.export(format="coreml", int8=True, nms=True, imgsz=[640, 384])
@@ -89,7 +89,7 @@ Ensure you have the following before you start:
The Ultralytics YOLO iOS App is designed to be intuitive:
- **Real-Time Detection:** Launch the app and aim your camera at objects to detect them instantly.
-- **Multiple AI Models:** Select from a range of Ultralytics YOLOv8 models, from YOLOv8n 'nano' to YOLOv8x 'x-large'.
+- **Multiple AI Models:** Select from a range of Ultralytics YOLO11 models, from YOLO11n 'nano' to YOLO11x 'x-large'.
## 💡 Contribute
diff --git a/YOLO.xcodeproj/project.pbxproj b/YOLO.xcodeproj/project.pbxproj
index 68916e9..7ee32c7 100644
--- a/YOLO.xcodeproj/project.pbxproj
+++ b/YOLO.xcodeproj/project.pbxproj
@@ -13,16 +13,17 @@
636EFCAF21E62DD300DE43BC /* VideoCapture.swift in Sources */ = {isa = PBXBuildFile; fileRef = 636EFCA221E62DD300DE43BC /* VideoCapture.swift */; };
636EFCB321E62DD300DE43BC /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 636EFCA721E62DD300DE43BC /* AppDelegate.swift */; };
636EFCB921E62E3900DE43BC /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 636EFCB821E62E3900DE43BC /* Assets.xcassets */; };
- 6381D2182B7817C200ABA4E8 /* yolov8l.mlpackage in Sources */ = {isa = PBXBuildFile; fileRef = 6381D2132B7817C200ABA4E8 /* yolov8l.mlpackage */; };
- 6381D2192B7817C200ABA4E8 /* yolov8x.mlpackage in Sources */ = {isa = PBXBuildFile; fileRef = 6381D2142B7817C200ABA4E8 /* yolov8x.mlpackage */; };
- 6381D21A2B7817C200ABA4E8 /* yolov8s.mlpackage in Sources */ = {isa = PBXBuildFile; fileRef = 6381D2152B7817C200ABA4E8 /* yolov8s.mlpackage */; };
- 6381D21B2B7817C200ABA4E8 /* yolov8m.mlpackage in Sources */ = {isa = PBXBuildFile; fileRef = 6381D2162B7817C200ABA4E8 /* yolov8m.mlpackage */; };
- 6381D21C2B7817C200ABA4E8 /* yolov8n.mlpackage in Sources */ = {isa = PBXBuildFile; fileRef = 6381D2172B7817C200ABA4E8 /* yolov8n.mlpackage */; };
63CF371F2514455300E2DEA1 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 6323C44D22186177008AE681 /* LaunchScreen.storyboard */; };
63CF37202514455300E2DEA1 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 6323C44F22186177008AE681 /* Main.storyboard */; };
63CF37212514455300E2DEA1 /* ultralytics_yolo_logotype.png in Resources */ = {isa = PBXBuildFile; fileRef = 6323C45122186177008AE681 /* ultralytics_yolo_logotype.png */; };
+ 73E3C7FE2CE3AF1A00E2D85C /* yolo11m.mlpackage in Sources */ = {isa = PBXBuildFile; fileRef = 73E3C7FA2CE3AF1A00E2D85C /* yolo11m.mlpackage */; };
+ 73E3C7FF2CE3AF1A00E2D85C /* yolo11l.mlpackage in Sources */ = {isa = PBXBuildFile; fileRef = 73E3C7F92CE3AF1A00E2D85C /* yolo11l.mlpackage */; };
+ 73E3C8002CE3AF1A00E2D85C /* yolo11x.mlpackage in Sources */ = {isa = PBXBuildFile; fileRef = 73E3C7FD2CE3AF1A00E2D85C /* yolo11x.mlpackage */; };
+ 73E3C8012CE3AF1A00E2D85C /* yolo11s.mlpackage in Sources */ = {isa = PBXBuildFile; fileRef = 73E3C7FC2CE3AF1A00E2D85C /* yolo11s.mlpackage */; };
+ 73E3C8022CE3AF1A00E2D85C /* yolo11n.mlpackage in Sources */ = {isa = PBXBuildFile; fileRef = 73E3C7FB2CE3AF1A00E2D85C /* yolo11n.mlpackage */; };
8EDAA33950796844333D60A7 /* BoundingBoxView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8EDAA633C1F2B50286D16008 /* BoundingBoxView.swift */; };
/* End PBXBuildFile section */
+
/* Begin PBXFileReference section */
6323C44D22186177008AE681 /* LaunchScreen.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = LaunchScreen.storyboard; sourceTree = "
"; };
6323C44F22186177008AE681 /* Main.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = Main.storyboard; sourceTree = ""; };
@@ -34,12 +35,12 @@
636EFCA221E62DD300DE43BC /* VideoCapture.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VideoCapture.swift; sourceTree = ""; };
636EFCA721E62DD300DE43BC /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
636EFCB821E62E3900DE43BC /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
- 6381D2132B7817C200ABA4E8 /* yolov8l.mlpackage */ = {isa = PBXFileReference; lastKnownFileType = folder.mlpackage; path = yolov8l.mlpackage; sourceTree = ""; };
- 6381D2142B7817C200ABA4E8 /* yolov8x.mlpackage */ = {isa = PBXFileReference; lastKnownFileType = folder.mlpackage; path = yolov8x.mlpackage; sourceTree = ""; };
- 6381D2152B7817C200ABA4E8 /* yolov8s.mlpackage */ = {isa = PBXFileReference; lastKnownFileType = folder.mlpackage; path = yolov8s.mlpackage; sourceTree = ""; };
- 6381D2162B7817C200ABA4E8 /* yolov8m.mlpackage */ = {isa = PBXFileReference; lastKnownFileType = folder.mlpackage; path = yolov8m.mlpackage; sourceTree = ""; };
- 6381D2172B7817C200ABA4E8 /* yolov8n.mlpackage */ = {isa = PBXFileReference; lastKnownFileType = folder.mlpackage; path = yolov8n.mlpackage; sourceTree = ""; };
63B8B0A821E62A890026FBC3 /* .gitignore */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = .gitignore; sourceTree = ""; };
+ 73E3C7F92CE3AF1A00E2D85C /* yolo11l.mlpackage */ = {isa = PBXFileReference; lastKnownFileType = folder.mlpackage; path = yolo11l.mlpackage; sourceTree = ""; };
+ 73E3C7FA2CE3AF1A00E2D85C /* yolo11m.mlpackage */ = {isa = PBXFileReference; lastKnownFileType = folder.mlpackage; path = yolo11m.mlpackage; sourceTree = ""; };
+ 73E3C7FB2CE3AF1A00E2D85C /* yolo11n.mlpackage */ = {isa = PBXFileReference; lastKnownFileType = folder.mlpackage; path = yolo11n.mlpackage; sourceTree = ""; };
+ 73E3C7FC2CE3AF1A00E2D85C /* yolo11s.mlpackage */ = {isa = PBXFileReference; lastKnownFileType = folder.mlpackage; path = yolo11s.mlpackage; sourceTree = ""; };
+ 73E3C7FD2CE3AF1A00E2D85C /* yolo11x.mlpackage */ = {isa = PBXFileReference; lastKnownFileType = folder.mlpackage; path = yolo11x.mlpackage; sourceTree = ""; };
7BCB411721C3096100BFC4D0 /* YOLO.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = YOLO.app; sourceTree = BUILT_PRODUCTS_DIR; };
8EDAA633C1F2B50286D16008 /* BoundingBoxView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BoundingBoxView.swift; sourceTree = ""; };
8EDAAA4507D2D23D7FAB827F /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; };
@@ -86,11 +87,11 @@
63A946D8271800E20001C3ED /* Models */ = {
isa = PBXGroup;
children = (
- 6381D2132B7817C200ABA4E8 /* yolov8l.mlpackage */,
- 6381D2162B7817C200ABA4E8 /* yolov8m.mlpackage */,
- 6381D2172B7817C200ABA4E8 /* yolov8n.mlpackage */,
- 6381D2152B7817C200ABA4E8 /* yolov8s.mlpackage */,
- 6381D2142B7817C200ABA4E8 /* yolov8x.mlpackage */,
+ 73E3C7F92CE3AF1A00E2D85C /* yolo11l.mlpackage */,
+ 73E3C7FA2CE3AF1A00E2D85C /* yolo11m.mlpackage */,
+ 73E3C7FB2CE3AF1A00E2D85C /* yolo11n.mlpackage */,
+ 73E3C7FC2CE3AF1A00E2D85C /* yolo11s.mlpackage */,
+ 73E3C7FD2CE3AF1A00E2D85C /* yolo11x.mlpackage */,
);
path = Models;
sourceTree = "";
@@ -209,14 +210,14 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
- 6381D21B2B7817C200ABA4E8 /* yolov8m.mlpackage in Sources */,
- 6381D21C2B7817C200ABA4E8 /* yolov8n.mlpackage in Sources */,
636EFCAF21E62DD300DE43BC /* VideoCapture.swift in Sources */,
636166EA251443B20054FA7E /* ThresholdProvider.swift in Sources */,
- 6381D2182B7817C200ABA4E8 /* yolov8l.mlpackage in Sources */,
- 6381D21A2B7817C200ABA4E8 /* yolov8s.mlpackage in Sources */,
- 6381D2192B7817C200ABA4E8 /* yolov8x.mlpackage in Sources */,
636EFCB321E62DD300DE43BC /* AppDelegate.swift in Sources */,
+ 73E3C7FE2CE3AF1A00E2D85C /* yolo11m.mlpackage in Sources */,
+ 73E3C7FF2CE3AF1A00E2D85C /* yolo11l.mlpackage in Sources */,
+ 73E3C8002CE3AF1A00E2D85C /* yolo11x.mlpackage in Sources */,
+ 73E3C8012CE3AF1A00E2D85C /* yolo11s.mlpackage in Sources */,
+ 73E3C8022CE3AF1A00E2D85C /* yolo11n.mlpackage in Sources */,
636EFCAA21E62DD300DE43BC /* ViewController.swift in Sources */,
8EDAA33950796844333D60A7 /* BoundingBoxView.swift in Sources */,
);
@@ -354,12 +355,12 @@
INFOPLIST_FILE = YOLO/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = "Ultralytics YOLO";
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.developer-tools";
- IPHONEOS_DEPLOYMENT_TARGET = 14.0;
+ IPHONEOS_DEPLOYMENT_TARGET = 16.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
- MARKETING_VERSION = 8.2.0;
+ MARKETING_VERSION = 8.3.0;
PRODUCT_BUNDLE_IDENTIFIER = com.ultralytics.iDetection;
PRODUCT_NAME = "$(TARGET_NAME)";
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
@@ -382,12 +383,12 @@
INFOPLIST_FILE = YOLO/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = "Ultralytics YOLO";
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.developer-tools";
- IPHONEOS_DEPLOYMENT_TARGET = 14.0;
+ IPHONEOS_DEPLOYMENT_TARGET = 16.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
- MARKETING_VERSION = 8.2.0;
+ MARKETING_VERSION = 8.3.0;
PRODUCT_BUNDLE_IDENTIFIER = com.ultralytics.iDetection;
PRODUCT_NAME = "$(TARGET_NAME)";
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
diff --git a/YOLO/Info.plist b/YOLO/Info.plist
index a7022ec..e41ead4 100644
--- a/YOLO/Info.plist
+++ b/YOLO/Info.plist
@@ -21,7 +21,7 @@
CFBundleShortVersionString
$(MARKETING_VERSION)
CFBundleVersion
- 25
+ 55
ITSAppUsesNonExemptEncryption
LSRequiresIPhoneOS
diff --git a/YOLO/Main.storyboard b/YOLO/Main.storyboard
index 549bc72..b558fc8 100644
--- a/YOLO/Main.storyboard
+++ b/YOLO/Main.storyboard
@@ -1,9 +1,9 @@
-
+
-
+
@@ -41,11 +41,11 @@
-
-
-
-
-
+
+
+
+
+
@@ -139,7 +139,7 @@
-
+
@@ -331,10 +331,10 @@
-
+
-
+
diff --git a/YOLO/ViewController.swift b/YOLO/ViewController.swift
index 840d91d..25f352d 100644
--- a/YOLO/ViewController.swift
+++ b/YOLO/ViewController.swift
@@ -1,7 +1,7 @@
// Ultralytics YOLO 🚀 - AGPL-3.0 License
//
// Main View Controller for Ultralytics YOLO App
-// This file is part of the Ultralytics YOLO app, enabling real-time object detection using YOLOv8 models on iOS devices.
+// This file is part of the Ultralytics YOLO app, enabling real-time object detection using YOLO11 models on iOS devices.
// Licensed under AGPL-3.0. For commercial use, refer to Ultralytics licensing: https://ultralytics.com/license
// Access the source code: https://github.com/ultralytics/yolo-ios-app
//
@@ -17,7 +17,7 @@ import CoreMedia
import UIKit
import Vision
-var mlModel = try! yolov8m(configuration: .init()).model
+var mlModel = try! yolo11m(configuration: .init()).model
class ViewController: UIViewController {
@IBOutlet var videoPreview: UIView!
@@ -55,6 +55,9 @@ class ViewController: UIViewController {
var t3 = CACurrentMediaTime() // FPS start
var t4 = 0.0 // FPS dt smoothed
// var cameraOutput: AVCapturePhotoOutput!
+ var longSide: CGFloat = 3
+ var shortSide: CGFloat = 4
+ var frameSizeCaptured = false
// Developer mode
let developerMode = UserDefaults.standard.bool(forKey: "developer_mode") // developer mode selected in settings
@@ -127,6 +130,7 @@ class ViewController: UIViewController {
@objc func orientationDidChange() {
videoCapture.updateVideoOrientation()
+ // frameSizeCaptured = false
}
@IBAction func vibrate(_ sender: Any) {
@@ -140,20 +144,20 @@ class ViewController: UIViewController {
/// Switch model
switch segmentedControl.selectedSegmentIndex {
case 0:
- self.labelName.text = "YOLOv8n"
- mlModel = try! yolov8n(configuration: .init()).model
+ self.labelName.text = "YOLO11n"
+ mlModel = try! yolo11n(configuration: .init()).model
case 1:
- self.labelName.text = "YOLOv8s"
- mlModel = try! yolov8s(configuration: .init()).model
+ self.labelName.text = "YOLO11s"
+ mlModel = try! yolo11s(configuration: .init()).model
case 2:
- self.labelName.text = "YOLOv8m"
- mlModel = try! yolov8m(configuration: .init()).model
+ self.labelName.text = "YOLO11m"
+ mlModel = try! yolo11m(configuration: .init()).model
case 3:
- self.labelName.text = "YOLOv8l"
- mlModel = try! yolov8l(configuration: .init()).model
+ self.labelName.text = "YOLO11l"
+ mlModel = try! yolo11l(configuration: .init()).model
case 4:
- self.labelName.text = "YOLOv8x"
- mlModel = try! yolov8x(configuration: .init()).model
+ self.labelName.text = "YOLO11x"
+ mlModel = try! yolo11x(configuration: .init()).model
default:
break
}
@@ -222,7 +226,7 @@ class ViewController: UIViewController {
}
func setLabels() {
- self.labelName.text = "YOLOv8m"
+ self.labelName.text = "YOLO11m"
self.labelVersion.text = "Version " + UserDefaults.standard.string(forKey: "app_version")!
}
@@ -279,6 +283,28 @@ class ViewController: UIViewController {
let maxBoundingBoxViews = 100
var boundingBoxViews = [BoundingBoxView]()
var colors: [String: UIColor] = [:]
+ let ultralyticsColorsolors: [UIColor] = [
+ UIColor(red: 4 / 255, green: 42 / 255, blue: 255 / 255, alpha: 0.6), // #042AFF
+ UIColor(red: 11 / 255, green: 219 / 255, blue: 235 / 255, alpha: 0.6), // #0BDBEB
+ UIColor(red: 243 / 255, green: 243 / 255, blue: 243 / 255, alpha: 0.6), // #F3F3F3
+ UIColor(red: 0 / 255, green: 223 / 255, blue: 183 / 255, alpha: 0.6), // #00DFB7
+ UIColor(red: 17 / 255, green: 31 / 255, blue: 104 / 255, alpha: 0.6), // #111F68
+ UIColor(red: 255 / 255, green: 111 / 255, blue: 221 / 255, alpha: 0.6), // #FF6FDD
+ UIColor(red: 255 / 255, green: 68 / 255, blue: 79 / 255, alpha: 0.6), // #FF444F
+ UIColor(red: 204 / 255, green: 237 / 255, blue: 0 / 255, alpha: 0.6), // #CCED00
+ UIColor(red: 0 / 255, green: 243 / 255, blue: 68 / 255, alpha: 0.6), // #00F344
+ UIColor(red: 189 / 255, green: 0 / 255, blue: 255 / 255, alpha: 0.6), // #BD00FF
+ UIColor(red: 0 / 255, green: 180 / 255, blue: 255 / 255, alpha: 0.6), // #00B4FF
+ UIColor(red: 221 / 255, green: 0 / 255, blue: 186 / 255, alpha: 0.6), // #DD00BA
+ UIColor(red: 0 / 255, green: 255 / 255, blue: 255 / 255, alpha: 0.6), // #00FFFF
+ UIColor(red: 38 / 255, green: 192 / 255, blue: 0 / 255, alpha: 0.6), // #26C000
+ UIColor(red: 1 / 255, green: 255 / 255, blue: 179 / 255, alpha: 0.6), // #01FFB3
+ UIColor(red: 125 / 255, green: 36 / 255, blue: 255 / 255, alpha: 0.6), // #7D24FF
+ UIColor(red: 123 / 255, green: 0 / 255, blue: 104 / 255, alpha: 0.6), // #7B0068
+ UIColor(red: 255 / 255, green: 27 / 255, blue: 108 / 255, alpha: 0.6), // #FF1B6C
+ UIColor(red: 252 / 255, green: 109 / 255, blue: 47 / 255, alpha: 0.6), // #FC6D2F
+ UIColor(red: 162 / 255, green: 255 / 255, blue: 11 / 255, alpha: 0.6), // #A2FF0B
+ ]
func setUpBoundingBoxViews() {
// Ensure all bounding box views are initialized up to the maximum allowed.
@@ -292,14 +318,15 @@ class ViewController: UIViewController {
}
// Assign random colors to the classes.
+ var count = 0
for label in classLabels {
- if colors[label] == nil { // if key not in dict
- colors[label] = UIColor(
- red: CGFloat.random(in: 0...1),
- green: CGFloat.random(in: 0...1),
- blue: CGFloat.random(in: 0...1),
- alpha: 0.6)
+ let color = ultralyticsColorsolors[count]
+ count += 1
+ if count > 19 {
+ count = 0
}
+ colors[label] = color
+
}
}
@@ -330,7 +357,13 @@ class ViewController: UIViewController {
func predict(sampleBuffer: CMSampleBuffer) {
if currentBuffer == nil, let pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) {
currentBuffer = pixelBuffer
-
+ if !frameSizeCaptured {
+ let frameWidth = CGFloat(CVPixelBufferGetWidth(pixelBuffer))
+ let frameHeight = CGFloat(CVPixelBufferGetHeight(pixelBuffer))
+ longSide = max(frameWidth, frameHeight)
+ shortSide = min(frameWidth, frameHeight)
+ frameSizeCaptured = true
+ }
/// - Tag: MappingOrientation
// The frame is always oriented based on the camera sensor,
// so in most cases Vision needs to rotate it for the model to work as expected.
@@ -449,18 +482,7 @@ class ViewController: UIViewController {
}
func show(predictions: [VNRecognizedObjectObservation]) {
- let width = videoPreview.bounds.width // 375 pix
- let height = videoPreview.bounds.height // 812 pix
var str = ""
-
- // ratio = videoPreview AR divided by sessionPreset AR
- var ratio: CGFloat = 1.0
- if videoCapture.captureSession.sessionPreset == .photo {
- ratio = (height / width) / (4.0 / 3.0) // .photo
- } else {
- ratio = (height / width) / (16.0 / 9.0) // .hd4K3840x2160, .hd1920x1080, .hd1280x720 etc.
- }
-
// date
let date = Date()
let calendar = Calendar.current
@@ -473,89 +495,139 @@ class ViewController: UIViewController {
self.labelSlider.text =
String(predictions.count) + " items (max " + String(Int(slider.value)) + ")"
- for i in 0..= 1 { // iPhone ratio = 1.218
- let offset = (1 - ratio) * (0.5 - rect.minX)
- let transform = CGAffineTransform(scaleX: 1, y: -1).translatedBy(x: offset, y: -1)
- rect = rect.applying(transform)
- rect.size.width *= ratio
- } else { // iPad ratio = 0.75
- let offset = (ratio - 1) * (0.5 - rect.maxY)
- let transform = CGAffineTransform(scaleX: 1, y: -1).translatedBy(x: 0, y: offset - 1)
- rect = rect.applying(transform)
- ratio = (height / width) / (3.0 / 4.0)
- rect.size.height /= ratio
- }
+ if UIDevice.current.orientation == .portrait {
- // Scale normalized to pixels [375, 812] [width, height]
- rect = VNImageRectForNormalizedRect(rect, Int(width), Int(height))
-
- // The labels array is a list of VNClassificationObservation objects,
- // with the highest scoring class first in the list.
- let bestClass = prediction.labels[0].identifier
- let confidence = prediction.labels[0].confidence
- // print(confidence, rect) // debug (confidence, xywh) with xywh origin top left (pixels)
- let label = String(format: "%@ %.1f", bestClass, confidence * 100)
- let alpha = CGFloat((confidence - 0.2) / (1.0 - 0.2) * 0.9)
- // Show the bounding box.
- boundingBoxViews[i].show(
- frame: rect,
- label: label,
- color: colors[bestClass] ?? UIColor.white,
- alpha: alpha) // alpha 0 (transparent) to 1 (opaque) for conf threshold 0.2 to 1.0)
-
- if developerMode {
- // Write
- if save_detections {
- str += String(
- format: "%.3f %.3f %.3f %@ %.2f %.1f %.1f %.1f %.1f\n",
- sec_day, freeSpace(), UIDevice.current.batteryLevel, bestClass, confidence,
- rect.origin.x, rect.origin.y, rect.size.width, rect.size.height)
+ // ratio = videoPreview AR divided by sessionPreset AR
+ var ratio: CGFloat = 1.0
+ if videoCapture.captureSession.sessionPreset == .photo {
+ ratio = (height / width) / (4.0 / 3.0) // .photo
+ } else {
+ ratio = (height / width) / (16.0 / 9.0) // .hd4K3840x2160, .hd1920x1080, .hd1280x720 etc.
+ }
+
+ for i in 0..= 1 { // iPhone ratio = 1.218
+ let offset = (1 - ratio) * (0.5 - rect.minX)
+ let transform = CGAffineTransform(scaleX: 1, y: -1).translatedBy(x: offset, y: -1)
+ rect = rect.applying(transform)
+ rect.size.width *= ratio
+ } else { // iPad ratio = 0.75
+ let offset = (ratio - 1) * (0.5 - rect.maxY)
+ let transform = CGAffineTransform(scaleX: 1, y: -1).translatedBy(x: 0, y: offset - 1)
+ rect = rect.applying(transform)
+ ratio = (height / width) / (3.0 / 4.0)
+ rect.size.height /= ratio
+ }
+
+ // Scale normalized to pixels [375, 812] [width, height]
+ rect = VNImageRectForNormalizedRect(rect, Int(width), Int(height))
+
+ // The labels array is a list of VNClassificationObservation objects,
+ // with the highest scoring class first in the list.
+ let bestClass = prediction.labels[0].identifier
+ let confidence = prediction.labels[0].confidence
+ // print(confidence, rect) // debug (confidence, xywh) with xywh origin top left (pixels)
+ let label = String(format: "%@ %.1f", bestClass, confidence * 100)
+ let alpha = CGFloat((confidence - 0.2) / (1.0 - 0.2) * 0.9)
+ // Show the bounding box.
+ boundingBoxViews[i].show(
+ frame: rect,
+ label: label,
+ color: colors[bestClass] ?? UIColor.white,
+ alpha: alpha) // alpha 0 (transparent) to 1 (opaque) for conf threshold 0.2 to 1.0)
+
+ if developerMode {
+ // Write
+ if save_detections {
+ str += String(
+ format: "%.3f %.3f %.3f %@ %.2f %.1f %.1f %.1f %.1f\n",
+ sec_day, freeSpace(), UIDevice.current.batteryLevel, bestClass, confidence,
+ rect.origin.x, rect.origin.y, rect.size.width, rect.size.height)
+ }
+ }
+ } else {
+ boundingBoxViews[i].hide()
}
+ }
+ } else {
+ let frameAspectRatio = longSide / shortSide
+ let viewAspectRatio = width / height
+ var scaleX: CGFloat = 1.0
+ var scaleY: CGFloat = 1.0
+ var offsetX: CGFloat = 0.0
+ var offsetY: CGFloat = 0.0
+
+ if frameAspectRatio > viewAspectRatio {
+ scaleY = height / shortSide
+ scaleX = scaleY
+ offsetX = (longSide * scaleX - width) / 2
} else {
- boundingBoxViews[i].hide()
+ scaleX = width / longSide
+ scaleY = scaleX
+ offsetY = (shortSide * scaleY - height) / 2
}
- }
+ for i in 0..