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