gapi_infer_onnx_test.cpp 42 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126
  1. // This file is part of OpenCV project.
  2. // It is subject to the license terms in the LICENSE file found in the top-level directory
  3. // of this distribution and at http://opencv.org/license.html.
  4. //
  5. // Copyright (C) 2020 Intel Corporation
  6. #include "../test_precomp.hpp"
  7. #ifdef HAVE_ONNX
  8. #include <stdexcept>
  9. #include <codecvt> // wstring_convert
  10. #include <onnxruntime_cxx_api.h>
  11. #include <ade/util/iota_range.hpp>
  12. #include <ade/util/algorithm.hpp>
  13. #include <opencv2/gapi/own/convert.hpp>
  14. #include <opencv2/gapi/infer/onnx.hpp>
  15. namespace {
  16. class TestMediaBGR final: public cv::MediaFrame::IAdapter {
  17. cv::Mat m_mat;
  18. using Cb = cv::MediaFrame::View::Callback;
  19. Cb m_cb;
  20. public:
  21. explicit TestMediaBGR(cv::Mat m, Cb cb = [](){})
  22. : m_mat(m), m_cb(cb) {
  23. }
  24. cv::GFrameDesc meta() const override {
  25. return cv::GFrameDesc{cv::MediaFormat::BGR, cv::Size(m_mat.cols, m_mat.rows)};
  26. }
  27. cv::MediaFrame::View access(cv::MediaFrame::Access) override {
  28. cv::MediaFrame::View::Ptrs pp = { m_mat.ptr(), nullptr, nullptr, nullptr };
  29. cv::MediaFrame::View::Strides ss = { m_mat.step, 0u, 0u, 0u };
  30. return cv::MediaFrame::View(std::move(pp), std::move(ss), Cb{m_cb});
  31. }
  32. };
  33. class TestMediaNV12 final: public cv::MediaFrame::IAdapter {
  34. cv::Mat m_y;
  35. cv::Mat m_uv;
  36. public:
  37. TestMediaNV12(cv::Mat y, cv::Mat uv) : m_y(y), m_uv(uv) {
  38. }
  39. cv::GFrameDesc meta() const override {
  40. return cv::GFrameDesc{cv::MediaFormat::NV12, cv::Size(m_y.cols, m_y.rows)};
  41. }
  42. cv::MediaFrame::View access(cv::MediaFrame::Access) override {
  43. cv::MediaFrame::View::Ptrs pp = {
  44. m_y.ptr(), m_uv.ptr(), nullptr, nullptr
  45. };
  46. cv::MediaFrame::View::Strides ss = {
  47. m_y.step, m_uv.step, 0u, 0u
  48. };
  49. return cv::MediaFrame::View(std::move(pp), std::move(ss));
  50. }
  51. };
  52. struct ONNXInitPath {
  53. ONNXInitPath() {
  54. cvtest::addDataSearchEnv("OPENCV_GAPI_ONNX_MODEL_PATH");
  55. }
  56. };
  57. static ONNXInitPath g_init_path;
  58. cv::Mat initMatrixRandU(const int type, const cv::Size& sz_in) {
  59. const cv::Mat in_mat = cv::Mat(sz_in, type);
  60. if (CV_MAT_DEPTH(type) < CV_32F) {
  61. cv::randu(in_mat, cv::Scalar::all(0), cv::Scalar::all(255));
  62. } else {
  63. const int fscale = 256; // avoid bits near ULP, generate stable test input
  64. cv::Mat in_mat32s(in_mat.size(), CV_MAKE_TYPE(CV_32S, CV_MAT_CN(type)));
  65. cv::randu(in_mat32s, cv::Scalar::all(0), cv::Scalar::all(255 * fscale));
  66. in_mat32s.convertTo(in_mat, type, 1.0f / fscale, 0);
  67. }
  68. return in_mat;
  69. }
  70. } // anonymous namespace
  71. namespace opencv_test
  72. {
  73. namespace {
  74. // FIXME: taken from the DNN module
  75. void normAssert(cv::InputArray& ref, cv::InputArray& test,
  76. const char *comment /*= ""*/,
  77. const double l1 = 0.00001, const double lInf = 0.0001) {
  78. const double normL1 = cvtest::norm(ref, test, cv::NORM_L1) / ref.getMat().total();
  79. EXPECT_LE(normL1, l1) << comment;
  80. const double normInf = cvtest::norm(ref, test, cv::NORM_INF);
  81. EXPECT_LE(normInf, lInf) << comment;
  82. }
  83. inline std::string findModel(const std::string &model_name) {
  84. return findDataFile("vision/" + model_name + ".onnx", false);
  85. }
  86. inline void toCHW(const cv::Mat& src, cv::Mat& dst) {
  87. dst.create(cv::Size(src.cols, src.rows * src.channels()), CV_32F);
  88. std::vector<cv::Mat> planes;
  89. for (int i = 0; i < src.channels(); ++i) {
  90. planes.push_back(dst.rowRange(i * src.rows, (i + 1) * src.rows));
  91. }
  92. cv::split(src, planes);
  93. }
  94. inline int toCV(ONNXTensorElementDataType prec) {
  95. switch (prec) {
  96. case ONNX_TENSOR_ELEMENT_DATA_TYPE_UINT8: return CV_8U;
  97. case ONNX_TENSOR_ELEMENT_DATA_TYPE_FLOAT: return CV_32F;
  98. case ONNX_TENSOR_ELEMENT_DATA_TYPE_INT32: return CV_32S;
  99. case ONNX_TENSOR_ELEMENT_DATA_TYPE_INT64: return CV_32S;
  100. default: GAPI_Error("Unsupported data type");
  101. }
  102. return -1;
  103. }
  104. void copyFromONNX(Ort::Value &v, cv::Mat& mat) {
  105. const auto info = v.GetTensorTypeAndShapeInfo();
  106. const auto prec = info.GetElementType();
  107. const auto shape = info.GetShape();
  108. const std::vector<int> dims(shape.begin(), shape.end());
  109. mat.create(dims, toCV(prec));
  110. switch (prec) {
  111. #define HANDLE(E,T) \
  112. case E: std::copy_n(v.GetTensorMutableData<T>(), \
  113. mat.total(), \
  114. reinterpret_cast<T*>(mat.data)); \
  115. break;
  116. HANDLE(ONNX_TENSOR_ELEMENT_DATA_TYPE_UINT8, uint8_t);
  117. HANDLE(ONNX_TENSOR_ELEMENT_DATA_TYPE_FLOAT, float);
  118. HANDLE(ONNX_TENSOR_ELEMENT_DATA_TYPE_INT32, int);
  119. #undef HANDLE
  120. case ONNX_TENSOR_ELEMENT_DATA_TYPE_INT64: {
  121. const auto o_ptr = v.GetTensorMutableData<int64_t>();
  122. const auto g_ptr = reinterpret_cast<int*>(mat.data);
  123. std::transform(o_ptr, o_ptr + mat.total(), g_ptr,
  124. [](int64_t el) { return static_cast<int>(el); });
  125. break;
  126. }
  127. default: GAPI_Error("ONNX. Unsupported data type");
  128. }
  129. }
  130. inline std::vector<int64_t> toORT(const cv::MatSize &sz) {
  131. return cv::to_own<int64_t>(sz);
  132. }
  133. inline std::vector<const char*> getCharNames(const std::vector<std::string>& names) {
  134. std::vector<const char*> out_ptrs;
  135. out_ptrs.reserve(names.size());
  136. ade::util::transform(names, std::back_inserter(out_ptrs),
  137. [](const std::string& name) { return name.c_str(); });
  138. return out_ptrs;
  139. }
  140. template<typename T>
  141. void copyToOut(const cv::Mat& onnx_out, const T end_mark, cv::Mat& gapi_out) {
  142. // This function is part of some remap__ function.
  143. // You can set graph output size (gapi_out) larger than real out from ONNX
  144. // so you have to add something for separate correct data and garbage.
  145. // For example, end of data can be marked with -1 (for positive values)
  146. // or you can put size of correct data at first/last element of output matrix.
  147. const size_t size = std::min(onnx_out.total(), gapi_out.total());
  148. std::copy(onnx_out.begin<T>(),
  149. onnx_out.begin<T>() + size,
  150. gapi_out.begin<T>());
  151. if (gapi_out.total() > onnx_out.total()) {
  152. T* gptr = gapi_out.ptr<T>();
  153. gptr[size] = end_mark;
  154. }
  155. }
  156. void remapYolo(const std::unordered_map<std::string, cv::Mat> &onnx,
  157. std::unordered_map<std::string, cv::Mat> &gapi) {
  158. GAPI_Assert(onnx.size() == 1u);
  159. GAPI_Assert(gapi.size() == 1u);
  160. // Result from Run method
  161. const cv::Mat& in = onnx.begin()->second;
  162. GAPI_Assert(in.depth() == CV_32F);
  163. // Configured output
  164. cv::Mat& out = gapi.begin()->second;
  165. // Simple copy
  166. copyToOut<float>(in, -1.f, out);
  167. }
  168. void remapYoloV3(const std::unordered_map<std::string, cv::Mat> &onnx,
  169. std::unordered_map<std::string, cv::Mat> &gapi) {
  170. // Simple copy for outputs
  171. const cv::Mat& in_boxes = onnx.at("yolonms_layer_1/ExpandDims_1:0");
  172. const cv::Mat& in_scores = onnx.at("yolonms_layer_1/ExpandDims_3:0");
  173. const cv::Mat& in_indices = onnx.at("yolonms_layer_1/concat_2:0");
  174. GAPI_Assert(in_boxes.depth() == CV_32F);
  175. GAPI_Assert(in_scores.depth() == CV_32F);
  176. GAPI_Assert(in_indices.depth() == CV_32S);
  177. cv::Mat& out_boxes = gapi.at("out1");
  178. cv::Mat& out_scores = gapi.at("out2");
  179. cv::Mat& out_indices = gapi.at("out3");
  180. copyToOut<float>(in_boxes, -1.f, out_boxes);
  181. copyToOut<float>(in_scores, -1.f, out_scores);
  182. copyToOut<int>(in_indices, -1, out_indices);
  183. }
  184. void remapToIESSDOut(const std::vector<cv::Mat> &detections,
  185. cv::Mat &ssd_output) {
  186. GAPI_Assert(detections.size() == 4u);
  187. for (const auto &det_el : detections) {
  188. GAPI_Assert(det_el.depth() == CV_32F);
  189. GAPI_Assert(!det_el.empty());
  190. }
  191. // SSD-MobilenetV1 structure check
  192. ASSERT_EQ(1u, detections[0].total());
  193. ASSERT_EQ(detections[2].total(), detections[0].total() * 100);
  194. ASSERT_EQ(detections[2].total(), detections[3].total());
  195. ASSERT_EQ((detections[2].total() * 4), detections[1].total());
  196. const int num_objects = static_cast<int>(detections[0].ptr<float>()[0]);
  197. GAPI_Assert(num_objects <= (ssd_output.size[2] - 1));
  198. const float *in_boxes = detections[1].ptr<float>();
  199. const float *in_scores = detections[2].ptr<float>();
  200. const float *in_classes = detections[3].ptr<float>();
  201. float *ptr = ssd_output.ptr<float>();
  202. for (int i = 0; i < num_objects; ++i) {
  203. ptr[0] = 0.f; // "image_id"
  204. ptr[1] = in_classes[i]; // "label"
  205. ptr[2] = in_scores[i]; // "confidence"
  206. ptr[3] = in_boxes[4 * i + 1]; // left
  207. ptr[4] = in_boxes[4 * i + 0]; // top
  208. ptr[5] = in_boxes[4 * i + 3]; // right
  209. ptr[6] = in_boxes[4 * i + 2]; // bottom
  210. ptr += 7;
  211. in_boxes += 4;
  212. }
  213. if (num_objects < ssd_output.size[2] - 1) {
  214. // put a -1 mark at the end of output blob if there is space left
  215. ptr[0] = -1.f;
  216. }
  217. }
  218. void remapSSDPorts(const std::unordered_map<std::string, cv::Mat> &onnx,
  219. std::unordered_map<std::string, cv::Mat> &gapi) {
  220. // Assemble ONNX-processed outputs back to a single 1x1x200x7 blob
  221. // to preserve compatibility with OpenVINO-based SSD pipeline
  222. const cv::Mat &num_detections = onnx.at("num_detections:0");
  223. const cv::Mat &detection_boxes = onnx.at("detection_boxes:0");
  224. const cv::Mat &detection_scores = onnx.at("detection_scores:0");
  225. const cv::Mat &detection_classes = onnx.at("detection_classes:0");
  226. cv::Mat &ssd_output = gapi.at("detection_output");
  227. remapToIESSDOut({num_detections, detection_boxes, detection_scores, detection_classes}, ssd_output);
  228. }
  229. void reallocSSDPort(const std::unordered_map<std::string, cv::Mat> &/*onnx*/,
  230. std::unordered_map<std::string, cv::Mat> &gapi) {
  231. gapi["detection_boxes"].create(1000, 3000, CV_32FC3);
  232. }
  233. void remapRCNNPortsC(const std::unordered_map<std::string, cv::Mat> &onnx,
  234. std::unordered_map<std::string, cv::Mat> &gapi) {
  235. // Simple copy for outputs
  236. const cv::Mat& in_boxes = onnx.at("6379");
  237. const cv::Mat& in_labels = onnx.at("6381");
  238. const cv::Mat& in_scores = onnx.at("6383");
  239. GAPI_Assert(in_boxes.depth() == CV_32F);
  240. GAPI_Assert(in_labels.depth() == CV_32S);
  241. GAPI_Assert(in_scores.depth() == CV_32F);
  242. cv::Mat& out_boxes = gapi.at("out1");
  243. cv::Mat& out_labels = gapi.at("out2");
  244. cv::Mat& out_scores = gapi.at("out3");
  245. copyToOut<float>(in_boxes, -1.f, out_boxes);
  246. copyToOut<int>(in_labels, -1, out_labels);
  247. copyToOut<float>(in_scores, -1.f, out_scores);
  248. }
  249. void remapRCNNPortsDO(const std::unordered_map<std::string, cv::Mat> &onnx,
  250. std::unordered_map<std::string, cv::Mat> &gapi) {
  251. // Simple copy for outputs
  252. const cv::Mat& in_boxes = onnx.at("6379");
  253. const cv::Mat& in_scores = onnx.at("6383");
  254. GAPI_Assert(in_boxes.depth() == CV_32F);
  255. GAPI_Assert(in_scores.depth() == CV_32F);
  256. cv::Mat& out_boxes = gapi.at("out1");
  257. cv::Mat& out_scores = gapi.at("out2");
  258. copyToOut<float>(in_boxes, -1.f, out_boxes);
  259. copyToOut<float>(in_scores, -1.f, out_scores);
  260. }
  261. class ONNXtest : public ::testing::Test {
  262. public:
  263. std::string model_path;
  264. size_t num_in, num_out;
  265. std::vector<cv::Mat> out_gapi;
  266. std::vector<cv::Mat> out_onnx;
  267. cv::Mat in_mat;
  268. ONNXtest() {
  269. env = Ort::Env(ORT_LOGGING_LEVEL_WARNING, "test");
  270. memory_info = Ort::MemoryInfo::CreateCpu(OrtArenaAllocator, OrtMemTypeDefault);
  271. out_gapi.resize(1);
  272. }
  273. template<typename T>
  274. void infer(const std::vector<cv::Mat>& ins,
  275. std::vector<cv::Mat>& outs,
  276. std::vector<std::string>&& custom_out_names = {}) {
  277. // Prepare session
  278. #ifndef _WIN32
  279. session = Ort::Session(env, model_path.c_str(), session_options);
  280. #else
  281. std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t> converter;
  282. std::wstring w_model_path = converter.from_bytes(model_path.c_str());
  283. session = Ort::Session(env, w_model_path.c_str(), session_options);
  284. #endif
  285. num_in = session.GetInputCount();
  286. num_out = session.GetOutputCount();
  287. GAPI_Assert(num_in == ins.size());
  288. in_node_names.clear();
  289. out_node_names.clear();
  290. // Inputs Run params
  291. std::vector<Ort::Value> in_tensors;
  292. for(size_t i = 0; i < num_in; ++i) {
  293. auto in_node_name_p = session.GetInputNameAllocated(i, allocator);
  294. in_node_names.emplace_back(in_node_name_p.get());
  295. in_node_dims = toORT(ins[i].size);
  296. in_tensors.emplace_back(Ort::Value::CreateTensor<T>(memory_info,
  297. const_cast<T*>(ins[i].ptr<T>()),
  298. ins[i].total(),
  299. in_node_dims.data(),
  300. in_node_dims.size()));
  301. }
  302. // Outputs Run params
  303. if (custom_out_names.empty()) {
  304. for(size_t i = 0; i < num_out; ++i) {
  305. auto out_node_name_p = session.GetOutputNameAllocated(i, allocator);
  306. out_node_names.emplace_back(out_node_name_p.get());
  307. }
  308. } else {
  309. out_node_names = std::move(custom_out_names);
  310. }
  311. // Input/output order by names
  312. const auto in_run_names = getCharNames(in_node_names);
  313. const auto out_run_names = getCharNames(out_node_names);
  314. num_out = out_run_names.size();
  315. // Run
  316. auto result = session.Run(Ort::RunOptions{nullptr},
  317. in_run_names.data(),
  318. &in_tensors.front(),
  319. num_in,
  320. out_run_names.data(),
  321. num_out);
  322. // Copy outputs
  323. GAPI_Assert(result.size() == num_out);
  324. for (size_t i = 0; i < num_out; ++i) {
  325. const auto info = result[i].GetTensorTypeAndShapeInfo();
  326. const auto shape = info.GetShape();
  327. const auto type = toCV(info.GetElementType());
  328. const std::vector<int> dims(shape.begin(), shape.end());
  329. outs.emplace_back(dims, type);
  330. copyFromONNX(result[i], outs.back());
  331. }
  332. }
  333. // One input/output overload
  334. template<typename T>
  335. void infer(const cv::Mat& in, cv::Mat& out) {
  336. std::vector<cv::Mat> result;
  337. infer<T>(std::vector<cv::Mat>{in}, result);
  338. GAPI_Assert(result.size() == 1u);
  339. out = result.front();
  340. }
  341. // One input overload
  342. template<typename T>
  343. void infer(const cv::Mat& in,
  344. std::vector<cv::Mat>& outs,
  345. std::vector<std::string>&& custom_out_names = {}) {
  346. infer<T>(std::vector<cv::Mat>{in}, outs, std::move(custom_out_names));
  347. }
  348. void validate() {
  349. GAPI_Assert(!out_gapi.empty() && !out_onnx.empty());
  350. ASSERT_EQ(out_gapi.size(), out_onnx.size());
  351. const auto size = out_gapi.size();
  352. for (size_t i = 0; i < size; ++i) {
  353. normAssert(out_onnx[i], out_gapi[i], "Test outputs");
  354. }
  355. }
  356. void useModel(const std::string& model_name) {
  357. model_path = findModel(model_name);
  358. }
  359. private:
  360. Ort::Env env{nullptr};
  361. Ort::MemoryInfo memory_info{nullptr};
  362. Ort::AllocatorWithDefaultOptions allocator;
  363. Ort::SessionOptions session_options;
  364. Ort::Session session{nullptr};
  365. std::vector<int64_t> in_node_dims;
  366. std::vector<std::string> in_node_names;
  367. std::vector<std::string> out_node_names;
  368. };
  369. class ONNXClassification : public ONNXtest {
  370. public:
  371. const cv::Scalar mean = { 0.485, 0.456, 0.406 };
  372. const cv::Scalar std = { 0.229, 0.224, 0.225 };
  373. // Rois for InferList, InferList2
  374. const std::vector<cv::Rect> rois = {
  375. cv::Rect(cv::Point{ 0, 0}, cv::Size{80, 120}),
  376. cv::Rect(cv::Point{50, 100}, cv::Size{250, 360})
  377. };
  378. // FIXME(dm): There's too much "preprocess" routines in this file
  379. // Only one must stay but better design it wisely (and later)
  380. void preprocess(const cv::Mat& src, cv::Mat& dst, bool norm = true) {
  381. const int new_h = 224;
  382. const int new_w = 224;
  383. cv::Mat tmp, cvt, rsz;
  384. cv::resize(src, rsz, cv::Size(new_w, new_h));
  385. rsz.convertTo(cvt, CV_32F, norm ? 1.f / 255 : 1.f);
  386. tmp = norm
  387. ? (cvt - mean) / std
  388. : cvt;
  389. toCHW(tmp, dst);
  390. dst = dst.reshape(1, {1, 3, new_h, new_w});
  391. }
  392. };
  393. class ONNXMediaFrame : public ONNXClassification {
  394. public:
  395. const std::vector<cv::Rect> rois = {
  396. cv::Rect(cv::Point{ 0, 0}, cv::Size{80, 120}),
  397. cv::Rect(cv::Point{50, 100}, cv::Size{250, 360}),
  398. cv::Rect(cv::Point{70, 10}, cv::Size{20, 260}),
  399. cv::Rect(cv::Point{5, 15}, cv::Size{200, 160}),
  400. };
  401. const cv::Size sz{640, 480};
  402. const cv::Mat m_in_y = initMatrixRandU(CV_8UC1, sz);
  403. const cv::Mat m_in_uv = initMatrixRandU(CV_8UC2, sz / 2);
  404. };
  405. class ONNXGRayScale : public ONNXtest {
  406. public:
  407. void preprocess(const cv::Mat& src, cv::Mat& dst) {
  408. const int new_h = 64;
  409. const int new_w = 64;
  410. cv::Mat cvc, rsz, cvt;
  411. cv::cvtColor(src, cvc, cv::COLOR_BGR2GRAY);
  412. cv::resize(cvc, rsz, cv::Size(new_w, new_h));
  413. rsz.convertTo(cvt, CV_32F);
  414. toCHW(cvt, dst);
  415. dst = dst.reshape(1, {1, 1, new_h, new_w});
  416. }
  417. };
  418. class ONNXWithRemap : public ONNXtest {
  419. private:
  420. size_t step_by_outs = 0;
  421. public:
  422. // This function checks each next cv::Mat in out_gapi vector for next call.
  423. // end_mark is edge of correct data
  424. template <typename T>
  425. void validate(const T end_mark) {
  426. GAPI_Assert(!out_gapi.empty() && !out_onnx.empty());
  427. ASSERT_EQ(out_gapi.size(), out_onnx.size());
  428. GAPI_Assert(step_by_outs < out_gapi.size());
  429. const T* op = out_onnx.at(step_by_outs).ptr<T>();
  430. const T* gp = out_gapi.at(step_by_outs).ptr<T>();
  431. // Checking that graph output larger than onnx output
  432. const auto out_size = std::min(out_onnx.at(step_by_outs).total(), out_gapi.at(step_by_outs).total());
  433. GAPI_Assert(out_size != 0u);
  434. for (size_t d_idx = 0; d_idx < out_size; ++d_idx) {
  435. if (gp[d_idx] == end_mark) break;
  436. ASSERT_EQ(op[d_idx], gp[d_idx]);
  437. }
  438. ++step_by_outs;
  439. }
  440. };
  441. class ONNXRCNN : public ONNXWithRemap {
  442. private:
  443. const cv::Scalar rcnn_mean = { 102.9801, 115.9465, 122.7717 };
  444. const float range_max = 1333;
  445. const float range_min = 800;
  446. public:
  447. void preprocess(const cv::Mat& src, cv::Mat& dst) {
  448. cv::Mat rsz, cvt, chw, mn;
  449. const auto get_ratio = [&](const int dim) -> float {
  450. return ((dim > range_max) || (dim < range_min))
  451. ? dim > range_max
  452. ? range_max / dim
  453. : range_min / dim
  454. : 1.f;
  455. };
  456. const auto ratio_h = get_ratio(src.rows);
  457. const auto ratio_w = get_ratio(src.cols);
  458. const auto new_h = static_cast<int>(ratio_h * src.rows);
  459. const auto new_w = static_cast<int>(ratio_w * src.cols);
  460. cv::resize(src, rsz, cv::Size(new_w, new_h));
  461. rsz.convertTo(cvt, CV_32F, 1.f);
  462. toCHW(cvt, chw);
  463. mn = chw - rcnn_mean;
  464. const int padded_h = std::ceil(new_h / 32.f) * 32;
  465. const int padded_w = std::ceil(new_w / 32.f) * 32;
  466. cv::Mat pad_im(cv::Size(padded_w, 3 * padded_h), CV_32F, 0.f);
  467. pad_im(cv::Rect(0, 0, mn.cols, mn.rows)) += mn;
  468. dst = pad_im.reshape(1, {3, padded_h, padded_w});
  469. }
  470. };
  471. class ONNXYoloV3 : public ONNXWithRemap {
  472. public:
  473. std::vector<cv::Mat> ins;
  474. void constructYoloInputs(const cv::Mat& src) {
  475. const int yolo_in_h = 416;
  476. const int yolo_in_w = 416;
  477. cv::Mat yolov3_input, shape, prep_mat;
  478. cv::resize(src, yolov3_input, cv::Size(yolo_in_w, yolo_in_h));
  479. shape.create(cv::Size(2, 1), CV_32F);
  480. float* ptr = shape.ptr<float>();
  481. ptr[0] = src.cols;
  482. ptr[1] = src.rows;
  483. preprocess(yolov3_input, prep_mat);
  484. ins = {prep_mat, shape};
  485. }
  486. private:
  487. void preprocess(const cv::Mat& src, cv::Mat& dst) {
  488. cv::Mat cvt;
  489. src.convertTo(cvt, CV_32F, 1.f / 255.f);
  490. toCHW(cvt, dst);
  491. dst = dst.reshape(1, {1, 3, 416, 416});
  492. }
  493. };
  494. } // anonymous namespace
  495. TEST_F(ONNXClassification, Infer)
  496. {
  497. useModel("classification/squeezenet/model/squeezenet1.0-9");
  498. in_mat = cv::imread(findDataFile("cv/dpm/cat.png", false));
  499. // ONNX_API code
  500. cv::Mat processed_mat;
  501. preprocess(in_mat, processed_mat, false); // NO normalization for 1.0-9, see #23597
  502. infer<float>(processed_mat, out_onnx);
  503. // G_API code
  504. G_API_NET(SqueezNet, <cv::GMat(cv::GMat)>, "squeeznet");
  505. cv::GMat in;
  506. cv::GMat out = cv::gapi::infer<SqueezNet>(in);
  507. cv::GComputation comp(cv::GIn(in), cv::GOut(out));
  508. auto net = cv::gapi::onnx::Params<SqueezNet> {
  509. model_path
  510. }.cfgNormalize({false});
  511. comp.apply(cv::gin(in_mat),
  512. cv::gout(out_gapi.front()),
  513. cv::compile_args(cv::gapi::networks(net)));
  514. // Validate
  515. validate();
  516. }
  517. TEST_F(ONNXClassification, InferTensor)
  518. {
  519. useModel("classification/squeezenet/model/squeezenet1.0-9");
  520. in_mat = cv::imread(findDataFile("cv/dpm/cat.png", false));
  521. // Create tensor
  522. cv::Mat tensor;
  523. preprocess(in_mat, tensor, false); // NO normalization for 1.0-9, see #23597
  524. // ONNX_API code
  525. infer<float>(tensor, out_onnx);
  526. // G_API code
  527. G_API_NET(SqueezNet, <cv::GMat(cv::GMat)>, "squeeznet");
  528. cv::GMat in;
  529. cv::GMat out = cv::gapi::infer<SqueezNet>(in);
  530. cv::GComputation comp(cv::GIn(in), cv::GOut(out));
  531. auto net = cv::gapi::onnx::Params<SqueezNet> {
  532. model_path
  533. }.cfgNormalize({false});
  534. comp.apply(cv::gin(tensor),
  535. cv::gout(out_gapi.front()),
  536. cv::compile_args(cv::gapi::networks(net)));
  537. // Validate
  538. validate();
  539. }
  540. TEST_F(ONNXClassification, InferROI)
  541. {
  542. useModel("classification/squeezenet/model/squeezenet1.0-9");
  543. in_mat = cv::imread(findDataFile("cv/dpm/cat.png", false));
  544. const auto ROI = rois.at(0);
  545. // ONNX_API code
  546. cv::Mat roi_mat;
  547. preprocess(in_mat(ROI), roi_mat, false); // NO normalization for 1.0-9, see #23597
  548. infer<float>(roi_mat, out_onnx);
  549. // G_API code
  550. G_API_NET(SqueezNet, <cv::GMat(cv::GMat)>, "squeeznet");
  551. cv::GMat in;
  552. cv::GOpaque<cv::Rect> rect;
  553. cv::GMat out = cv::gapi::infer<SqueezNet>(rect, in);
  554. cv::GComputation comp(cv::GIn(in, rect), cv::GOut(out));
  555. auto net = cv::gapi::onnx::Params<SqueezNet> {
  556. model_path
  557. }.cfgNormalize({false});
  558. comp.apply(cv::gin(in_mat, ROI),
  559. cv::gout(out_gapi.front()),
  560. cv::compile_args(cv::gapi::networks(net)));
  561. // Validate
  562. validate();
  563. }
  564. TEST_F(ONNXClassification, InferROIList)
  565. {
  566. useModel("classification/squeezenet/model/squeezenet1.0-9");
  567. in_mat = cv::imread(findDataFile("cv/dpm/cat.png", false));
  568. // ONNX_API code
  569. for (size_t i = 0; i < rois.size(); ++i) {
  570. cv::Mat roi_mat;
  571. preprocess(in_mat(rois[i]), roi_mat, false); // NO normalization for 1.0-9, see #23597
  572. infer<float>(roi_mat, out_onnx);
  573. }
  574. // G_API code
  575. G_API_NET(SqueezNet, <cv::GMat(cv::GMat)>, "squeeznet");
  576. cv::GMat in;
  577. cv::GArray<cv::Rect> rr;
  578. cv::GArray<cv::GMat> out = cv::gapi::infer<SqueezNet>(rr, in);
  579. cv::GComputation comp(cv::GIn(in, rr), cv::GOut(out));
  580. // NOTE: We have to normalize U8 tensor
  581. // so cfgMeanStd() is here
  582. auto net = cv::gapi::onnx::Params<SqueezNet> {
  583. model_path
  584. }.cfgNormalize({false});
  585. comp.apply(cv::gin(in_mat, rois),
  586. cv::gout(out_gapi),
  587. cv::compile_args(cv::gapi::networks(net)));
  588. // Validate
  589. validate();
  590. }
  591. TEST_F(ONNXClassification, Infer2ROIList)
  592. {
  593. useModel("classification/squeezenet/model/squeezenet1.0-9");
  594. in_mat = cv::imread(findDataFile("cv/dpm/cat.png", false));
  595. // ONNX_API code
  596. for (size_t i = 0; i < rois.size(); ++i) {
  597. cv::Mat roi_mat;
  598. preprocess(in_mat(rois[i]), roi_mat, false); // NO normalization for 1.0-9, see #23597
  599. infer<float>(roi_mat, out_onnx);
  600. }
  601. // G_API code
  602. G_API_NET(SqueezNet, <cv::GMat(cv::GMat)>, "squeeznet");
  603. cv::GMat in;
  604. cv::GArray<cv::Rect> rr;
  605. cv::GArray<cv::GMat> out = cv::gapi::infer2<SqueezNet>(in, rr);
  606. cv::GComputation comp(cv::GIn(in, rr), cv::GOut(out));
  607. // NOTE: We have to normalize U8 tensor
  608. // so cfgMeanStd() is here
  609. auto net = cv::gapi::onnx::Params<SqueezNet> {
  610. model_path
  611. }.cfgNormalize({false});
  612. comp.apply(cv::gin(in_mat, rois),
  613. cv::gout(out_gapi),
  614. cv::compile_args(cv::gapi::networks(net)));
  615. // Validate
  616. validate();
  617. }
  618. TEST_F(ONNXWithRemap, InferDynamicInputTensor)
  619. {
  620. useModel("object_detection_segmentation/tiny-yolov2/model/tinyyolov2-8");
  621. in_mat = cv::imread(findDataFile("cv/dpm/cat.png", false));
  622. // Create tensor
  623. cv::Mat cvt, rsz, tensor;
  624. cv::resize(in_mat, rsz, cv::Size{416, 416});
  625. rsz.convertTo(cvt, CV_32F, 1.f / 255.f);
  626. toCHW(cvt, tensor);
  627. tensor = tensor.reshape(1, {1, 3, 416, 416});
  628. // ONNX_API code
  629. infer<float>(tensor, out_onnx);
  630. // G_API code
  631. G_API_NET(YoloNet, <cv::GMat(cv::GMat)>, "YoloNet");
  632. cv::GMat in;
  633. cv::GMat out = cv::gapi::infer<YoloNet>(in);
  634. cv::GComputation comp(cv::GIn(in), cv::GOut(out));
  635. auto net = cv::gapi::onnx::Params<YoloNet>{ model_path }
  636. .cfgPostProc({cv::GMatDesc{CV_32F, {1, 125, 13, 13}}}, remapYolo)
  637. .cfgOutputLayers({"out"});
  638. comp.apply(cv::gin(tensor),
  639. cv::gout(out_gapi.front()),
  640. cv::compile_args(cv::gapi::networks(net)));
  641. // Validate
  642. validate<float>(-1.f);
  643. }
  644. TEST_F(ONNXGRayScale, InferImage)
  645. {
  646. useModel("body_analysis/emotion_ferplus/model/emotion-ferplus-8");
  647. in_mat = cv::imread(findDataFile("cv/dpm/cat.png", false));
  648. // ONNX_API code
  649. cv::Mat prep_mat;
  650. preprocess(in_mat, prep_mat);
  651. infer<float>(prep_mat, out_onnx);
  652. // G_API code
  653. G_API_NET(EmotionNet, <cv::GMat(cv::GMat)>, "emotion-ferplus");
  654. cv::GMat in;
  655. cv::GMat out = cv::gapi::infer<EmotionNet>(in);
  656. cv::GComputation comp(cv::GIn(in), cv::GOut(out));
  657. auto net = cv::gapi::onnx::Params<EmotionNet> { model_path }
  658. .cfgNormalize({ false }); // model accepts 0..255 range in FP32;
  659. comp.apply(cv::gin(in_mat),
  660. cv::gout(out_gapi.front()),
  661. cv::compile_args(cv::gapi::networks(net)));
  662. // Validate
  663. validate();
  664. }
  665. TEST_F(ONNXWithRemap, InferMultiOutput)
  666. {
  667. useModel("object_detection_segmentation/ssd-mobilenetv1/model/ssd_mobilenet_v1_10");
  668. in_mat = cv::imread(findDataFile("cv/dpm/cat.png", false));
  669. // ONNX_API code
  670. const auto prep_mat = in_mat.reshape(1, {1, in_mat.rows, in_mat.cols, in_mat.channels()});
  671. infer<uint8_t>(prep_mat, out_onnx);
  672. cv::Mat onnx_conv_out({1, 1, 200, 7}, CV_32F);
  673. remapToIESSDOut({out_onnx[3], out_onnx[0], out_onnx[2], out_onnx[1]}, onnx_conv_out);
  674. out_onnx.clear();
  675. out_onnx.push_back(onnx_conv_out);
  676. // G_API code
  677. G_API_NET(MobileNet, <cv::GMat(cv::GMat)>, "ssd_mobilenet");
  678. cv::GMat in;
  679. cv::GMat out = cv::gapi::infer<MobileNet>(in);
  680. cv::GComputation comp(cv::GIn(in), cv::GOut(out));
  681. auto net = cv::gapi::onnx::Params<MobileNet>{ model_path }
  682. .cfgOutputLayers({"detection_output"})
  683. .cfgPostProc({cv::GMatDesc{CV_32F, {1, 1, 200, 7}}}, remapSSDPorts);
  684. comp.apply(cv::gin(in_mat),
  685. cv::gout(out_gapi.front()),
  686. cv::compile_args(cv::gapi::networks(net)));
  687. // Validate
  688. validate<float>(-1.f);
  689. }
  690. TEST_F(ONNXMediaFrame, InferBGR)
  691. {
  692. useModel("classification/squeezenet/model/squeezenet1.0-9");
  693. in_mat = cv::imread(findDataFile("cv/dpm/cat.png", false));
  694. // ONNX_API code
  695. cv::Mat processed_mat;
  696. preprocess(in_mat, processed_mat, false); // NO normalization for 1.0-9, see #23597
  697. infer<float>(processed_mat, out_onnx);
  698. // G_API code
  699. auto frame = MediaFrame::Create<TestMediaBGR>(in_mat);
  700. G_API_NET(SqueezNet, <cv::GMat(cv::GMat)>, "squeeznet");
  701. cv::GFrame in;
  702. cv::GMat out = cv::gapi::infer<SqueezNet>(in);
  703. cv::GComputation comp(cv::GIn(in), cv::GOut(out));
  704. // NOTE: We have to normalize U8 tensor
  705. // so cfgMeanStd() is here
  706. auto net = cv::gapi::onnx::Params<SqueezNet> {
  707. model_path
  708. }.cfgNormalize({false});
  709. comp.apply(cv::gin(frame),
  710. cv::gout(out_gapi.front()),
  711. cv::compile_args(cv::gapi::networks(net)));
  712. // Validate
  713. validate();
  714. }
  715. TEST_F(ONNXMediaFrame, InferYUV)
  716. {
  717. useModel("classification/squeezenet/model/squeezenet1.0-9");
  718. in_mat = cv::imread(findDataFile("cv/dpm/cat.png", false));
  719. const auto frame = MediaFrame::Create<TestMediaNV12>(m_in_y, m_in_uv);
  720. // ONNX_API code
  721. cv::Mat pp;
  722. cvtColorTwoPlane(m_in_y, m_in_uv, pp, cv::COLOR_YUV2BGR_NV12);
  723. cv::Mat processed_mat;
  724. preprocess(pp, processed_mat, false); // NO normalization for 1.0-9, see #23597
  725. infer<float>(processed_mat, out_onnx);
  726. // G_API code
  727. G_API_NET(SqueezNet, <cv::GMat(cv::GMat)>, "squeeznet");
  728. cv::GFrame in;
  729. cv::GMat out = cv::gapi::infer<SqueezNet>(in);
  730. cv::GComputation comp(cv::GIn(in), cv::GOut(out));
  731. // NOTE: We have to normalize U8 tensor
  732. // so cfgMeanStd() is here
  733. auto net = cv::gapi::onnx::Params<SqueezNet> {
  734. model_path
  735. }.cfgNormalize({false});
  736. comp.apply(cv::gin(frame),
  737. cv::gout(out_gapi.front()),
  738. cv::compile_args(cv::gapi::networks(net)));
  739. // Validate
  740. validate();
  741. }
  742. TEST_F(ONNXMediaFrame, InferROIBGR)
  743. {
  744. useModel("classification/squeezenet/model/squeezenet1.0-9");
  745. in_mat = cv::imread(findDataFile("cv/dpm/cat.png", false));
  746. auto frame = MediaFrame::Create<TestMediaBGR>(in_mat);
  747. // ONNX_API code
  748. cv::Mat roi_mat;
  749. preprocess(in_mat(rois.front()), roi_mat, false); // NO normalization for 1.0-9, see #23597
  750. infer<float>(roi_mat, out_onnx);
  751. // G_API code
  752. G_API_NET(SqueezNet, <cv::GMat(cv::GMat)>, "squeeznet");
  753. cv::GFrame in;
  754. cv::GOpaque<cv::Rect> rect;
  755. cv::GMat out = cv::gapi::infer<SqueezNet>(rect, in);
  756. cv::GComputation comp(cv::GIn(in, rect), cv::GOut(out));
  757. // NOTE: We have to normalize U8 tensor
  758. // so cfgMeanStd() is here
  759. auto net = cv::gapi::onnx::Params<SqueezNet> {
  760. model_path
  761. }.cfgNormalize({false});
  762. comp.apply(cv::gin(frame, rois.front()),
  763. cv::gout(out_gapi.front()),
  764. cv::compile_args(cv::gapi::networks(net)));
  765. // Validate
  766. validate();
  767. }
  768. TEST_F(ONNXMediaFrame, InferROIYUV)
  769. {
  770. useModel("classification/squeezenet/model/squeezenet1.0-9");
  771. in_mat = cv::imread(findDataFile("cv/dpm/cat.png", false));
  772. const auto frame = MediaFrame::Create<TestMediaNV12>(m_in_y, m_in_uv);
  773. // ONNX_API code
  774. cv::Mat pp;
  775. cvtColorTwoPlane(m_in_y, m_in_uv, pp, cv::COLOR_YUV2BGR_NV12);
  776. cv::Mat roi_mat;
  777. preprocess(pp(rois.front()), roi_mat, false); // NO normalization for 1.0-9, see #23597
  778. infer<float>(roi_mat, out_onnx);
  779. // G_API code
  780. G_API_NET(SqueezNet, <cv::GMat(cv::GMat)>, "squeeznet");
  781. cv::GFrame in;
  782. cv::GOpaque<cv::Rect> rect;
  783. cv::GMat out = cv::gapi::infer<SqueezNet>(rect, in);
  784. cv::GComputation comp(cv::GIn(in, rect), cv::GOut(out));
  785. // NOTE: We have to normalize U8 tensor
  786. // so cfgMeanStd() is here
  787. auto net = cv::gapi::onnx::Params<SqueezNet> {
  788. model_path
  789. }.cfgNormalize({false});
  790. comp.apply(cv::gin(frame, rois.front()),
  791. cv::gout(out_gapi.front()),
  792. cv::compile_args(cv::gapi::networks(net)));
  793. // Validate
  794. validate();
  795. }
  796. TEST_F(ONNXMediaFrame, InferListBGR)
  797. {
  798. useModel("classification/squeezenet/model/squeezenet1.0-9");
  799. in_mat = cv::imread(findDataFile("cv/dpm/cat.png", false));
  800. const auto frame = MediaFrame::Create<TestMediaBGR>(in_mat);
  801. // ONNX_API code
  802. for (size_t i = 0; i < rois.size(); ++i) {
  803. cv::Mat roi_mat;
  804. preprocess(in_mat(rois[i]), roi_mat, false); // NO normalization for 1.0-9, see #23597
  805. infer<float>(roi_mat, out_onnx);
  806. }
  807. // G_API code
  808. G_API_NET(SqueezNet, <cv::GMat(cv::GMat)>, "squeeznet");
  809. cv::GFrame in;
  810. cv::GArray<cv::Rect> rr;
  811. cv::GArray<cv::GMat> out = cv::gapi::infer<SqueezNet>(rr, in);
  812. cv::GComputation comp(cv::GIn(in, rr), cv::GOut(out));
  813. // NOTE: We have to normalize U8 tensor
  814. // so cfgMeanStd() is here
  815. auto net = cv::gapi::onnx::Params<SqueezNet> {
  816. model_path
  817. }.cfgNormalize({false});
  818. comp.apply(cv::gin(frame, rois),
  819. cv::gout(out_gapi),
  820. cv::compile_args(cv::gapi::networks(net)));
  821. // Validate
  822. validate();
  823. }
  824. TEST_F(ONNXMediaFrame, InferListYUV)
  825. {
  826. useModel("classification/squeezenet/model/squeezenet1.0-9");
  827. in_mat = cv::imread(findDataFile("cv/dpm/cat.png", false));
  828. const auto frame = MediaFrame::Create<TestMediaNV12>(m_in_y, m_in_uv);
  829. // ONNX_API code
  830. cv::Mat pp;
  831. cvtColorTwoPlane(m_in_y, m_in_uv, pp, cv::COLOR_YUV2BGR_NV12);
  832. for (size_t i = 0; i < rois.size(); ++i) {
  833. cv::Mat roi_mat;
  834. preprocess(pp(rois[i]), roi_mat, false); // NO normalization for 1.0-9, see #23597
  835. infer<float>(roi_mat, out_onnx);
  836. }
  837. // G_API code
  838. G_API_NET(SqueezNet, <cv::GMat(cv::GMat)>, "squeeznet");
  839. cv::GFrame in;
  840. cv::GArray<cv::Rect> rr;
  841. cv::GArray<cv::GMat> out = cv::gapi::infer<SqueezNet>(rr, in);
  842. cv::GComputation comp(cv::GIn(in, rr), cv::GOut(out));
  843. // NOTE: We have to normalize U8 tensor
  844. // so cfgMeanStd() is here
  845. auto net = cv::gapi::onnx::Params<SqueezNet> {
  846. model_path
  847. }.cfgNormalize({false});
  848. comp.apply(cv::gin(frame, rois),
  849. cv::gout(out_gapi),
  850. cv::compile_args(cv::gapi::networks(net)));
  851. // Validate
  852. validate();
  853. }
  854. TEST_F(ONNXRCNN, InferWithDisabledOut)
  855. {
  856. useModel("object_detection_segmentation/faster-rcnn/model/FasterRCNN-10");
  857. in_mat = cv::imread(findDataFile("cv/dpm/cat.png", false));
  858. cv::Mat pp;
  859. preprocess(in_mat, pp);
  860. // ONNX_API code
  861. infer<float>(pp, out_onnx, {"6379", "6383"});
  862. // G_API code
  863. using FRCNNOUT = std::tuple<cv::GMat, cv::GMat>;
  864. G_API_NET(FasterRCNN, <FRCNNOUT(cv::GMat)>, "FasterRCNN");
  865. auto net = cv::gapi::onnx::Params<FasterRCNN>{model_path}
  866. .cfgOutputLayers({"out1", "out2"})
  867. .cfgPostProc({cv::GMatDesc{CV_32F, {7,4}},
  868. cv::GMatDesc{CV_32F, {7}}}, remapRCNNPortsDO, {"6383", "6379"});
  869. cv::GMat in, out1, out2;
  870. std::tie(out1, out2) = cv::gapi::infer<FasterRCNN>(in);
  871. cv::GComputation comp(cv::GIn(in), cv::GOut(out1, out2));
  872. out_gapi.resize(num_out);
  873. comp.apply(cv::gin(pp),
  874. cv::gout(out_gapi[0], out_gapi[1]),
  875. cv::compile_args(cv::gapi::networks(net)));
  876. // Validate
  877. validate<float>(-1.f);
  878. validate<float>(-1.f);
  879. }
  880. TEST_F(ONNXMediaFrame, InferList2BGR)
  881. {
  882. useModel("classification/squeezenet/model/squeezenet1.0-9");
  883. in_mat = cv::imread(findDataFile("cv/dpm/cat.png", false));
  884. const auto frame = MediaFrame::Create<TestMediaBGR>(in_mat);
  885. // ONNX_API code
  886. for (size_t i = 0; i < rois.size(); ++i) {
  887. cv::Mat roi_mat;
  888. preprocess(in_mat(rois[i]), roi_mat, false); // NO normalization for 1.0-9, see #23597
  889. infer<float>(roi_mat, out_onnx);
  890. }
  891. // G_API code
  892. G_API_NET(SqueezNet, <cv::GMat(cv::GMat)>, "squeeznet");
  893. cv::GFrame in;
  894. cv::GArray<cv::Rect> rr;
  895. cv::GArray<cv::GMat> out = cv::gapi::infer2<SqueezNet>(in, rr);
  896. cv::GComputation comp(cv::GIn(in, rr), cv::GOut(out));
  897. // NOTE: We have to normalize U8 tensor
  898. // so cfgMeanStd() is here
  899. auto net = cv::gapi::onnx::Params<SqueezNet> {
  900. model_path
  901. }.cfgNormalize({false});
  902. comp.apply(cv::gin(frame, rois),
  903. cv::gout(out_gapi),
  904. cv::compile_args(cv::gapi::networks(net)));
  905. // Validate
  906. validate();
  907. }
  908. TEST_F(ONNXMediaFrame, InferList2YUV)
  909. {
  910. useModel("classification/squeezenet/model/squeezenet1.0-9");
  911. in_mat = cv::imread(findDataFile("cv/dpm/cat.png", false));
  912. const auto frame = MediaFrame::Create<TestMediaNV12>(m_in_y, m_in_uv);
  913. // ONNX_API code
  914. cv::Mat pp;
  915. cvtColorTwoPlane(m_in_y, m_in_uv, pp, cv::COLOR_YUV2BGR_NV12);
  916. for (size_t i = 0; i < rois.size(); ++i) {
  917. cv::Mat roi_mat;
  918. preprocess(pp(rois[i]), roi_mat);
  919. infer<float>(roi_mat, out_onnx);
  920. }
  921. // G_API code
  922. G_API_NET(SqueezNet, <cv::GMat(cv::GMat)>, "squeeznet");
  923. cv::GFrame in;
  924. cv::GArray<cv::Rect> rr;
  925. cv::GArray<cv::GMat> out = cv::gapi::infer2<SqueezNet>(in, rr);
  926. cv::GComputation comp(cv::GIn(in, rr), cv::GOut(out));
  927. // NOTE: We have to normalize U8 tensor
  928. // so cfgMeanStd() is here
  929. auto net = cv::gapi::onnx::Params<SqueezNet> { model_path }.cfgMeanStd({ mean }, { std });
  930. comp.apply(cv::gin(frame, rois),
  931. cv::gout(out_gapi),
  932. cv::compile_args(cv::gapi::networks(net)));
  933. // Validate
  934. validate();
  935. }
  936. TEST_F(ONNXYoloV3, InferConstInput)
  937. {
  938. useModel("object_detection_segmentation/yolov3/model/yolov3-10");
  939. in_mat = cv::imread(findDataFile("cv/dpm/cat.png", false));
  940. constructYoloInputs(in_mat);
  941. // ONNX_API code
  942. infer<float>(ins, out_onnx);
  943. // G_API code
  944. using OUT = std::tuple<cv::GMat, cv::GMat, cv::GMat>;
  945. G_API_NET(YoloNet, <OUT(cv::GMat)>, "yolov3");
  946. auto net = cv::gapi::onnx::Params<YoloNet>{model_path}
  947. .constInput("image_shape", ins[1])
  948. .cfgInputLayers({"input_1"})
  949. .cfgOutputLayers({"out1", "out2", "out3"})
  950. .cfgPostProc({cv::GMatDesc{CV_32F, {1, 10000, 4}},
  951. cv::GMatDesc{CV_32F, {1, 80, 10000}},
  952. cv::GMatDesc{CV_32S, {5, 3}}}, remapYoloV3);
  953. cv::GMat in, out1, out2, out3;
  954. std::tie(out1, out2, out3) = cv::gapi::infer<YoloNet>(in);
  955. cv::GComputation comp(cv::GIn(in), cv::GOut(out1, out2, out3));
  956. out_gapi.resize(num_out);
  957. comp.apply(cv::gin(ins[0]),
  958. cv::gout(out_gapi[0], out_gapi[1], out_gapi[2]),
  959. cv::compile_args(cv::gapi::networks(net)));
  960. // Validate
  961. validate<float>(-1.f);
  962. validate<float>(-1.f);
  963. validate<int>(-1);
  964. }
  965. TEST_F(ONNXYoloV3, InferBSConstInput)
  966. {
  967. // This test checks the case when a const input is used
  968. // and all input layer names are specified.
  969. // Const input has the advantage. It is expected behavior.
  970. useModel("object_detection_segmentation/yolov3/model/yolov3-10");
  971. in_mat = cv::imread(findDataFile("cv/dpm/cat.png", false));
  972. constructYoloInputs(in_mat);
  973. // Tensor with incorrect image size
  974. // is used for check case when InputLayers and constInput have same names
  975. cv::Mat bad_shape;
  976. bad_shape.create(cv::Size(2, 1), CV_32F);
  977. float* ptr = bad_shape.ptr<float>();
  978. ptr[0] = 590;
  979. ptr[1] = 12;
  980. // ONNX_API code
  981. infer<float>(ins, out_onnx);
  982. // G_API code
  983. using OUT = std::tuple<cv::GMat, cv::GMat, cv::GMat>;
  984. G_API_NET(YoloNet, <OUT(cv::GMat, cv::GMat)>, "yolov3");
  985. auto net = cv::gapi::onnx::Params<YoloNet>{model_path}
  986. // Data from const input will be used to infer
  987. .constInput("image_shape", ins[1])
  988. // image_shape - const_input has same name
  989. .cfgInputLayers({"input_1", "image_shape"})
  990. .cfgOutputLayers({"out1", "out2", "out3"})
  991. .cfgPostProc({cv::GMatDesc{CV_32F, {1, 10000, 4}},
  992. cv::GMatDesc{CV_32F, {1, 80, 10000}},
  993. cv::GMatDesc{CV_32S, {5, 3}}}, remapYoloV3);
  994. cv::GMat in1, in2, out1, out2, out3;
  995. std::tie(out1, out2, out3) = cv::gapi::infer<YoloNet>(in1, in2);
  996. cv::GComputation comp(cv::GIn(in1, in2), cv::GOut(out1, out2, out3));
  997. out_gapi.resize(num_out);
  998. comp.apply(cv::gin(ins[0], bad_shape),
  999. cv::gout(out_gapi[0], out_gapi[1], out_gapi[2]),
  1000. cv::compile_args(cv::gapi::networks(net)));
  1001. // Validate
  1002. validate<float>(-1.f);
  1003. validate<float>(-1.f);
  1004. validate<int>(-1);
  1005. }
  1006. TEST_F(ONNXRCNN, ConversionInt64to32)
  1007. {
  1008. useModel("object_detection_segmentation/faster-rcnn/model/FasterRCNN-10");
  1009. in_mat = cv::imread(findDataFile("cv/dpm/cat.png", false));
  1010. cv::Mat dst;
  1011. preprocess(in_mat, dst);
  1012. // ONNX_API code
  1013. infer<float>(dst, out_onnx);
  1014. // G_API code
  1015. using FRCNNOUT = std::tuple<cv::GMat,cv::GMat,cv::GMat>;
  1016. G_API_NET(FasterRCNN, <FRCNNOUT(cv::GMat)>, "FasterRCNN");
  1017. auto net = cv::gapi::onnx::Params<FasterRCNN>{model_path}
  1018. .cfgOutputLayers({"out1", "out2", "out3"})
  1019. .cfgPostProc({cv::GMatDesc{CV_32F, {7,4}},
  1020. cv::GMatDesc{CV_32S, {7}},
  1021. cv::GMatDesc{CV_32F, {7}}}, remapRCNNPortsC);
  1022. cv::GMat in, out1, out2, out3;
  1023. std::tie(out1, out2, out3) = cv::gapi::infer<FasterRCNN>(in);
  1024. cv::GComputation comp(cv::GIn(in), cv::GOut(out1, out2, out3));
  1025. out_gapi.resize(num_out);
  1026. comp.apply(cv::gin(dst),
  1027. cv::gout(out_gapi[0], out_gapi[1], out_gapi[2]),
  1028. cv::compile_args(cv::gapi::networks(net)));
  1029. // Validate
  1030. validate<float>(-1.f);
  1031. validate<int>(-1);
  1032. validate<float>(-1.f);
  1033. }
  1034. TEST_F(ONNXWithRemap, InferOutReallocation)
  1035. {
  1036. useModel("object_detection_segmentation/ssd-mobilenetv1/model/ssd_mobilenet_v1_10");
  1037. in_mat = cv::imread(findDataFile("cv/dpm/cat.png", false));
  1038. // G_API code
  1039. G_API_NET(MobileNet, <cv::GMat(cv::GMat)>, "ssd_mobilenet");
  1040. auto net = cv::gapi::onnx::Params<MobileNet>{model_path}
  1041. .cfgOutputLayers({"detection_boxes"})
  1042. .cfgPostProc({cv::GMatDesc{CV_32F, {1,100,4}}}, reallocSSDPort);
  1043. cv::GMat in;
  1044. cv::GMat out1;
  1045. out1 = cv::gapi::infer<MobileNet>(in);
  1046. cv::GComputation comp(cv::GIn(in), cv::GOut(out1));
  1047. EXPECT_THROW(comp.apply(cv::gin(in_mat),
  1048. cv::gout(out_gapi[0]),
  1049. cv::compile_args(cv::gapi::networks(net))), std::exception);
  1050. }
  1051. } // namespace opencv_test
  1052. #endif // HAVE_ONNX