@@ -23,7 +23,7 @@ Python and in their serialized form directly in C++.
23
23
The following paragraphs give an example of writing a TorchScript custom op to
24
24
call into `OpenCV <https://www.opencv.org >`_, a computer vision library written
25
25
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
27
27
to register your operator with the TorchScript runtime and finally how to
28
28
compile the operator and use it in Python and C++.
29
29
@@ -37,27 +37,10 @@ TorchScript as a custom operator. The first step is to write the implementation
37
37
of our custom operator in C++. Let's call the file for this implementation
38
38
``op.cpp `` and make it look like this:
39
39
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
61
44
62
45
The code for this operator is quite short. At the top of the file, we include
63
46
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``
92
75
objects as inputs. Fortunately, there is a way to do this **without copying
93
76
any ** data. In the first few lines,
94
77
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
101
82
102
83
we are calling `this constructor
103
84
<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
113
94
we're actually storing the data in a PyTorch tensor. We repeat this procedure to
114
95
convert the ``warp `` PyTorch tensor to the ``warp_mat `` OpenCV matrix:
115
96
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
122
101
123
102
Next, we are ready to call the OpenCV function we were so eager to use in
124
103
TorchScript: ``warpPerspective ``. For this, we pass the OpenCV function the
125
104
``image_mat `` and ``warp_mat `` matrices, as well as an empty output matrix
126
105
called ``output_mat ``. We also specify the size ``dsize `` we want the output
127
106
matrix (image) to be. It is hardcoded to ``8 x 8 `` for this example:
128
107
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
133
112
134
113
The final step in our custom operator implementation is to convert the
135
114
``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
139
118
we want to interpret as a PyTorch tensor. The call to ``torch::from_blob `` looks
140
119
like this:
141
120
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
145
125
146
126
We use the ``.ptr<float>() `` method on the OpenCV ``Mat `` class to get a raw
147
127
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
167
147
compiler to resolve references to our custom operator in TorchScript code.
168
148
Registration is very simple. For our case, we need to write:
169
149
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
174
154
175
155
somewhere in the global scope of our ``op.cpp `` file. This creates a global
176
156
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
230
210
that location as ``/path/to/libtorch ``. The contents of our ``CMakeLists.txt ``
231
211
file should then be the following:
232
212
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
249
215
250
216
.. warning ::
251
217
@@ -267,7 +233,7 @@ To now build our operator, we can run the following commands from our
267
233
268
234
$ mkdir build
269
235
$ 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) ' ) ..
271
237
-- The C compiler identification is GNU 5.4.0
272
238
-- The CXX compiler identification is GNU 5.4.0
273
239
-- Check for working C compiler: /usr/bin/cc
@@ -660,7 +626,7 @@ Along with a small ``CMakeLists.txt`` file:
660
626
661
627
At this point, we should be able to build the application:
662
628
663
- .. code-block:: cpp
629
+ .. code-block::
664
630
665
631
$ mkdir build
666
632
$ cd build
@@ -700,7 +666,7 @@ At this point, we should be able to build the application:
700
666
701
667
And run it without passing a model just yet:
702
668
703
- .. code-block:: cpp
669
+ .. code-block::
704
670
705
671
$ ./example_app
706
672
usage: example_app <path-to-exported-script-module>
@@ -727,7 +693,7 @@ The last line will serialize the script function into a file called
727
693
"example.pt". If we then pass this serialized model to our C++ application, we
728
694
can run it straight away:
729
695
730
- .. code-block:: cpp
696
+ .. code-block::
731
697
732
698
$ ./example_app example.pt
733
699
terminate called after throwing an instance of 'torch::jit::script::ErrorReport'
0 commit comments