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