custom_layers.hpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283
  1. #ifndef __OPENCV_SAMPLES_DNN_CUSTOM_LAYERS__
  2. #define __OPENCV_SAMPLES_DNN_CUSTOM_LAYERS__
  3. #include <opencv2/dnn.hpp>
  4. #include <opencv2/dnn/shape_utils.hpp> // getPlane
  5. //! [InterpLayer]
  6. class InterpLayer : public cv::dnn::Layer
  7. {
  8. public:
  9. InterpLayer(const cv::dnn::LayerParams &params) : Layer(params)
  10. {
  11. outWidth = params.get<int>("width", 0);
  12. outHeight = params.get<int>("height", 0);
  13. }
  14. static cv::Ptr<cv::dnn::Layer> create(cv::dnn::LayerParams& params)
  15. {
  16. return cv::Ptr<cv::dnn::Layer>(new InterpLayer(params));
  17. }
  18. virtual bool getMemoryShapes(const std::vector<std::vector<int> > &inputs,
  19. const int requiredOutputs,
  20. std::vector<std::vector<int> > &outputs,
  21. std::vector<std::vector<int> > &internals) const CV_OVERRIDE
  22. {
  23. CV_UNUSED(requiredOutputs); CV_UNUSED(internals);
  24. std::vector<int> outShape(4);
  25. outShape[0] = inputs[0][0]; // batch size
  26. outShape[1] = inputs[0][1]; // number of channels
  27. outShape[2] = outHeight;
  28. outShape[3] = outWidth;
  29. outputs.assign(1, outShape);
  30. return false;
  31. }
  32. // Implementation of this custom layer is based on https://github.com/cdmh/deeplab-public/blob/master/src/caffe/layers/interp_layer.cpp
  33. virtual void forward(cv::InputArrayOfArrays inputs_arr,
  34. cv::OutputArrayOfArrays outputs_arr,
  35. cv::OutputArrayOfArrays internals_arr) CV_OVERRIDE
  36. {
  37. if (inputs_arr.depth() == CV_16S)
  38. {
  39. // In case of DNN_TARGET_OPENCL_FP16 target the following method
  40. // converts data from FP16 to FP32 and calls this forward again.
  41. forward_fallback(inputs_arr, outputs_arr, internals_arr);
  42. return;
  43. }
  44. std::vector<cv::Mat> inputs, outputs;
  45. inputs_arr.getMatVector(inputs);
  46. outputs_arr.getMatVector(outputs);
  47. cv::Mat& inp = inputs[0];
  48. cv::Mat& out = outputs[0];
  49. const float* inpData = (float*)inp.data;
  50. float* outData = (float*)out.data;
  51. const int batchSize = inp.size[0];
  52. const int numChannels = inp.size[1];
  53. const int inpHeight = inp.size[2];
  54. const int inpWidth = inp.size[3];
  55. const float rheight = (outHeight > 1) ? static_cast<float>(inpHeight - 1) / (outHeight - 1) : 0.f;
  56. const float rwidth = (outWidth > 1) ? static_cast<float>(inpWidth - 1) / (outWidth - 1) : 0.f;
  57. for (int h2 = 0; h2 < outHeight; ++h2)
  58. {
  59. const float h1r = rheight * h2;
  60. const int h1 = static_cast<int>(h1r);
  61. const int h1p = (h1 < inpHeight - 1) ? 1 : 0;
  62. const float h1lambda = h1r - h1;
  63. const float h0lambda = 1.f - h1lambda;
  64. for (int w2 = 0; w2 < outWidth; ++w2)
  65. {
  66. const float w1r = rwidth * w2;
  67. const int w1 = static_cast<int>(w1r);
  68. const int w1p = (w1 < inpWidth - 1) ? 1 : 0;
  69. const float w1lambda = w1r - w1;
  70. const float w0lambda = 1.f - w1lambda;
  71. const float* pos1 = inpData + h1 * inpWidth + w1;
  72. float* pos2 = outData + h2 * outWidth + w2;
  73. for (int c = 0; c < batchSize * numChannels; ++c)
  74. {
  75. pos2[0] =
  76. h0lambda * (w0lambda * pos1[0] + w1lambda * pos1[w1p]) +
  77. h1lambda * (w0lambda * pos1[h1p * inpWidth] + w1lambda * pos1[h1p * inpWidth + w1p]);
  78. pos1 += inpWidth * inpHeight;
  79. pos2 += outWidth * outHeight;
  80. }
  81. }
  82. }
  83. }
  84. private:
  85. int outWidth, outHeight;
  86. };
  87. //! [InterpLayer]
  88. //! [ResizeBilinearLayer]
  89. class ResizeBilinearLayer CV_FINAL : public cv::dnn::Layer
  90. {
  91. public:
  92. ResizeBilinearLayer(const cv::dnn::LayerParams &params) : Layer(params)
  93. {
  94. CV_Assert(!params.get<bool>("align_corners", false));
  95. CV_Assert(!blobs.empty());
  96. for (size_t i = 0; i < blobs.size(); ++i)
  97. CV_Assert(blobs[i].type() == CV_32SC1);
  98. // There are two cases of input blob: a single blob which contains output
  99. // shape and two blobs with scaling factors.
  100. if (blobs.size() == 1)
  101. {
  102. CV_Assert(blobs[0].total() == 2);
  103. outHeight = blobs[0].at<int>(0, 0);
  104. outWidth = blobs[0].at<int>(0, 1);
  105. factorHeight = factorWidth = 0;
  106. }
  107. else
  108. {
  109. CV_Assert(blobs.size() == 2); CV_Assert(blobs[0].total() == 1); CV_Assert(blobs[1].total() == 1);
  110. factorHeight = blobs[0].at<int>(0, 0);
  111. factorWidth = blobs[1].at<int>(0, 0);
  112. outHeight = outWidth = 0;
  113. }
  114. }
  115. static cv::Ptr<cv::dnn::Layer> create(cv::dnn::LayerParams& params)
  116. {
  117. return cv::Ptr<cv::dnn::Layer>(new ResizeBilinearLayer(params));
  118. }
  119. virtual bool getMemoryShapes(const std::vector<std::vector<int> > &inputs,
  120. const int,
  121. std::vector<std::vector<int> > &outputs,
  122. std::vector<std::vector<int> > &) const CV_OVERRIDE
  123. {
  124. std::vector<int> outShape(4);
  125. outShape[0] = inputs[0][0]; // batch size
  126. outShape[1] = inputs[0][1]; // number of channels
  127. outShape[2] = outHeight != 0 ? outHeight : (inputs[0][2] * factorHeight);
  128. outShape[3] = outWidth != 0 ? outWidth : (inputs[0][3] * factorWidth);
  129. outputs.assign(1, outShape);
  130. return false;
  131. }
  132. virtual void finalize(cv::InputArrayOfArrays, cv::OutputArrayOfArrays outputs_arr) CV_OVERRIDE
  133. {
  134. std::vector<cv::Mat> outputs;
  135. outputs_arr.getMatVector(outputs);
  136. if (!outWidth && !outHeight)
  137. {
  138. outHeight = outputs[0].size[2];
  139. outWidth = outputs[0].size[3];
  140. }
  141. }
  142. // This implementation is based on a reference implementation from
  143. // https://github.com/tensorflow/tensorflow/blob/master/tensorflow/contrib/lite/kernels/internal/reference/reference_ops.h
  144. virtual void forward(cv::InputArrayOfArrays inputs_arr,
  145. cv::OutputArrayOfArrays outputs_arr,
  146. cv::OutputArrayOfArrays internals_arr) CV_OVERRIDE
  147. {
  148. if (inputs_arr.depth() == CV_16S)
  149. {
  150. // In case of DNN_TARGET_OPENCL_FP16 target the following method
  151. // converts data from FP16 to FP32 and calls this forward again.
  152. forward_fallback(inputs_arr, outputs_arr, internals_arr);
  153. return;
  154. }
  155. std::vector<cv::Mat> inputs, outputs;
  156. inputs_arr.getMatVector(inputs);
  157. outputs_arr.getMatVector(outputs);
  158. cv::Mat& inp = inputs[0];
  159. cv::Mat& out = outputs[0];
  160. const float* inpData = (float*)inp.data;
  161. float* outData = (float*)out.data;
  162. const int batchSize = inp.size[0];
  163. const int numChannels = inp.size[1];
  164. const int inpHeight = inp.size[2];
  165. const int inpWidth = inp.size[3];
  166. float heightScale = static_cast<float>(inpHeight) / outHeight;
  167. float widthScale = static_cast<float>(inpWidth) / outWidth;
  168. for (int b = 0; b < batchSize; ++b)
  169. {
  170. for (int y = 0; y < outHeight; ++y)
  171. {
  172. float input_y = y * heightScale;
  173. int y0 = static_cast<int>(std::floor(input_y));
  174. int y1 = std::min(y0 + 1, inpHeight - 1);
  175. for (int x = 0; x < outWidth; ++x)
  176. {
  177. float input_x = x * widthScale;
  178. int x0 = static_cast<int>(std::floor(input_x));
  179. int x1 = std::min(x0 + 1, inpWidth - 1);
  180. for (int c = 0; c < numChannels; ++c)
  181. {
  182. float interpolation =
  183. inpData[offset(inp.size, c, x0, y0, b)] * (1 - (input_y - y0)) * (1 - (input_x - x0)) +
  184. inpData[offset(inp.size, c, x0, y1, b)] * (input_y - y0) * (1 - (input_x - x0)) +
  185. inpData[offset(inp.size, c, x1, y0, b)] * (1 - (input_y - y0)) * (input_x - x0) +
  186. inpData[offset(inp.size, c, x1, y1, b)] * (input_y - y0) * (input_x - x0);
  187. outData[offset(out.size, c, x, y, b)] = interpolation;
  188. }
  189. }
  190. }
  191. }
  192. }
  193. private:
  194. static inline int offset(const cv::MatSize& size, int c, int x, int y, int b)
  195. {
  196. return x + size[3] * (y + size[2] * (c + size[1] * b));
  197. }
  198. int outWidth, outHeight, factorWidth, factorHeight;
  199. };
  200. //! [ResizeBilinearLayer]
  201. //
  202. // The following code is used only to generate tutorials documentation.
  203. //
  204. //! [A custom layer interface]
  205. class MyLayer : public cv::dnn::Layer
  206. {
  207. public:
  208. //! [MyLayer::MyLayer]
  209. MyLayer(const cv::dnn::LayerParams &params);
  210. //! [MyLayer::MyLayer]
  211. //! [MyLayer::create]
  212. static cv::Ptr<cv::dnn::Layer> create(cv::dnn::LayerParams& params);
  213. //! [MyLayer::create]
  214. //! [MyLayer::getMemoryShapes]
  215. virtual bool getMemoryShapes(const std::vector<std::vector<int> > &inputs,
  216. const int requiredOutputs,
  217. std::vector<std::vector<int> > &outputs,
  218. std::vector<std::vector<int> > &internals) const CV_OVERRIDE;
  219. //! [MyLayer::getMemoryShapes]
  220. //! [MyLayer::forward]
  221. virtual void forward(cv::InputArrayOfArrays inputs,
  222. cv::OutputArrayOfArrays outputs,
  223. cv::OutputArrayOfArrays internals) CV_OVERRIDE;
  224. //! [MyLayer::forward]
  225. //! [MyLayer::finalize]
  226. virtual void finalize(cv::InputArrayOfArrays inputs,
  227. cv::OutputArrayOfArrays outputs) CV_OVERRIDE;
  228. //! [MyLayer::finalize]
  229. };
  230. //! [A custom layer interface]
  231. //! [Register a custom layer]
  232. #include <opencv2/dnn/layer.details.hpp> // CV_DNN_REGISTER_LAYER_CLASS
  233. static inline void loadNet()
  234. {
  235. CV_DNN_REGISTER_LAYER_CLASS(Interp, InterpLayer);
  236. // ...
  237. //! [Register a custom layer]
  238. //! [Register InterpLayer]
  239. CV_DNN_REGISTER_LAYER_CLASS(Interp, InterpLayer);
  240. cv::dnn::Net caffeNet = cv::dnn::readNet("/path/to/config.prototxt", "/path/to/weights.caffemodel");
  241. //! [Register InterpLayer]
  242. //! [Register ResizeBilinearLayer]
  243. CV_DNN_REGISTER_LAYER_CLASS(ResizeBilinear, ResizeBilinearLayer);
  244. cv::dnn::Net tfNet = cv::dnn::readNet("/path/to/graph.pb");
  245. //! [Register ResizeBilinearLayer]
  246. if (false) loadNet(); // To prevent unused function warning.
  247. }
  248. #endif // __OPENCV_SAMPLES_DNN_CUSTOM_LAYERS__