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.
5 // Copyright (C) 2018-2019 Intel Corporation
7 #if !defined(GAPI_STANDALONE)
11 #include "opencv2/gapi/own/assert.hpp"
12 #include "opencv2/core/traits.hpp"
13 #include "opencv2/imgproc/types_c.h"
15 #include "opencv2/gapi/core.hpp"
16 #include "opencv2/gapi/imgproc.hpp"
18 #include "opencv2/gapi/own/types.hpp"
20 #include "opencv2/gapi/fluid/gfluidbuffer.hpp"
21 #include "opencv2/gapi/fluid/gfluidkernel.hpp"
22 #include "opencv2/gapi/fluid/imgproc.hpp"
24 #include "gfluidbuffer_priv.hpp"
25 #include "gfluidbackend.hpp"
26 #include "gfluidutils.hpp"
28 #include "gfluidimgproc_func.hpp"
30 #include "opencv2/imgproc/hal/hal.hpp"
31 #include "opencv2/core/hal/intrin.hpp"
40 //----------------------------------
42 // Fluid kernels: RGB2Gray, BGR2Gray
44 //----------------------------------
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};
52 // G' = Y' - 0.394*U' - 0.581*V'
54 static const float coef_yuv2rgb_bt601[4] = {1.140f, -0.394f, -0.581f, 2.032f};
56 static void run_rgb2gray(Buffer &dst, const View &src, float coef_r, float coef_g, float coef_b)
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());
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);
67 const auto *in = src.InLine<uchar>(0);
68 auto *out = dst.OutLine<uchar>();
70 int width = dst.length();
72 run_rgb2gray_impl(out, in, width, coef_r, coef_g, coef_b);
75 GAPI_FLUID_KERNEL(GFluidRGB2GrayCustom, cv::gapi::imgproc::GRGB2GrayCustom, false)
77 static const int Window = 1;
79 static void run(const View &src, float coef_r, float coef_g, float coef_b, Buffer &dst)
81 run_rgb2gray(dst, src, coef_r, coef_g, coef_b);
85 GAPI_FLUID_KERNEL(GFluidRGB2Gray, cv::gapi::imgproc::GRGB2Gray, false)
87 static const int Window = 1;
89 static void run(const View &src, Buffer &dst)
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);
98 GAPI_FLUID_KERNEL(GFluidBGR2Gray, cv::gapi::imgproc::GBGR2Gray, false)
100 static const int Window = 1;
102 static void run(const View &src, Buffer &dst)
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);
111 //--------------------------------------
113 // Fluid kernels: RGB-to-YUV, YUV-to-RGB
115 //--------------------------------------
117 static void run_rgb2yuv(Buffer &dst, const View &src, const float coef[5])
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());
125 const auto *in = src.InLine<uchar>(0);
126 auto *out = dst.OutLine<uchar>();
128 int width = dst.length();
130 run_rgb2yuv_impl(out, in, width, coef);
133 static void run_yuv2rgb(Buffer &dst, const View &src, const float coef[4])
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());
141 const auto *in = src.InLine<uchar>(0);
142 auto *out = dst.OutLine<uchar>();
144 int width = dst.length();
146 run_yuv2rgb_impl(out, in, width, coef);
149 GAPI_FLUID_KERNEL(GFluidRGB2YUV, cv::gapi::imgproc::GRGB2YUV, false)
151 static const int Window = 1;
153 static void run(const View &src, Buffer &dst)
155 run_rgb2yuv(dst, src, coef_rgb2yuv_bt601);
159 GAPI_FLUID_KERNEL(GFluidYUV2RGB, cv::gapi::imgproc::GYUV2RGB, false)
161 static const int Window = 1;
163 static void run(const View &src, Buffer &dst)
165 run_yuv2rgb(dst, src, coef_yuv2rgb_bt601);
169 //--------------------------------------
171 // Fluid kernels: RGB-to-Lab, BGR-to-LUV
173 //--------------------------------------
175 enum LabLUV { LL_Lab, LL_LUV };
177 #define LabLuv_reference 0 // 1=use reference code of RGB/BGR to LUV/Lab, 0=don't
181 // gamma-correction (inverse) for sRGB, 1/gamma=2.4 for inverse, like for Mac OS (?)
182 static inline float f_gamma(float x)
184 return x <= 0.04045f ? x*(1.f/12.92f) : std::pow((x + 0.055f)*(1/1.055f), 2.4f);
187 // saturate into interval [0, 1]
188 static inline float clip01(float value)
195 static inline void f_rgb2xyz(float R, float G, float B,
196 float& X, float& Y, float& Z)
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);
203 static inline void f_xyz2lab(float X, float Y, float Z,
204 float& L, float& a, float& b)
206 // CIE XYZ values of reference white point for D65 illuminant
207 static const float Xn = 0.950456f, Yn = 1.f, Zn = 1.088754f;
209 // Other coefficients below:
210 // 7.787f = (29/3)^3/(29*4)
211 // 0.008856f = (6/29)^3
214 float x = X/Xn, y = Y/Yn, z = Z/Zn;
216 auto f = [](float t){ return t>0.008856f? std::cbrt(t): (7.787f*t + 16.f/116.f); };
218 float fx = f(x), fy = f(y), fz = f(z);
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);
225 static inline void f_xyz2luv(float X, float Y, float Z,
226 float& L, float& u, float& v)
228 static const float un = 0.19793943f, vn = 0.46831096f;
230 float u1 = 4*X / (X + 15*Y + 3*Z);
231 float v1 = 9*Y / (X + 15*Y + 3*Z);
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);
238 template<LabLUV labluv, int blue=0>
239 static void run_rgb2labluv_reference(uchar out[], const uchar in[], int width)
241 for (int w=0; w < width; w++)
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;
253 f_rgb2xyz(R, G, B, X, Y, Z);
256 if (LL_Lab == labluv)
259 f_xyz2lab(X, Y, Z, L, a, b);
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);
265 else if (LL_LUV == labluv)
268 f_xyz2luv(X, Y, Z, L, u, v);
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);
275 CV_Error(cv::Error::StsBadArg, "unsupported color conversion");;
279 #endif // LabLuv_reference
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)
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());
292 const auto *in = src.InLine<uchar>(0);
293 auto *out = dst.OutLine<uchar>();
295 int width = dst.length();
298 run_rgb2labluv_reference<labluv, blue>(out, in, width);
300 uchar *dst_data = out;
301 const uchar *src_data = in;
302 size_t src_step = width;
303 size_t dst_step = width;
307 bool swapBlue = (blue == 2);
308 bool isLab = (LL_Lab == labluv);
310 cv::hal::cvtBGRtoLab(src_data, src_step, dst_data, dst_step,
311 width, height, depth, scn, swapBlue, isLab, srgb);
315 GAPI_FLUID_KERNEL(GFluidRGB2Lab, cv::gapi::imgproc::GRGB2Lab, false)
317 static const int Window = 1;
319 static void run(const View &src, Buffer &dst)
321 static const int blue = 2; // RGB: 0=red, 1=green, 2=blue
322 run_rgb2labluv<LL_Lab, blue>(dst, src);
326 GAPI_FLUID_KERNEL(GFluidBGR2LUV, cv::gapi::imgproc::GBGR2LUV, false)
328 static const int Window = 1;
330 static void run(const View &src, Buffer &dst)
332 static const int blue = 0; // BGR: 0=blue, 1=green, 2=red
333 run_rgb2labluv<LL_LUV, blue>(dst, src);
337 //-------------------------------
339 // Fluid kernels: blur, boxFilter
341 //-------------------------------
343 static const int maxKernelSize = 9;
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[])
349 GAPI_Assert(kernelSize.width <= maxKernelSize);
350 GAPI_Assert(kernelSize.width == kernelSize.height);
352 int kernel = kernelSize.width;
353 int border = (kernel - 1) / 2;
355 const SRC *in[ maxKernelSize ];
358 for (int i=0; i < kernel; i++)
360 in[i] = src.InLine<SRC>(i - border);
363 out = dst.OutLine<DST>();
365 int width = dst.length();
366 int chan = dst.meta().chan;
368 if (kernelSize.width == 3 && kernelSize.height == 3)
371 int y0 = dst.priv().writeStart();
373 float kx[3] = {1, 1, 1};
376 float scale=1, delta=0;
380 run_sepfilter3x3_impl(out, in, width, chan, kx, ky, border, scale, delta, buf, y, y0);
383 GAPI_DbgAssert(chan <= 4);
385 for (int w=0; w < width; w++)
387 float sum[4] = {0, 0, 0, 0};
389 for (int i=0; i < kernel; i++)
391 for (int j=0; j < kernel; j++)
393 for (int c=0; c < chan; c++)
394 sum[c] += in[i][(w + j - border)*chan + c];
398 for (int c=0; c < chan; c++)
400 float result = normalize? sum[c]/(kernel * kernel) : sum[c];
402 out[w*chan + c] = saturate<DST>(result, rintf);
408 GAPI_FLUID_KERNEL(GFluidBlur, cv::gapi::imgproc::GBlur, true)
410 static const int Window = 3;
412 static void run(const View &src, const cv::Size& kernelSize, const cv::Point& anchor,
413 int /* borderType */, const cv::Scalar& /* borderValue */, Buffer &dst,
416 // TODO: support sizes 3, 5, 7, 9, ...
417 GAPI_Assert(kernelSize.width == 3 && kernelSize.height == 3);
419 // TODO: suport non-trivial anchor
420 GAPI_Assert(anchor.x == -1 && anchor.y == -1);
422 static const bool normalize = true;
424 int width = src.length();
425 int chan = src.meta().chan;
426 int length = width * chan;
429 buf[0] = scratch.OutLine<float>();
430 buf[1] = buf[0] + length;
431 buf[2] = buf[1] + length;
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);
439 CV_Error(cv::Error::StsBadArg, "unsupported combination of types");
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 */,
449 int width = in.size.width;
452 int buflen = width * chan * Window; // work buffers
454 cv::gapi::own::Size bufsize(buflen, 1);
455 GMatDesc bufdesc = {CV_32F, 1, bufsize};
456 Buffer buffer(bufdesc);
457 scratch = std::move(buffer);
460 static void resetScratch(Buffer& /* scratch */)
464 static Border getBorder(const cv::GMatDesc& /* src */,
465 const cv::Size & /* kernelSize */,
466 const cv::Point & /* anchor */,
468 const cv::Scalar & borderValue)
470 return { borderType, borderValue};
474 GAPI_FLUID_KERNEL(GFluidBoxFilter, cv::gapi::imgproc::GBoxFilter, true)
476 static const int Window = 3;
478 static void run(const View & src,
480 const cv::Size & kernelSize,
481 const cv::Point & anchor,
483 int /* borderType */,
484 const cv::Scalar& /* borderValue */,
488 // TODO: support sizes 3, 5, 7, 9, ...
489 GAPI_Assert(kernelSize.width == 3 && kernelSize.height == 3);
491 // TODO: suport non-trivial anchor
492 GAPI_Assert(anchor.x == -1 && anchor.y == -1);
494 int width = src.length();
495 int chan = src.meta().chan;
496 int length = width * chan;
499 buf[0] = scratch.OutLine<float>();
500 buf[1] = buf[0] + length;
501 buf[2] = buf[1] + length;
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);
512 CV_Error(cv::Error::StsBadArg, "unsupported combination of types");
515 static void initScratch(const GMatDesc & in,
517 const cv::Size & /* kernelSize */,
518 const cv::Point & /* anchor */,
519 bool /* normalize */,
520 int /* borderType */,
521 const cv::Scalar& /* borderValue */,
524 int width = in.size.width;
527 int buflen = width * chan * Window; // work buffers
529 cv::gapi::own::Size bufsize(buflen, 1);
530 GMatDesc bufdesc = {CV_32F, 1, bufsize};
531 Buffer buffer(bufdesc);
532 scratch = std::move(buffer);
535 static void resetScratch(Buffer& /* scratch */)
539 static Border getBorder(const cv::GMatDesc& /* src */,
541 const cv::Size & /* kernelSize */,
542 const cv::Point & /* anchor */,
543 bool /* normalize */,
545 const cv::Scalar & borderValue)
547 return { borderType, borderValue};
551 //-------------------------
553 // Fluid kernels: sepFilter
555 //-------------------------
558 static void getKernel(T k[], const cv::Mat& kernel)
560 GAPI_Assert(kernel.channels() == 1);
562 int depth = CV_MAT_DEPTH(kernel.type());
563 int cols = kernel.cols;
564 int rows = kernel.rows;
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) );
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) );
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) );
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) );
588 default: CV_Error(cv::Error::StsBadArg, "unsupported kernel type");
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,
600 constexpr int kMax = 11;
601 GAPI_Assert(kxLen <= kMax && kyLen <= kMax);
606 int xborder = (kxLen - 1) / 2;
607 int yborder = (kyLen - 1) / 2;
609 for (int i=0; i < kyLen; i++)
611 in[i] = src.InLine<SRC>(i - yborder);
614 out = dst.OutLine<DST>();
616 int width = dst.length();
617 int chan = dst.meta().chan;
619 // optimized 3x3 vs reference
620 if (kxLen == 3 && kyLen == 3)
623 int y0 = dst.priv().writeStart();
625 int border = xborder;
626 run_sepfilter3x3_impl(out, in, width, chan, kx, ky, border, scale, delta, buf, y, y0);
630 int length = chan * width;
631 int xshift = chan * xborder;
635 for (int k=0; k < kyLen; k++)
637 const SRC *inp[kMax] = {nullptr};
639 for (int j=0; j < kxLen; j++)
641 inp[j] = in[k] + (j - xborder)*xshift;
644 for (int l=0; l < length; l++)
647 for (int j=0; j < kxLen; j++)
649 sum += inp[j][l] * kx[j];
657 for (int l=0; l < length; l++)
660 for (int k=0; k < kyLen; k++)
662 sum += buf[k][l] * ky[k];
664 out[l] = saturate<DST>(sum*scale + delta, rintf);
669 GAPI_FLUID_KERNEL(GFluidSepFilter, cv::gapi::imgproc::GSepFilter, true)
671 static const int Window = 3;
673 static void run(const View& src,
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 */,
684 // TODO: support non-trivial anchors
685 GAPI_Assert(anchor.x == -1 && anchor.y == -1);
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));
691 int kxLen = kernX.rows * kernX.cols;
692 int kyLen = kernY.rows * kernY.cols;
694 GAPI_Assert(kyLen == 3);
696 float *kx = scratch.OutLine<float>();
697 float *ky = kx + kxLen;
699 int width = src.meta().size.width;
700 int chan = src.meta().chan;
701 int length = width * chan;
705 buf[1] = buf[0] + length;
706 buf[2] = buf[1] + length;
709 float delta = static_cast<float>(delta_[0]);
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);
721 CV_Error(cv::Error::StsBadArg, "unsupported combination of types");
724 static void initScratch(const GMatDesc& in,
728 const Point & /* anchor */,
729 const Scalar & /* delta */,
730 int /* borderType */,
731 const Scalar & /* borderValue */,
734 int kxLen = kernX.rows * kernX.cols;
735 int kyLen = kernY.rows * kernY.cols;
737 int width = in.size.width;
740 int buflen = kxLen + kyLen + // x, y kernels
741 width * chan * Window; // work buffers
743 cv::gapi::own::Size bufsize(buflen, 1);
744 GMatDesc bufdesc = {CV_32F, 1, bufsize};
745 Buffer buffer(bufdesc);
746 scratch = std::move(buffer);
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);
755 static void resetScratch(Buffer& /* scratch */)
759 static Border getBorder(const cv::GMatDesc& /* src */,
761 const cv::Mat& /* kernX */,
762 const cv::Mat& /* kernY */,
763 const cv::Point& /* anchor */,
764 const cv::Scalar& /* delta */,
766 const cv::Scalar& borderValue)
768 return { borderType, borderValue};
772 //----------------------------
774 // Fluid kernels: gaussianBlur
776 //----------------------------
778 GAPI_FLUID_KERNEL(GFluidGaussBlur, cv::gapi::imgproc::GGaussBlur, true)
780 // TODO: support kernel height 3, 5, 7, 9, ...
781 static const int Window = 3;
783 static void run(const View & src,
784 const cv::Size & ksize,
787 int /* borderType */,
788 const cv::Scalar& /* borderValue */,
792 GAPI_Assert(ksize.height == 3);
794 int kxsize = ksize.width;
795 int kysize = ksize.height;
797 auto *kx = scratch.OutLine<float>(); // cached kernX data
798 auto *ky = kx + kxsize; // cached kernY data
800 int width = src.meta().size.width;
801 int chan = src.meta().chan;
802 int length = width * chan;
805 buf[0] = ky + kysize;
806 buf[1] = buf[0] + length;
807 buf[2] = buf[1] + length;
809 auto anchor = cv::Point(-1, -1);
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);
820 CV_Error(cv::Error::StsBadArg, "unsupported combination of types");
823 static void initScratch(const GMatDesc& in,
824 const cv::Size & ksize,
827 int /* borderType */,
828 const cv::Scalar & /* borderValue */,
831 int kxsize = ksize.width;
832 int kysize = ksize.height;
834 int width = in.size.width;
837 int buflen = kxsize + kysize + // x, y kernels
838 width * chan * Window; // work buffers
840 cv::gapi::own::Size bufsize(buflen, 1);
841 GMatDesc bufdesc = {CV_32F, 1, bufsize};
842 Buffer buffer(bufdesc);
843 scratch = std::move(buffer);
845 // FIXME: fill buffer at resetScratch stage?
848 sigmaX = 0.3 * ((kxsize - 1)/2. - 1) + 0.8;
853 Mat kernX = getGaussianKernel(kxsize, sigmaX, CV_32F);
856 if (sigmaY != sigmaX)
857 kernY = getGaussianKernel(kysize, sigmaY, CV_32F);
859 auto *kx = scratch.OutLine<float>();
860 auto *ky = kx + kxsize;
862 getKernel(kx, kernX);
863 getKernel(ky, kernY);
866 static void resetScratch(Buffer& /* scratch */)
870 static Border getBorder(const cv::GMatDesc& /* src */,
871 const cv::Size & /* ksize */,
875 const cv::Scalar & borderValue)
877 return { borderType, borderValue};
881 //---------------------
883 // Fluid kernels: Sobel
885 //---------------------
887 template<typename DST, typename SRC>
888 static void run_sobel(Buffer& dst,
893 float scale, // default: 1
894 float delta, // default: 0
897 static const int kmax = 11;
898 GAPI_Assert(ksize <= kmax);
900 const SRC *in[ kmax ];
903 int border = (ksize - 1) / 2;
904 for (int i=0; i < ksize; i++)
906 in[i] = src.InLine<SRC>(i - border);
909 out = dst.OutLine<DST>();
911 int width = dst.length();
912 int chan = dst.meta().chan;
914 GAPI_DbgAssert(ksize == 3);
915 // float buf[3][width * chan];
918 int y0 = dst.priv().writeStart();
919 // int y1 = dst.priv().writeEnd();
921 run_sepfilter3x3_impl(out, in, width, chan, kx, ky, border, scale, delta, buf, y, y0);
924 GAPI_FLUID_KERNEL(GFluidSobel, cv::gapi::imgproc::GSobel, true)
926 static const int Window = 3;
928 static void run(const View & src,
935 int /* borderType */,
936 const cv::Scalar& /* borderValue */,
940 // TODO: support kernel height 3, 5, 7, 9, ...
941 GAPI_Assert(ksize == 3 || ksize == FILTER_SCHARR);
943 int ksz = (ksize == FILTER_SCHARR)? 3: ksize;
945 auto *kx = scratch.OutLine<float>();
948 int width = dst.meta().size.width;
949 int chan = dst.meta().chan;
953 buf[1] = buf[0] + width*chan;
954 buf[2] = buf[1] + width*chan;
956 auto scale = static_cast<float>(_scale);
957 auto delta = static_cast<float>(_delta);
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);
970 CV_Error(cv::Error::StsBadArg, "unsupported combination of types");
973 static void initScratch(const GMatDesc& in,
980 int /* borderType */,
981 const Scalar & /* borderValue */,
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;
988 int width = in.size.width;
991 int buflen = ksz + ksz // kernels: kx, ky
992 + ksz * width * chan; // working buffers
994 cv::gapi::own::Size bufsize(buflen, 1);
995 GMatDesc bufdesc = {CV_32F, 1, bufsize};
996 Buffer buffer(bufdesc);
997 scratch = std::move(buffer);
999 auto *kx = scratch.OutLine<float>();
1000 auto *ky = kx + ksz;
1002 Mat kxmat(1, ksize, CV_32FC1, kx);
1003 Mat kymat(ksize, 1, CV_32FC1, ky);
1004 getDerivKernels(kxmat, kymat, dx, dy, ksize);
1007 static void resetScratch(Buffer& /* scratch */)
1011 static Border getBorder(const cv::GMatDesc& /* src */,
1019 const cv::Scalar & borderValue)
1021 return {borderType, borderValue};
1025 //------------------------
1027 // Fluid kernels: filter2D
1029 //------------------------
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 */,
1037 static const int maxLines = 9;
1038 GAPI_Assert(k_rows <= maxLines);
1040 const SRC *in[ maxLines ];
1043 int border_x = (k_cols - 1) / 2;
1044 int border_y = (k_rows - 1) / 2;
1046 for (int i=0; i < k_rows; i++)
1048 in[i] = src.InLine<SRC>(i - border_y);
1051 out = dst.OutLine<DST>();
1053 int width = dst.length();
1054 int chan = dst.meta().chan;
1055 int length = width * chan;
1057 // manually optimized for 3x3
1058 if (k_rows == 3 && k_cols == 3)
1061 run_filter2d_3x3_impl(out, in, width, chan, k, scale, delta);
1065 // reference: any kernel size
1066 for (int l=0; l < length; l++)
1070 for (int i=0; i < k_rows; i++)
1071 for (int j=0; j < k_cols; j++)
1073 sum += in[i][l + (j - border_x)*chan] * k[k_cols*i + j];
1076 float result = sum + delta;
1078 out[l] = saturate<DST>(result, rintf);
1082 GAPI_FLUID_KERNEL(GFluidFilter2D, cv::gapi::imgproc::GFilter2D, true)
1084 static const int Window = 3;
1086 static void run(const View & src,
1088 const cv::Mat & kernel,
1089 const cv::Point & anchor,
1090 const cv::Scalar& delta_,
1091 int /* borderType */,
1092 const cv::Scalar& /* borderValue */,
1096 // TODO: support non-trivial anchors
1097 GAPI_Assert(anchor.x == -1 && anchor.y == -1);
1099 // TODO: support kernel heights 3, 5, 7, 9, ...
1100 GAPI_Assert(kernel.rows == 3 && kernel.cols == 3);
1102 float delta = static_cast<float>(delta_[0]);
1104 int k_rows = kernel.rows;
1105 int k_cols = kernel.cols;
1107 const float *k = scratch.OutLine<float>(); // copy of kernel.data
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);
1118 CV_Error(cv::Error::StsBadArg, "unsupported combination of types");
1121 static void initScratch(const cv::GMatDesc& /* in */,
1123 const cv::Mat & kernel,
1124 const cv::Point & /* anchor */,
1125 const cv::Scalar & /* delta */,
1126 int /* borderType */,
1127 const cv::Scalar & /* borderValue */,
1130 int krows = kernel.rows;
1131 int kcols = kernel.cols;
1133 int buflen = krows * kcols; // kernel size
1135 cv::gapi::own::Size bufsize(buflen, 1);
1136 GMatDesc bufdesc = {CV_32F, 1, bufsize};
1137 Buffer buffer(bufdesc);
1138 scratch = std::move(buffer);
1140 // FIXME: move to resetScratch stage ?
1141 float *data = scratch.OutLine<float>();
1142 getKernel(data, kernel);
1145 static void resetScratch(Buffer& /* scratch */)
1149 static Border getBorder(const cv::GMatDesc& /* src */,
1151 const cv::Mat& /* kernel */,
1152 const cv::Point& /* anchor */,
1153 const cv::Scalar& /* delta */,
1155 const cv::Scalar& borderValue)
1157 return { borderType, borderValue};
1161 //-----------------------------
1163 // Fluid kernels: erode, dilate
1165 //-----------------------------
1167 static MorphShape detect_morph3x3_shape(const uchar kernel[])
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]}
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])
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])
1188 template<typename DST, typename SRC>
1189 static void run_morphology( Buffer& dst,
1195 const cv::Point & /* anchor */,
1196 Morphology morphology)
1198 static_assert(std::is_same<DST, SRC>::value, "unsupported combination of types");
1200 GAPI_Assert(M_ERODE == morphology || M_DILATE == morphology);
1202 static const int maxLines = 9;
1203 GAPI_Assert(k_rows <= maxLines);
1205 const SRC *in[ maxLines ];
1208 int border_x = (k_cols - 1) / 2;
1209 int border_y = (k_rows - 1) / 2;
1211 for (int i=0; i < k_rows; i++)
1213 in[i] = src.InLine<SRC>(i - border_y);
1216 out = dst.OutLine<DST>();
1218 int width = dst.length();
1219 int chan = dst.meta().chan;
1221 // call optimized code, if 3x3
1222 if (3 == k_rows && 3 == k_cols)
1224 run_morphology3x3_impl(out, in, width, chan, k, k_type, morphology);
1228 // reference: any size of k[]
1229 int length = width * chan;
1230 for (int l=0; l < length; l++)
1233 if (M_ERODE == morphology)
1235 result = std::numeric_limits<SRC>::max();
1237 else // if (M_DILATE == morphology)
1239 result = std::numeric_limits<SRC>::min();
1242 for (int i=0; i < k_rows; i++)
1243 for (int j=0; j < k_cols; j++)
1245 if ( k[k_cols*i + j] )
1247 if (M_ERODE == morphology)
1249 result = (std::min)(result, in[i][l + (j - border_x)*chan]);
1251 else // if (M_DILATE == morphology)
1253 result = (std::max)(result, in[i][l + (j - border_x)*chan]);
1258 out[l] = saturate<DST>(result, rintf);
1262 GAPI_FLUID_KERNEL(GFluidErode, cv::gapi::imgproc::GErode, true)
1264 static const int Window = 3;
1266 static void run(const View & src,
1267 const cv::Mat & kernel,
1268 const cv::Point & anchor,
1270 int /* borderType */,
1271 const cv::Scalar& /* borderValue */,
1275 // TODO: support non-trivial anchors
1276 GAPI_Assert(anchor.x == -1 && anchor.y == -1);
1278 // TODO: support kernel heights 3, 5, 7, 9, ...
1279 GAPI_Assert(kernel.rows == 3 && kernel.cols == 3);
1281 // TODO: support iterations > 1
1282 GAPI_Assert(iterations == 1);
1284 int k_rows = kernel.rows;
1285 int k_cols = kernel.cols;
1286 int k_size = k_rows * k_cols;
1288 auto *k = scratch.OutLine<uchar>(); // copy of kernel.data
1289 auto k_type = static_cast<MorphShape>(k[k_size]);
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);
1297 CV_Error(cv::Error::StsBadArg, "unsupported combination of types");
1300 static void initScratch(const GMatDesc& /* in */,
1302 const Point & /* anchor */,
1303 int /* iterations */,
1304 int /* borderType */,
1305 const cv::Scalar & /* borderValue */,
1308 int k_rows = kernel.rows;
1309 int k_cols = kernel.cols;
1310 int k_size = k_rows * k_cols;
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);
1317 // FIXME: move to resetScratch stage ?
1318 auto *k = scratch.OutLine<uchar>();
1319 getKernel(k, kernel);
1321 if (3 == k_rows && 3 == k_cols)
1322 k[k_size] = static_cast<uchar>(detect_morph3x3_shape(k));
1324 k[k_size] = static_cast<uchar>(M_UNDEF);
1327 static void resetScratch(Buffer& /* scratch */)
1331 static Border getBorder(const cv::GMatDesc& /* src */,
1332 const cv::Mat & /* kernel */,
1333 const cv::Point & /* anchor */,
1334 int /* iterations */,
1336 const cv::Scalar& borderValue)
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) };
1343 return { borderType, borderValue };
1348 GAPI_FLUID_KERNEL(GFluidDilate, cv::gapi::imgproc::GDilate, true)
1350 static const int Window = 3;
1352 static void run(const View & src,
1353 const cv::Mat & kernel,
1354 const cv::Point & anchor,
1356 int /* borderType */,
1357 const cv::Scalar& /* borderValue */,
1361 // TODO: support non-trivial anchors
1362 GAPI_Assert(anchor.x == -1 && anchor.y == -1);
1364 // TODO: support kernel heights 3, 5, 7, 9, ...
1365 GAPI_Assert(kernel.rows == 3 && kernel.cols == 3);
1367 // TODO: support iterations > 1
1368 GAPI_Assert(iterations == 1);
1370 int k_rows = kernel.rows;
1371 int k_cols = kernel.cols;
1372 int k_size = k_rows * k_cols;
1374 auto *k = scratch.OutLine<uchar>(); // copy of kernel.data
1375 auto k_type = static_cast<MorphShape>(k[k_size]);
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);
1383 CV_Error(cv::Error::StsBadArg, "unsupported combination of types");
1386 static void initScratch(const GMatDesc& /* in */,
1388 const Point & /* anchor */,
1389 int /* iterations */,
1390 int /* borderType */,
1391 const cv::Scalar & /* borderValue */,
1394 int k_rows = kernel.rows;
1395 int k_cols = kernel.cols;
1396 int k_size = k_rows * k_cols;
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);
1403 // FIXME: move to resetScratch stage ?
1404 auto *k = scratch.OutLine<uchar>();
1405 getKernel(k, kernel);
1407 if (3 == k_rows && 3 == k_cols)
1408 k[k_size] = static_cast<uchar>(detect_morph3x3_shape(k));
1410 k[k_size] = static_cast<uchar>(M_UNDEF);
1413 static void resetScratch(Buffer& /* scratch */)
1417 static Border getBorder(const cv::GMatDesc& /* src */,
1418 const cv::Mat & /* kernel */,
1419 const cv::Point & /* anchor */,
1420 int /* iterations */,
1422 const cv::Scalar& borderValue)
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) };
1429 return { borderType, borderValue };
1434 //--------------------------
1436 // Fluid kernels: medianBlur
1438 //--------------------------
1440 template<typename DST, typename SRC>
1441 static void run_medianblur( Buffer& dst,
1445 static_assert(std::is_same<DST, SRC>::value, "unsupported combination of types");
1447 constexpr int kmax = 9;
1448 GAPI_Assert(ksize <= kmax);
1450 const SRC *in[ kmax ];
1453 int border = (ksize - 1) / 2;
1455 for (int i=0; i < ksize; i++)
1457 in[i] = src.InLine<SRC>(i - border);
1460 out = dst.OutLine<DST>(0);
1462 int width = dst.length();
1463 int chan = dst.meta().chan;
1465 // optimized: if 3x3
1469 run_medblur3x3_impl(out, in, width, chan);
1473 // reference: any ksize
1475 int length = width * chan;
1476 int klength = ksize * ksize;
1477 int klenhalf = klength / 2;
1479 for (int l=0; l < length; l++)
1481 SRC neighbours[kmax * kmax];
1483 for (int i=0; i < ksize; i++)
1484 for (int j=0; j < ksize; j++)
1486 neighbours[i*ksize + j] = in[i][l + (j - border)*chan];
1489 std::nth_element(neighbours, neighbours + klenhalf, neighbours + klength);
1491 out[l] = saturate<DST>(neighbours[klenhalf], rintf);
1495 GAPI_FLUID_KERNEL(GFluidMedianBlur, cv::gapi::imgproc::GMedianBlur, false)
1497 static const int Window = 3;
1499 static void run(const View & src,
1503 // TODO: support kernel sizes: 3, 5, 7, 9, ...
1504 GAPI_Assert(ksize == 3);
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);
1512 CV_Error(cv::Error::StsBadArg, "unsupported combination of types");
1515 static Border getBorder(const cv::GMatDesc& /* src */,
1518 int borderType = cv::BORDER_REPLICATE;
1519 auto borderValue = cv::Scalar();
1520 return { borderType, borderValue };
1524 } // namespace fliud
1528 cv::gapi::GKernelPackage cv::gapi::imgproc::fluid::kernels()
1530 using namespace cv::gapi::fluid;
1532 return cv::gapi::kernels
1535 , GFluidRGB2GrayCustom
1550 , GFluidCanny -- not fluid (?)
1551 , GFluidEqualizeHist -- not fluid
1556 #endif // !defined(GAPI_STANDALONE)