Skip to content
imaged

Image classification

Run an image model and read its highest-scoring classes.

Use your own .slab model and a runtime license. Model files carry their input sizes, preprocessing, labels, and backend preferences.

Run inference off the main thread.

// Classify an image and return the five most likely classes.
//
// Objective-C calls Slab's C interface directly through the Slab framework.

@import Foundation;
@import Slab;

// Slab strings are (pointer, length) UTF-8 pairs.
static NSString *SlabString(uint8_t *ptr, size_t len) {
    return len ? [[NSString alloc] initWithBytes:ptr length:len encoding:NSUTF8StringEncoding] : @"";
}

static NSError *SlabErrorWith(NSString *message) {
    return [NSError errorWithDomain:@"Slab" code:1 userInfo:@{NSLocalizedDescriptionKey: message}];
}

NSArray<NSString *> *SlabClassify(NSString *modelPath, NSString *imagePath, NSString *license, NSError **error) {
    NSData *token = [license dataUsingEncoding:NSUTF8StringEncoding];
    NSData *model = [modelPath dataUsingEncoding:NSUTF8StringEncoding];
    NSData *image = [imagePath dataUsingEncoding:NSUTF8StringEncoding];

    // 1. Configure once and attach the runtime license.
    uint8_t *cfg = slab_config_new();
    if (!slab_config_set_runtime_license(cfg, (uint8_t *)token.bytes, token.length)) {
        if (error) *error = SlabErrorWith(SlabString(slab_config_runtime_license_error_message_ptr(cfg),
                                                     slab_config_runtime_license_error_message_len(cfg)));
        slab_config_free(cfg);
        return nil;
    }

    // 2. Open the model; it carries its own preprocessing and labels.
    uint8_t *session = slab_session_new(cfg);
    uint8_t *frame = slab_frame_new();
    uint8_t *input = slab_input_new();
    uint8_t *output = slab_output_new();
    NSMutableArray<NSString *> *results = nil;

    slab_session_set_warmup(session, 1);
    if (!slab_session_open(session, (uint8_t *)model.bytes, model.length)) {
        if (error) *error = SlabErrorWith(SlabString(slab_session_error_message_ptr(session), slab_session_error_message_len(session)));
    } else if (!slab_frame_load_file(frame, (uint8_t *)image.bytes, image.length)) {
        if (error) *error = SlabErrorWith(SlabString(slab_frame_error_message_ptr(frame), slab_frame_error_message_len(frame)));
    } else {
        // 3. Run and rank the classes. Labels are empty when the model has none.
        slab_input_set_frame(input, frame);
        if (!slab_session_run(session, input, output)) {
            if (error) *error = SlabErrorWith(SlabString(slab_session_error_message_ptr(session), slab_session_error_message_len(session)));
        } else {
            results = [NSMutableArray array];
            int32_t k = slab_output_compute_top_k(output, 0, 5, 1);
            for (int32_t r = 0; r < k; r++) {
                NSString *label = SlabString(slab_output_top_label_ptr(output, r), slab_output_top_label_len(output, r));
                [results addObject:[NSString stringWithFormat:@"%@ %.4f",
                                    label.length ? label : @(slab_output_top_class_id(output, r)).stringValue,
                                    slab_output_top_score(output, r)]];
            }
        }
    }

    // 4. Free in any order.
    slab_output_free(output);
    slab_input_free(input);
    slab_frame_free(frame);
    slab_session_free(session);
    slab_config_free(cfg);
    return results;
}