Skip to content

Commit 8891664

Browse files
committed
torch_script_custom_ops restructure
Signed-off-by: Edward Z. Yang <ezyang@fb.com>
1 parent 0d48b72 commit 8891664

File tree

3 files changed

+80
-65
lines changed

3 files changed

+80
-65
lines changed

advanced_source/torch_script_custom_ops.rst

Lines changed: 31 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ Python and in their serialized form directly in C++.
2323
The following paragraphs give an example of writing a TorchScript custom op to
2424
call into `OpenCV <https://www.opencv.org>`_, a computer vision library written
2525
in C++. We will discuss how to work with tensors in C++, how to efficiently
26-
convert them to third party tensor formats (in this case, OpenCV ``Mat``s), how
26+
convert them to third party tensor formats (in this case, OpenCV ``Mat``), how
2727
to register your operator with the TorchScript runtime and finally how to
2828
compile the operator and use it in Python and C++.
2929

@@ -37,27 +37,10 @@ TorchScript as a custom operator. The first step is to write the implementation
3737
of our custom operator in C++. Let's call the file for this implementation
3838
``op.cpp`` and make it look like this:
3939

40-
.. code-block:: cpp
41-
42-
#include <opencv2/opencv.hpp>
43-
#include <torch/script.h>
44-
45-
torch::Tensor warp_perspective(torch::Tensor image, torch::Tensor warp) {
46-
cv::Mat image_mat(/*rows=*/image.size(0),
47-
/*cols=*/image.size(1),
48-
/*type=*/CV_32FC1,
49-
/*data=*/image.data<float>());
50-
cv::Mat warp_mat(/*rows=*/warp.size(0),
51-
/*cols=*/warp.size(1),
52-
/*type=*/CV_32FC1,
53-
/*data=*/warp.data<float>());
54-
55-
cv::Mat output_mat;
56-
cv::warpPerspective(image_mat, output_mat, warp_mat, /*dsize=*/{8, 8});
57-
58-
torch::Tensor output = torch::from_blob(output_mat.ptr<float>(), /*sizes=*/{8, 8});
59-
return output.clone();
60-
}
40+
.. literalinclude:: ../advanced_source/torch_script_custom_ops/op.cpp
41+
:language: cpp
42+
:start-after: BEGIN warp_perspective
43+
:end-before: END warp_perspective
6144

6245
The code for this operator is quite short. At the top of the file, we include
6346
the OpenCV header file, ``opencv2/opencv.hpp``, alongside the ``torch/script.h``
@@ -92,12 +75,10 @@ tensors to OpenCV matrices, as OpenCV's ``warpPerspective`` expects ``cv::Mat``
9275
objects as inputs. Fortunately, there is a way to do this **without copying
9376
any** data. In the first few lines,
9477

95-
.. code-block:: cpp
96-
97-
cv::Mat image_mat(/*rows=*/image.size(0),
98-
/*cols=*/image.size(1),
99-
/*type=*/CV_32FC1,
100-
/*data=*/image.data<float>());
78+
.. literalinclude:: ../advanced_source/torch_script_custom_ops/op.cpp
79+
:language: cpp
80+
:start-after: BEGIN image_mat
81+
:end-before: END image_mat
10182

10283
we are calling `this constructor
10384
<https://docs.opencv.org/trunk/d3/d63/classcv_1_1Mat.html#a922de793eabcec705b3579c5f95a643e>`_
@@ -113,23 +94,21 @@ subsequent OpenCV routines with the library's native matrix type, even though
11394
we're actually storing the data in a PyTorch tensor. We repeat this procedure to
11495
convert the ``warp`` PyTorch tensor to the ``warp_mat`` OpenCV matrix:
11596

116-
.. code-block:: cpp
117-
118-
cv::Mat warp_mat(/*rows=*/warp.size(0),
119-
/*cols=*/warp.size(1),
120-
/*type=*/CV_32FC1,
121-
/*data=*/warp.data<float>());
97+
.. literalinclude:: ../advanced_source/torch_script_custom_ops/op.cpp
98+
:language: cpp
99+
:start-after: BEGIN warp_mat
100+
:end-before: END warp_mat
122101

123102
Next, we are ready to call the OpenCV function we were so eager to use in
124103
TorchScript: ``warpPerspective``. For this, we pass the OpenCV function the
125104
``image_mat`` and ``warp_mat`` matrices, as well as an empty output matrix
126105
called ``output_mat``. We also specify the size ``dsize`` we want the output
127106
matrix (image) to be. It is hardcoded to ``8 x 8`` for this example:
128107

129-
.. code-block:: cpp
130-
131-
cv::Mat output_mat;
132-
cv::warpPerspective(image_mat, output_mat, warp_mat, /*dsize=*/{8, 8});
108+
.. literalinclude:: ../advanced_source/torch_script_custom_ops/op.cpp
109+
:language: cpp
110+
:start-after: BEGIN output_mat
111+
:end-before: END output_mat
133112

134113
The final step in our custom operator implementation is to convert the
135114
``output_mat`` back into a PyTorch tensor, so that we can further use it in
@@ -139,9 +118,10 @@ other direction. In this case, PyTorch provides a ``torch::from_blob`` method. A
139118
we want to interpret as a PyTorch tensor. The call to ``torch::from_blob`` looks
140119
like this:
141120

142-
.. code-block:: cpp
143-
144-
torch::from_blob(output_mat.ptr<float>(), /*sizes=*/{8, 8})
121+
.. literalinclude:: ../advanced_source/torch_script_custom_ops/op.cpp
122+
:language: cpp
123+
:start-after: BEGIN output_tensor
124+
:end-before: END output_tensor
145125

