1212// VNClassificationObservation result types. The implementation extracts both the top prediction
1313// and the top 5 predictions with their confidence scores, enabling rich user feedback.
1414
15+ import Accelerate
1516import Foundation
1617import UIKit
1718import Vision
1819
1920/// Specialized predictor for YOLO classification models that identify the subject of an image.
2021public class Classifier : BasePredictor , @unchecked Sendable {
2122
22- override func setConfidenceThreshold( confidence: Double ) {
23- confidenceThreshold = confidence
24- detector? . featureProvider = ThresholdProvider (
25- iouThreshold: iouThreshold, confidenceThreshold: confidenceThreshold)
26- }
27-
28- override func setIouThreshold( iou: Double ) {
29- iouThreshold = iou
30- detector? . featureProvider = ThresholdProvider (
31- iouThreshold: iouThreshold, confidenceThreshold: confidenceThreshold)
32- }
33-
3423 override func processObservations( for request: VNRequest , error: Error ? ) {
3524 let imageWidth = inputSize. width
3625 let imageHeight = inputSize. height
@@ -43,22 +32,29 @@ public class Classifier: BasePredictor, @unchecked Sendable {
4332 let multiArray = observation. first? . featureValue. multiArrayValue
4433
4534 if let multiArray = multiArray {
46- // Initialize an array to store the classes
47- var valuesArray = [ Double ] ( )
35+ // Apply softmax to convert raw logits to probabilities
36+ var floatValues = [ Float ] ( repeating : 0 , count : multiArray . count )
4837 for i in 0 ..< multiArray. count {
49- let value = multiArray [ i] . doubleValue
50- valuesArray. append ( value)
38+ floatValues [ i] = multiArray [ i] . floatValue
39+ }
40+ var softmaxOutput = [ Float] ( repeating: 0 , count: floatValues. count)
41+ var count = Int32 ( floatValues. count)
42+ vvexpf ( & softmaxOutput, floatValues, & count)
43+ var sumExp : Float = 0
44+ vDSP_sve ( softmaxOutput, 1 , & sumExp, vDSP_Length ( floatValues. count) )
45+ if sumExp > 0 {
46+ vDSP_vsdiv ( softmaxOutput, 1 , & sumExp, & softmaxOutput, 1 , vDSP_Length ( floatValues. count) )
5147 }
5248
5349 var indexedMap = [ Int: Double] ( )
54- for (index, value) in valuesArray . enumerated ( ) {
55- indexedMap [ index] = value
50+ for (index, value) in softmaxOutput . enumerated ( ) {
51+ indexedMap [ index] = Double ( value)
5652 }
5753
5854 let sortedMap = indexedMap. sorted { $0. value > $1. value }
5955
6056 // top1
61- if let ( topIndex, topScore) = sortedMap. first {
57+ if let ( topIndex, topScore) = sortedMap. first, topIndex < labels . count {
6258 let top1Label = labels [ topIndex]
6359 let top1Conf = Float ( topScore)
6460 probs. top1 = top1Label
@@ -70,7 +66,7 @@ public class Classifier: BasePredictor, @unchecked Sendable {
7066 var top5Labels : [ String ] = [ ]
7167 var top5Confs : [ Float ] = [ ]
7268
73- for (index, value) in topObservations {
69+ for (index, value) in topObservations where index < labels . count {
7470 top5Labels. append ( labels [ index] )
7571 top5Confs. append ( Float ( value) )
7672 }
@@ -92,7 +88,7 @@ public class Classifier: BasePredictor, @unchecked Sendable {
9288 top1 = topObservation. identifier
9389 top1Conf = Float ( topObservation. confidence)
9490 }
95- for i in 0 ... candidateNumber - 1 {
91+ for i in 0 ..< candidateNumber {
9692 let observation = observations [ i]
9793 let label = observation. identifier
9894 let confidence : Float = Float ( observation. confidence)
@@ -110,7 +106,6 @@ public class Classifier: BasePredictor, @unchecked Sendable {
110106 self . t3 = CACurrentMediaTime ( )
111107
112108 self . currentOnInferenceTimeListener? . on ( inferenceTime: self . t2 * 1000 , fpsRate: 1 / self . t4) // t2 seconds to ms
113- // self.currentOnFpsRateListener?.on(fpsRate: 1 / self.t4)
114109 let result = YOLOResult (
115110 orig_shape: inputSize, boxes: [ ] , probs: probs, speed: self . t2, fps: 1 / self . t4,
116111 names: labels)
@@ -133,28 +128,33 @@ public class Classifier: BasePredictor, @unchecked Sendable {
133128 do {
134129 try requestHandler. perform ( [ request] )
135130 if let observation = request. results as? [ VNCoreMLFeatureValueObservation ] {
136- _ = [ [ String: Any] ] ( )
137-
138131 // Get the MLMultiArray from the observation
139132 let multiArray = observation. first? . featureValue. multiArrayValue
140133
141134 if let multiArray = multiArray {
142- // Initialize an array to store the classes
143- var valuesArray = [ Double ] ( )
135+ // Apply softmax to convert raw logits to probabilities
136+ var floatValues = [ Float ] ( repeating : 0 , count : multiArray . count )
144137 for i in 0 ..< multiArray. count {
145- let value = multiArray [ i] . doubleValue
146- valuesArray. append ( value)
138+ floatValues [ i] = multiArray [ i] . floatValue
139+ }
140+ var softmaxOutput = [ Float] ( repeating: 0 , count: floatValues. count)
141+ var count = Int32 ( floatValues. count)
142+ vvexpf ( & softmaxOutput, floatValues, & count)
143+ var sumExp : Float = 0
144+ vDSP_sve ( softmaxOutput, 1 , & sumExp, vDSP_Length ( floatValues. count) )
145+ if sumExp > 0 {
146+ vDSP_vsdiv ( softmaxOutput, 1 , & sumExp, & softmaxOutput, 1 , vDSP_Length ( floatValues. count) )
147147 }
148148
149149 var indexedMap = [ Int: Double] ( )
150- for (index, value) in valuesArray . enumerated ( ) {
151- indexedMap [ index] = value
150+ for (index, value) in softmaxOutput . enumerated ( ) {
151+ indexedMap [ index] = Double ( value)
152152 }
153153
154154 let sortedMap = indexedMap. sorted { $0. value > $1. value }
155155
156156 // top1
157- if let ( topIndex, topScore) = sortedMap. first {
157+ if let ( topIndex, topScore) = sortedMap. first, topIndex < labels . count {
158158 let top1Label = labels [ topIndex]
159159 let top1Conf = Float ( topScore)
160160 probs. top1 = top1Label
@@ -166,7 +166,7 @@ public class Classifier: BasePredictor, @unchecked Sendable {
166166 var top5Labels : [ String ] = [ ]
167167 var top5Confs : [ Float ] = [ ]
168168
169- for (index, value) in topObservations {
169+ for (index, value) in topObservations where index < labels . count {
170170 top5Labels. append ( labels [ index] )
171171 top5Confs. append ( Float ( value) )
172172 }
@@ -188,7 +188,7 @@ public class Classifier: BasePredictor, @unchecked Sendable {
188188 top1 = topObservation. identifier
189189 top1Conf = Float ( topObservation. confidence)
190190 }
191- for i in 0 ... candidateNumber - 1 {
191+ for i in 0 ..< candidateNumber {
192192 let observation = observations [ i]
193193 let label = observation. identifier
194194 let confidence : Float = Float ( observation. confidence)
0 commit comments