@@ -17,9 +17,11 @@ using CoreML::Specification::ArrayFeatureType;
17
17
using CoreML::Specification::FeatureDescription;
18
18
using CoreML::Specification::ImageFeatureType;
19
19
using CoreML::Specification::ImageFeatureType_ImageSizeRange;
20
+ using CoreML::Specification::Model;
20
21
using CoreML::Specification::ModelDescription;
21
22
using CoreML::Specification::NeuralNetworkLayer;
22
23
using CoreML::Specification::NeuralNetworkPreprocessing;
24
+ using CoreML::Specification::NonMaximumSuppressionLayerParams;
23
25
using CoreML::Specification::SizeRange;
24
26
using turi::coreml::MLModelWrapper;
25
27
@@ -28,6 +30,8 @@ namespace turi {
28
30
29
31
namespace {
30
32
33
+ constexpr size_t MAX_NUM_BOXES_FOR_NMS_LAYER = 64 ;
34
+
31
35
constexpr char CONFIDENCE_STR[] = " Boxes × Class confidence (see user-defined metadata \" classes\" )" ;
32
36
constexpr char COORDINATES_STR[] = " Boxes × [x, y, width, height] (relative to image size)" ;
33
37
@@ -164,36 +168,13 @@ ImageFeatureType* set_image_feature(
164
168
return image_feature;
165
169
}
166
170
167
- } // namespace
168
-
169
- std::shared_ptr<MLModelWrapper> export_object_detector_model (
170
- neural_net::pipeline_spec raw_pipeline, size_t num_classes,
171
- size_t num_predictions, flex_list class_labels,
172
- std::map<std::string, flexible_type> options) {
173
- // Set up Pipeline
174
- CoreML::Specification::Model model_pipeline;
175
- model_pipeline.set_specificationversion (3 );
176
- ModelDescription* pipeline_desc = model_pipeline.mutable_description ();
177
-
178
- // Adopt the model pipeline passed to us as input.
179
- std::unique_ptr<CoreML::Specification::Pipeline> raw_pipeline_spec =
180
- std::move (raw_pipeline).move_coreml_spec ();
181
- model_pipeline.mutable_pipeline ()->Swap (raw_pipeline_spec.get ());
182
-
183
- if (!options[" include_non_maximum_suppression" ].to <bool >()){
184
- // Only support this case for models supporting spec version 1, which means
185
- // no pipeline models.
186
- ASSERT_EQ (model_pipeline.pipeline ().models_size (), 1 );
187
-
188
- auto model_wrapper = std::make_shared<MLModelWrapper>(
189
- std::make_shared<CoreML::Model>(model_pipeline.pipeline ().models (0 )));
190
-
191
- return model_wrapper;
192
- }
193
-
194
- // Add Non Maximum Suppression model to pipeline
195
- auto * model_nms = model_pipeline.mutable_pipeline ()->add_models ();
196
- model_nms->set_specificationversion (3 );
171
+ void set_non_maximum_suppression_model (Model* model_nms,
172
+ ModelDescription* pipeline_desc,
173
+ float num_classes, float num_predictions,
174
+ const flex_list& class_labels,
175
+ float confidence_threshold,
176
+ float iou_threshold) {
177
+ model_nms->set_specificationversion (CoreML::MLMODEL_SPECIFICATION_VERSION);
197
178
198
179
ModelDescription* nms_desc = model_nms->mutable_description ();
199
180
@@ -229,31 +210,26 @@ std::shared_ptr<MLModelWrapper> export_object_detector_model(
229
210
}
230
211
231
212
// Write Features for Non Maximum Suppression
232
- first_layer_nms->set_iouthreshold (options[ " iou_threshold" ] );
233
- first_layer_nms->set_confidencethreshold (options[ " confidence_threshold" ] );
213
+ first_layer_nms->set_iouthreshold (iou_threshold);
214
+ first_layer_nms->set_confidencethreshold (confidence_threshold);
234
215
first_layer_nms->set_confidenceinputfeaturename (" raw_confidence" );
235
216
first_layer_nms->set_coordinatesinputfeaturename (" raw_coordinates" );
236
217
first_layer_nms->set_iouthresholdinputfeaturename (" iouThreshold" );
237
218
first_layer_nms->set_confidencethresholdinputfeaturename (" confidenceThreshold" );
238
219
first_layer_nms->set_confidenceoutputfeaturename (" confidence" );
239
220
first_layer_nms->set_coordinatesoutputfeaturename (" coordinates" );
240
221
241
- // Copy input feature descriptions from the first model in the pipeline.
242
- *pipeline_desc->mutable_input () =
243
- model_pipeline.pipeline ().models (0 ).description ().input ();
244
-
245
222
// Write FeatureDescription for the IOU Threshold input.
246
- FeatureDescription* iou_threshold = pipeline_desc->add_input ();
247
- set_threshold_feature (iou_threshold , " iouThreshold" ,
248
- iou_threshold_description (options[ " iou_threshold" ] ));
249
- set_feature_optional (iou_threshold );
223
+ FeatureDescription* iou_threshold_desc = pipeline_desc->add_input ();
224
+ set_threshold_feature (iou_threshold_desc , " iouThreshold" ,
225
+ iou_threshold_description (iou_threshold));
226
+ set_feature_optional (iou_threshold_desc );
250
227
251
228
// Write FeatureDescription for the Confidence Threshold input.
252
- FeatureDescription* confidence_threshold = pipeline_desc->add_input ();
253
- set_threshold_feature (
254
- confidence_threshold, " confidenceThreshold" ,
255
- confidence_threshold_description (options[" confidence_threshold" ]));
256
- set_feature_optional (confidence_threshold);
229
+ FeatureDescription* confidence_threshold_desc = pipeline_desc->add_input ();
230
+ set_threshold_feature (confidence_threshold_desc, " confidenceThreshold" ,
231
+ confidence_threshold_description (confidence_threshold));
232
+ set_feature_optional (confidence_threshold_desc);
257
233
258
234
// Write FeatureDescription for the Confidence output.
259
235
set_predictions_feature (pipeline_desc->add_output (), " confidence" , num_predictions, num_classes,
@@ -262,7 +238,136 @@ std::shared_ptr<MLModelWrapper> export_object_detector_model(
262
238
// Write FeatureDescription for the Coordinates output.
263
239
set_predictions_feature (pipeline_desc->add_output (), " coordinates" , num_predictions, 4 ,
264
240
false , true , COORDINATES_STR);
241
+ }
242
+
243
+ void add_non_maximum_suppression_layer (Model* model_nn,
244
+ ModelDescription* pipeline_desc,
245
+ size_t num_classes, size_t max_boxes,
246
+ float confidence_threshold,
247
+ float iou_threshold) {
248
+ // The model we're modifying must be a NeuralNetwork.
249
+ ASSERT_TRUE (model_nn->has_neuralnetwork ());
250
+
251
+ // Append the actual NMS layer.
252
+ NeuralNetworkLayer* nms_layer =
253
+ model_nn->mutable_neuralnetwork ()->add_layers ();
254
+ nms_layer->set_name (" nonmaximumsuppression" );
255
+
256
+ // Name the inputs and outputs.
257
+ nms_layer->add_input (" raw_coordinates" );
258
+ nms_layer->add_input (" raw_confidence" );
259
+ nms_layer->add_input (" iouThreshold" );
260
+ nms_layer->add_input (" confidenceThreshold" );
261
+ nms_layer->add_output (" coordinates" );
262
+ nms_layer->add_output (" confidence" );
263
+ nms_layer->add_output (" indicesOfBoxes" );
264
+ nms_layer->add_output (" numberOfBoxes" );
265
+
266
+ // Write the parameters of the NMS layer.
267
+ NonMaximumSuppressionLayerParams* nms_params =
268
+ nms_layer->mutable_nonmaximumsuppression ();
269
+ nms_params->set_iouthreshold (iou_threshold);
270
+ nms_params->set_scorethreshold (confidence_threshold);
271
+ nms_params->set_maxboxes (
272
+ static_cast <::_tc_google::protobuf::uint64>(max_boxes));
273
+ nms_params->set_perclasssuppression (false );
274
+
275
+ // Add the necessary feature descriptions to both the NN model and to the
276
+ // overall pipeline.
277
+
278
+ // Adjust the model description to reflect the new inputs and outputs.
279
+ ModelDescription* model_desc = model_nn->mutable_description ();
280
+
281
+ // Write FeatureDescription for the IOU Threshold input.
282
+ FeatureDescription* iou_threshold_desc = pipeline_desc->add_input ();
283
+ set_array_feature (iou_threshold_desc, " iouThreshold" ,
284
+ iou_threshold_description (iou_threshold), {1 });
285
+ set_feature_optional (iou_threshold_desc);
286
+ model_desc->add_input ()->CopyFrom (*iou_threshold_desc);
287
+
288
+ // Write FeatureDescription for the Confidence Threshold input.
289
+ FeatureDescription* confidence_threshold_desc = pipeline_desc->add_input ();
290
+ set_array_feature (confidence_threshold_desc, " confidenceThreshold" ,
291
+ confidence_threshold_description (confidence_threshold),
292
+ {1 });
293
+ set_feature_optional (confidence_threshold_desc);
294
+ model_desc->add_input ()->CopyFrom (*confidence_threshold_desc);
265
295
296
+ // Write FeatureDescription for the Confidence output.
297
+ FeatureDescription* confidence_desc = pipeline_desc->add_output ();
298
+ set_predictions_feature (confidence_desc, " confidence" , max_boxes, num_classes,
299
+ false , true , CONFIDENCE_STR);
300
+ model_desc->add_output ()->CopyFrom (*confidence_desc);
301
+
302
+ // Write FeatureDescription for the Coordinates output.
303
+ FeatureDescription* coordinates_desc = pipeline_desc->add_output ();
304
+ set_predictions_feature (coordinates_desc, " coordinates" , max_boxes, 4 , false ,
305
+ true , COORDINATES_STR);
306
+ model_desc->add_output ()->CopyFrom (*coordinates_desc);
307
+
308
+ // Write FeatureDescription for the numberOfBoxes output.
309
+ FeatureDescription* number_of_boxes_desc = pipeline_desc->add_output ();
310
+ set_array_feature (number_of_boxes_desc, " numberOfBoxes" ,
311
+ " The number of valid output bounding boxes" , {1 });
312
+ model_desc->add_output ()->CopyFrom (*number_of_boxes_desc);
313
+
314
+ // Write FeatureDescription for the indicesOfBoxes output.
315
+ FeatureDescription* indices_of_boxes_desc = pipeline_desc->add_output ();
316
+ set_array_feature (indices_of_boxes_desc, " indicesOfBoxes" ,
317
+ " For each output bounding box, the index of the "
318
+ " corresponding input bounding box" ,
319
+ {max_boxes});
320
+ model_desc->add_output ()->CopyFrom (*indices_of_boxes_desc);
321
+ }
322
+
323
+ } // namespace
324
+
325
+ std::shared_ptr<MLModelWrapper> export_object_detector_model (
326
+ neural_net::pipeline_spec raw_pipeline, size_t num_classes,
327
+ size_t num_predictions, flex_list class_labels, float confidence_threshold,
328
+ float iou_threshold, bool include_non_maximum_suppression,
329
+ bool use_nms_layer) {
330
+ // Set up Pipeline
331
+ CoreML::Specification::Model model_pipeline;
332
+ model_pipeline.set_specificationversion (
333
+ CoreML::MLMODEL_SPECIFICATION_VERSION);
334
+ ModelDescription* pipeline_desc = model_pipeline.mutable_description ();
335
+
336
+ // Adopt the model pipeline passed to us as input.
337
+ std::unique_ptr<CoreML::Specification::Pipeline> raw_pipeline_spec =
338
+ std::move (raw_pipeline).move_coreml_spec ();
339
+ model_pipeline.mutable_pipeline ()->Swap (raw_pipeline_spec.get ());
340
+
341
+ if (!include_non_maximum_suppression) {
342
+ // Only support this case for models supporting spec version 1, which means
343
+ // no pipeline models.
344
+ ASSERT_EQ (model_pipeline.pipeline ().models_size (), 1 );
345
+
346
+ auto model_wrapper = std::make_shared<MLModelWrapper>(
347
+ std::make_shared<CoreML::Model>(model_pipeline.pipeline ().models (0 )));
348
+
349
+ return model_wrapper;
350
+ }
351
+
352
+ // Copy input feature descriptions from the first model in the pipeline.
353
+ *pipeline_desc->mutable_input () =
354
+ model_pipeline.pipeline ().models (0 ).description ().input ();
355
+
356
+ if (use_nms_layer) {
357
+ int num_models = model_pipeline.pipeline ().models_size ();
358
+ ASSERT_GT (num_models, 0 );
359
+ Model* model_nn =
360
+ model_pipeline.mutable_pipeline ()->mutable_models (num_models - 1 );
361
+ add_non_maximum_suppression_layer (model_nn, pipeline_desc, num_classes,
362
+ MAX_NUM_BOXES_FOR_NMS_LAYER,
363
+ confidence_threshold, iou_threshold);
364
+ } else {
365
+ // Add Non Maximum Suppression model to pipeline
366
+ auto * model_nms = model_pipeline.mutable_pipeline ()->add_models ();
367
+ set_non_maximum_suppression_model (model_nms, pipeline_desc, num_classes,
368
+ num_predictions, class_labels,
369
+ confidence_threshold, iou_threshold);
370
+ }
266
371
267
372
// Wrap the pipeline
268
373
auto pipeline_wrapper = std::make_shared<MLModelWrapper>(
@@ -278,7 +383,7 @@ std::shared_ptr<MLModelWrapper> export_activity_classifier_model(
278
383
const flex_list& class_labels, const flex_string& target)
279
384
{
280
385
CoreML::Specification::Model model;
281
- model.set_specificationversion (1 );
386
+ model.set_specificationversion (CoreML::MLMODEL_SPECIFICATION_VERSION );
282
387
283
388
// Write the model description.
284
389
ModelDescription* model_desc = model.mutable_description ();
@@ -338,7 +443,7 @@ std::shared_ptr<coreml::MLModelWrapper> export_style_transfer_model(
338
443
std::string content_feature, std::string style_feature, size_t num_styles) {
339
444
340
445
CoreML::Specification::Model model;
341
- model.set_specificationversion (3 );
446
+ model.set_specificationversion (CoreML::MLMODEL_SPECIFICATION_VERSION );
342
447
343
448
ModelDescription* model_desc = model.mutable_description ();
344
449
@@ -398,7 +503,7 @@ std::shared_ptr<coreml::MLModelWrapper> export_drawing_classifier_model(
398
503
const flex_list& class_labels, const flex_string& target)
399
504
{
400
505
CoreML::Specification::Model model;
401
- model.set_specificationversion (1 );
506
+ model.set_specificationversion (CoreML::MLMODEL_SPECIFICATION_VERSION );
402
507
403
508
// Write the model description.
404
509
ModelDescription* model_desc = model.mutable_description ();
0 commit comments