146126
We use the ``.ptr<float>()`` method on the OpenCV ``Mat`` class to get a raw
147127
pointer to the underlying data (just like ``.data<float>()`` for the PyTorch
@@ -167,10 +147,10 @@ with the TorchScript runtime and compiler. This will allow the TorchScript
167147
compiler to resolve references to our custom operator in TorchScript code.
168148
Registration is very simple. For our case, we need to write:
169149

170-
.. code-block:: cpp
171-
172-
static auto registry =
173-
torch::RegisterOperators("my_ops::warp_perspective", &warp_perspective);
150+
.. literalinclude:: ../advanced_source/torch_script_custom_ops/op.cpp
151+
:language: cpp
152+
:start-after: BEGIN registry
153+
:end-before: END registry
174154

175155
somewhere in the global scope of our ``op.cpp`` file. This creates a global
176156
variable ``registry``, which will register our operator with TorchScript in its
@@ -230,22 +210,8 @@ somewhere accessible in your file system. The following paragraphs will refer to
230210
that location as ``/path/to/libtorch``. The contents of our ``CMakeLists.txt``
231211
file should then be the following:
232212

233-
.. code-block:: cmake
234-
235-
cmake_minimum_required(VERSION 3.1 FATAL_ERROR)
236-
project(warp_perspective)
237-
238-
find_package(Torch REQUIRED)
239-
find_package(OpenCV REQUIRED)
240-
241-
# Define our library target
242-
add_library(warp_perspective SHARED op.cpp)
243-
# Enable C++11
244-
target_compile_features(warp_perspective PRIVATE cxx_range_for)
245-
# Link against LibTorch
246-
target_link_libraries(warp_perspective "${TORCH_LIBRARIES}")
247-
# Link against OpenCV
248-
target_link_libraries(warp_perspective opencv_core opencv_imgproc)
213+
.. literalinclude:: ../advanced_source/torch_script_custom_ops/CMakeLists.txt
214+
:language: cpp
249215

250216
.. warning::
251217

@@ -267,7 +233,7 @@ To now build our operator, we can run the following commands from our
267233
268234
$ mkdir build
269235
$ cd build
270-
$ cmake -DCMAKE_PREFIX_PATH=/path/to/libtorch ..
236+
$ cmake -DCMAKE_PREFIX_PATH=$(python -c 'import torch.utils; print(torch.utils.cmake_prefix_path)') ..
271237
-- The C compiler identification is GNU 5.4.0
272238
-- The CXX compiler identification is GNU 5.4.0
273239
-- Check for working C compiler: /usr/bin/cc
@@ -660,7 +626,7 @@ Along with a small ``CMakeLists.txt`` file:
660626
661627
At this point, we should be able to build the application:
662628
663-
.. code-block:: cpp
629+
.. code-block::
664630
665631
$ mkdir build
666632
$ cd build
@@ -700,7 +666,7 @@ At this point, we should be able to build the application:
700666
701667
And run it without passing a model just yet:
702668
703-
.. code-block:: cpp
669+
.. code-block::
704670
705671
$ ./example_app
706672
usage: example_app <path-to-exported-script-module>
@@ -727,7 +693,7 @@ The last line will serialize the script function into a file called
727693
"example.pt". If we then pass this serialized model to our C++ application, we
728694
can run it straight away:
729695
730-
.. code-block:: cpp
696+
.. code-block::
731697
732698
$ ./example_app example.pt
733699
terminate called after throwing an instance of 'torch::jit::script::ErrorReport'
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
cmake_minimum_required(VERSION 3.1 FATAL_ERROR)
2+
project(warp_perspective)
3+
4+
find_package(Torch REQUIRED)
5+
find_package(OpenCV REQUIRED)
6+
7+
# Define our library target
8+
add_library(warp_perspective SHARED op.cpp)
9+
# Enable C++14
10+
target_compile_features(warp_perspective PRIVATE cxx_std_14)
11+
# Link against LibTorch
12+
target_link_libraries(warp_perspective "${TORCH_LIBRARIES}")
13+
# Link against OpenCV
14+
target_link_libraries(warp_perspective opencv_core opencv_imgproc)
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
#include <opencv2/opencv.hpp>
2+
#include <torch/script.h>
3+
4+
// BEGIN warp_perspective
5+
torch::Tensor warp_perspective(torch::Tensor image, torch::Tensor warp) {
6+
// BEGIN image_mat
7+
cv::Mat image_mat(/*rows=*/image.size(0),
8+
/*cols=*/image.size(1),
9+
/*type=*/CV_32FC1,
10+
/*data=*/image.data_ptr<float>());
11+
// END image_mat
12+
13+
// BEGIN warp_mat
14+
cv::Mat warp_mat(/*rows=*/warp.size(0),
15+
/*cols=*/warp.size(1),
16+
/*type=*/CV_32FC1,
17+
/*data=*/warp.data_ptr<float>());
18+
// END warp_mat
19+
20+
// BEGIN output_mat
21+
cv::Mat output_mat;
22+
cv::warpPerspective(image_mat, output_mat, warp_mat, /*dsize=*/{8, 8});
23+
// END output_mat
24+
25+
// BEGIN output_tensor
26+
torch::Tensor output = torch::from_blob(output_mat.ptr<float>(), /*sizes=*/{8, 8});
27+
return output.clone();
28+
// END output_tensor
29+
}
30+
// END warp_perspective
31+
32+
// BEGIN registry
33+
static auto registry =
34+
torch::RegisterOperators("my_ops::warp_perspective", &warp_perspective);
35+
// END registry

0 commit comments

Comments
 (0)