Skip to content

Commit 511e360

Browse files
authored
9.0b1 Release (#2559)
1 parent 1c0e5cb commit 511e360

File tree

73 files changed

+2497
-881
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

73 files changed

+2497
-881
lines changed

coremlpython/CoreMLPython.h

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,11 @@
2020
#import <optional>
2121

2222
#ifndef BUILT_WITH_MACOS15_SDK
23-
#define BUILT_WITH_MACOS15_SDK \
24-
!(TARGET_OS_OSX && (!defined(__MAC_15_0) || __MAC_OS_X_VERSION_MAX_ALLOWED < __MAC_15_0))
23+
#if TARGET_OS_OSX && defined(__MAC_15_0) && __MAC_OS_X_VERSION_MAX_ALLOWED >= __MAC_15_0
24+
#define BUILT_WITH_MACOS15_SDK 1
25+
#else
26+
#define BUILT_WITH_MACOS15_SDK 0
27+
#endif
2528
#endif
2629

2730
// Print BUILT_WITH_MACOS15_SDK value
@@ -67,6 +70,11 @@ namespace CoreML {
6770
return (MLState *)m_impl;
6871
}
6972
#endif
73+
// Retrieves the value of the specified state variable from the model.
74+
py::object readState(const std::string& stateName) const;
75+
76+
// Sets the value of the specified state variable in the model.
77+
void writeState(const std::string& stateName, py::object value);
7078

7179
private:
7280
// Type erase `m_impl` otherwise it will result in a compiler warning.

coremlpython/CoreMLPython.mm

Lines changed: 540 additions & 311 deletions
Large diffs are not rendered by default.

coremlpython/CoreMLPythonArray.mm

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
// Copyright (c) 2025, Apple Inc. All rights reserved.
2+
//
3+
// Use of this source code is governed by a BSD-3-clause license that can be
4+
// found in the LICENSE.txt file or at https://opensource.org/licenses/BSD-3-Clause
5+
16
#import "CoreMLPythonArray.h"
27

38
@implementation PybindCompatibleArray
@@ -6,15 +11,22 @@ + (MLMultiArrayDataType)dataTypeOf:(py::array)array {
611
const auto& dt = array.dtype();
712
char kind = dt.kind();
813
size_t itemsize = dt.itemsize();
9-
14+
15+
1016
if(kind == 'i' && itemsize == 4) {
1117
return MLMultiArrayDataTypeInt32;
12-
} else if(kind == 'f' && itemsize == 4) {
18+
}
19+
#if BUILT_WITH_MACOS26_SDK
20+
else if (kind == 'i' && itemsize == 1) {
21+
return MLMultiArrayDataTypeInt8;
22+
}
23+
#endif
24+
else if(kind == 'f' && itemsize == 4) {
1325
return MLMultiArrayDataTypeFloat32;
1426
} else if( (kind == 'f' || kind == 'd') && itemsize == 8) {
1527
return MLMultiArrayDataTypeDouble;
1628
}
17-
29+
1830
throw std::runtime_error("Unsupported array type: " + std::to_string(kind) + " with itemsize = " + std::to_string(itemsize));
1931
}
2032

@@ -29,7 +41,7 @@ + (MLMultiArrayDataType)dataTypeOf:(py::array)array {
2941
+ (NSArray<NSNumber *> *)stridesOf:(py::array)array {
3042
// numpy strides is in bytes.
3143
// this type must return number of ELEMENTS! (as per mlkit)
32-
44+
3345
NSMutableArray<NSNumber *> *ret = [[NSMutableArray alloc] init];
3446
for (size_t i=0; i<array.ndim(); i++) {
3547
size_t stride = array.strides(i) / array.itemsize();

coremlpython/CoreMLPythonUtils.mm

Lines changed: 45 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -11,21 +11,10 @@
1111

1212
#import <Accelerate/Accelerate.h>
1313

14-
#if PY_MAJOR_VERSION < 3
15-
16-
#pragma clang diagnostic push
17-
#pragma clang diagnostic ignored "-Wmacro-redefined"
18-
#define PyBytes_Check(name) PyString_Check(name)
19-
#pragma clang diagnostic pop
20-
#define PyAnyInteger_Check(name) (PyLong_Check(name) || PyInt_Check(name))
21-
22-
#else
23-
2414
#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
2515
#include <numpy/arrayobject.h>
26-
#define PyAnyInteger_Check(name) (PyLong_Check(name) || (_import_array(), PyArray_IsScalar(name, Integer)))
2716

28-
#endif
17+
#define PyAnyInteger_Check(name) (PyLong_Check(name) || (_import_array(), PyArray_IsScalar(name, Integer)))
2918

3019
using namespace CoreML::Python;
3120

@@ -59,14 +48,14 @@
5948

6049
@autoreleasepool {
6150
NSMutableDictionary<NSString *, NSObject *> *inputDict = [[NSMutableDictionary<NSString *, NSObject *> alloc] init];
62-
51+
6352
for (const auto element : dict) {
6453
std::string key = element.first.cast<std::string>();
6554
NSString *nsKey = [NSString stringWithUTF8String:key.c_str()];
6655
id nsValue = Utils::convertValueToObjC(element.second);
6756
inputDict[nsKey] = nsValue;
6857
}
69-
58+
7059
feautreProvider = [[MLDictionaryFeatureProvider alloc] initWithDictionary:inputDict error:&localError];
7160
}
7261

@@ -134,17 +123,17 @@
134123
}
135124

136125
static MLFeatureValue * convertValueToDictionary(const py::handle& handle) {
137-
126+
138127
if(!PyDict_Check(handle.ptr())) {
139128
throw std::runtime_error("Not a dictionary.");
140129
}
141-
130+
142131
// Get the first value in the dictionary; use that as a hint.
143132
PyObject *key = nullptr, *value = nullptr;
144133
Py_ssize_t pos = 0;
145-
134+
146135
int has_values = PyDict_Next(handle.ptr(), &pos, &key, &value);
147-
136+
148137
// Is it an empty dict? If so, just return an empty dictionary.
149138
if(!has_values) {
150139
return [MLFeatureValue featureValueWithDictionary:@{} error:nullptr];
@@ -222,12 +211,12 @@ static void handleCVReturn(CVReturn status) {
222211
static MLFeatureValue * convertValueToImage(const py::handle& handle) {
223212
// assumes handle is a valid PIL image!
224213
CVPixelBufferRef pixelBuffer = nil;
225-
214+
226215
size_t width = handle.attr("width").cast<size_t>();
227216
size_t height = handle.attr("height").cast<size_t>();
228217
OSType format;
229218
std::string formatStr = handle.attr("mode").cast<std::string>();
230-
219+
231220
if (formatStr == "RGB") {
232221
format = kCVPixelFormatType_32BGRA;
233222
} else if (formatStr == "RGBA") {
@@ -242,18 +231,18 @@ static void handleCVReturn(CVReturn status) {
242231
msg << "Supported types are: RGB, RGBA, L.";
243232
throw std::runtime_error(msg.str());
244233
}
245-
234+
246235
CVReturn status = CVPixelBufferCreate(kCFAllocatorDefault, width, height, format, NULL, &pixelBuffer);
247236
handleCVReturn(status);
248-
237+
249238
// get bytes out of the PIL image
250239
py::object tobytes = handle.attr("tobytes");
251240
py::object bytesResult = tobytes();
252241
assert(PyBytes_Check(bytesResult.ptr()));
253242
Py_ssize_t bytesLength = PyBytes_Size(bytesResult.ptr());
254243
assert(bytesLength >= 0);
255244
const char *bytesPtr = PyBytes_AsString(bytesResult.ptr());
256-
245+
257246
// copy data into the CVPixelBuffer
258247
status = CVPixelBufferLockBaseAddress(pixelBuffer, 0);
259248
handleCVReturn(status);
@@ -268,33 +257,33 @@ static void handleCVReturn(CVReturn status) {
268257
srcBuffer.data = const_cast<char *>(srcPointer);
269258
srcBuffer.width = width;
270259
srcBuffer.height = height;
271-
260+
272261
vImage_Buffer dstBuffer;
273262
memset(&dstBuffer, 0, sizeof(dstBuffer));
274263
dstBuffer.data = baseAddress;
275264
dstBuffer.width = width;
276265
dstBuffer.height = height;
277266

278267
if (formatStr == "RGB") {
279-
268+
280269
// convert RGB to BGRA
281270
assert(bytesLength == width * height * 3);
282271

283272
srcBuffer.rowBytes = width * 3;
284273
dstBuffer.rowBytes = bytesPerRow;
285274
vImageConvert_RGB888toBGRA8888(&srcBuffer, NULL, 255, &dstBuffer, false, 0);
286-
275+
287276
} else if (formatStr == "RGBA") {
288-
277+
289278
// convert RGBA to BGRA
290279
assert(bytesLength == width * height * 4);
291280
srcBuffer.rowBytes = width * 4;
292281
dstBuffer.rowBytes = bytesPerRow;
293282
uint8_t permuteMap[4] = { 2, 1, 0, 3 };
294283
vImagePermuteChannels_ARGB8888(&srcBuffer, &dstBuffer, permuteMap, 0);
295-
284+
296285
} else if (formatStr == "L") {
297-
286+
298287
// 8 bit grayscale.
299288
assert(bytesLength == width * height);
300289

@@ -303,7 +292,7 @@ static void handleCVReturn(CVReturn status) {
303292
vImageCopyBuffer(&srcBuffer, &dstBuffer, 1, 0);
304293

305294
} else if (formatStr == "F") {
306-
295+
307296
// convert Float32 to Float16.
308297
assert(bytesLength == width * height * sizeof(Float32));
309298

@@ -317,14 +306,14 @@ static void handleCVReturn(CVReturn status) {
317306
msg << "Supported types are: RGB, RGBA, L.";
318307
throw std::runtime_error(msg.str());
319308
}
320-
309+
321310
#ifdef COREML_SHOW_PIL_IMAGES
322311
if (formatStr == "RGB") {
323312
// for debugging purposes, convert back to PIL image and show it
324313
py::object scope = py::module::import("__main__").attr("__dict__");
325314
py::eval<py::eval_single_statement>("import PIL.Image", scope);
326315
py::object pilImage = py::eval<py::eval_expr>("PIL.Image");
327-
316+
328317
std::string cvPixelStr(count, 0);
329318
const char *basePtr = static_cast<char *>(baseAddress);
330319
for (size_t row = 0; row < height; row++) {
@@ -334,7 +323,7 @@ static void handleCVReturn(CVReturn status) {
334323
}
335324
}
336325
}
337-
326+
338327
py::bytes cvPixelBytes = py::bytes(cvPixelStr);
339328
py::object frombytes = pilImage.attr("frombytes");
340329
py::str mode = "RGB";
@@ -343,62 +332,62 @@ static void handleCVReturn(CVReturn status) {
343332
img.attr("show")();
344333
}
345334
#endif
346-
335+
347336
status = CVPixelBufferUnlockBaseAddress(pixelBuffer, 0);
348337
handleCVReturn(status);
349338

350339
MLFeatureValue *fv = [MLFeatureValue featureValueWithPixelBuffer:pixelBuffer];
351340
CVPixelBufferRelease(pixelBuffer);
352-
341+
353342
return fv;
354343
}
355344

356345
static bool IsPILImage(const py::handle& handle) {
357346
// TODO put try/catch around this?
358-
347+
359348
try {
360349
py::module::import("PIL.Image");
361350
} catch(...) {
362351
return false;
363352
}
364-
353+
365354
py::object scope = py::module::import("__main__").attr("__dict__");
366355
py::eval<py::eval_single_statement>("import PIL.Image", scope);
367356
py::handle imageTypeHandle = py::eval<py::eval_expr>("PIL.Image.Image", scope);
368357
assert(PyType_Check(imageTypeHandle.ptr())); // should be a Python type
369-
358+
370359
return PyObject_TypeCheck(handle.ptr(), (PyTypeObject *)(imageTypeHandle.ptr()));
371360
}
372361

373362
MLFeatureValue * Utils::convertValueToObjC(const py::handle& handle) {
374-
363+
375364
if (PyAnyInteger_Check(handle.ptr())) {
376365
try {
377366
int64_t val = handle.cast<int64_t>();
378367
return [MLFeatureValue featureValueWithInt64:val];
379368
} catch(...) {}
380369
}
381-
370+
382371
if (PyFloat_Check(handle.ptr())) {
383372
try {
384373
double val = handle.cast<double>();
385374
return [MLFeatureValue featureValueWithDouble:val];
386375
} catch(...) {}
387376
}
388-
377+
389378
if (PyBytes_Check(handle.ptr()) || PyUnicode_Check(handle.ptr())) {
390379
try {
391380
std::string val = handle.cast<std::string>();
392381
return [MLFeatureValue featureValueWithString:[NSString stringWithUTF8String:val.c_str()]];
393382
} catch(...) {}
394383
}
395-
384+
396385
if (PyDict_Check(handle.ptr())) {
397386
try {
398387
return convertValueToDictionary(handle);
399388
} catch(...) {}
400389
}
401-
390+
402391
if(PyList_Check(handle.ptr()) || PyTuple_Check(handle.ptr())) {
403392
try {
404393
return convertValueToSequence(handle);
@@ -410,11 +399,11 @@ static bool IsPILImage(const py::handle& handle) {
410399
return convertValueToArray(handle);
411400
} catch(...) {}
412401
}
413-
402+
414403
if (IsPILImage(handle)) {
415404
return convertValueToImage(handle);
416405
}
417-
406+
418407
py::print("Error: value type not convertible:");
419408
py::print(handle);
420409
throw std::runtime_error("value type not convertible");
@@ -474,6 +463,11 @@ static size_t sizeOfArrayElement(MLMultiArrayDataType type) {
474463
__block py::object array;
475464
[value getBytesWithHandler:^(const void *bytes, NSInteger size) {
476465
switch (type) {
466+
#if BUILT_WITH_MACOS26_SDK
467+
case MLMultiArrayDataTypeInt8:
468+
array = py::array(shape, strides, reinterpret_cast<const int8_t *>(bytes));
469+
break;
470+
#endif
477471
case MLMultiArrayDataTypeInt32:
478472
array = py::array(shape, strides, reinterpret_cast<const int32_t *>(bytes));
479473
break;
@@ -508,7 +502,7 @@ static size_t sizeOfArrayElement(MLMultiArrayDataType type) {
508502
NSString *nskey = static_cast<NSString *>(key);
509503
pykey = py::str([nskey UTF8String]);
510504
}
511-
505+
512506
NSNumber *value = dict[key];
513507
ret[pykey] = py::float_([value doubleValue]);
514508
}
@@ -519,7 +513,7 @@ static size_t sizeOfArrayElement(MLMultiArrayDataType type) {
519513
if (CVPixelBufferIsPlanar(value)) {
520514
throw std::runtime_error("Only non-planar CVPixelBuffers are currently supported by this Python binding.");
521515
}
522-
516+
523517
// supports grayscale and BGRA format types
524518
auto formatType = CVPixelBufferGetPixelFormatType(value);
525519
assert(formatType == kCVPixelFormatType_32BGRA
@@ -553,10 +547,10 @@ static size_t sizeOfArrayElement(MLMultiArrayDataType type) {
553547

554548
auto result = CVPixelBufferLockBaseAddress(value, kCVPixelBufferLock_ReadOnly);
555549
assert(result == kCVReturnSuccess);
556-
550+
557551
uint8_t *src = reinterpret_cast<uint8_t*>(CVPixelBufferGetBaseAddress(value));
558552
assert(src != nullptr);
559-
553+
560554
size_t srcBytesPerRow = CVPixelBufferGetBytesPerRow(value);
561555

562556
// Prepare for vImage blitting
@@ -587,10 +581,10 @@ static size_t sizeOfArrayElement(MLMultiArrayDataType type) {
587581
msg << "Unsupported pixel format type: " << std::hex << std::setfill('0') << std::setw(4) << formatType << ". ";
588582
throw std::runtime_error(msg.str());
589583
}
590-
584+
591585
result = CVPixelBufferUnlockBaseAddress(value, kCVPixelBufferLock_ReadOnly);
592586
assert(result == kCVReturnSuccess);
593-
587+
594588
py::object scope = py::module::import("__main__").attr("__dict__");
595589
py::eval<py::eval_single_statement>("import PIL.Image", scope);
596590
py::object pilImage = py::eval<py::eval_expr>("PIL.Image", scope);

0 commit comments

Comments
 (0)