def convert_flexible_coremodel(model_path, input_name, output_name): spec = coremltools.utils.load_spec(model_path) img_size_ranges = flexible_shape_utils.NeuralNetworkImageSizeRange() img_size_ranges.add_height_range((100, 1920)) img_size_ranges.add_width_range((100, 1920)) flexible_shape_utils.update_image_size_range(spec, feature_name=input_name, size_range=img_size_ranges) flexible_shape_utils.update_image_size_range(spec, feature_name=output_name, size_range=img_size_ranges) coremltools.utils.save_spec(spec, model_path) model_spec = coremltools.utils.load_spec(model_path) model_fp16_spec = coremltools.utils.convert_neural_network_spec_weights_to_fp16( model_spec) coremltools.utils.save_spec(model_fp16_spec, model_path)
def _set_user_inputs(proto, inputs): for input_type in inputs: shape = input_type.shape if isinstance(shape, EnumeratedShapes): if isinstance(input_type, ImageType): default_height, default_width = 0, 0 for inp in proto.description.input: if inp.name == input_type.name: default_height = inp.type.imageType.height default_width = inp.type.imageType.width break image_sizes = [] if input_type.channel_first: for s in shape.shapes: if s.shape[-2] == default_height and s.shape[ -1] == default_width: continue image_sizes.append( flexible_shape_utils.NeuralNetworkImageSize( height=s.shape[-2], width=s.shape[-1])) else: for s in shape.shapes: if s.shape[-3] == default_height and s.shape[ -2] == default_width: continue image_sizes.append( flexible_shape_utils.NeuralNetworkImageSize( height=s.shape[-3], width=s.shape[-2])) add_enumerated_image_sizes(proto, input_type.name, sizes=image_sizes) else: add_multiarray_ndshape_enumeration( proto, input_type.name, [tuple(s.shape) for s in shape.shapes]) elif isinstance(shape, Shape): shape = shape.shape # This is shape in Shape if all([ not isinstance(s, RangeDim) and not is_symbolic(s) and s > 0 for s in shape ]): continue if isinstance(input_type, ImageType): img_range = flexible_shape_utils.NeuralNetworkImageSizeRange() if input_type.channel_first: H = shape[-2] W = shape[-1] else: H = shape[-3] W = shape[-2] if isinstance(H, RangeDim): img_range.add_height_range((H.lower_bound, H.upper_bound)) elif is_symbolic(H): img_range.add_height_range((1, -1)) else: img_range.add_height_range((H, H)) if isinstance(W, RangeDim): img_range.add_width_range((W.lower_bound, W.upper_bound)) elif is_symbolic(W): img_range.add_width_range((1, -1)) else: img_range.add_width_range((W, W)) flexible_shape_utils.update_image_size_range( proto, input_type.name, img_range) else: lb = [] ub = [] for s in shape: if isinstance(s, RangeDim): lb.append(s.lower_bound) ub.append(s.upper_bound) elif is_symbolic(s): lb.append(1) ub.append(-1) else: lb.append(s) ub.append(s) set_multiarray_ndshape_range(proto, input_type.name, lower_bounds=lb, upper_bounds=ub)
def load(prog, weights_dir, resume_on_errors=False, **kwargs): if "main" not in prog.functions: raise ValueError("main function not found in program") mil_passes.mil_backend_passes(prog) # if user has specified "ClassifierConfig", then add the "classify" op to the prog classifier_config = kwargs.get("classifier_config", None) predicted_feature_name = None predicted_probabilities_name = None if classifier_config is not None: predicted_feature_name, predicted_probabilities_name = _add_classify_op( prog, classifier_config) input_types = prog.main_input_types weight_path = os.path.join(weights_dir, _WEIGHTS_FILE_NAME) blob_writer = BlobWriter(weight_path) function_protos = {} for func_name, func in prog.functions.items(): function_protos[func_name] = convert_function(func, prog.parameters, blob_writer) proto = pm.Program( version=1, functions=function_protos, ) input_features = [] output_features = [] symbolic_inputs = [] image_input_names = { } # these are the model inputs marked as image by the user input_shape_map = {} for input_type in input_types: if isinstance(input_type, ImageType): image_input_names[input_type.name] = input_type # error checking for input(s) marked as images if input_type.name not in list( prog.functions["main"].inputs.keys()): msg = "Provided image input '{}' is not one of the inputs of the MIL program" raise ValueError(msg.format(input_type.name)) input_shape_map[input_type.name] = input_type for name, var in prog.functions["main"].inputs.items(): input_feature_type = ft.FeatureType() # error checking for input(s) marked as images # an image input must be of type tensor in program proto # (since an image type does not exist in MIL program) if name in image_input_names and \ not types.is_tensor(var.sym_type): raise ValueError( "For the image input, '{}', its type in the MIL program must be tensor. " "Instead it is {}.".format(name, var.sym_type.__type_info__())) if types.is_tensor(var.sym_type): shape = var.sym_type.get_shape() if any_variadic(shape): raise ValueError( "Variable rank model inputs are not supported!") if any_symbolic(shape): symbolic_inputs.append(name) # We extract the default input shape given by user first if name in input_shape_map: shape = input_shape_map[name].shape.default else: logging.warning( "Input shape not fully specified by enumerated shapes or range dim! 1 will be used for dimension not specified instead." ) # If no input shape is provided (ex. auto conversion of -1 in Tensorflow) shape = [1 if is_symbolic(d) else d for d in shape] if name not in image_input_names: # make a feature type of Type "multiArrayType" array_type = ft.ArrayFeatureType( shape=shape, dataType=cast_to_framework_io_dtype(var, False)) input_feature_type.multiArrayType.CopyFrom(array_type) else: if len(shape) < 3: raise ValueError( "Image input, '{}', must have rank at least 3. Instead it has rank {}" .format(name, len(shape))) # make a feature type of Type "imageType" input_type = image_input_names[name] if not input_type.channel_first: raise ValueError( "Image input, '{}', must be in the channel_first format" .format(name)) if input_type.color_layout == "G": clr_space = ft.ImageFeatureType.ColorSpace.GRAYSCALE elif input_type.color_layout == "BGR": clr_space = ft.ImageFeatureType.ColorSpace.BGR else: clr_space = ft.ImageFeatureType.ColorSpace.RGB image_type = ft.ImageFeatureType(width=shape[-1], height=shape[-2], colorSpace=clr_space) input_feature_type.imageType.CopyFrom(image_type) input_features.append( ml.FeatureDescription(name=name, type=input_feature_type)) elif types.is_scalar(var.sym_type): array_type = ft.ArrayFeatureType( shape=[1], dataType=cast_to_framework_io_dtype(var, False)) input_feature_type.multiArrayType.CopyFrom(array_type) input_features.append( ml.FeatureDescription(name=var.name, type=input_feature_type)) else: raise NotImplementedError() for var in prog.functions["main"].outputs: output_feature_type = ft.FeatureType() if types.is_tensor(var.sym_type) or types.is_primitive(var.sym_type): dataType = None if classifier_config is None or var.name != predicted_feature_name: # Not a classifier output, make sure model output type matches with ML Program type. dataType = cast_to_framework_io_dtype(var, True) else: # Classifier outputs are set up separately, so default to fp32 for now. dataType = ft.ArrayFeatureType.ArrayDataType.FLOAT32 array_type = ft.ArrayFeatureType(shape=None, dataType=dataType) output_feature_type.multiArrayType.CopyFrom(array_type) output_features.append( ml.FeatureDescription(name=var.name, type=output_feature_type)) elif (types.is_dict(var.sym_type)): output_feature_type.dictionaryType.MergeFromString(b"") keytype, valtype = var.sym_type.T if types.is_str(keytype): output_feature_type.dictionaryType.stringKeyType.MergeFromString( b"") elif (keytype == types_int64): output_feature_type.dictionaryType.int64KeyType.MergeFromString( b"") else: raise ValueError("Dictionary key type not supported.") output_features.append( ml.FeatureDescription(name=var.name, type=output_feature_type)) else: raise NotImplementedError() # Model description desc = ml.ModelDescription(input=input_features, output=output_features) if classifier_config is not None: desc.predictedFeatureName = predicted_feature_name desc.predictedProbabilitiesName = predicted_probabilities_name # Manually edit output type of predictedFeatureName. # It doesn't use MLMultiArray and really uses a "primitive" type. for output in desc.output: if output.name == predicted_feature_name: if type(classifier_config.class_labels[0]) == int: output.type.int64Type.MergeFromString(b"") else: output.type.stringType.MergeFromString(b"") break # Create ML Model model = ml.Model(description=desc, specificationVersion=_SPECIFICATION_VERSION_IOS_15) model.mlProgram.CopyFrom(proto) # Set symbolic shapes for input_name in symbolic_inputs: input_type = input_shape_map.get(input_name, None) if isinstance(input_type, ImageType): if isinstance(input_type.shape, EnumeratedShapes): enumerated_shapes = [] for s in input_type.shape.shapes: enumerated_shapes.append( NeuralNetworkImageSize(height=s.shape[-2], width=s.shape[-1])) add_enumerated_image_sizes(model, input_name, sizes=enumerated_shapes) else: img_range = NeuralNetworkImageSizeRange() H = input_type.shape.shape[-2] W = input_type.shape.shape[-1] if isinstance(H, RangeDim): img_range.add_height_range((H.lower_bound, H.upper_bound)) elif is_symbolic(H): img_range.add_height_range((1, -1)) else: img_range.add_height_range((H, H)) if isinstance(W, RangeDim): img_range.add_width_range((W.lower_bound, W.upper_bound)) elif is_symbolic(W): img_range.add_width_range((1, -1)) else: img_range.add_width_range((W, W)) update_image_size_range(model, input_name, img_range) elif isinstance(input_type, TensorType): if isinstance(input_type.shape, EnumeratedShapes): add_multiarray_ndshape_enumeration( model, input_name, [tuple(s.shape) for s in input_type.shape.shapes]) else: lb = [] ub = [] for s in input_type.shape.shape: if isinstance(s, RangeDim): lb.append(s.lower_bound) ub.append(s.upper_bound) elif is_symbolic(s): lb.append(1) ub.append(-1) else: lb.append(s) ub.append(s) set_multiarray_ndshape_range(model, input_name, lower_bounds=lb, upper_bounds=ub) elif input_type is None: sym_type = prog.functions["main"].inputs[input_name].sym_type lb = [] ub = [] for s in sym_type.get_shape(): if is_symbolic(s): lb.append(1) ub.append(-1) else: lb.append(s) ub.append(s) set_multiarray_ndshape_range(model, input_name, lower_bounds=lb, upper_bounds=ub) # Set optional inputs _set_optional_inputs(model, input_types) return model
model_file = open(model_in, 'rb') model_proto = onnx_pb.ModelProto() model_proto.ParseFromString(model_file.read()) print("prepare to convert...") coreml_model = convert(model_proto, preprocessing_args={ 'image_scale': (1.0 / 255.0 / 58.50182), 'blue_bias': (-109.496254 / 255.0 / 58.50182), 'green_bias': (-118.698456 / 255.0 / 58.50182), 'red_bias': (-124.68751 / 255.0 / 58.50182), 'is_bgr': True }, image_input_names=['gemfield' ]) #, image_output_names=['745']) #coreml_model = convert(model_proto, preprocessing_args= {'image_scale' : (1.0/255), 'is_bgr':True}, image_input_names=['gemfield'],image_output_names=['745']) coreml_model.save(model_out) #### import coremltools from coremltools.models.neural_network import flexible_shape_utils spec = coremltools.utils.load_spec(model_out) img_size_ranges = flexible_shape_utils.NeuralNetworkImageSizeRange() img_size_ranges.add_height_range((384, 640)) img_size_ranges.add_width_range((384, 640)) flexible_shape_utils.update_image_size_range(spec, feature_name='gemfield', size_range=img_size_ranges) #flexible_shape_utils.update_image_size_range(spec, feature_name='745', size_range=img_size_ranges) coremltools.utils.save_spec(spec, 'flex_{}'.format(model_out)) ######