@@ -23,7 +23,7 @@ Python and in their serialized form directly in C++.
2323The following paragraphs give an example of writing a TorchScript custom op to
2424call into `OpenCV <https://www.opencv.org >`_, a computer vision library written
2525in 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
2727to register your operator with the TorchScript runtime and finally how to
2828compile 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
3737of 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
6245The code for this operator is quite short. At the top of the file, we include
6346the 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``
9275objects as inputs. Fortunately, there is a way to do this **without copying
9376any ** 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
10283we 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
11394we're actually storing the data in a PyTorch tensor. We repeat this procedure to
11495convert 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
123102Next, we are ready to call the OpenCV function we were so eager to use in
124103TorchScript: ``warpPerspective ``. For this, we pass the OpenCV function the
125104``image_mat `` and ``warp_mat `` matrices, as well as an empty output matrix
126105called ``output_mat ``. We also specify the size ``dsize `` we want the output
127106matrix (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
134113The 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
139118we want to interpret as a PyTorch tensor. The call to ``torch::from_blob `` looks
140119like 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
146126We use the ``.ptr<float>() `` method on the OpenCV ``Mat `` class to get a raw
147127pointer 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
167147compiler to resolve references to our custom operator in TorchScript code.
168148Registration 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
175155somewhere in the global scope of our ``op.cpp `` file. This creates a global
176156variable ``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
230210that location as ``/path/to/libtorch ``. The contents of our ``CMakeLists.txt ``
231211file 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
661627At 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
701667And 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
728694can 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'
0 commit comments