opencv_frame.h 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323
  1. /*************************************************************************
  2. * Copyright (C) [2020] by Cambricon, Inc. All rights reserved
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * The above copyright notice and this permission notice shall be included in
  11. * all copies or substantial portions of the Software.
  12. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
  13. * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  14. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
  15. * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  16. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  17. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  18. * THE SOFTWARE.
  19. *************************************************************************/
  20. #ifndef INFER_SERVER_OPENCV_FRAME_H_
  21. #define INFER_SERVER_OPENCV_FRAME_H_
  22. #include <opencv2/opencv.hpp>
  23. #include <map>
  24. #include <utility>
  25. #include <vector>
  26. #include "video_helper.h"
  27. // a header only implementation
  28. namespace infer_server {
  29. namespace video {
  30. struct OpencvFrame {
  31. cv::Mat img;
  32. PixelFmt fmt;
  33. BoundingBox roi;
  34. };
  35. namespace detail {
  36. void ClipBoundingBox(BoundingBox* box) noexcept;
  37. } // namespace detail
  38. struct OpencvPreproc {
  39. public:
  40. OpencvPreproc(PixelFmt dst_fmt, std::vector<float> mean, std::vector<float> std, bool normalize,
  41. bool keep_aspect_ratio, int pad_value, bool transpose, DataType src_depth)
  42. : dst_fmt_(dst_fmt),
  43. mean_(std::move(mean)),
  44. std_(std::move(std)),
  45. normalize_(normalize),
  46. keep_aspect_ratio_(keep_aspect_ratio),
  47. transpose_(transpose),
  48. pad_value_(pad_value),
  49. depth_(src_depth) {
  50. if (normalize_) {
  51. if (mean_.empty()) mean_.resize(GetChannelNum(), 0.f);
  52. if (std_.empty()) std_.resize(GetChannelNum(), 1.f);
  53. for (auto& m : mean_) {
  54. m *= 255;
  55. }
  56. for (auto& s : std_) {
  57. s *= 255;
  58. }
  59. }
  60. if (mean_.empty() && !std_.empty()) mean_ = {0.f, 0.f, 0.f, 0.f};
  61. if (!mean_.empty() && std_.empty()) std_ = {1.f, 1.f, 1.f, 1.f};
  62. mean_std_ = (!mean_.empty()) && (!std_.empty());
  63. }
  64. bool operator()(ModelIO* model_input, const InferData& in, const ModelInfo& m) {
  65. const auto& frame = in.GetLref<OpencvFrame>();
  66. cv::Mat tmp_cvt;
  67. auto src_fmt = frame.fmt;
  68. if (src_fmt != dst_fmt_) {
  69. int code = GetCvtCode(src_fmt);
  70. if (code == -1) {
  71. return false;
  72. }
  73. cv::cvtColor(frame.img, tmp_cvt, code);
  74. } else {
  75. tmp_cvt = frame.img;
  76. }
  77. cv::Mat img;
  78. auto roi = frame.roi;
  79. if (roi.w == 0 || roi.h == 0) {
  80. img = tmp_cvt;
  81. } else {
  82. detail::ClipBoundingBox(&roi);
  83. img = tmp_cvt(
  84. cv::Rect(roi.x * tmp_cvt.cols, roi.y * tmp_cvt.rows, roi.w * tmp_cvt.cols, roi.h * tmp_cvt.rows));
  85. }
  86. cv::Mat tmp_transpose;
  87. if (transpose_) {
  88. cv::transpose(img, tmp_transpose);
  89. } else {
  90. tmp_transpose = img;
  91. }
  92. auto src_width = tmp_transpose.cols;
  93. auto src_height = tmp_transpose.rows;
  94. DimOrder input_order = m.InputLayout(0).order;
  95. auto s = m.InputShape(0);
  96. int dst_width, dst_height;
  97. if (input_order == DimOrder::NCHW) {
  98. dst_width = s[3];
  99. dst_height = s[2];
  100. } else if (input_order == DimOrder::NHWC) {
  101. dst_width = s[2];
  102. dst_height = s[1];
  103. } else {
  104. std::cerr << "not supported dim order";
  105. return false;
  106. }
  107. Buffer& b = model_input->buffers[0];
  108. cv::Mat tmp_resize;
  109. if (src_height != dst_height || src_width != dst_width) {
  110. if (keep_aspect_ratio_) {
  111. if (dst_fmt_ == PixelFmt::RGBA || dst_fmt_ == PixelFmt::BGRA) {
  112. tmp_resize = cv::Mat(dst_height, dst_width, CV_8UC4, cv::Scalar(pad_value_));
  113. } else if (dst_fmt_ == PixelFmt::RGB24 || dst_fmt_ == PixelFmt::BGR24) {
  114. tmp_resize = cv::Mat(dst_height, dst_width, CV_8UC3, cv::Scalar(pad_value_));
  115. } else {
  116. std::cerr << "unsupport fmt for model input." << std::endl;
  117. }
  118. auto rect = KeepAspectRatio(src_width, src_height, dst_width, dst_height);
  119. cv::Mat resize_keepaspectratio = tmp_resize(rect);
  120. cv::resize(tmp_transpose, resize_keepaspectratio, cv::Size(rect.width, rect.height));
  121. } else {
  122. cv::resize(tmp_transpose, tmp_resize, cv::Size(dst_width, dst_height));
  123. }
  124. } else {
  125. tmp_resize = tmp_transpose;
  126. }
  127. uint32_t channel_num = GetChannelNum();
  128. if (channel_num == 0) {
  129. std::cerr << "unsupport fmt for model input." << std::endl;
  130. return false;
  131. }
  132. DataType dst_dtype = m.InputLayout(0).dtype;
  133. int cv_code = CV_MAKETYPE(dst_dtype == DataType::UINT8 ? CV_8U : CV_32F, channel_num);
  134. cv::Mat dst(dst_height, dst_width, cv_code, b.MutableData());
  135. if (mean_std_) {
  136. if (dst_dtype == DataType::UINT8) {
  137. std::cerr << "Opencv preproc not support normalization, means or std with UINT8 data type" << std::endl;
  138. return false;
  139. }
  140. if (channel_num != mean_.size() || channel_num != std_.size()) {
  141. std::cerr << "expect the size of mean or std is " << channel_num << " but mean_ size is " << mean_.size()
  142. << " and std_ size is " << std_.size() << "." << std::endl;
  143. return false;
  144. }
  145. MeanStd(tmp_resize, dst);
  146. } else if (dst_dtype != DataType::UINT8) {
  147. tmp_resize.convertTo(dst, cv_code, 1.0/255.0);
  148. } else {
  149. tmp_resize.copyTo(dst);
  150. }
  151. return true;
  152. }
  153. private:
  154. uint32_t GetChannelNum();
  155. cv::Rect KeepAspectRatio(int src_w, int src_h, int dst_w, int dst_h);
  156. bool MeanStd(cv::Mat img, cv::Mat dst);
  157. int GetCvtCode(PixelFmt src_fmt);
  158. private:
  159. PixelFmt dst_fmt_;
  160. std::vector<float> mean_;
  161. std::vector<float> std_;
  162. bool mean_std_ = false;
  163. bool normalize_ = false;
  164. bool keep_aspect_ratio_ = true;
  165. bool transpose_ = false;
  166. int pad_value_ = 0;
  167. DataType depth_ = DataType::UINT8;
  168. public:
  169. static PreprocessorHost::ProcessFunction GetFunction(PixelFmt dst_fmt, std::vector<float> mean = {},
  170. std::vector<float> std = {}, bool normalize = false,
  171. bool keep_aspect_ratio = true, int pad_value = 0,
  172. bool transpose = false, DataType src_depth = DataType::UINT8) {
  173. return PreprocessorHost::ProcessFunction(
  174. OpencvPreproc(dst_fmt, std::move(mean), std::move(std), normalize,
  175. keep_aspect_ratio, pad_value, transpose, src_depth));
  176. }
  177. };
  178. inline uint32_t OpencvPreproc::GetChannelNum() {
  179. switch (dst_fmt_) {
  180. case PixelFmt::RGB24:
  181. case PixelFmt::BGR24:
  182. return 3;
  183. case PixelFmt::RGBA:
  184. case PixelFmt::BGRA:
  185. return 4;
  186. default:
  187. std::cerr << "Unsupport dst_fmt in OpencvPreproc." << std::endl;
  188. }
  189. return 0;
  190. }
  191. inline cv::Rect OpencvPreproc::KeepAspectRatio(int src_w, int src_h, int dst_w, int dst_h) {
  192. float src_ratio = static_cast<float>(src_w) / src_h;
  193. float dst_ratio = static_cast<float>(dst_w) / dst_h;
  194. cv::Rect res;
  195. if (src_ratio < dst_ratio) {
  196. int pad_lenth = dst_w - src_ratio * dst_h;
  197. pad_lenth = (pad_lenth % 2) ? pad_lenth - 1 : pad_lenth;
  198. if (dst_w - pad_lenth / 2 < 0) return {};
  199. res.width = dst_w - pad_lenth;
  200. res.x = pad_lenth / 2;
  201. res.y = 0;
  202. res.height = dst_h;
  203. } else if (src_ratio > dst_ratio) {
  204. int pad_lenth = dst_h - dst_w / src_ratio;
  205. pad_lenth = (pad_lenth % 2) ? pad_lenth - 1 : pad_lenth;
  206. if (dst_h - pad_lenth / 2 < 0) return {};
  207. res.height = dst_h - pad_lenth;
  208. res.y = pad_lenth / 2;
  209. res.x = 0;
  210. res.width = dst_w;
  211. } else {
  212. res.x = 0;
  213. res.y = 0;
  214. res.width = dst_w;
  215. res.height = dst_h;
  216. }
  217. return res;
  218. }
  219. inline bool OpencvPreproc::MeanStd(cv::Mat img, cv::Mat dst) {
  220. float* input_data = reinterpret_cast<float*>(dst.data);
  221. size_t len = img.rows * img.cols;
  222. uint32_t channel_num = GetChannelNum();
  223. if (depth_ == DataType::UINT8) {
  224. const uint8_t* iimg = reinterpret_cast<const uint8_t*>(img.data);
  225. for (uint32_t idx = 0; idx < len; ++idx) {
  226. for (uint32_t ch = 0; ch < channel_num; ++ch) {
  227. input_data[idx * channel_num + ch] = (static_cast<float>(iimg[idx * channel_num + ch]) - mean_[ch]) / std_[ch];
  228. }
  229. }
  230. } else {
  231. const float* fimg = reinterpret_cast<const float*>(img.data);
  232. for (uint32_t idx = 0; idx < len; ++idx) {
  233. for (uint32_t ch = 0; ch < channel_num; ++ch) {
  234. input_data[idx * channel_num + ch] = (fimg[idx * channel_num + ch] - mean_[ch]) / std_[ch];
  235. }
  236. }
  237. }
  238. return true;
  239. }
  240. inline int OpencvPreproc::GetCvtCode(PixelFmt src_fmt) {
  241. // clang-format off
  242. // dst_fmt: {src_fmt : cvt_code}
  243. static std::map<PixelFmt, std::map<PixelFmt, int>> color_cvt_map{
  244. {PixelFmt::BGR24,
  245. {
  246. {PixelFmt::RGB24, cv::COLOR_RGB2BGR},
  247. {PixelFmt::RGBA, cv::COLOR_RGBA2BGR},
  248. {PixelFmt::BGRA, cv::COLOR_BGRA2BGR},
  249. {PixelFmt::NV12, cv::COLOR_YUV2BGR_NV12},
  250. {PixelFmt::NV21, cv::COLOR_YUV2BGR_NV21},
  251. {PixelFmt::I420, cv::COLOR_YUV2BGR_I420}
  252. },
  253. },
  254. {PixelFmt::RGB24,
  255. {
  256. {PixelFmt::BGR24, cv::COLOR_BGR2RGB},
  257. {PixelFmt::RGBA, cv::COLOR_RGBA2RGB},
  258. {PixelFmt::BGRA, cv::COLOR_BGRA2RGB},
  259. {PixelFmt::NV12, cv::COLOR_YUV2RGB_NV12},
  260. {PixelFmt::NV21, cv::COLOR_YUV2RGB_NV21},
  261. {PixelFmt::I420, cv::COLOR_YUV2RGB_I420}
  262. },
  263. },
  264. {PixelFmt::RGBA,
  265. {
  266. {PixelFmt::BGR24, cv::COLOR_BGR2RGBA},
  267. {PixelFmt::RGB24, cv::COLOR_RGB2RGBA},
  268. {PixelFmt::BGRA, cv::COLOR_BGRA2RGBA},
  269. {PixelFmt::NV12, cv::COLOR_YUV2RGBA_NV12},
  270. {PixelFmt::NV21, cv::COLOR_YUV2RGBA_NV21},
  271. {PixelFmt::I420, cv::COLOR_YUV2RGBA_I420}
  272. },
  273. },
  274. {PixelFmt::BGRA,
  275. {
  276. {PixelFmt::BGR24, cv::COLOR_BGR2BGRA},
  277. {PixelFmt::RGB24, cv::COLOR_RGB2BGRA},
  278. {PixelFmt::RGBA, cv::COLOR_RGBA2BGRA},
  279. {PixelFmt::NV12, cv::COLOR_YUV2BGRA_NV12},
  280. {PixelFmt::NV21, cv::COLOR_YUV2BGRA_NV21},
  281. {PixelFmt::I420, cv::COLOR_YUV2BGRA_I420}
  282. }
  283. }
  284. };
  285. // clang-format on
  286. try {
  287. return color_cvt_map.at(dst_fmt_).at(src_fmt);
  288. } catch (std::out_of_range& e) {
  289. std::cerr << "Unsupport code for cvtcolor." << std::endl;
  290. return -1;
  291. }
  292. }
  293. } // namespace video
  294. } // namespace infer_server
  295. #endif // INFER_SERVER_OPENCV_FRAME_H_