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) 2018 Intel Corporation
6 
7 #if !defined(GAPI_STANDALONE)
8 
9 #include "precomp.hpp"
10 
11 #include <opencv2/gapi/own/assert.hpp>
12 #include <opencv2/core/traits.hpp>
13 #include <opencv2/imgproc/types_c.h>
14 
15 #include <opencv2/gapi/core.hpp>
16 #include <opencv2/gapi/imgproc.hpp>
17 
18 #include <opencv2/gapi/fluid/gfluidbuffer.hpp>
19 #include <opencv2/gapi/fluid/gfluidkernel.hpp>
20 #include <opencv2/gapi/fluid/imgproc.hpp>
21 
22 #include "gfluidbuffer_priv.hpp"
23 #include "gfluidbackend.hpp"
24 #include "gfluidutils.hpp"
25 
26 #include "gfluidimgproc_func.hpp"
27 
28 #include <opencv2/imgproc/hal/hal.hpp>
29 #include <opencv2/core/hal/intrin.hpp>
30 
31 #include <cmath>
32 #include <algorithm>
33 
34 namespace cv {
35 namespace gapi {
36 namespace fluid {
37 
38 //----------------------------------
39 //
40 // Fluid kernels: RGB2Gray, BGR2Gray
41 //
42 //----------------------------------
43 
44 // Y' = 0.299*R' + 0.587*G' + 0.114*B'
45 // U' = (B' - Y')*0.492
46 // V' = (R' - Y')*0.877
47 static const float coef_rgb2yuv_bt601[5] = {0.299f, 0.587f, 0.114f, 0.492f, 0.877f};
48 
49 // R' = Y' + 1.140*V'
50 // G' = Y' - 0.394*U' - 0.581*V'
51 // B' = Y' + 2.032*U'
52 static const float coef_yuv2rgb_bt601[4] = {1.140f, -0.394f, -0.581f, 2.032f};
53 
run_rgb2gray(Buffer & dst,const View & src,float coef_r,float coef_g,float coef_b)54 static void run_rgb2gray(Buffer &dst, const View &src, float coef_r, float coef_g, float coef_b)
55 {
56     GAPI_Assert(src.meta().depth == CV_8U);
57     GAPI_Assert(dst.meta().depth == CV_8U);
58     GAPI_Assert(src.meta().chan == 3);
59     GAPI_Assert(dst.meta().chan == 1);
60     GAPI_Assert(src.length() == dst.length());
61 
62     GAPI_Assert(coef_r < 1 && coef_g < 1 && coef_b < 1);
63     GAPI_Assert(std::abs(coef_r + coef_g + coef_b - 1) < 0.001);
64 
65     const auto *in  = src.InLine<uchar>(0);
66           auto *out = dst.OutLine<uchar>();
67 
68     int width = dst.length();
69 
70     run_rgb2gray_impl(out, in, width, coef_r, coef_g, coef_b);
71 }
72 
GAPI_FLUID_KERNEL(GFluidRGB2GrayCustom,cv::gapi::imgproc::GRGB2GrayCustom,false)73 GAPI_FLUID_KERNEL(GFluidRGB2GrayCustom, cv::gapi::imgproc::GRGB2GrayCustom, false)
74 {
75     static const int Window = 1;
76 
77     static void run(const View &src, float coef_r, float coef_g, float coef_b, Buffer &dst)
78     {
79         run_rgb2gray(dst, src, coef_r, coef_g, coef_b);
80     }
81 };
82 
GAPI_FLUID_KERNEL(GFluidRGB2Gray,cv::gapi::imgproc::GRGB2Gray,false)83 GAPI_FLUID_KERNEL(GFluidRGB2Gray, cv::gapi::imgproc::GRGB2Gray, false)
84 {
85     static const int Window = 1;
86 
87     static void run(const View &src, Buffer &dst)
88     {
89         float coef_r = coef_rgb2yuv_bt601[0];
90         float coef_g = coef_rgb2yuv_bt601[1];
91         float coef_b = coef_rgb2yuv_bt601[2];
92         run_rgb2gray(dst, src, coef_r, coef_g, coef_b);
93     }
94 };
95 
GAPI_FLUID_KERNEL(GFluidBGR2Gray,cv::gapi::imgproc::GBGR2Gray,false)96 GAPI_FLUID_KERNEL(GFluidBGR2Gray, cv::gapi::imgproc::GBGR2Gray, false)
97 {
98     static const int Window = 1;
99 
100     static void run(const View &src, Buffer &dst)
101     {
102         float coef_r = coef_rgb2yuv_bt601[0];
103         float coef_g = coef_rgb2yuv_bt601[1];
104         float coef_b = coef_rgb2yuv_bt601[2];
105         run_rgb2gray(dst, src, coef_b, coef_g, coef_r);
106     }
107 };
108 
109 //--------------------------------------
110 //
111 // Fluid kernels: RGB-to-YUV, YUV-to-RGB
112 //
113 //--------------------------------------
114 
run_rgb2yuv(Buffer & dst,const View & src,const float coef[5])115 static void run_rgb2yuv(Buffer &dst, const View &src, const float coef[5])
116 {
117     GAPI_Assert(src.meta().depth == CV_8U);
118     GAPI_Assert(dst.meta().depth == CV_8U);
119     GAPI_Assert(src.meta().chan == 3);
120     GAPI_Assert(dst.meta().chan == 3);
121     GAPI_Assert(src.length() == dst.length());
122 
123     const auto *in  = src.InLine<uchar>(0);
124           auto *out = dst.OutLine<uchar>();
125 
126     int width = dst.length();
127 
128     run_rgb2yuv_impl(out, in, width, coef);
129 }
130 
run_yuv2rgb(Buffer & dst,const View & src,const float coef[4])131 static void run_yuv2rgb(Buffer &dst, const View &src, const float coef[4])
132 {
133     GAPI_Assert(src.meta().depth == CV_8U);
134     GAPI_Assert(dst.meta().depth == CV_8U);
135     GAPI_Assert(src.meta().chan == 3);
136     GAPI_Assert(dst.meta().chan == 3);
137     GAPI_Assert(src.length() == dst.length());
138 
139     const auto *in  = src.InLine<uchar>(0);
140           auto *out = dst.OutLine<uchar>();
141 
142     int width = dst.length();
143 
144     run_yuv2rgb_impl(out, in, width, coef);
145 }
146 
GAPI_FLUID_KERNEL(GFluidRGB2YUV,cv::gapi::imgproc::GRGB2YUV,false)147 GAPI_FLUID_KERNEL(GFluidRGB2YUV, cv::gapi::imgproc::GRGB2YUV, false)
148 {
149     static const int Window = 1;
150 
151     static void run(const View &src, Buffer &dst)
152     {
153         run_rgb2yuv(dst, src, coef_rgb2yuv_bt601);
154     }
155 };
156 
GAPI_FLUID_KERNEL(GFluidYUV2RGB,cv::gapi::imgproc::GYUV2RGB,false)157 GAPI_FLUID_KERNEL(GFluidYUV2RGB, cv::gapi::imgproc::GYUV2RGB, false)
158 {
159     static const int Window = 1;
160 
161     static void run(const View &src, Buffer &dst)
162     {
163         run_yuv2rgb(dst, src, coef_yuv2rgb_bt601);
164     }
165 };
166 
167 //--------------------------------------
168 //
169 // Fluid kernels: RGB-to-Lab, BGR-to-LUV
170 //
171 //--------------------------------------
172 
173 enum LabLUV { LL_Lab, LL_LUV };
174 
175 #define LabLuv_reference 0  // 1=use reference code of RGB/BGR to LUV/Lab, 0=don't
176 
177 #if LabLuv_reference
178 
179 // gamma-correction (inverse) for sRGB, 1/gamma=2.4 for inverse, like for Mac OS (?)
f_gamma(float x)180 static inline float f_gamma(float x)
181 {
182     return x <= 0.04045f ? x*(1.f/12.92f) : std::pow((x + 0.055f)*(1/1.055f), 2.4f);
183 }
184 
185 // saturate into interval [0, 1]
clip01(float value)186 static inline float clip01(float value)
187 {
188     return value < 0? 0:
189            value > 1? 1:
190            value;
191 }
192 
f_rgb2xyz(float R,float G,float B,float & X,float & Y,float & Z)193 static inline void f_rgb2xyz(float  R, float  G, float  B,
194                              float& X, float& Y, float& Z)
195 {
196     X = clip01(0.412453f*R + 0.357580f*G + 0.180423f*B);
197     Y = clip01(0.212671f*R + 0.715160f*G + 0.072169f*B);
198     Z = clip01(0.019334f*R + 0.119193f*G + 0.950227f*B);
199 }
200 
f_xyz2lab(float X,float Y,float Z,float & L,float & a,float & b)201 static inline void f_xyz2lab(float  X, float  Y, float  Z,
202                              float& L, float& a, float& b)
203 {
204     // CIE XYZ values of reference white point for D65 illuminant
205     static const float Xn = 0.950456f, Yn = 1.f, Zn = 1.088754f;
206 
207     // Other coefficients below:
208     // 7.787f    = (29/3)^3/(29*4)
209     // 0.008856f = (6/29)^3
210     // 903.3     = (29/3)^3
211 
212     float x = X/Xn, y = Y/Yn, z = Z/Zn;
213 
214     auto f = [](float t){ return t>0.008856f? std::cbrt(t): (7.787f*t + 16.f/116.f); };
215 
216     float fx = f(x), fy = f(y), fz = f(z);
217 
218     L = y > 0.008856f ? (116.f*std::cbrt(y) - 16.f) : (903.3f * y);
219     a = 500.f * (fx - fy);
220     b = 200.f * (fy - fz);
221 }
222 
f_xyz2luv(float X,float Y,float Z,float & L,float & u,float & v)223 static inline void f_xyz2luv(float  X, float  Y, float  Z,
224                              float& L, float& u, float& v)
225 {
226     static const float un = 0.19793943f, vn = 0.46831096f;
227 
228     float u1 = 4*X / (X + 15*Y + 3*Z);
229     float v1 = 9*Y / (X + 15*Y + 3*Z);
230 
231     L = Y > 0.008856f ? (116.f*std::cbrt(Y) - 16.f) : (903.3f * Y);
232     u = 13*L * (u1 - un);
233     v = 13*L * (v1 - vn);
234 }
235 
236 template<LabLUV labluv, int blue=0>
run_rgb2labluv_reference(uchar out[],const uchar in[],int width)237 static void run_rgb2labluv_reference(uchar out[], const uchar in[], int width)
238 {
239     for (int w=0; w < width; w++)
240     {
241         float R, G, B;
242         B = in[3*w +    blue ] / 255.f;
243         G = in[3*w +    1    ] / 255.f;
244         R = in[3*w + (2^blue)] / 255.f;
245 
246         B = f_gamma( B );
247         G = f_gamma( G );
248         R = f_gamma( R );
249 
250         float X, Y, Z;
251         f_rgb2xyz(R, G, B, X, Y, Z);
252 
253         // compile-time `if`
254         if (LL_Lab == labluv)
255         {
256             float L, a, b;
257             f_xyz2lab(X, Y, Z, L, a, b);
258 
259             out[3*w    ] = saturate<uchar>(L * 255.f/100, roundf);
260             out[3*w + 1] = saturate<uchar>(a + 128, roundf);
261             out[3*w + 2] = saturate<uchar>(b + 128, roundf);
262         }
263         else if (LL_LUV == labluv)
264         {
265             float L, u, v;
266             f_xyz2luv(X, Y, Z, L, u, v);
267 
268             out[3*w    ] = saturate<uchar>( L        * 255.f/100, roundf);
269             out[3*w + 1] = saturate<uchar>((u + 134) * 255.f/354, roundf);
270             out[3*w + 2] = saturate<uchar>((v + 140) * 255.f/262, roundf);
271         }
272         else
273             CV_Error(cv::Error::StsBadArg, "unsupported color conversion");;
274     }
275 }
276 
277 #endif  // LabLuv_reference
278 
279 // compile-time parameters: output format (Lab/LUV),
280 // and position of blue channel in BGR/RGB (0 or 2)
281 template<LabLUV labluv, int blue=0>
run_rgb2labluv(Buffer & dst,const View & src)282 static void run_rgb2labluv(Buffer &dst, const View &src)
283 {
284     GAPI_Assert(src.meta().depth == CV_8U);
285     GAPI_Assert(dst.meta().depth == CV_8U);
286     GAPI_Assert(src.meta().chan == 3);
287     GAPI_Assert(dst.meta().chan == 3);
288     GAPI_Assert(src.length() == dst.length());
289 
290     const auto *in  = src.InLine<uchar>(0);
291           auto *out = dst.OutLine<uchar>();
292 
293     int width = dst.length();
294 
295 #if LabLuv_reference
296     run_rgb2labluv_reference<labluv, blue>(out, in, width);
297 #else
298     uchar *dst_data = out;
299     const uchar *src_data = in;
300     size_t src_step = width;
301     size_t dst_step = width;
302     int height = 1;
303     int depth = CV_8U;
304     int scn = 3;
305     bool swapBlue = (blue == 2);
306     bool isLab = (LL_Lab == labluv);
307     bool srgb = true;
308     cv::hal::cvtBGRtoLab(src_data, src_step, dst_data, dst_step,
309                width, height, depth, scn, swapBlue, isLab, srgb);
310 #endif
311 }
312 
GAPI_FLUID_KERNEL(GFluidRGB2Lab,cv::gapi::imgproc::GRGB2Lab,false)313 GAPI_FLUID_KERNEL(GFluidRGB2Lab, cv::gapi::imgproc::GRGB2Lab, false)
314 {
315     static const int Window = 1;
316 
317     static void run(const View &src, Buffer &dst)
318     {
319         static const int blue = 2; // RGB: 0=red, 1=green, 2=blue
320         run_rgb2labluv<LL_Lab, blue>(dst, src);
321     }
322 };
323 
GAPI_FLUID_KERNEL(GFluidBGR2LUV,cv::gapi::imgproc::GBGR2LUV,false)324 GAPI_FLUID_KERNEL(GFluidBGR2LUV, cv::gapi::imgproc::GBGR2LUV, false)
325 {
326     static const int Window = 1;
327 
328     static void run(const View &src, Buffer &dst)
329     {
330         static const int blue = 0; // BGR: 0=blue, 1=green, 2=red
331         run_rgb2labluv<LL_LUV, blue>(dst, src);
332     }
333 };
334 
335 //-------------------------------
336 //
337 // Fluid kernels: blur, boxFilter
338 //
339 //-------------------------------
340 
341 static const int maxKernelSize = 9;
342 
343 template<typename DST, typename SRC>
run_boxfilter(Buffer & dst,const View & src,const cv::Size & kernelSize,const cv::Point &,bool normalize,float * buf[])344 static void run_boxfilter(Buffer &dst, const View &src, const cv::Size &kernelSize,
345                           const cv::Point& /* anchor */, bool normalize, float *buf[])
346 {
347     GAPI_Assert(kernelSize.width <= maxKernelSize);
348     GAPI_Assert(kernelSize.width == kernelSize.height);
349 
350     int kernel = kernelSize.width;
351     int border = (kernel - 1) / 2;
352 
353     const SRC *in[ maxKernelSize ];
354           DST *out;
355 
356     for (int i=0; i < kernel; i++)
357     {
358         in[i] = src.InLine<SRC>(i - border);
359     }
360 
361     out = dst.OutLine<DST>();
362 
363     int width = dst.length();
364     int chan  = dst.meta().chan;
365 
366     if (kernelSize.width == 3 && kernelSize.height == 3)
367     {
368         int y  = dst.y();
369         int y0 = dst.priv().writeStart();
370 
371         float  kx[3] = {1, 1, 1};
372         float *ky = kx;
373 
374         float scale=1, delta=0;
375         if (normalize)
376             scale = 1/9.f;
377 
378         run_sepfilter3x3_impl(out, in, width, chan, kx, ky, border, scale, delta, buf, y, y0);
379     } else
380     {
381         GAPI_DbgAssert(chan <= 4);
382 
383         for (int w=0; w < width; w++)
384         {
385             float sum[4] = {0, 0, 0, 0};
386 
387             for (int i=0; i < kernel; i++)
388             {
389                 for (int j=0; j < kernel; j++)
390                 {
391                     for (int c=0; c < chan; c++)
392                         sum[c] += in[i][(w + j - border)*chan + c];
393                 }
394             }
395 
396             for (int c=0; c < chan; c++)
397             {
398                 float result = normalize? sum[c]/(kernel * kernel) : sum[c];
399 
400                 out[w*chan + c] = saturate<DST>(result, rintf);
401             }
402         }
403     }
404 }
405 
GAPI_FLUID_KERNEL(GFluidBlur,cv::gapi::imgproc::GBlur,true)406 GAPI_FLUID_KERNEL(GFluidBlur, cv::gapi::imgproc::GBlur, true)
407 {
408     static const int Window = 3;
409 
410     static void run(const View &src, const cv::Size& kernelSize, const cv::Point& anchor,
411                     int /* borderType */, const cv::Scalar& /* borderValue */, Buffer &dst,
412                     Buffer& scratch)
413     {
414         // TODO: support sizes 3, 5, 7, 9, ...
415         GAPI_Assert(kernelSize.width  == 3 && kernelSize.height == 3);
416 
417         // TODO: support non-trivial anchor
418         GAPI_Assert(anchor.x == -1 && anchor.y == -1);
419 
420         static const bool normalize = true;
421 
422         int width = src.length();
423         int chan  = src.meta().chan;
424         int length = width * chan;
425 
426         float *buf[3];
427         buf[0] = scratch.OutLine<float>();
428         buf[1] = buf[0] + length;
429         buf[2] = buf[1] + length;
430 
431         //     DST     SRC     OP             __VA_ARGS__
432         UNARY_(uchar , uchar , run_boxfilter, dst, src, kernelSize, anchor, normalize, buf);
433         UNARY_(ushort, ushort, run_boxfilter, dst, src, kernelSize, anchor, normalize, buf);
434         UNARY_( short,  short, run_boxfilter, dst, src, kernelSize, anchor, normalize, buf);
435         UNARY_( float,  float, run_boxfilter, dst, src, kernelSize, anchor, normalize, buf);
436 
437         CV_Error(cv::Error::StsBadArg, "unsupported combination of types");
438     }
439 
440     static void initScratch(const GMatDesc   & in,
441                             const cv::Size   & /* ksize */,
442                             const cv::Point  & /* anchor */,
443                                   int          /* borderType */,
444                             const cv::Scalar & /* borderValue */,
445                                   Buffer     & scratch)
446     {
447         int width = in.size.width;
448         int chan  = in.chan;
449 
450         int buflen = width * chan * Window;  // work buffers
451 
452         cv::Size bufsize(buflen, 1);
453         GMatDesc bufdesc = {CV_32F, 1, bufsize};
454         Buffer buffer(bufdesc);
455         scratch = std::move(buffer);
456     }
457 
458     static void resetScratch(Buffer& /* scratch */)
459     {
460     }
461 
462     static Border getBorder(const cv::GMatDesc& /* src */,
463                             const cv::Size    & /* kernelSize */,
464                             const cv::Point   & /* anchor */,
465                                       int          borderType,
466                             const cv::Scalar  &    borderValue)
467     {
468         return { borderType, borderValue};
469     }
470 };
471 
GAPI_FLUID_KERNEL(GFluidBoxFilter,cv::gapi::imgproc::GBoxFilter,true)472 GAPI_FLUID_KERNEL(GFluidBoxFilter, cv::gapi::imgproc::GBoxFilter, true)
473 {
474     static const int Window = 3;
475 
476     static void run(const     View  &    src,
477                               int     /* ddepth */,
478                     const cv::Size  &    kernelSize,
479                     const cv::Point &    anchor,
480                               bool       normalize,
481                               int     /* borderType */,
482                     const cv::Scalar& /* borderValue */,
483                               Buffer&    dst,
484                               Buffer&    scratch)
485     {
486         // TODO: support sizes 3, 5, 7, 9, ...
487         GAPI_Assert(kernelSize.width  == 3 && kernelSize.height == 3);
488 
489         // TODO: support non-trivial anchor
490         GAPI_Assert(anchor.x == -1 && anchor.y == -1);
491 
492         int width = src.length();
493         int chan  = src.meta().chan;
494         int length = width * chan;
495 
496         float *buf[3];
497         buf[0] = scratch.OutLine<float>();
498         buf[1] = buf[0] + length;
499         buf[2] = buf[1] + length;
500 
501         //     DST     SRC     OP             __VA_ARGS__
502         UNARY_(uchar , uchar , run_boxfilter, dst, src, kernelSize, anchor, normalize, buf);
503         UNARY_( float, uchar , run_boxfilter, dst, src, kernelSize, anchor, normalize, buf);
504         UNARY_(ushort, ushort, run_boxfilter, dst, src, kernelSize, anchor, normalize, buf);
505         UNARY_( float, ushort, run_boxfilter, dst, src, kernelSize, anchor, normalize, buf);
506         UNARY_( short,  short, run_boxfilter, dst, src, kernelSize, anchor, normalize, buf);
507         UNARY_( float,  short, run_boxfilter, dst, src, kernelSize, anchor, normalize, buf);
508         UNARY_( float,  float, run_boxfilter, dst, src, kernelSize, anchor, normalize, buf);
509 
510         CV_Error(cv::Error::StsBadArg, "unsupported combination of types");
511     }
512 
513     static void initScratch(const GMatDesc  & in,
514                                       int     /* ddepth */,
515                             const cv::Size  & /* kernelSize */,
516                             const cv::Point & /* anchor */,
517                                       bool    /*  normalize */,
518                                       int     /* borderType */,
519                             const cv::Scalar& /* borderValue */,
520                                   Buffer    &  scratch)
521     {
522         int width = in.size.width;
523         int chan  = in.chan;
524 
525         int buflen = width * chan * Window;  // work buffers
526 
527         cv::Size bufsize(buflen, 1);
528         GMatDesc bufdesc = {CV_32F, 1, bufsize};
529         Buffer buffer(bufdesc);
530         scratch = std::move(buffer);
531     }
532 
533     static void resetScratch(Buffer& /* scratch */)
534     {
535     }
536 
537     static Border getBorder(const cv::GMatDesc& /* src */,
538                                       int       /* ddepth */,
539                             const cv::Size    & /* kernelSize */,
540                             const cv::Point   & /* anchor */,
541                                       bool      /* normalize */,
542                                       int          borderType,
543                             const cv::Scalar  &    borderValue)
544     {
545         return { borderType, borderValue};
546     }
547 };
548 
549 //-------------------------
550 //
551 // Fluid kernels: sepFilter
552 //
553 //-------------------------
554 
555 template<typename T>
getKernel(T k[],const cv::Mat & kernel)556 static void getKernel(T k[], const cv::Mat& kernel)
557 {
558     GAPI_Assert(kernel.channels() == 1);
559 
560     int depth = CV_MAT_DEPTH(kernel.type());
561     int cols = kernel.cols;
562     int rows = kernel.rows;
563 
564     switch ( depth )
565     {
566     case CV_8U:
567         for (int h=0; h < rows; h++)
568         for (int w=0; w < cols; w++)
569             k[h*cols + w] = static_cast<T>( kernel.at<uchar>(h, w) );
570         break;
571     case CV_16U:
572         for (int h=0; h < rows; h++)
573         for (int w=0; w < cols; w++)
574             k[h*cols + w] = static_cast<T>( kernel.at<ushort>(h, w) );
575         break;
576     case CV_16S:
577         for (int h=0; h < rows; h++)
578         for (int w=0; w < cols; w++)
579             k[h*cols + w] = static_cast<T>( kernel.at<short>(h, w) );
580         break;
581     case CV_32F:
582         for (int h=0; h < rows; h++)
583         for (int w=0; w < cols; w++)
584             k[h*cols + w] = static_cast<T>( kernel.at<float>(h, w) );
585         break;
586     default: CV_Error(cv::Error::StsBadArg, "unsupported kernel type");
587     }
588 }
589 
590 template<typename DST, typename SRC>
run_sepfilter(Buffer & dst,const View & src,const float kx[],int kxLen,const float ky[],int kyLen,const cv::Point &,float scale,float delta,float * buf[])591 static void run_sepfilter(Buffer& dst, const View& src,
592                           const float kx[], int kxLen,
593                           const float ky[], int kyLen,
594                           const cv::Point& /* anchor */,
595                           float scale, float delta,
596                           float *buf[])
597 {
598     constexpr int kMax = 11;
599     GAPI_Assert(kxLen <= kMax && kyLen <= kMax);
600     GAPI_Assert(kxLen == kyLen);
601 
602     const SRC *in[kMax];
603           DST *out;
604 
605     int xborder = (kxLen - 1) / 2;
606     int yborder = (kyLen - 1) / 2;
607 
608     for (int i=0; i < kyLen; i++)
609     {
610         in[i] = src.InLine<SRC>(i - yborder);
611     }
612 
613     out = dst.OutLine<DST>();
614 
615     int width = dst.length();
616     int chan  = dst.meta().chan;
617 
618     // optimized 3x3 vs reference
619     if (kxLen == 3 && kyLen == 3)
620     {
621         int y  = dst.y();
622         int y0 = dst.priv().writeStart();
623 
624         int border = xborder;
625         run_sepfilter3x3_impl(out, in, width, chan, kx, ky, border, scale, delta, buf, y, y0);
626     }
627     else if (kxLen == 5 && kyLen == 5)
628     {
629         int y = dst.y();
630         int y0 = dst.priv().writeStart();
631 
632         run_sepfilter5x5_impl(out, in, width, chan, kx, ky, xborder, scale, delta, buf, y, y0);
633     }
634     else
635     {
636         int length = chan * width;
637         int xshift = chan;
638 
639         // horizontal pass
640 
641         for (int k=0; k < kyLen; k++)
642         {
643             const SRC *inp[kMax] = {nullptr};
644 
645             for (int j=0; j < kxLen; j++)
646             {
647                 inp[j] = in[k] + (j - xborder)*xshift;
648             }
649 
650             for (int l=0; l < length; l++)
651             {
652                 float sum = 0;
653                 for (int j=0; j < kxLen; j++)
654                 {
655                     sum += inp[j][l] * kx[j];
656                 }
657                 buf[k][l] = sum;
658             }
659         }
660 
661         // vertical pass
662 
663         for (int l=0; l < length; l++)
664         {
665             float sum = 0;
666             for (int k=0; k < kyLen; k++)
667             {
668                 sum += buf[k][l] * ky[k];
669             }
670             out[l] = saturate<DST>(sum*scale + delta, rintf);
671         }
672     }
673 }
674 
GAPI_FLUID_KERNEL(GFluidSepFilter,cv::gapi::imgproc::GSepFilter,true)675 GAPI_FLUID_KERNEL(GFluidSepFilter, cv::gapi::imgproc::GSepFilter, true)
676 {
677     static const int Window = 3;
678 
679     static void run(const     View&      src,
680                               int     /* ddepth */,
681                     const cv::Mat&       kernX,
682                     const cv::Mat&       kernY,
683                     const cv::Point&     anchor,
684                     const cv::Scalar&    delta_,
685                               int     /* borderType */,
686                     const cv::Scalar& /* borderValue */,
687                               Buffer&    dst,
688                               Buffer&    scratch)
689     {
690         // TODO: support non-trivial anchors
691         GAPI_Assert(anchor.x == -1 && anchor.y == -1);
692 
693         // TODO: support kernel heights 3, 5, 7, 9, ...
694         GAPI_Assert((kernY.rows == 1 || kernY.cols == 1)  && (kernY.cols * kernY.rows == 3));
695         GAPI_Assert((kernX.rows == 1 || kernX.cols == 1));
696 
697         int kxLen = kernX.rows * kernX.cols;
698         int kyLen = kernY.rows * kernY.cols;
699 
700         GAPI_Assert(kyLen == 3);
701 
702         float *kx = scratch.OutLine<float>();
703         float *ky = kx + kxLen;
704 
705         int width = src.meta().size.width;
706         int chan  = src.meta().chan;
707         int length = width * chan;
708 
709         float *buf[3];
710         buf[0] = ky + kyLen;
711         buf[1] = buf[0] + length;
712         buf[2] = buf[1] + length;
713 
714         float scale = 1;
715         float delta = static_cast<float>(delta_[0]);
716 
717         //     DST     SRC     OP             __VA_ARGS__
718         UNARY_(uchar , uchar , run_sepfilter, dst, src, kx, kxLen, ky, kyLen, anchor, scale, delta, buf);
719         UNARY_( short, uchar , run_sepfilter, dst, src, kx, kxLen, ky, kyLen, anchor, scale, delta, buf);
720         UNARY_( float, uchar , run_sepfilter, dst, src, kx, kxLen, ky, kyLen, anchor, scale, delta, buf);
721         UNARY_(ushort, ushort, run_sepfilter, dst, src, kx, kxLen, ky, kyLen, anchor, scale, delta, buf);
722         UNARY_( float, ushort, run_sepfilter, dst, src, kx, kxLen, ky, kyLen, anchor, scale, delta, buf);
723         UNARY_( short,  short, run_sepfilter, dst, src, kx, kxLen, ky, kyLen, anchor, scale, delta, buf);
724         UNARY_( float,  short, run_sepfilter, dst, src, kx, kxLen, ky, kyLen, anchor, scale, delta, buf);
725         UNARY_( float,  float, run_sepfilter, dst, src, kx, kxLen, ky, kyLen, anchor, scale, delta, buf);
726 
727         CV_Error(cv::Error::StsBadArg, "unsupported combination of types");
728     }
729 
730     static void initScratch(const GMatDesc&    in,
731                                   int       /* ddepth */,
732                             const Mat     &    kernX,
733                             const Mat     &    kernY,
734                             const Point   & /* anchor */,
735                             const Scalar  & /* delta */,
736                                   int       /* borderType */,
737                             const Scalar  & /* borderValue */,
738                                   Buffer  &    scratch)
739     {
740         int kxLen = kernX.rows * kernX.cols;
741         int kyLen = kernY.rows * kernY.cols;
742 
743         int width = in.size.width;
744         int chan  = in.chan;
745 
746         int buflen = kxLen + kyLen +         // x, y kernels
747                      width * chan * Window;  // work buffers
748 
749         cv::Size bufsize(buflen, 1);
750         GMatDesc bufdesc = {CV_32F, 1, bufsize};
751         Buffer buffer(bufdesc);
752         scratch = std::move(buffer);
753 
754         // FIXME: move to resetScratch stage ?
755         float *kx = scratch.OutLine<float>();
756         float *ky = kx + kxLen;
757         getKernel(kx, kernX);
758         getKernel(ky, kernY);
759     }
760 
761     static void resetScratch(Buffer& /* scratch */)
762     {
763     }
764 
765     static Border getBorder(const cv::GMatDesc& /* src */,
766                                       int       /* ddepth */,
767                             const cv::Mat&      /* kernX */,
768                             const cv::Mat&      /* kernY */,
769                             const cv::Point&    /* anchor */,
770                             const cv::Scalar&   /* delta */,
771                                       int          borderType,
772                             const cv::Scalar&      borderValue)
773     {
774         return { borderType, borderValue};
775     }
776 };
777 
778 //----------------------------
779 //
780 // Fluid kernels: gaussianBlur
781 //
782 //----------------------------
783 
GAPI_FLUID_KERNEL(GFluidGaussBlur,cv::gapi::imgproc::GGaussBlur,true)784 GAPI_FLUID_KERNEL(GFluidGaussBlur, cv::gapi::imgproc::GGaussBlur, true)
785 {
786     // TODO: support kernel height 3, 5, 7, 9, ...
787 
788     static void run(const     View  &    src,
789                     const cv::Size  &    ksize,
790                               double  /* sigmaX */,
791                               double  /* sigmaY */,
792                               int     /* borderType */,
793                     const cv::Scalar& /* borderValue */,
794                               Buffer&    dst,
795                               Buffer&    scratch)
796     {
797         GAPI_Assert(ksize.height == ksize.width);
798         GAPI_Assert((ksize.height == 3) || (ksize.height == 5));
799         const int kxsize = ksize.width;
800         int kysize = ksize.height;
801 
802         auto *kx = scratch.OutLine<float>(); // cached kernX data
803         auto *ky = kx + kxsize;              // cached kernY data
804 
805         int width = src.meta().size.width;
806         int chan  = src.meta().chan;
807         int length = width * chan;
808 
809         constexpr int buffSize = 5;
810         GAPI_Assert(ksize.height <= buffSize);
811 
812         float *buf[buffSize] = { nullptr };
813 
814         buf[0] = ky + kysize;
815         for (int i = 1; i < ksize.height; ++i)
816         {
817             buf[i] = buf[i - 1] + length;
818         }
819 
820         auto  anchor = cv::Point(-1, -1);
821 
822         float scale = 1;
823         float delta = 0;
824 
825         //     DST     SRC     OP             __VA_ARGS__
826         UNARY_(uchar , uchar , run_sepfilter, dst, src, kx, kxsize, ky, kysize, anchor, scale, delta, buf);
827         UNARY_(ushort, ushort, run_sepfilter, dst, src, kx, kxsize, ky, kysize, anchor, scale, delta, buf);
828         UNARY_( short,  short, run_sepfilter, dst, src, kx, kxsize, ky, kysize, anchor, scale, delta, buf);
829         UNARY_( float,  float, run_sepfilter, dst, src, kx, kxsize, ky, kysize, anchor, scale, delta, buf);
830 
831         CV_Error(cv::Error::StsBadArg, "unsupported combination of types");
832     }
833 
834     static void initScratch(const GMatDesc&    in,
835                             const cv::Size &   ksize,
836                                   double       sigmaX,
837                                   double       sigmaY,
838                                   int          /* borderType */,
839                             const cv::Scalar & /* borderValue */,
840                                   Buffer  &    scratch)
841     {
842         GAPI_Assert(ksize.height == ksize.width);
843         int kxsize = ksize.width;
844         int kysize = ksize.height;
845 
846         int width = in.size.width;
847         int chan  = in.chan;
848 
849         int buflen = kxsize + kysize +       // x, y kernels
850                      width * chan * ksize.height;  // work buffers
851 
852         cv::Size bufsize(buflen, 1);
853         GMatDesc bufdesc = {CV_32F, 1, bufsize};
854         Buffer buffer(bufdesc);
855         scratch = std::move(buffer);
856 
857         // FIXME: fill buffer at resetScratch stage?
858 
859         if (sigmaX == 0)
860             sigmaX = 0.3 * ((kxsize - 1)/2. - 1) + 0.8;
861 
862         if (sigmaY == 0)
863             sigmaY = sigmaX;
864 
865         Mat kernX = getGaussianKernel(kxsize, sigmaX, CV_32F);
866 
867         Mat kernY = kernX;
868         if (sigmaY != sigmaX)
869             kernY = getGaussianKernel(kysize, sigmaY, CV_32F);
870 
871         auto *kx = scratch.OutLine<float>();
872         auto *ky = kx + kxsize;
873 
874         getKernel(kx, kernX);
875         getKernel(ky, kernY);
876     }
877 
878     static void resetScratch(Buffer& /* scratch */)
879     {
880     }
881 
882     static Border getBorder(const cv::GMatDesc& /* src */,
883                             const cv::Size    & /* ksize */,
884                                       double    /* sigmaX */,
885                                       double    /* sigmaY */,
886                                       int          borderType,
887                             const cv::Scalar  &    borderValue)
888     {
889         return { borderType, borderValue};
890     }
891 
892     static int getWindow(const cv::GMatDesc& /* src */,
893                          const cv::Size&        ksize,
894                          double              /* sigmaX */,
895                          double              /* sigmaY */,
896                          int                 /* borderType */,
897                          const cv::Scalar&   /* borderValue */)
898     {
899         GAPI_Assert(ksize.height == ksize.width);
900         return ksize.height;
901     }
902 };
903 
904 //---------------------
905 //
906 // Fluid kernels: Sobel
907 //
908 //---------------------
909 
910 template<typename DST, typename SRC>
run_sobel(Buffer & dst,const View & src,const float kx[],const float ky[],int ksize,float scale,float delta,float * buf[])911 static void run_sobel(Buffer& dst,
912                 const View  & src,
913                 const float   kx[],
914                 const float   ky[],
915                       int     ksize,
916                       float   scale,  // default: 1
917                       float   delta,  // default: 0
918                       float  *buf[])
919 {
920     static const int kmax = 11;
921     GAPI_Assert(ksize <= kmax);
922 
923     const SRC *in[ kmax ];
924           DST *out;
925 
926     int border = (ksize - 1) / 2;
927     for (int i=0; i < ksize; i++)
928     {
929         in[i] = src.InLine<SRC>(i - border);
930     }
931 
932     out = dst.OutLine<DST>();
933 
934     int width = dst.length();
935     int chan  = dst.meta().chan;
936 
937     GAPI_DbgAssert(ksize == 3);
938 //  float buf[3][width * chan];
939 
940     int y  = dst.y();
941     int y0 = dst.priv().writeStart();
942 //  int y1 = dst.priv().writeEnd();
943 
944     run_sepfilter3x3_impl(out, in, width, chan, kx, ky, border, scale, delta, buf, y, y0);
945 }
946 
GAPI_FLUID_KERNEL(GFluidSobel,cv::gapi::imgproc::GSobel,true)947 GAPI_FLUID_KERNEL(GFluidSobel, cv::gapi::imgproc::GSobel, true)
948 {
949     static const int Window = 3;
950 
951     static void run(const     View  &    src,
952                               int     /* ddepth */,
953                               int     /* dx */,
954                               int     /* dy */,
955                               int        ksize,
956                               double    _scale,
957                               double    _delta,
958                               int     /* borderType */,
959                     const cv::Scalar& /* borderValue */,
960                               Buffer&    dst,
961                               Buffer&    scratch)
962     {
963         // TODO: support kernel height 3, 5, 7, 9, ...
964         GAPI_Assert(ksize == 3 || ksize == FILTER_SCHARR);
965 
966         int ksz = (ksize == FILTER_SCHARR)? 3: ksize;
967 
968         auto *kx = scratch.OutLine<float>();
969         auto *ky = kx + ksz;
970 
971         int width = dst.meta().size.width;
972         int chan  = dst.meta().chan;
973 
974         float *buf[3];
975         buf[0] = ky + ksz;
976         buf[1] = buf[0] + width*chan;
977         buf[2] = buf[1] + width*chan;
978 
979         auto scale = static_cast<float>(_scale);
980         auto delta = static_cast<float>(_delta);
981 
982         //     DST     SRC     OP         __VA_ARGS__
983         UNARY_(uchar , uchar , run_sobel, dst, src, kx, ky, ksz, scale, delta, buf);
984         UNARY_(ushort, ushort, run_sobel, dst, src, kx, ky, ksz, scale, delta, buf);
985         UNARY_( short, uchar , run_sobel, dst, src, kx, ky, ksz, scale, delta, buf);
986         UNARY_( short, ushort, run_sobel, dst, src, kx, ky, ksz, scale, delta, buf);
987         UNARY_( short,  short, run_sobel, dst, src, kx, ky, ksz, scale, delta, buf);
988         UNARY_( float, uchar , run_sobel, dst, src, kx, ky, ksz, scale, delta, buf);
989         UNARY_( float, ushort, run_sobel, dst, src, kx, ky, ksz, scale, delta, buf);
990         UNARY_( float,  short, run_sobel, dst, src, kx, ky, ksz, scale, delta, buf);
991         UNARY_( float,  float, run_sobel, dst, src, kx, ky, ksz, scale, delta, buf);
992 
993         CV_Error(cv::Error::StsBadArg, "unsupported combination of types");
994     }
995 
996     static void initScratch(const GMatDesc&    in,
997                                   int       /* ddepth */,
998                                   int          dx,
999                                   int          dy,
1000                                   int          ksize,
1001                                   double    /* scale */,
1002                                   double    /* delta */,
1003                                   int       /* borderType */,
1004                             const Scalar  & /* borderValue */,
1005                                   Buffer  &    scratch)
1006     {
1007         // TODO: support kernel height 3, 5, 7, 9, ...
1008         GAPI_Assert(ksize == 3 || ksize == FILTER_SCHARR);
1009         int ksz = (ksize == FILTER_SCHARR) ? 3 : ksize;
1010 
1011         int width = in.size.width;
1012         int chan  = in.chan;
1013 
1014         int buflen = ksz + ksz            // kernels: kx, ky
1015                    + ksz * width * chan;  // working buffers
1016 
1017         cv::Size bufsize(buflen, 1);
1018         GMatDesc bufdesc = {CV_32F, 1, bufsize};
1019         Buffer buffer(bufdesc);
1020         scratch = std::move(buffer);
1021 
1022         auto *kx = scratch.OutLine<float>();
1023         auto *ky = kx + ksz;
1024 
1025         Mat kxmat(1, ksize, CV_32FC1, kx);
1026         Mat kymat(ksize, 1, CV_32FC1, ky);
1027         getDerivKernels(kxmat, kymat, dx, dy, ksize);
1028     }
1029 
1030     static void resetScratch(Buffer& /* scratch */)
1031     {
1032     }
1033 
1034     static Border getBorder(const cv::GMatDesc& /* src */,
1035                                       int       /* ddepth */,
1036                                       int       /* dx */,
1037                                       int       /* dy */,
1038                                       int       /* ksize */,
1039                                       double    /* scale */,
1040                                       double    /* delta */,
1041                                       int          borderType,
1042                             const cv::Scalar  &    borderValue)
1043     {
1044         return {borderType, borderValue};
1045     }
1046 };
1047 
1048 //---------------------
1049 //
1050 // Fluid kernels: SobelXY
1051 //
1052 //---------------------
1053 
GAPI_FLUID_KERNEL(GFluidSobelXY,cv::gapi::imgproc::GSobelXY,true)1054 GAPI_FLUID_KERNEL(GFluidSobelXY, cv::gapi::imgproc::GSobelXY, true)
1055 {
1056     static const int Window = 3;
1057 
1058     struct BufHelper
1059     {
1060         float *kx_dx, *ky_dx,
1061               *kx_dy, *ky_dy;
1062         float *buf_start;
1063         int buf_width, buf_chan;
1064 
1065         static int length(int ksz, int width, int chan)
1066         {
1067             return ksz + ksz + ksz + ksz    // kernels: kx_dx, ky_dx, kx_dy, ky_dy
1068                    + 2 * ksz * width * chan;
1069         }
1070 
1071         BufHelper(int ksz, int width, int chan, Buffer& scratch)
1072         {
1073             kx_dx = scratch.OutLine<float>();
1074             ky_dx = kx_dx + ksz;
1075             kx_dy = ky_dx + ksz;
1076             ky_dy = kx_dy + ksz;
1077             buf_start = ky_dy + ksz;
1078             buf_width = width;
1079             buf_chan = chan;
1080         }
1081 
1082         float* operator [](int i) {
1083             return buf_start + i *  buf_width * buf_chan;
1084         }
1085     };
1086 
1087     static void run(const     View  &    in,
1088                               int     /* ddepth */,
1089                               int     /* order */,
1090                               int        ksize,
1091                               double    _scale,
1092                               double    _delta,
1093                               int     /* borderType */,
1094                     const cv::Scalar& /* borderValue */,
1095                               Buffer&    out_x,
1096                               Buffer&    out_y,
1097                               Buffer&    scratch)
1098     {
1099         // TODO: support kernel height 3, 5, 7, 9, ...
1100         GAPI_Assert(ksize == 3 || ksize == FILTER_SCHARR);
1101 
1102         int ksz = (ksize == FILTER_SCHARR)? 3: ksize;
1103 
1104         GAPI_Assert(out_x.meta().size.width == out_y.meta().size.width);
1105         GAPI_Assert(out_x.meta().chan == out_y.meta().chan);
1106 
1107         int width = out_x.meta().size.width;
1108         int chan  = out_x.meta().chan;
1109 
1110         BufHelper buf_helper(ksz, width, chan, scratch);
1111 
1112         auto *kx_dx = buf_helper.kx_dx;
1113         auto *ky_dx = buf_helper.ky_dx;
1114         auto *kx_dy = buf_helper.kx_dy;
1115         auto *ky_dy = buf_helper.ky_dy;
1116 
1117         // Scratch buffer layout:
1118         // |kx_dx|ky_dx|kx_dy|ky_dy|3 lines for horizontal kernel|3 lines for vertical kernel|
1119         float *buf[3];
1120         buf[0] = buf_helper[0];
1121         buf[1] = buf_helper[1];
1122         buf[2] = buf_helper[2];
1123 
1124         auto scale = static_cast<float>(_scale);
1125         auto delta = static_cast<float>(_delta);
1126 
1127         auto calc = [&](const View& src, Buffer& dst, float* kx, float* ky) {
1128             //     DST     SRC     OP         __VA_ARGS__
1129             UNARY_(uchar , uchar , run_sobel, dst, src, kx, ky, ksz, scale, delta, buf);
1130             UNARY_(ushort, ushort, run_sobel, dst, src, kx, ky, ksz, scale, delta, buf);
1131             UNARY_( short, uchar , run_sobel, dst, src, kx, ky, ksz, scale, delta, buf);
1132             UNARY_( short, ushort, run_sobel, dst, src, kx, ky, ksz, scale, delta, buf);
1133             UNARY_( short,  short, run_sobel, dst, src, kx, ky, ksz, scale, delta, buf);
1134             UNARY_( float, uchar , run_sobel, dst, src, kx, ky, ksz, scale, delta, buf);
1135             UNARY_( float, ushort, run_sobel, dst, src, kx, ky, ksz, scale, delta, buf);
1136             UNARY_( float,  short, run_sobel, dst, src, kx, ky, ksz, scale, delta, buf);
1137             UNARY_( float,  float, run_sobel, dst, src, kx, ky, ksz, scale, delta, buf);
1138 
1139             CV_Error(cv::Error::StsBadArg, "unsupported combination of types");
1140         };
1141 
1142         // calculate x-derivative
1143         calc(in, out_x, kx_dx, ky_dx);
1144 
1145         // Move pointers to calculate dy(preventing buffer data corruption)
1146         buf[0] = buf_helper[3];
1147         buf[1] = buf_helper[4];
1148         buf[2] = buf_helper[5];
1149 
1150         // calculate y-derivative
1151         calc(in, out_y, kx_dy, ky_dy);
1152     }
1153 
1154     static void initScratch(const GMatDesc&    in,
1155                                   int       /* ddepth */,
1156                                   int          order,
1157                                   int          ksize,
1158                                   double    /* scale */,
1159                                   double    /* delta */,
1160                                   int       /* borderType */,
1161                             const Scalar  & /* borderValue */,
1162                                   Buffer  &    scratch)
1163     {
1164         // TODO: support kernel height 3, 5, 7, 9, ...
1165         GAPI_Assert(ksize == 3 || ksize == FILTER_SCHARR);
1166         int ksz = (ksize == FILTER_SCHARR) ? 3 : ksize;
1167 
1168         int width = in.size.width;
1169         int chan  = in.chan;
1170         int buflen = BufHelper::length(ksz, width, chan);
1171 
1172         cv::Size bufsize(buflen, 1);
1173         GMatDesc bufdesc = {CV_32F, 1, bufsize};
1174         Buffer buffer(bufdesc);
1175         scratch = std::move(buffer);
1176 
1177         BufHelper buf_helper(ksz, width, chan, scratch);
1178 
1179         auto *kx_dx = buf_helper.kx_dx;
1180         auto *ky_dx = buf_helper.ky_dx;
1181         auto *kx_dy = buf_helper.kx_dy;
1182         auto *ky_dy = buf_helper.ky_dy;
1183 
1184         Mat kxmatX(1, ksize, CV_32FC1, kx_dx);
1185         Mat kymatX(ksize, 1, CV_32FC1, ky_dx);
1186         getDerivKernels(kxmatX, kymatX, order, 0, ksize);
1187 
1188         Mat kxmatY(1, ksize, CV_32FC1, kx_dy);
1189         Mat kymatY(ksize, 1, CV_32FC1, ky_dy);
1190         getDerivKernels(kxmatY, kymatY, 0, order, ksize);
1191     }
1192 
1193     static void resetScratch(Buffer& /* scratch */)
1194     {
1195     }
1196 
1197     static Border getBorder(const cv::GMatDesc& /* src */,
1198                                       int       /* ddepth */,
1199                                       int       /* order */,
1200                                       int       /* ksize */,
1201                                       double    /* scale */,
1202                                       double    /* delta */,
1203                                       int          borderType,
1204                             const cv::Scalar  &    borderValue)
1205     {
1206         return {borderType, borderValue};
1207     }
1208 };
1209 
1210 //------------------------
1211 //
1212 // Fluid kernels: filter2D
1213 //
1214 //------------------------
1215 
1216 template<typename DST, typename SRC>
run_filter2d(Buffer & dst,const View & src,const float k[],int k_rows,int k_cols,const cv::Point &,float delta=0)1217 static void run_filter2d(Buffer& dst, const View& src,
1218                          const float k[], int k_rows, int k_cols,
1219                          const cv::Point& /* anchor */,
1220                          float delta=0)
1221 {
1222     static const int maxLines = 9;
1223     GAPI_Assert(k_rows <= maxLines);
1224 
1225     const SRC *in[ maxLines ];
1226           DST *out;
1227 
1228     int border_x = (k_cols - 1) / 2;
1229     int border_y = (k_rows - 1) / 2;
1230 
1231     for (int i=0; i < k_rows; i++)
1232     {
1233         in[i] = src.InLine<SRC>(i - border_y);
1234     }
1235 
1236     out = dst.OutLine<DST>();
1237 
1238     int width = dst.length();
1239     int chan  = dst.meta().chan;
1240     int length = width * chan;
1241 
1242     // manually optimized for 3x3
1243     if (k_rows == 3 && k_cols == 3)
1244     {
1245         float scale = 1;
1246         run_filter2d_3x3_impl(out, in, width, chan, k, scale, delta);
1247         return;
1248     }
1249 
1250     // reference: any kernel size
1251     for (int l=0; l < length; l++)
1252     {
1253         float sum = 0;
1254 
1255         for (int i=0; i < k_rows; i++)
1256         for (int j=0; j < k_cols; j++)
1257         {
1258             sum += in[i][l + (j - border_x)*chan] * k[k_cols*i + j];
1259         }
1260 
1261         float result = sum + delta;
1262 
1263         out[l] = saturate<DST>(result, rintf);
1264     }
1265 }
1266 
GAPI_FLUID_KERNEL(GFluidFilter2D,cv::gapi::imgproc::GFilter2D,true)1267 GAPI_FLUID_KERNEL(GFluidFilter2D, cv::gapi::imgproc::GFilter2D, true)
1268 {
1269     static const int Window = 3;
1270 
1271     static void run(const     View  &    src,
1272                               int     /* ddepth */,
1273                     const cv::Mat   &    kernel,
1274                     const cv::Point &    anchor,
1275                     const cv::Scalar&    delta_,
1276                               int     /* borderType */,
1277                     const cv::Scalar& /* borderValue */,
1278                               Buffer&    dst,
1279                               Buffer&    scratch)
1280     {
1281         // TODO: support non-trivial anchors
1282         GAPI_Assert(anchor.x == -1 && anchor.y == -1);
1283 
1284         // TODO: support kernel heights 3, 5, 7, 9, ...
1285         GAPI_Assert(kernel.rows == 3 && kernel.cols == 3);
1286 
1287         float delta = static_cast<float>(delta_[0]);
1288 
1289         int k_rows = kernel.rows;
1290         int k_cols = kernel.cols;
1291 
1292         const float *k = scratch.OutLine<float>(); // copy of kernel.data
1293 
1294         //     DST     SRC     OP            __VA_ARGS__
1295         UNARY_(uchar , uchar , run_filter2d, dst, src, k, k_rows, k_cols, anchor, delta);
1296         UNARY_(ushort, ushort, run_filter2d, dst, src, k, k_rows, k_cols, anchor, delta);
1297         UNARY_( short,  short, run_filter2d, dst, src, k, k_rows, k_cols, anchor, delta);
1298         UNARY_( float, uchar , run_filter2d, dst, src, k, k_rows, k_cols, anchor, delta);
1299         UNARY_( float, ushort, run_filter2d, dst, src, k, k_rows, k_cols, anchor, delta);
1300         UNARY_( float,  short, run_filter2d, dst, src, k, k_rows, k_cols, anchor, delta);
1301         UNARY_( float,  float, run_filter2d, dst, src, k, k_rows, k_cols, anchor, delta);
1302 
1303         CV_Error(cv::Error::StsBadArg, "unsupported combination of types");
1304     }
1305 
1306     static void initScratch(const cv::GMatDesc& /* in */,
1307                                       int       /* ddepth */,
1308                             const cv::Mat     &    kernel,
1309                             const cv::Point   & /* anchor */,
1310                             const cv::Scalar  & /* delta */,
1311                                       int       /* borderType */,
1312                             const cv::Scalar  & /* borderValue */,
1313                                       Buffer  &    scratch)
1314     {
1315         int krows = kernel.rows;
1316         int kcols = kernel.cols;
1317 
1318         int buflen = krows * kcols;  // kernel size
1319 
1320         cv::Size bufsize(buflen, 1);
1321         GMatDesc bufdesc = {CV_32F, 1, bufsize};
1322         Buffer buffer(bufdesc);
1323         scratch = std::move(buffer);
1324 
1325         // FIXME: move to resetScratch stage ?
1326         float *data = scratch.OutLine<float>();
1327         getKernel(data, kernel);
1328     }
1329 
1330     static void resetScratch(Buffer& /* scratch */)
1331     {
1332     }
1333 
1334     static Border getBorder(const cv::GMatDesc& /* src */,
1335                                       int       /* ddepth */,
1336                             const cv::Mat&      /* kernel */,
1337                             const cv::Point&    /* anchor */,
1338                             const cv::Scalar&   /* delta */,
1339                                       int          borderType,
1340                             const cv::Scalar&      borderValue)
1341     {
1342         return { borderType, borderValue};
1343     }
1344 };
1345 
1346 //-----------------------------
1347 //
1348 // Fluid kernels: erode, dilate
1349 //
1350 //-----------------------------
1351 
detect_morph3x3_shape(const uchar kernel[])1352 static MorphShape detect_morph3x3_shape(const uchar kernel[])
1353 {
1354     const uchar k[3][3] = {
1355         { kernel[0], kernel[1], kernel[2]},
1356         { kernel[3], kernel[4], kernel[5]},
1357         { kernel[6], kernel[7], kernel[8]}
1358     };
1359 
1360     if (k[0][0] && k[0][1] && k[0][2] &&
1361         k[1][0] && k[1][1] && k[1][2] &&
1362         k[2][0] && k[2][1] && k[2][2])
1363         return M_FULL;
1364 
1365     if (!k[0][0] && k[0][1] && !k[0][2] &&
1366          k[1][0] && k[1][1] &&  k[1][2] &&
1367         !k[2][0] && k[2][1] && !k[2][2])
1368         return M_CROSS;
1369 
1370     return M_UNDEF;
1371 }
1372 
1373 template<typename DST, typename SRC>
run_morphology(Buffer & dst,const View & src,const uchar k[],int k_rows,int k_cols,MorphShape k_type,const cv::Point &,Morphology morphology)1374 static void run_morphology(          Buffer&    dst,
1375                            const     View  &    src,
1376                            const     uchar      k[],
1377                                      int        k_rows,
1378                                      int        k_cols,
1379                                      MorphShape k_type,
1380                            const cv::Point & /* anchor */,
1381                                      Morphology morphology)
1382 {
1383     static_assert(std::is_same<DST, SRC>::value, "unsupported combination of types");
1384 
1385     GAPI_Assert(M_ERODE == morphology || M_DILATE == morphology);
1386 
1387     static const int maxLines = 9;
1388     GAPI_Assert(k_rows <= maxLines);
1389 
1390     const SRC *in[ maxLines ];
1391           DST *out;
1392 
1393     int border_x = (k_cols - 1) / 2;
1394     int border_y = (k_rows - 1) / 2;
1395 
1396     for (int i=0; i < k_rows; i++)
1397     {
1398         in[i] = src.InLine<SRC>(i - border_y);
1399     }
1400 
1401     out = dst.OutLine<DST>();
1402 
1403     int width = dst.length();
1404     int chan  = dst.meta().chan;
1405 
1406     // call optimized code, if 3x3
1407     if (3 == k_rows && 3 == k_cols)
1408     {
1409         run_morphology3x3_impl(out, in, width, chan, k, k_type, morphology);
1410         return;
1411     }
1412 
1413     // reference: any size of k[]
1414     int length = width * chan;
1415     for (int l=0; l < length; l++)
1416     {
1417         SRC result;
1418         if (M_ERODE == morphology)
1419         {
1420             result = std::numeric_limits<SRC>::max();
1421         }
1422         else // if (M_DILATE == morphology)
1423         {
1424             result = std::numeric_limits<SRC>::min();
1425         }
1426 
1427         for (int i=0; i < k_rows; i++)
1428         for (int j=0; j < k_cols; j++)
1429         {
1430             if ( k[k_cols*i + j] )
1431             {
1432                 if (M_ERODE == morphology)
1433                 {
1434                     result = (std::min)(result, in[i][l + (j - border_x)*chan]);
1435                 }
1436                 else // if (M_DILATE == morphology)
1437                 {
1438                     result = (std::max)(result, in[i][l + (j - border_x)*chan]);
1439                 }
1440             }
1441         }
1442 
1443         out[l] = saturate<DST>(result, rintf);
1444     }
1445 }
1446 
GAPI_FLUID_KERNEL(GFluidErode,cv::gapi::imgproc::GErode,true)1447 GAPI_FLUID_KERNEL(GFluidErode, cv::gapi::imgproc::GErode, true)
1448 {
1449     static const int Window = 3;
1450 
1451     static void run(const     View  &    src,
1452                     const cv::Mat   &    kernel,
1453                     const cv::Point &    anchor,
1454                               int        iterations,
1455                               int     /* borderType */,
1456                     const cv::Scalar& /* borderValue */,
1457                               Buffer&    dst,
1458                               Buffer&    scratch)
1459     {
1460         // TODO: support non-trivial anchors
1461         GAPI_Assert(anchor.x == -1 && anchor.y == -1);
1462 
1463         // TODO: support kernel heights 3, 5, 7, 9, ...
1464         GAPI_Assert(kernel.rows == 3 && kernel.cols == 3);
1465 
1466         // TODO: support iterations > 1
1467         GAPI_Assert(iterations == 1);
1468 
1469         int k_rows = kernel.rows;
1470         int k_cols = kernel.cols;
1471         int k_size = k_rows * k_cols;
1472 
1473         auto *k = scratch.OutLine<uchar>(); // copy of kernel.data
1474         auto k_type = static_cast<MorphShape>(k[k_size]);
1475 
1476         //     DST     SRC     OP              __VA_ARGS__
1477         UNARY_(uchar , uchar , run_morphology, dst, src, k, k_rows, k_cols, k_type, anchor, M_ERODE);
1478         UNARY_(ushort, ushort, run_morphology, dst, src, k, k_rows, k_cols, k_type, anchor, M_ERODE);
1479         UNARY_( short,  short, run_morphology, dst, src, k, k_rows, k_cols, k_type, anchor, M_ERODE);
1480         UNARY_( float,  float, run_morphology, dst, src, k, k_rows, k_cols, k_type, anchor, M_ERODE);
1481 
1482         CV_Error(cv::Error::StsBadArg, "unsupported combination of types");
1483     }
1484 
1485     static void initScratch(const GMatDesc& /* in */,
1486                             const Mat     &    kernel,
1487                             const Point   & /* anchor */,
1488                                   int       /* iterations */,
1489                                   int       /* borderType */,
1490                             const cv::Scalar  & /* borderValue */,
1491                                   Buffer  &    scratch)
1492     {
1493         int k_rows = kernel.rows;
1494         int k_cols = kernel.cols;
1495         int k_size = k_rows * k_cols;
1496 
1497         cv::Size bufsize(k_size + 1, 1);
1498         GMatDesc bufdesc = {CV_8U, 1, bufsize};
1499         Buffer buffer(bufdesc);
1500         scratch = std::move(buffer);
1501 
1502         // FIXME: move to resetScratch stage ?
1503         auto *k = scratch.OutLine<uchar>();
1504         getKernel(k, kernel);
1505 
1506         if (3 == k_rows && 3 == k_cols)
1507             k[k_size] = static_cast<uchar>(detect_morph3x3_shape(k));
1508         else
1509             k[k_size] = static_cast<uchar>(M_UNDEF);
1510     }
1511 
1512     static void resetScratch(Buffer& /* scratch */)
1513     {
1514     }
1515 
1516     static Border getBorder(const cv::GMatDesc& /* src */,
1517                             const cv::Mat   &   /* kernel */,
1518                             const cv::Point &   /* anchor */,
1519                                       int       /* iterations */,
1520                                       int          borderType,
1521                             const cv::Scalar&      borderValue)
1522     {
1523     #if 1
1524         // TODO: saturate borderValue to image type in general case (not only maximal border)
1525         GAPI_Assert(borderType == cv::BORDER_CONSTANT && borderValue[0] == DBL_MAX);
1526         return { borderType, cv::Scalar::all(INT_MAX) };
1527     #else
1528         return { borderType, borderValue };
1529     #endif
1530     }
1531 };
1532 
GAPI_FLUID_KERNEL(GFluidDilate,cv::gapi::imgproc::GDilate,true)1533 GAPI_FLUID_KERNEL(GFluidDilate, cv::gapi::imgproc::GDilate, true)
1534 {
1535     static const int Window = 3;
1536 
1537     static void run(const     View  &    src,
1538                     const cv::Mat   &    kernel,
1539                     const cv::Point &    anchor,
1540                               int        iterations,
1541                               int     /* borderType */,
1542                     const cv::Scalar& /* borderValue */,
1543                               Buffer&    dst,
1544                               Buffer&    scratch)
1545     {
1546         // TODO: support non-trivial anchors
1547         GAPI_Assert(anchor.x == -1 && anchor.y == -1);
1548 
1549         // TODO: support kernel heights 3, 5, 7, 9, ...
1550         GAPI_Assert(kernel.rows == 3 && kernel.cols == 3);
1551 
1552         // TODO: support iterations > 1
1553         GAPI_Assert(iterations == 1);
1554 
1555         int k_rows = kernel.rows;
1556         int k_cols = kernel.cols;
1557         int k_size = k_rows * k_cols;
1558 
1559         auto *k = scratch.OutLine<uchar>(); // copy of kernel.data
1560         auto k_type = static_cast<MorphShape>(k[k_size]);
1561 
1562         //     DST     SRC     OP              __VA_ARGS__
1563         UNARY_(uchar , uchar , run_morphology, dst, src, k, k_rows, k_cols, k_type, anchor, M_DILATE);
1564         UNARY_(ushort, ushort, run_morphology, dst, src, k, k_rows, k_cols, k_type, anchor, M_DILATE);
1565         UNARY_( short,  short, run_morphology, dst, src, k, k_rows, k_cols, k_type, anchor, M_DILATE);
1566         UNARY_( float,  float, run_morphology, dst, src, k, k_rows, k_cols, k_type, anchor, M_DILATE);
1567 
1568         CV_Error(cv::Error::StsBadArg, "unsupported combination of types");
1569     }
1570 
1571     static void initScratch(const GMatDesc& /* in */,
1572                             const Mat     &    kernel,
1573                             const Point   & /* anchor */,
1574                               int           /* iterations */,
1575                                   int       /* borderType */,
1576                             const cv::Scalar  & /* borderValue */,
1577                                   Buffer  &    scratch)
1578     {
1579         int k_rows = kernel.rows;
1580         int k_cols = kernel.cols;
1581         int k_size = k_rows * k_cols;
1582 
1583         cv::Size bufsize(k_size + 1, 1);
1584         GMatDesc bufdesc = {CV_8U, 1, bufsize};
1585         Buffer buffer(bufdesc);
1586         scratch = std::move(buffer);
1587 
1588         // FIXME: move to resetScratch stage ?
1589         auto *k = scratch.OutLine<uchar>();
1590         getKernel(k, kernel);
1591 
1592         if (3 == k_rows && 3 == k_cols)
1593             k[k_size] = static_cast<uchar>(detect_morph3x3_shape(k));
1594         else
1595             k[k_size] = static_cast<uchar>(M_UNDEF);
1596     }
1597 
1598     static void resetScratch(Buffer& /* scratch */)
1599     {
1600     }
1601 
1602     static Border getBorder(const cv::GMatDesc& /* src */,
1603                             const cv::Mat   &   /* kernel */,
1604                             const cv::Point &   /* anchor */,
1605                                       int       /* iterations */,
1606                                       int       borderType,
1607                             const cv::Scalar&   borderValue)
1608     {
1609     #if 1
1610         // TODO: fix borderValue for Dilate in general case (not only minimal border)
1611         GAPI_Assert(borderType == cv::BORDER_CONSTANT && borderValue[0] == DBL_MAX);
1612         return { borderType, cv::Scalar::all(INT_MIN) };
1613     #else
1614         return { borderType, borderValue };
1615     #endif
1616     }
1617 };
1618 
1619 //--------------------------
1620 //
1621 // Fluid kernels: medianBlur
1622 //
1623 //--------------------------
1624 
1625 template<typename DST, typename SRC>
run_medianblur(Buffer & dst,const View & src,int ksize)1626 static void run_medianblur(      Buffer& dst,
1627                            const View  & src,
1628                                  int     ksize)
1629 {
1630     static_assert(std::is_same<DST, SRC>::value, "unsupported combination of types");
1631 
1632     constexpr int kmax = 9;
1633     GAPI_Assert(ksize <= kmax);
1634 
1635     const SRC *in[ kmax ];
1636           DST *out;
1637 
1638     int border = (ksize - 1) / 2;
1639 
1640     for (int i=0; i < ksize; i++)
1641     {
1642         in[i] = src.InLine<SRC>(i - border);
1643     }
1644 
1645     out = dst.OutLine<DST>(0);
1646 
1647     int width = dst.length();
1648     int chan  = dst.meta().chan;
1649 
1650     // optimized: if 3x3
1651 
1652     if (3 == ksize)
1653     {
1654         run_medblur3x3_impl(out, in, width, chan);
1655         return;
1656     }
1657 
1658     // reference: any ksize
1659 
1660     int length = width * chan;
1661     int klength = ksize * ksize;
1662     int klenhalf = klength / 2;
1663 
1664     for (int l=0; l < length; l++)
1665     {
1666         SRC neighbours[kmax * kmax];
1667 
1668         for (int i=0; i < ksize; i++)
1669         for (int j=0; j < ksize; j++)
1670         {
1671             neighbours[i*ksize + j] = in[i][l + (j - border)*chan];
1672         }
1673 
1674         std::nth_element(neighbours, neighbours + klenhalf, neighbours + klength);
1675 
1676         out[l] = saturate<DST>(neighbours[klenhalf], rintf);
1677     }
1678 }
1679 
GAPI_FLUID_KERNEL(GFluidMedianBlur,cv::gapi::imgproc::GMedianBlur,false)1680 GAPI_FLUID_KERNEL(GFluidMedianBlur, cv::gapi::imgproc::GMedianBlur, false)
1681 {
1682     static const int Window = 3;
1683 
1684     static void run(const View  & src,
1685                           int     ksize,
1686                           Buffer& dst)
1687     {
1688         // TODO: support kernel sizes: 3, 5, 7, 9, ...
1689         GAPI_Assert(ksize == 3);
1690 
1691         //     DST     SRC     OP              __VA_ARGS__
1692         UNARY_(uchar , uchar , run_medianblur, dst, src, ksize);
1693         UNARY_(ushort, ushort, run_medianblur, dst, src, ksize);
1694         UNARY_( short,  short, run_medianblur, dst, src, ksize);
1695         UNARY_( float,  float, run_medianblur, dst, src, ksize);
1696 
1697         CV_Error(cv::Error::StsBadArg, "unsupported combination of types");
1698     }
1699 
1700     static Border getBorder(const cv::GMatDesc& /* src */,
1701                                       int       /* ksize */)
1702     {
1703         int  borderType  = cv::BORDER_REPLICATE;
1704         auto borderValue = cv::Scalar();
1705         return { borderType, borderValue };
1706     }
1707 };
1708 
GAPI_FLUID_KERNEL(GFluidRGB2YUV422,cv::gapi::imgproc::GRGB2YUV422,false)1709 GAPI_FLUID_KERNEL(GFluidRGB2YUV422, cv::gapi::imgproc::GRGB2YUV422, false)
1710 {
1711     static const int Window = 1;
1712     static const auto Kind = cv::GFluidKernel::Kind::Filter;
1713 
1714     static void run(const cv::gapi::fluid::View& in,
1715                     cv::gapi::fluid::Buffer& out)
1716     {
1717         const auto *src = in.InLine<uchar>(0);
1718         auto *dst = out.OutLine<uchar>();
1719 
1720         run_rgb2yuv422_impl(dst, src, in.length());
1721     }
1722 };
1723 
GAPI_FLUID_KERNEL(GFluidRGB2HSV,cv::gapi::imgproc::GRGB2HSV,true)1724 GAPI_FLUID_KERNEL(GFluidRGB2HSV, cv::gapi::imgproc::GRGB2HSV, true)
1725 {
1726     static const int Window = 1;
1727     static const auto Kind = cv::GFluidKernel::Kind::Filter;
1728 
1729     static void run(const cv::gapi::fluid::View&   in,
1730                     cv::gapi::fluid::Buffer& out,
1731                     cv::gapi::fluid::Buffer& scratch)
1732     {
1733         const auto *src = in.InLine<uchar>(0);
1734         auto *dst = out.OutLine<uchar>();
1735 
1736         auto* sdiv_table = scratch.OutLine<int>(0);
1737         auto* hdiv_table = sdiv_table + 256;
1738 
1739         run_rgb2hsv_impl(dst, src, sdiv_table, hdiv_table, in.length());
1740     }
1741 
1742     static void initScratch(const cv::GMatDesc& /* in */,
1743                             cv::gapi::fluid::Buffer& scratch)
1744     {
1745         const int hsv_shift = 12;
1746 
1747         cv::GMatDesc desc;
1748         desc.chan  = 1;
1749         desc.depth = CV_32S;
1750         desc.size  = cv::Size(512, 1);
1751 
1752         cv::gapi::fluid::Buffer buffer(desc);
1753         scratch = std::move(buffer);
1754 
1755         auto* sdiv_table = scratch.OutLine<int>(0);
1756         auto* hdiv_table = sdiv_table + 256;
1757 
1758         sdiv_table[0] = hdiv_table[0] = 0;
1759         for(int i = 1; i < 256; i++ )
1760         {
1761             sdiv_table[i] = cv::saturate_cast<int>((255 << hsv_shift)/(1.*i));
1762             hdiv_table[i] = cv::saturate_cast<int>((180 << hsv_shift)/(6.*i));
1763         }
1764 
1765     }
1766 
1767     static void resetScratch(cv::gapi::fluid::Buffer& /* scratch */)
1768     {
1769     }
1770 };
1771 
GAPI_FLUID_KERNEL(GFluidBayerGR2RGB,cv::gapi::imgproc::GBayerGR2RGB,false)1772 GAPI_FLUID_KERNEL(GFluidBayerGR2RGB, cv::gapi::imgproc::GBayerGR2RGB, false)
1773 {
1774     static const int Window = 3;
1775     static const int LPI    = 2;
1776 
1777     static void run(const cv::gapi::fluid::View& in,
1778                     cv::gapi::fluid::Buffer& out)
1779     {
1780         const int height = in.meta().size.height;
1781         const int border_size = 1;
1782         const int width = in.length();
1783 
1784         constexpr int num_lines = LPI + 2 * border_size;
1785         const uchar* src[num_lines];
1786         uchar* dst[LPI];
1787 
1788         for (int i = 0; i < LPI; ++i)
1789         {
1790             dst[i] = out.OutLine<uchar>(i);
1791         }
1792 
1793         for (int i = 0; i < num_lines; ++i)
1794         {
1795             src[i] = in.InLine<uchar>(i - 1);
1796         }
1797 
1798         if (in.y() == -1)
1799         {
1800             run_bayergr2rgb_bg_impl(dst[1], src + border_size, width);
1801             std::copy_n(dst[1], width * 3, dst[0]);
1802         }
1803         else if (in.y() == height - LPI - 2 * border_size + 1)
1804         {
1805             run_bayergr2rgb_gr_impl(dst[0], src, width);
1806             std::copy_n(dst[0], width * 3, dst[1]);
1807         }
1808         else
1809         {
1810             run_bayergr2rgb_gr_impl(dst[0], src, width);
1811             run_bayergr2rgb_bg_impl(dst[1], src + border_size, width);
1812         }
1813     }
1814 
1815     static cv::gapi::fluid::Border getBorder(const cv::GMatDesc&)
1816     {
1817         int  borderType  = cv::BORDER_CONSTANT;
1818         auto borderValue = cv::Scalar();
1819 
1820         return { borderType, borderValue };
1821     }
1822 };
1823 
1824 } // namespace fluid
1825 } // namespace gapi
1826 } // namespace cv
1827 
kernels()1828 cv::gapi::GKernelPackage cv::gapi::imgproc::fluid::kernels()
1829 {
1830     using namespace cv::gapi::fluid;
1831 
1832     return cv::gapi::kernels
1833     <   GFluidBGR2Gray
1834       , GFluidRGB2Gray
1835       , GFluidRGB2GrayCustom
1836       , GFluidRGB2YUV
1837       , GFluidYUV2RGB
1838       , GFluidRGB2Lab
1839       , GFluidBGR2LUV
1840       , GFluidBlur
1841       , GFluidSepFilter
1842       , GFluidBoxFilter
1843       , GFluidFilter2D
1844       , GFluidErode
1845       , GFluidDilate
1846       , GFluidMedianBlur
1847       , GFluidGaussBlur
1848       , GFluidSobel
1849       , GFluidSobelXY
1850       , GFluidRGB2YUV422
1851       , GFluidRGB2HSV
1852       , GFluidBayerGR2RGB
1853     #if 0
1854       , GFluidCanny        -- not fluid (?)
1855       , GFluidEqualizeHist -- not fluid
1856     #endif
1857     >();
1858 }
1859 
1860 #endif // !defined(GAPI_STANDALONE)
1861