Skip to content

Commit 27eb514

Browse files
committed
make warmup use same shape and update planBatch signature
1 parent 845ac27 commit 27eb514

3 files changed

Lines changed: 34 additions & 33 deletions

File tree

swift/Sources/CoreAIObjectDetector/ObjectDetector.swift

Lines changed: 28 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -80,17 +80,29 @@ public struct ObjectDetector {
8080

8181
// MARK: - Inference
8282

83-
/// Warm up the backend (e.g. trigger Metal kernel compilation) with a dummy pass.
84-
public func warmup() async throws {
83+
/// Warm up the backend (e.g. trigger Metal kernel compilation) with a dummy
84+
/// pass at the same `(B, H, W)` that subsequent `detect()` calls will use.
85+
/// For static-shape models the arguments are ignored — `planBatch` falls
86+
/// back to the descriptor's fixed dims.
87+
public func warmup(imageCount: Int = 1, parameters: DetectionParameters = .default) async throws {
8588
guard case .ndArray(let imageDescriptor) = functionDescriptor.inputDescriptor(of: imageInputName) else {
8689
throw DetectionRuntimeError.invalidConfiguration(
8790
"No array descriptor for image input '\(imageInputName)'"
8891
)
8992
}
90-
let defaults = DetectionParameters()
91-
let warmupShape = zip(imageDescriptor.shape, [1, 3, defaults.inputHeight, defaults.inputWidth])
92-
.map { actual, fallback in actual >= 0 ? actual : fallback }
93-
let resolved = imageDescriptor.resolvingDynamicDimensions(warmupShape)
93+
let expectedShape = imageDescriptor.shape
94+
guard expectedShape.count == 4 else {
95+
throw DetectionRuntimeError.invalidConfiguration(
96+
"Expected 4-dimensional input shape, got \(expectedShape.count)"
97+
)
98+
}
99+
let plan = try Self.planBatch(
100+
expectedShape: expectedShape,
101+
imageCount: imageCount,
102+
parameters: parameters
103+
)
104+
let resolved = imageDescriptor.resolvingDynamicDimensions(
105+
[plan.batch, 3, plan.height, plan.width])
94106
_ = try await function.run(inputs: [imageInputName: NDArray(descriptor: resolved)])
95107
}
96108

@@ -142,7 +154,7 @@ public struct ObjectDetector {
142154

143155
let plan = try Self.planBatch(
144156
expectedShape: expectedShape,
145-
imageSizes: images.map { CGSize(width: $0.width, height: $0.height) },
157+
imageCount: images.count,
146158
parameters: parameters
147159
)
148160

@@ -249,29 +261,28 @@ public struct ObjectDetector {
249261

250262
/// Resolve the concrete `(B, H, W)` to bind the model with, given the
251263
/// model's expected shape (which may contain `-1` for dynamic dims), the
252-
/// list of input image sizes, and the user's parameter overrides.
264+
/// number of input images, and the user's parameter overrides.
253265
///
254266
/// Resolution rules:
255-
/// - **Batch**: always `images.count`. A static-batch model must match.
267+
/// - **Batch**: always `imageCount`. A static-batch model must match.
256268
/// - **Spatial dims**: a dynamic `-1` dim is filled from
257269
/// `parameters.inputHeight` / `inputWidth`. A static dim is taken
258270
/// from the model descriptor (the parameters' values are ignored for
259271
/// that axis).
260272
static func planBatch(
261273
expectedShape: [Int],
262-
imageSizes: [CGSize],
274+
imageCount: Int,
263275
parameters: DetectionParameters
264276
) throws -> BatchPlan {
265-
guard !imageSizes.isEmpty else {
266-
throw DetectionRuntimeError.invalidConfiguration("planBatch requires at least one image")
277+
guard imageCount >= 1 else {
278+
throw DetectionRuntimeError.invalidConfiguration("planBatch requires imageCount >= 1")
267279
}
268280

269-
// Resolve batch from image count; verify it matches a static batch dim.
270-
let targetBatch = imageSizes.count
281+
// Verify image count matches a static batch dim.
271282
let batchExpected = expectedShape[0]
272-
if batchExpected >= 0 && batchExpected != targetBatch {
283+
if batchExpected >= 0 && batchExpected != imageCount {
273284
throw DetectionRuntimeError.invalidConfiguration(
274-
"Model expects fixed batch=\(batchExpected) but caller supplied \(targetBatch) image(s)"
285+
"Model expects fixed batch=\(batchExpected) but caller supplied \(imageCount) image(s)"
275286
)
276287
}
277288

@@ -280,7 +291,7 @@ public struct ObjectDetector {
280291
let height = heightExpected < 0 ? parameters.inputHeight : heightExpected
281292
let width = widthExpected < 0 ? parameters.inputWidth : widthExpected
282293

283-
return BatchPlan(batch: targetBatch, height: height, width: width)
294+
return BatchPlan(batch: imageCount, height: height, width: width)
284295
}
285296

286297
// MARK: - Name Discovery

swift/Sources/Tools/object-detector/ObjectDetectionMain.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ struct ObjectDetectorCLI: AsyncParsableCommand {
9797

9898
if warmup {
9999
if verbose { print("Running warmup...") }
100-
try await detector.warmup()
100+
try await detector.warmup(imageCount: loaded.count, parameters: params)
101101
}
102102

103103
if verbose { print("Running detection on \(loaded.count) image(s)...") }

swift/Tests/ObjectDetectorTests/ObjectDetectorTests.swift

Lines changed: 5 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,7 @@ struct ObjectDetectorTests {
220220
let p = DetectionParameters()
221221
let plan = try ObjectDetector.planBatch(
222222
expectedShape: [-1, 3, -1, -1],
223-
imageSizes: [CGSize(width: 728, height: 408)],
223+
imageCount: 1,
224224
parameters: .default
225225
)
226226
#expect(plan == ObjectDetector.BatchPlan(batch: 1, height: p.inputHeight, width: p.inputWidth))
@@ -231,11 +231,7 @@ struct ObjectDetectorTests {
231231
let p = DetectionParameters()
232232
let plan = try ObjectDetector.planBatch(
233233
expectedShape: [-1, 3, -1, -1],
234-
imageSizes: [
235-
CGSize(width: 1280, height: 720),
236-
CGSize(width: 600, height: 800),
237-
CGSize(width: 1024, height: 768),
238-
],
234+
imageCount: 3,
239235
parameters: .default
240236
)
241237
#expect(plan == ObjectDetector.BatchPlan(batch: 3, height: p.inputHeight, width: p.inputWidth))
@@ -248,10 +244,7 @@ struct ObjectDetectorTests {
248244
params.inputWidth = 512
249245
let plan = try ObjectDetector.planBatch(
250246
expectedShape: [-1, 3, -1, -1],
251-
imageSizes: [
252-
CGSize(width: 1280, height: 720),
253-
CGSize(width: 600, height: 800),
254-
],
247+
imageCount: 2,
255248
parameters: params
256249
)
257250
#expect(plan == ObjectDetector.BatchPlan(batch: 2, height: 512, width: 512))
@@ -266,7 +259,7 @@ struct ObjectDetectorTests {
266259
params.inputWidth = 512
267260
let plan = try ObjectDetector.planBatch(
268261
expectedShape: [1, 3, 800, 800],
269-
imageSizes: [CGSize(width: 640, height: 480)],
262+
imageCount: 1,
270263
parameters: params
271264
)
272265
#expect(plan == ObjectDetector.BatchPlan(batch: 1, height: 800, width: 800))
@@ -277,10 +270,7 @@ struct ObjectDetectorTests {
277270
#expect(throws: DetectionRuntimeError.self) {
278271
try ObjectDetector.planBatch(
279272
expectedShape: [1, 3, -1, -1],
280-
imageSizes: [
281-
CGSize(width: 1000, height: 1000),
282-
CGSize(width: 1000, height: 1000),
283-
],
273+
imageCount: 2,
284274
parameters: .default
285275
)
286276
}

0 commit comments

Comments
 (0)