Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add affine_grid op #5225

Merged
merged 39 commits into from
Jul 31, 2023
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
c54ccf4
initial change
liqunfu May 13, 2023
6de815c
passed 2d non-align_corners function body in parser test
liqunfu May 14, 2023
275293d
passed 2/3-d align or not corvers with parser test
liqunfu May 14, 2023
6a02339
update def FunctionBody and test models
liqunfu May 14, 2023
13ff153
shape inference (incomplete), reference implementation, and more tests
liqunfu May 22, 2023
3cec59d
all tests passed, fixed most comments
liqunfu May 22, 2023
cfdd63c
replace mul of ones with add of zeros
liqunfu May 22, 2023
ba3d48f
merge
liqunfu May 23, 2023
eed4463
lint and undo parser test
liqunfu May 23, 2023
eee0c33
size to be int64 only - shape dtype
liqunfu May 23, 2023
86233f7
skip affine_grid test_backend_onnxruntime.py
liqunfu May 23, 2023
945b5be
skip test comment
liqunfu May 23, 2023
f195e19
lint
liqunfu May 23, 2023
82329ed
lint
liqunfu May 23, 2023
3122077
all_tensor_types_ir4
liqunfu May 23, 2023
30ef8c4
docs
liqunfu May 23, 2023
2de0700
formatting
liqunfu May 23, 2023
9f20a65
formatting
liqunfu May 23, 2023
0cb6410
formatting
liqunfu May 23, 2023
2639591
function mody to avoid using epsilon
liqunfu May 30, 2023
dcd567a
Merge branch 'main' into liqun/affine_grid
liqunfu Jun 21, 2023
6de7e00
use reference implementation to generate test case for affine_grid
liqunfu Jun 22, 2023
364c39c
has both test types
liqunfu Jun 27, 2023
5bd2de8
merge main
liqunfu Jul 9, 2023
090ae54
lint
liqunfu Jul 10, 2023
76b0aad
lint
liqunfu Jul 10, 2023
05e6ae5
crlf to lf
liqunfu Jul 11, 2023
ac4627e
Merge branch 'main' into liqun/affine_grid
liqunfu Jul 11, 2023
a5b9d23
TestCoverage.md
liqunfu Jul 11, 2023
ba2f2e3
symbolic tests
liqunfu Jul 11, 2023
31d670e
Merge branch 'main' into liqun/affine_grid
liqunfu Jul 19, 2023
63baf6d
docs/TestCoverage.md
liqunfu Jul 19, 2023
07e48cc
reviewers' comments
liqunfu Jul 27, 2023
29e72ac
Merge branch 'main' into liqun/affine_grid
liqunfu Jul 27, 2023
c9b9cc8
remove unneeded test cases that use ref evaluator
liqunfu Jul 27, 2023
57c49e8
lint
liqunfu Jul 27, 2023
bc12bd6
add comment for assert
liqunfu Jul 31, 2023
45ac099
Merge branch 'main' into liqun/affine_grid
liqunfu Jul 31, 2023
f47cdcd
lint
liqunfu Jul 31, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 74 additions & 1 deletion docs/TestCoverage.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
* [Overall Test Coverage](#overall-test-coverage)
# Node Test Coverage
## Summary
Node tests have covered 173/186 (93.01%, 5 generators excluded) common operators.
Node tests have covered 174/187 (93.05%, 5 generators excluded) common operators.

Node tests have covered 0/0 (N/A) experimental operators.

Expand Down Expand Up @@ -344,6 +344,79 @@ expect(node, inputs=[x, y], outputs=[x + y], name="test_add_uint8")
</details>


### AffineGrid
There are 2 test cases, listed as following:
<details>
<summary>2d</summary>

```python
angle = np.array([np.pi / 4, np.pi / 3])
offset_x = np.array([5.0, 2.5])
offset_y = np.array([-3.3, 1.1])
shear_x = np.array([-0.5, 0.5])
shear_y = np.array([0.3, -0.3])
scale_x = np.array([2.2, 1.1])
scale_y = np.array([3.1, 0.9])
theta_2d = create_affine_matrix_2d(angle, offset_x, offset_y, shear_x, shear_y, scale_x, scale_y)
N, C, W, H = len(angle), 3, 5, 6
data_size = (W, H)
for align_corners in [0, 1]:
node = onnx.helper.make_node(
"AffineGrid",
inputs=["theta", "size"],
outputs=["grid"],
align_corners=align_corners
)

original_grid = construct_original_grid(data_size, align_corners)
grid = apply_affine_transform(theta_2d, original_grid)

test_name = "test_affine_grid_2d"
if align_corners == 1:
test_name += "_align_corners"
expect(node, inputs=[theta_2d, np.array([N, C, W, H])], outputs=[grid], name=test_name)
```

</details>
<details>
<summary>3d</summary>

```python
angle1 = np.array([np.pi / 4, np.pi / 3])
angle2 = np.array([np.pi / 6, np.pi / 2])
offset_x = np.array([5.0, 2.5])
offset_y = np.array([-3.3, 1.1])
offset_z = np.array([-1.1, 2.2])
shear_x = np.array([-0.5, 0.5])
shear_y = np.array([0.3, -0.3])
shear_z = np.array([0.7, -0.2])
scale_x = np.array([2.2, 1.1])
scale_y = np.array([3.1, 0.9])
scale_z = np.array([0.5, 1.5])

theta_3d = create_affine_matrix_3d(angle1, angle2, offset_x, offset_y, offset_z, shear_x, shear_y, shear_z, scale_x, scale_y, scale_z)
N, C, D, W, H = len(angle1), 3, 4, 5, 6
data_size = (D, W, H)
for align_corners in [0, 1]:
node = onnx.helper.make_node(
"AffineGrid",
inputs=["theta", "size"],
outputs=["grid"],
align_corners=align_corners
)

original_grid = construct_original_grid(data_size, align_corners)
grid = apply_affine_transform(theta_3d, original_grid)

test_name = "test_affine_grid_3d"
if align_corners == 1:
test_name += "_align_corners"
expect(node, inputs=[theta_3d, np.array([N, C, D, W, H])], outputs=[grid], name=test_name)
```

</details>


### And
There are 2 test cases, listed as following:
<details>
Expand Down
107 changes: 107 additions & 0 deletions onnx/backend/test/case/node/affinegrid.py
liqunfu marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
# Copyright (c) ONNX Project Contributors
#
# SPDX-License-Identifier: Apache-2.0

import numpy as np

import onnx
from onnx.backend.test.case.base import Base
from onnx.backend.test.case.node import expect
from onnx.reference.ops.op_affine_grid import construct_original_grid, apply_affine_transform

def create_affine_matrix_3d(angle1, angle2, offset_x, offset_y, offset_z, shear_x, shear_y, shear_z, scale_x, scale_y, scale_z):
rot_x = np.stack([np.ones_like(angle1), np.zeros_like(angle1), np.zeros_like(angle1),
np.zeros_like(angle1), np.cos(angle1), -np.sin(angle1),
np.zeros_like(angle1), np.sin(angle1), np.cos(angle1)], axis=-1).reshape(-1, 3, 3)
rot_y = np.stack([np.cos(angle2), np.zeros_like(angle2), np.sin(angle2),
np.zeros_like(angle2), np.ones_like(angle2), np.zeros_like(angle2),
-np.sin(angle2), np.zeros_like(angle2), np.cos(angle2)], axis=-1).reshape(-1, 3, 3)
shear = np.stack([np.ones_like(shear_x), shear_x, shear_y,
shear_z, np.ones_like(shear_x), shear_x,
shear_y, shear_x, np.ones_like(shear_x)], axis=-1).reshape(-1, 3, 3)
scale = np.stack([scale_x, np.zeros_like(scale_x), np.zeros_like(scale_x),
np.zeros_like(scale_x), scale_y, np.zeros_like(scale_x),
np.zeros_like(scale_x), np.zeros_like(scale_x), scale_z], axis=-1).reshape(-1, 3, 3)
translation = np.transpose(np.array([offset_x, offset_y, offset_z])).reshape(-1, 1, 3)
rotation_matrix = rot_y @ rot_x @ shear @ scale # (N, 3, 3)
rotation_matrix = np.transpose(rotation_matrix, (0, 2, 1))
affine_matrix = np.hstack((rotation_matrix, translation))
affine_matrix = np.transpose(affine_matrix, (0, 2, 1))
return affine_matrix

def create_affine_matrix_2d(angle1, offset_x, offset_y, shear_x, shear_y, scale_x, scale_y):
rot = np.stack([np.cos(angle1), -np.sin(angle1),
np.sin(angle1), np.cos(angle1)], axis=-1).reshape(-1, 2, 2)
shear = np.stack([np.ones_like(shear_x), shear_x,
shear_y, np.ones_like(shear_x)], axis=-1).reshape(-1, 2, 2)
scale = np.stack([scale_x, np.zeros_like(scale_x),
np.zeros_like(scale_x), scale_y], axis=-1).reshape(-1, 2, 2)
translation = np.transpose(np.array([offset_x, offset_y])).reshape(-1, 1, 2)
rotation_matrix = rot @ shear @ scale # (N, 3, 3)
rotation_matrix = np.transpose(rotation_matrix, (0, 2, 1))
affine_matrix = np.hstack((rotation_matrix, translation))
affine_matrix = np.transpose(affine_matrix, (0, 2, 1))
return affine_matrix


class AffineGrid(Base):
@staticmethod
def export_2d() -> None:
angle = np.array([np.pi / 4, np.pi / 3])
offset_x = np.array([5.0, 2.5])
offset_y = np.array([-3.3, 1.1])
shear_x = np.array([-0.5, 0.5])
shear_y = np.array([0.3, -0.3])
scale_x = np.array([2.2, 1.1])
scale_y = np.array([3.1, 0.9])
theta_2d = create_affine_matrix_2d(angle, offset_x, offset_y, shear_x, shear_y, scale_x, scale_y)
N, C, W, H = len(angle), 3, 5, 6
data_size = (W, H)
for align_corners in [0, 1]:
liqunfu marked this conversation as resolved.
Show resolved Hide resolved
node = onnx.helper.make_node(
"AffineGrid",
inputs=["theta", "size"],
outputs=["grid"],
align_corners=align_corners
)

original_grid = construct_original_grid(data_size, align_corners)
grid = apply_affine_transform(theta_2d, original_grid)

test_name = "test_affine_grid_2d"
if align_corners == 1:
test_name += "_align_corners"
expect(node, inputs=[theta_2d, np.array([N, C, W, H])], outputs=[grid], name=test_name)

@staticmethod
def export_3d() -> None:
angle1 = np.array([np.pi / 4, np.pi / 3])
angle2 = np.array([np.pi / 6, np.pi / 2])
offset_x = np.array([5.0, 2.5])
offset_y = np.array([-3.3, 1.1])
offset_z = np.array([-1.1, 2.2])
shear_x = np.array([-0.5, 0.5])
shear_y = np.array([0.3, -0.3])
shear_z = np.array([0.7, -0.2])
scale_x = np.array([2.2, 1.1])
scale_y = np.array([3.1, 0.9])
scale_z = np.array([0.5, 1.5])

theta_3d = create_affine_matrix_3d(angle1, angle2, offset_x, offset_y, offset_z, shear_x, shear_y, shear_z, scale_x, scale_y, scale_z)
N, C, D, W, H = len(angle1), 3, 4, 5, 6
data_size = (D, W, H)
for align_corners in [0, 1]:
node = onnx.helper.make_node(
"AffineGrid",
inputs=["theta", "size"],
outputs=["grid"],
align_corners=align_corners
)

original_grid = construct_original_grid(data_size, align_corners)
grid = apply_affine_transform(theta_3d, original_grid)

test_name = "test_affine_grid_3d"
if align_corners == 1:
test_name += "_align_corners"
expect(node, inputs=[theta_3d, np.array([N, C, D, W, H])], outputs=[grid], name=test_name)
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
2 changes: 2 additions & 0 deletions onnx/defs/operator_sets.h
Original file line number Diff line number Diff line change
Expand Up @@ -1102,12 +1102,14 @@ class OpSet_Onnx_ver19 {
};

// Forward declarations for ai.onnx version 20
class ONNX_OPERATOR_SET_SCHEMA_CLASS_NAME(Onnx, 20, AffineGrid);
class ONNX_OPERATOR_SET_SCHEMA_CLASS_NAME(Onnx, 20, GridSample);

// Iterate over schema from ai.onnx version 20
class OpSet_Onnx_ver20 {
public:
static void ForEachSchema(std::function<void(OpSchema&&)> fn) {
fn(GetOpSchema<ONNX_OPERATOR_SET_SCHEMA_CLASS_NAME(Onnx, 20, AffineGrid)>());
fn(GetOpSchema<ONNX_OPERATOR_SET_SCHEMA_CLASS_NAME(Onnx, 20, GridSample)>());
}
};
Expand Down
Loading