|
7 | 7 | # best to use those functions because they can produce ONNX operators according to the ONNX version specified in the |
8 | 8 | # `container` argument. Notice that those function behaviors are defined in a way very similar to ONNX-1.2. |
9 | 9 |
|
| 10 | +import numpy as np |
10 | 11 | from ...proto import onnx_proto |
11 | 12 |
|
12 | 13 | def _create_name_or_use_existing_one(scope, op_type, name): |
@@ -438,3 +439,46 @@ def apply_softmax(scope, input_name, output_name, container, operator_name=None, |
438 | 439 | def apply_normalization(scope, input_name, output_name, container, operator_name=None, axis=1, p=2): |
439 | 440 | name = _create_name_or_use_existing_one(scope, 'LpNormalization', operator_name) |
440 | 441 | container.add_node('LpNormalization', input_name, output_name, name=name, p=p, axis=axis) |
| 442 | + |
| 443 | +def apply_crop_height_width(scope, input_name, output_name, container, operator_name=None, |
| 444 | + top_border=0, bottom_border=0, left_border=0, right_border=0): |
| 445 | + name = scope.get_unique_operator_name('CropHeightWidth') |
| 446 | + if container.target_opset < 9: |
| 447 | + # If operator set < 9, we can use the experimental Crop in ONNX. |
| 448 | + attrs = {'name': name, 'border': [left_border, top_border, right_border, bottom_border]} |
| 449 | + container.add_node('Crop', input_name, output_name, **attrs) |
| 450 | + else: |
| 451 | + # The experimental Crop in ONNX is removed after operator set 9, so we |
| 452 | + # switch to ONNX DynamicSlice operator. |
| 453 | + |
| 454 | + # CoreML only crops H- and W-axes. |
| 455 | + axes = [2, 3] |
| 456 | + axes_name = scope.get_unique_variable_name(name + '_axes') |
| 457 | + container.add_initializer(axes_name, onnx_proto.TensorProto.INT64, |
| 458 | + [len(axes)], axes) |
| 459 | + |
| 460 | + # Number of cropped pixels is the starting index of the remained region. |
| 461 | + starts = [top_border, left_border] |
| 462 | + starts_name = scope.get_unique_variable_name(name + '_starts') |
| 463 | + container.add_initializer(starts_name, onnx_proto.TensorProto.INT64, |
| 464 | + [len(starts)], starts) |
| 465 | + |
| 466 | + # First we assume no cropping is needed at the end of those axes. |
| 467 | + # We will change this right below depending on Crop's configuration. |
| 468 | + ends = [np.iinfo(np.int64).max] * 2 |
| 469 | + |
| 470 | + # Crop n pixel means the end index (exclusive) is -n. Note that indexing |
| 471 | + # system is zero-based. |
| 472 | + if bottom_border > 0: |
| 473 | + ends[0] = -bottom_border |
| 474 | + if right_border > 0: |
| 475 | + ends[1] = -right_border |
| 476 | + |
| 477 | + # Add the adjusted ends. |
| 478 | + ends_name = scope.get_unique_variable_name(name + '_ends') |
| 479 | + container.add_initializer(ends_name, onnx_proto.TensorProto.INT64, |
| 480 | + [len(ends)], ends) |
| 481 | + |
| 482 | + # Collect all input names as a list because DynamicSlice has multiple inputs. |
| 483 | + input_list = [input_name, starts_name, ends_name, axes_name] |
| 484 | + container.add_node('DynamicSlice', input_list, output_name, op_version=9) |
0 commit comments