def convert_InstanceNormalizationLayer(scope, operator, container): from keras2onnx.common.onnx_ops import OnnxOperatorBuilder op = operator.raw_operator params = op.get_weights() assert len(op.input_shape) == 4 beta = params[0].reshape(1, 1, 1, 1).astype('float32') gamma = params[1].reshape(1, 1, 1, 1).astype('float32') oopb = OnnxOperatorBuilder(container, scope) reducemean_1 = oopb.add_node('ReduceMean', [operator.inputs[0].full_name], operator.inputs[0].full_name + '_reduce_mean_1', axes=[1, 2, 3], keepdims=1) sub_1 = oopb.add_node('Sub', [operator.inputs[0].full_name, reducemean_1], operator.inputs[0].full_name + '_sub_1') mul = oopb.add_node('Mul', [sub_1, sub_1], operator.inputs[0].full_name + '_mul') reducemean_2 = oopb.add_node('ReduceMean', [mul], operator.inputs[0].full_name + '_reduce_mean_2', axes=[1, 2, 3], keepdims=1) sqrt = oopb.add_node('Sqrt', [reducemean_2], operator.inputs[0].full_name + '_sqrt') add = oopb.add_node('Add', [sqrt, ('_start', oopb.float, np.array([op.epsilon], dtype='float32'))], operator.inputs[0].full_name + '_add') div = oopb.add_node('Div', [sub_1, add], operator.inputs[0].full_name + '_div') mul_scale = oopb.add_node('Mul', [div, ('_start', oopb.float, beta)], operator.inputs[0].full_name + '_mul_scale') add_bias = oopb.add_node('Add', [mul_scale, ('_start', oopb.float, gamma)], operator.inputs[0].full_name + '_add_bias') apply_identity(scope, add_bias, operator.outputs[0].full_name, container)
def convert_tf_crop_and_resize(scope, operator, container): if operator.target_opset < 11: raise ValueError("CropAndResize op is not supported for opset < 11") oopb = OnnxOperatorBuilder(container, scope) node = operator.raw_operator mode_value = node.get_attr('method') transpose_node = oopb.apply_transpose(operator.inputs[0].full_name, name=operator.full_name + '_transpose_1', perm=[0, 3, 1, 2]) cropandresize = oopb.add_node('CropAndResize', transpose_node + operator.input_full_names[1:], operator.full_name + '_crop_and_resize', op_domain='com.microsoft', op_version=1, mode=mode_value) oopb.apply_op_with_output("apply_transpose", cropandresize, operator.output_full_names, name=operator.full_name + '_transpose_final', perm=[0, 2, 3, 1])
def convert_DetectionLayer(scope, operator, container): # type: (keras2onnx.common.InterimContext, keras2onnx.common.Operator, keras2onnx.common.OnnxObjectContainer) -> None DETECTION_MAX_INSTANCES = 100 DETECTION_NMS_THRESHOLD = 0.3 DETECTION_MIN_CONFIDENCE = 0.7 oopb = OnnxOperatorBuilder(container, scope) box_transpose = scope.get_unique_variable_name(operator.inputs[0].full_name + '_tx') score_transpose = scope.get_unique_variable_name(operator.inputs[1].full_name + '_tx') # apply_transpose(scope, operator.inputs[0].full_name, box_transpose, container, perm=[2, 0, 1]) apply_identity(scope, operator.inputs[0].full_name, box_transpose, container) # output shape: [num_batches, spatial_dimension, 4] score_identity = scope.get_unique_variable_name(operator.inputs[1].full_name + '_id') apply_identity(scope, operator.inputs[1].full_name, score_identity, container) # output shape: [num_batches, spatial_dimension, num_classes] deltas_transpose = scope.get_unique_variable_name(operator.inputs[2].full_name + '_tx') apply_identity(scope, operator.inputs[2].full_name, deltas_transpose, container) image_meta = scope.get_unique_variable_name(operator.inputs[3].full_name + '_tx') apply_identity(scope, operator.inputs[3].full_name, image_meta, container) windows_transpose = norm_boxes_graph(scope, operator, container, oopb, image_meta) delta_mul_output = convert_apply_box_deltas_graph(scope, operator, container, oopb, box_transpose, score_identity, deltas_transpose, windows_transpose) sliced_score = oopb.add_node('Slice', [score_identity, ('_start', oopb.int64, np.array([1], dtype='int64')), ('_end', oopb.int64, np.array([81], dtype='int64')), ('_axes', oopb.int64, np.array([2], dtype='int64')) ], operator.inputs[1].full_name + '_sliced') apply_transpose(scope, sliced_score, score_transpose, container, perm=[0, 2, 1]) # output shape: [num_batches, num_classes, spatial_dimension] max_output_size = scope.get_unique_variable_name('max_output_size') iou_threshold = scope.get_unique_variable_name('iou_threshold') score_threshold = scope.get_unique_variable_name('layer.score_threshold') container.add_initializer(max_output_size, onnx_proto.TensorProto.INT64, [], [DETECTION_MAX_INSTANCES]) container.add_initializer(iou_threshold, onnx_proto.TensorProto.FLOAT, [], [DETECTION_NMS_THRESHOLD]) container.add_initializer(score_threshold, onnx_proto.TensorProto.FLOAT, [], [DETECTION_MIN_CONFIDENCE]) nms_node = next((nd_ for nd_ in operator.nodelist if nd_.type == 'NonMaxSuppressionV3'), operator.nodelist[0]) nms_output = scope.get_unique_variable_name(operator.output_full_names[0] + '_nms') container.add_node("NonMaxSuppression", [delta_mul_output, score_transpose, max_output_size, iou_threshold, score_threshold], nms_output, op_version=operator.target_opset, name=nms_node.name) add_init = scope.get_unique_variable_name('add') container.add_initializer(add_init, onnx_proto.TensorProto.INT64, [1, 3], [0, 1, 0]) nms_output_add = scope.get_unique_variable_name(operator.output_full_names[0] + '_class_add') container.add_node("Add", [nms_output, add_init], nms_output_add, op_version=operator.target_opset, name=nms_node.name + '_class_idx_add') starts_init = scope.get_unique_variable_name('starts') ends_init = scope.get_unique_variable_name('ends') axes_init = scope.get_unique_variable_name('axes') container.add_initializer(starts_init, onnx_proto.TensorProto.INT32, [1], [1]) container.add_initializer(ends_init, onnx_proto.TensorProto.INT32, [1], [2]) container.add_initializer(axes_init, onnx_proto.TensorProto.INT32, [1], [1]) class_idx_output = scope.get_unique_variable_name(operator.output_full_names[0] + '_class_idx') container.add_node("Slice", [nms_output_add, starts_init, ends_init, axes_init], class_idx_output, op_version=operator.target_opset, name=nms_node.name+'_class_idx') # output shape: [num_selected_indices, 1] starts_init_2 = scope.get_unique_variable_name('starts') ends_init_2 = scope.get_unique_variable_name('ends') axes_init_2 = scope.get_unique_variable_name('axes') container.add_initializer(starts_init_2, onnx_proto.TensorProto.INT32, [1], [2]) container.add_initializer(ends_init_2, onnx_proto.TensorProto.INT32, [1], [3]) container.add_initializer(axes_init_2, onnx_proto.TensorProto.INT32, [1], [1]) box_idx_output = scope.get_unique_variable_name(operator.output_full_names[0] + '_box_idx') container.add_node("Slice", [nms_output_add, starts_init_2, ends_init_2, axes_init_2], box_idx_output, op_version=operator.target_opset, name=nms_node.name + '_box_idx') # output shape: [num_selected_indices, 1] box_idx_squeeze = scope.get_unique_variable_name(operator.output_full_names[0] + '_box_idx_squeeze') attrs = {'axes': [1]} container.add_node("Squeeze", box_idx_output, box_idx_squeeze, op_version=operator.target_opset, name=nms_node.name + '_box_idx_squeeze', **attrs) # output shape: [num_selected_indices] starts_init_3 = scope.get_unique_variable_name('starts') ends_init_3 = scope.get_unique_variable_name('ends') axes_init_3 = scope.get_unique_variable_name('axes') step_init_3 = scope.get_unique_variable_name('steps') container.add_initializer(starts_init_3, onnx_proto.TensorProto.INT32, [1], [2]) container.add_initializer(ends_init_3, onnx_proto.TensorProto.INT32, [1], [0]) container.add_initializer(axes_init_3, onnx_proto.TensorProto.INT32, [1], [1]) container.add_initializer(step_init_3, onnx_proto.TensorProto.INT32, [1], [-1]) from keras2onnx.common.data_types import Int32TensorType, FloatTensorType class_box_idx_output = scope.get_local_variable_or_declare_one(operator.output_full_names[0] + '_class_box_idx', type=Int32TensorType(shape=[None, 2])) container.add_node("Slice", [nms_output_add, starts_init_3, ends_init_3, axes_init_3, step_init_3], class_box_idx_output.full_name, op_version=operator.target_opset, name=nms_node.name + '_class_box_idx') # output shape: [num_selected_indices, 2] box_squeeze = scope.get_unique_variable_name(operator.output_full_names[0] + '_box_squeeze') attrs = {'axes': [0]} container.add_node("Squeeze", delta_mul_output, box_squeeze, op_version=operator.target_opset, name=nms_node.name + '_box_squeeze', **attrs) # output shape: [spatial_dimension, 4] score_squeeze = scope.get_local_variable_or_declare_one(operator.output_full_names[0] + '_score_squeeze', type=FloatTensorType(shape=[None])) attrs = {'axes': [0]} container.add_node("Squeeze", score_identity, score_squeeze.full_name, op_version=operator.target_opset, name=nms_node.name + '_score_squeeze', **attrs) # output shape: [spatial_dimension, num_classes] box_gather = scope.get_unique_variable_name(operator.output_full_names[0] + '_box_gather') attrs = {'axis': 0} container.add_node("Gather", [box_squeeze, box_idx_squeeze], box_gather, op_version=operator.target_opset, name=nms_node.name + '_box_gather', **attrs) # output shape: [num_selected_indices, 4] score_gather = scope.get_unique_variable_name(operator.output_full_names[0] + '_score_gather') container.add_node("GatherND", [score_squeeze.full_name, class_box_idx_output.full_name], score_gather, op_version=operator.target_opset, op_domain='com.microsoft', name=nms_node.name + '_score_gather') # output shape: [num_selected_indices] score_gather_unsqueeze = scope.get_unique_variable_name(operator.output_full_names[0] + '_score_gather_unsqueeze') attrs = {'axes': [1]} container.add_node("Unsqueeze", score_gather, score_gather_unsqueeze, op_version=operator.target_opset, name=nms_node.name + '_score_gather_unsqueeze', **attrs) # output shape: [num_selected_indices, 1] top_k_var = scope.get_unique_variable_name('topK') container.add_initializer(top_k_var, onnx_proto.TensorProto.FLOAT, [1], [100.0]) score_gather_shape = oopb.add_node('Shape', [score_gather], operator.inputs[1].full_name + '_score_gather_shape') attrs = {'to': 1} scope_gather_float = oopb.add_node('Cast', [score_gather_shape], operator.inputs[1].full_name + '_scope_gather_float', **attrs) top_k_min = oopb.add_node('Min', [scope_gather_float, top_k_var], operator.inputs[1].full_name + '_top_k_min') attrs = {'to': 7} top_k_min_int = oopb.add_node('Cast', [top_k_min], operator.inputs[1].full_name + '_top_k_min_int', **attrs) score_top_k_output_val = scope.get_unique_variable_name(operator.output_full_names[0] + '_score_top_k_output_val') # output shape: [num_top_K] score_top_k_output_idx = scope.get_unique_variable_name(operator.output_full_names[0] + '_score_top_k_output_idx') # output shape: [num_top_K] attrs = {'axis': 0} container.add_node('TopK', [score_gather, top_k_min_int], [score_top_k_output_val, score_top_k_output_idx], op_version=operator.target_opset, name=nms_node.name + '_topK', **attrs) class_idx_cast = scope.get_unique_variable_name(operator.output_full_names[0] + '_class_idx_cast') attrs = {'to': 1} container.add_node('Cast', class_idx_output, class_idx_cast, op_version=operator.target_opset, name=nms_node.name+'_class_idx_cast', **attrs) # output shape: [num_selected_indices, 1] concat_var = scope.get_unique_variable_name(operator.output_full_names[0] + '_concat_var') concat_node = next((nd_ for nd_ in operator.nodelist if nd_.type == 'Concat'), operator.nodelist[0]) attrs = {'axis': 1} container.add_node("Concat", [box_gather, class_idx_cast, score_gather_unsqueeze], concat_var, op_version=operator.target_opset, name=concat_node.name, **attrs) # output shape: [num_selected_indices, 6] all_gather = scope.get_unique_variable_name(operator.output_full_names[0] + '_all_gather') attrs = {'axis': 0} container.add_node("Gather", [concat_var, score_top_k_output_idx], all_gather, op_version=operator.target_opset, name=nms_node.name + '_all_gather', **attrs) # output shape: [num_top_K, 6] padded_result = oopb.add_node('Pad', [all_gather, np.array([0, 0, DETECTION_MAX_INSTANCES, 0], dtype=np.int64)], nms_node.name + '_padded_result', op_domain='com.microsoft') detection_final = oopb.add_node('Slice', [padded_result, ('_start', oopb.int64, np.array([0], dtype='int64')), ('_end', oopb.int64, np.array([DETECTION_MAX_INSTANCES], dtype='int64')), ('_axes', oopb.int64, np.array([0], dtype='int64')) ], nms_node.name + '_detection_final' ) attrs = {'axes': [0]} container.add_node("Unsqueeze", detection_final, operator.output_full_names[0], op_version=operator.target_opset, name=nms_node.name + '_concat_unsqueeze', **attrs)
def convert_keras_pooling_core(scope, operator, container, n_dims, op_type, input_perm_axes, output_perm_axes): op = operator.raw_operator no_permutation_required = op.data_format == 'channels_first' if hasattr( op, 'data_format') else False if no_permutation_required: adjusted_pooling_input = operator.inputs[0].full_name else: adjusted_pooling_input = scope.get_unique_variable_name( 'input_transposed') preprocessor_type = 'Transpose' preprocessor_attrs = { 'name': scope.get_unique_operator_name(preprocessor_type), 'perm': input_perm_axes } container.add_node(preprocessor_type, operator.inputs[0].full_name, adjusted_pooling_input, **preprocessor_attrs) is_global = type(op).__name__.startswith('Global') op_type_prefix = 'Global' if is_global else '' onnx_op_type = "AveragePool" if op_type == 'Avg' else 'MaxPool' attrs = {} op_version = 10 if container.target_opset >= 10 else 7 if not is_global: attrs['strides'] = list(op.strides) attrs['kernel_shape'] = op.pool_size attrs['op_version'] = op_version # In ONNX opset 10, the ceil_mode attribute was added to local MaxPool and AveragePool if container.target_opset >= 10: attrs['ceil_mode'] = 0 if op.padding == 'valid': attrs['auto_pad'] = 'VALID' elif op.padding == 'same': attrs['auto_pad'] = 'SAME_UPPER' else: raise RuntimeError("Unsupported padding type '{0}'".format( op.padding)) from keras2onnx.common.onnx_ops import OnnxOperatorBuilder oopb = OnnxOperatorBuilder(container, scope) if no_permutation_required: # In this case, the output of our Pool operator just match what Keras produces. pool_result = oopb.add_node(op_type_prefix + onnx_op_type, adjusted_pooling_input, operator.inputs[0].full_name + '_pooling', **attrs) else: # Put the output of Pool operator to an intermediate tensor. Laster we will apply a Transpose to match the # original Keras output format pool_result_1 = oopb.add_node( op_type_prefix + onnx_op_type, adjusted_pooling_input, operator.inputs[0].full_name + '_pooling', **attrs) # Generate a final Transpose pool_result = oopb.add_node('Transpose', pool_result_1, operator.inputs[0].full_name + '_transpose', perm=output_perm_axes) if is_global: import numpy as np squeeze_result = oopb.add_node( 'Reshape', [ pool_result, ('_start', oopb.int64, np.array([0, -1], dtype='int64')) ], operator.inputs[0].full_name + '_reshape') else: squeeze_result = pool_result container.add_node('Identity', squeeze_result, operator.outputs[0].full_name)
def convert_apply_box_deltas_graph(scope, operator, container, oopb, box_transpose, score_identity, deltas_transpose, windows_transpose): oopb = OnnxOperatorBuilder(container, scope) box_squeeze = oopb.apply_squeeze(box_transpose, name=operator.full_name + '_box_squeeze', axes=[0])[0] # output shape: [spatial_dimension, 4] deltas_squeeze = oopb.apply_squeeze(deltas_transpose, name=operator.full_name + '_deltas_squeeze', axes=[0])[0] # output shape: [spatial_dimension, num_classes, 4] score_squeeze = oopb.apply_squeeze(score_identity, name=operator.full_name + '_score_squeeze', axes=[0])[0] # output shape: [spatial_dimension, num_classes] class_ids = scope.get_unique_variable_name('class_ids') attrs = {'axis': 1} container.add_node('ArgMax', score_squeeze, class_ids, op_version=operator.target_opset, **attrs) # output shape: [spatial_dimension, 1] prob_shape = oopb.add_node('Shape', [score_squeeze], operator.inputs[1].full_name + '_prob_shape') prob_shape_0 = oopb.add_node( 'Slice', [ prob_shape, ('_start', oopb.int64, np.array([0], dtype='int64')), ('_end', oopb.int64, np.array([1], dtype='int64')), ('_axes', oopb.int64, np.array([0], dtype='int64')) ], operator.inputs[1].full_name + '_prob_shape_0') prob_range = oopb.add_node( 'Range', [ ('_start', oopb.int64, np.array([0], dtype='int64')), prob_shape_0, # ('_limit', oopb.int64, np.array([1000], dtype='int64')), ('_delta', oopb.int64, np.array([1], dtype='int64')) ], operator.inputs[1].full_name + '_prob_range', op_domain='com.microsoft', op_version=1) prob_range_unsqueeze = oopb.apply_unsqueeze([prob_range], operator.inputs[1].full_name + '_prob_range_unsqueeze', axes=[1])[0] # output shape: [spatial_dimension, 1] attrs = {'axis': 1} indices = oopb.add_node('Concat', [prob_range_unsqueeze, class_ids], operator.inputs[1].full_name + '_indices', **attrs) # output shape: [spatial_dimension, 2] deltas_specific = oopb.add_node( 'GatherND', [deltas_squeeze, indices], operator.inputs[2].full_name + '_deltas_specific') # output shape: [spatial_dimension, 4] BBOX_STD_DEV = np.array([0.1, 0.1, 0.2, 0.2], dtype='float32') delta_mul_output = oopb.add_node( 'Mul', [deltas_specific, ('_mul_constant', oopb.float, BBOX_STD_DEV)], operator.inputs[2].full_name + '_mul') # output shape: [spatial_dimension, 4] box_0 = oopb.add_node('Slice', [ box_squeeze, ('_start', oopb.int64, np.array([0], dtype='int64')), ('_end', oopb.int64, np.array([1], dtype='int64')), ('_axes', oopb.int64, np.array([1], dtype='int64')) ], operator.inputs[0].full_name + '_sliced_0') box_1 = oopb.add_node('Slice', [ box_squeeze, ('_start', oopb.int64, np.array([1], dtype='int64')), ('_end', oopb.int64, np.array([2], dtype='int64')), ('_axes', oopb.int64, np.array([1], dtype='int64')) ], operator.inputs[0].full_name + '_sliced_1') box_2 = oopb.add_node('Slice', [ box_squeeze, ('_start', oopb.int64, np.array([2], dtype='int64')), ('_end', oopb.int64, np.array([3], dtype='int64')), ('_axes', oopb.int64, np.array([1], dtype='int64')) ], operator.inputs[0].full_name + '_sliced_2') box_3 = oopb.add_node('Slice', [ box_squeeze, ('_start', oopb.int64, np.array([3], dtype='int64')), ('_end', oopb.int64, np.array([4], dtype='int64')), ('_axes', oopb.int64, np.array([1], dtype='int64')) ], operator.inputs[0].full_name + '_sliced_3') delta_0 = oopb.add_node('Slice', [ delta_mul_output, ('_start', oopb.int64, np.array([0], dtype='int64')), ('_end', oopb.int64, np.array([1], dtype='int64')), ('_axes', oopb.int64, np.array([1], dtype='int64')) ], operator.inputs[3].full_name + '_sliced_0') delta_1 = oopb.add_node('Slice', [ delta_mul_output, ('_start', oopb.int64, np.array([1], dtype='int64')), ('_end', oopb.int64, np.array([2], dtype='int64')), ('_axes', oopb.int64, np.array([1], dtype='int64')) ], operator.inputs[3].full_name + '_sliced_1') delta_2 = oopb.add_node('Slice', [ delta_mul_output, ('_start', oopb.int64, np.array([2], dtype='int64')), ('_end', oopb.int64, np.array([3], dtype='int64')), ('_axes', oopb.int64, np.array([1], dtype='int64')) ], operator.inputs[3].full_name + '_sliced_2') delta_3 = oopb.add_node('Slice', [ delta_mul_output, ('_start', oopb.int64, np.array([3], dtype='int64')), ('_end', oopb.int64, np.array([4], dtype='int64')), ('_axes', oopb.int64, np.array([1], dtype='int64')) ], operator.inputs[3].full_name + '_sliced_3') height = oopb.add_node('Sub', [box_2, box_0], operator.inputs[0].full_name + '_height') width = oopb.add_node('Sub', [box_3, box_1], operator.inputs[0].full_name + '_width') half_height_0 = oopb.add_node( 'Mul', [ height, ('_mul_constant', oopb.float, np.array([0.5], dtype='float32')) ], operator.inputs[0].full_name + '_half_height_0') half_width_0 = oopb.add_node( 'Mul', [ width, ('_mul_constant', oopb.float, np.array([0.5], dtype='float32')) ], operator.inputs[0].full_name + '_half_width_0') center_y_0 = oopb.add_node('Add', [box_0, half_height_0], operator.inputs[0].full_name + '_center_y_0') center_x_0 = oopb.add_node('Add', [box_1, half_width_0], operator.inputs[0].full_name + '_center_x_0') delta_height = oopb.add_node( 'Mul', [delta_0, height], operator.inputs[0].full_name + '_delta_height') delta_width = oopb.add_node('Mul', [delta_1, width], operator.inputs[0].full_name + '_delta_width') center_y_1 = oopb.add_node('Add', [center_y_0, delta_height], operator.inputs[0].full_name + '_center_y_1') center_x_1 = oopb.add_node('Add', [center_x_0, delta_width], operator.inputs[0].full_name + '_center_x_1') delta_2_exp = oopb.add_node('Exp', [delta_2], operator.inputs[0].full_name + '_delta_2_exp') delta_3_exp = oopb.add_node('Exp', [delta_3], operator.inputs[0].full_name + '_delta_3_exp') height_exp = oopb.add_node('Mul', [height, delta_2_exp], operator.inputs[0].full_name + '_height_exp') width_exp = oopb.add_node('Mul', [width, delta_3_exp], operator.inputs[0].full_name + '_width_exp') half_height_1 = oopb.add_node( 'Mul', [ height_exp, ('_mul_constant', oopb.float, np.array([0.5], dtype='float32')) ], operator.inputs[0].full_name + '_half_height_1') half_width_1 = oopb.add_node( 'Mul', [ width_exp, ('_mul_constant', oopb.float, np.array([0.5], dtype='float32')) ], operator.inputs[0].full_name + '_half_width_1') y1 = oopb.add_node('Sub', [center_y_1, half_height_1], operator.inputs[0].full_name + '_y1') x1 = oopb.add_node('Sub', [center_x_1, half_width_1], operator.inputs[0].full_name + '_x1') y2 = oopb.add_node('Add', [y1, height_exp], operator.inputs[0].full_name + '_y2') x2 = oopb.add_node('Add', [x1, width_exp], operator.inputs[0].full_name + '_x2') windows_squeeze = oopb.apply_squeeze(windows_transpose, name=operator.full_name + '_windows_squeeze', axes=[0])[0] wy1 = oopb.add_node('Slice', [ windows_squeeze, ('_start', oopb.int64, np.array([0], dtype='int64')), ('_end', oopb.int64, np.array([1], dtype='int64')), ('_axes', oopb.int64, np.array([0], dtype='int64')) ], operator.inputs[0].full_name + '_windows_0') wx1 = oopb.add_node('Slice', [ windows_squeeze, ('_start', oopb.int64, np.array([1], dtype='int64')), ('_end', oopb.int64, np.array([2], dtype='int64')), ('_axes', oopb.int64, np.array([0], dtype='int64')) ], operator.inputs[0].full_name + '_windows_1') wy2 = oopb.add_node('Slice', [ windows_squeeze, ('_start', oopb.int64, np.array([2], dtype='int64')), ('_end', oopb.int64, np.array([3], dtype='int64')), ('_axes', oopb.int64, np.array([0], dtype='int64')) ], operator.inputs[0].full_name + '_windows_2') wx2 = oopb.add_node('Slice', [ windows_squeeze, ('_start', oopb.int64, np.array([3], dtype='int64')), ('_end', oopb.int64, np.array([4], dtype='int64')), ('_axes', oopb.int64, np.array([0], dtype='int64')) ], operator.inputs[0].full_name + '_windows_3') y1_min = oopb.add_node('Min', [y1, wy2], operator.inputs[0].full_name + '_y1_min') x1_min = oopb.add_node('Min', [x1, wx2], operator.inputs[0].full_name + '_x1_min') y2_min = oopb.add_node('Min', [y2, wy2], operator.inputs[0].full_name + '_y2_min') x2_min = oopb.add_node('Min', [x2, wx2], operator.inputs[0].full_name + '_x2_min') y1_max = oopb.add_node('Max', [y1_min, wy1], operator.inputs[0].full_name + '_y1_max') x1_max = oopb.add_node('Max', [x1_min, wx1], operator.inputs[0].full_name + '_x1_max') y2_max = oopb.add_node('Max', [y2_min, wy1], operator.inputs[0].full_name + '_y2_max') x2_max = oopb.add_node('Max', [x2_min, wx1], operator.inputs[0].full_name + '_x2_max') concat_result = scope.get_unique_variable_name( operator.output_full_names[0] + '_concat_result') attrs = {'axis': 1} container.add_node("Concat", [y1_max, x1_max, y2_max, x2_max], concat_result, op_version=operator.target_opset, name=operator.outputs[0].full_name + '_concat_result', **attrs) concat_unsqueeze = oopb.apply_unsqueeze(concat_result, name=operator.full_name + '_concat_unsqueeze', axes=[0])[0] return concat_unsqueeze