Path: blob/master/modules/gapi/src/backends/fluid/gfluidimgproc.cpp
16344 views
// This file is part of OpenCV project.1// It is subject to the license terms in the LICENSE file found in the top-level directory2// of this distribution and at http://opencv.org/license.html.3//4// Copyright (C) 2018 Intel Corporation56#if !defined(GAPI_STANDALONE)78#include "precomp.hpp"910#include "opencv2/gapi/own/assert.hpp"11#include "opencv2/core/traits.hpp"12#include "opencv2/imgproc/types_c.h"1314#include "opencv2/gapi/core.hpp"15#include "opencv2/gapi/imgproc.hpp"1617#include "opencv2/gapi/own/types.hpp"1819#include "opencv2/gapi/fluid/gfluidbuffer.hpp"20#include "opencv2/gapi/fluid/gfluidkernel.hpp"2122#include "gfluidbuffer_priv.hpp"23#include "gfluidbackend.hpp"24#include "gfluidimgproc.hpp"25#include "gfluidutils.hpp"2627#include <cmath>28#include <cstdlib>2930namespace cv {31namespace gapi {32namespace fluid {3334//----------------------------------35//36// Fluid kernels: RGB2Gray, BGR2Gray37//38//----------------------------------3940// Y' = 0.299*R' + 0.587*G' + 0.114*B'41// U' = (B' - Y')*0.49242// V' = (R' - Y')*0.87743static const float coef_rgb2yuv_bt601[5] = {0.299f, 0.587f, 0.114f, 0.492f, 0.877f};4445// R' = Y' + 1.140*V'46// G' = Y' - 0.394*U' - 0.581*V'47// B' = Y' + 2.032*U'48static const float coef_yuv2rgb_bt601[4] = {1.140f, -0.394f, -0.581f, 2.032f};4950static void run_rgb2gray(Buffer &dst, const View &src, float coef_r, float coef_g, float coef_b)51{52GAPI_Assert(src.meta().depth == CV_8U);53GAPI_Assert(dst.meta().depth == CV_8U);54GAPI_Assert(src.meta().chan == 3);55GAPI_Assert(dst.meta().chan == 1);56GAPI_Assert(src.length() == dst.length());5758const auto *in = src.InLine<uchar>(0);59auto *out = dst.OutLine<uchar>();6061int width = dst.length();6263// TODO: Vectorize for SIMD64for (int w=0; w < width; w++)65{66uchar r = in[3*w ];67uchar g = in[3*w + 1];68uchar b = in[3*w + 2];69float result = coef_r*r + coef_g*g + coef_b*b;70out[w] = saturate<uchar>(result, roundf);71}72}7374GAPI_FLUID_KERNEL(GFluidRGB2GrayCustom, cv::gapi::imgproc::GRGB2GrayCustom, false)75{76static const int Window = 1;7778static void run(const View &src, float coef_r, float coef_g, float coef_b, Buffer &dst)79{80run_rgb2gray(dst, src, coef_r, coef_g, coef_b);81}82};8384GAPI_FLUID_KERNEL(GFluidRGB2Gray, cv::gapi::imgproc::GRGB2Gray, false)85{86static const int Window = 1;8788static void run(const View &src, Buffer &dst)89{90float coef_r = coef_rgb2yuv_bt601[0];91float coef_g = coef_rgb2yuv_bt601[1];92float coef_b = coef_rgb2yuv_bt601[2];93run_rgb2gray(dst, src, coef_r, coef_g, coef_b);94}95};9697GAPI_FLUID_KERNEL(GFluidBGR2Gray, cv::gapi::imgproc::GBGR2Gray, false)98{99static const int Window = 1;100101static void run(const View &src, Buffer &dst)102{103float coef_r = coef_rgb2yuv_bt601[0];104float coef_g = coef_rgb2yuv_bt601[1];105float coef_b = coef_rgb2yuv_bt601[2];106run_rgb2gray(dst, src, coef_b, coef_g, coef_r);107}108};109110//--------------------------------------111//112// Fluid kernels: RGB-to-YUV, YUV-to-RGB113//114//--------------------------------------115116static void run_rgb2yuv(Buffer &dst, const View &src, const float coef[5])117{118GAPI_Assert(src.meta().depth == CV_8U);119GAPI_Assert(dst.meta().depth == CV_8U);120GAPI_Assert(src.meta().chan == 3);121GAPI_Assert(dst.meta().chan == 3);122GAPI_Assert(src.length() == dst.length());123124const auto *in = src.InLine<uchar>(0);125auto *out = dst.OutLine<uchar>();126127int width = dst.length();128129// TODO: Vectorize for SIMD130for (int w=0; w < width; w++)131{132uchar r = in[3*w ];133uchar g = in[3*w + 1];134uchar b = in[3*w + 2];135float y = coef[0]*r + coef[1]*g + coef[2]*b;136float u = coef[3]*(b - y) + 128;137float v = coef[4]*(r - y) + 128;138out[3*w ] = saturate<uchar>(y, roundf);139out[3*w + 1] = saturate<uchar>(u, roundf);140out[3*w + 2] = saturate<uchar>(v, roundf);141}142}143144static void run_yuv2rgb(Buffer &dst, const View &src, const float coef[4])145{146GAPI_Assert(src.meta().depth == CV_8U);147GAPI_Assert(dst.meta().depth == CV_8U);148GAPI_Assert(src.meta().chan == 3);149GAPI_Assert(dst.meta().chan == 3);150GAPI_Assert(src.length() == dst.length());151152const auto *in = src.InLine<uchar>(0);153auto *out = dst.OutLine<uchar>();154155int width = dst.length();156157// TODO: Vectorize for SIMD158for (int w=0; w < width; w++)159{160uchar y = in[3*w ];161int u = in[3*w + 1] - 128;162int v = in[3*w + 2] - 128;163float r = y + coef[0]*v;164float g = y + coef[1]*u + coef[2]*v;165float b = y + coef[3]*u;166out[3*w ] = saturate<uchar>(r, roundf);167out[3*w + 1] = saturate<uchar>(g, roundf);168out[3*w + 2] = saturate<uchar>(b, roundf);169}170}171172GAPI_FLUID_KERNEL(GFluidRGB2YUV, cv::gapi::imgproc::GRGB2YUV, false)173{174static const int Window = 1;175176static void run(const View &src, Buffer &dst)177{178run_rgb2yuv(dst, src, coef_rgb2yuv_bt601);179}180};181182GAPI_FLUID_KERNEL(GFluidYUV2RGB, cv::gapi::imgproc::GYUV2RGB, false)183{184static const int Window = 1;185186static void run(const View &src, Buffer &dst)187{188run_yuv2rgb(dst, src, coef_yuv2rgb_bt601);189}190};191192//--------------------------------------193//194// Fluid kernels: RGB-to-Lab, BGR-to-LUV195//196//--------------------------------------197198enum LabLUV { LL_Lab, LL_LUV };199200// gamma-correction (inverse) for sRGB, 1/gamma=2.4 for inverse, like for Mac OS (?)201static inline float f_gamma(float x)202{203return x <= 0.04045f ? x*(1.f/12.92f) : std::pow((x + 0.055f)*(1/1.055f), 2.4f);204}205206// saturate into interval [0, 1]207static inline float clip01(float value)208{209return value < 0? 0:210value > 1? 1:211value;212}213214static inline void f_rgb2xyz(float R, float G, float B,215float& X, float& Y, float& Z)216{217X = clip01(0.412453f*R + 0.357580f*G + 0.180423f*B);218Y = clip01(0.212671f*R + 0.715160f*G + 0.072169f*B);219Z = clip01(0.019334f*R + 0.119193f*G + 0.950227f*B);220}221222static inline void f_xyz2lab(float X, float Y, float Z,223float& L, float& a, float& b)224{225// CIE XYZ values of reference white point for D65 illuminant226static const float Xn = 0.950456f, Yn = 1.f, Zn = 1.088754f;227228// Other coefficients below:229// 7.787f = (29/3)^3/(29*4)230// 0.008856f = (6/29)^3231// 903.3 = (29/3)^3232233float x = X/Xn, y = Y/Yn, z = Z/Zn;234235auto f = [](float t){ return t>0.008856f? std::cbrt(t): (7.787f*t + 16.f/116.f); };236237float fx = f(x), fy = f(y), fz = f(z);238239L = y > 0.008856f ? (116.f*std::cbrt(y) - 16.f) : (903.3f * y);240a = 500.f * (fx - fy);241b = 200.f * (fy - fz);242}243244static inline void f_xyz2luv(float X, float Y, float Z,245float& L, float& u, float& v)246{247static const float un = 0.19793943f, vn = 0.46831096f;248249float u1 = 4*X / (X + 15*Y + 3*Z);250float v1 = 9*Y / (X + 15*Y + 3*Z);251252L = Y > 0.008856f ? (116.f*std::cbrt(Y) - 16.f) : (903.3f * Y);253u = 13*L * (u1 - un);254v = 13*L * (v1 - vn);255}256257// compile-time parameters: output format (Lab/LUV),258// and position of blue channel in BGR/RGB (0 or 2)259template<LabLUV labluv, int blue=0>260static void run_rgb2labluv(Buffer &dst, const View &src)261{262GAPI_Assert(src.meta().depth == CV_8U);263GAPI_Assert(dst.meta().depth == CV_8U);264GAPI_Assert(src.meta().chan == 3);265GAPI_Assert(dst.meta().chan == 3);266GAPI_Assert(src.length() == dst.length());267268const auto *in = src.InLine<uchar>(0);269auto *out = dst.OutLine<uchar>();270271int width = dst.length();272273for (int w=0; w < width; w++)274{275float R, G, B;276B = in[3*w + blue ] / 255.f;277G = in[3*w + 1 ] / 255.f;278R = in[3*w + (2^blue)] / 255.f;279280B = f_gamma( B );281G = f_gamma( G );282R = f_gamma( R );283284float X, Y, Z;285f_rgb2xyz(R, G, B, X, Y, Z);286287// compile-time `if`288if (LL_Lab == labluv)289{290float L, a, b;291f_xyz2lab(X, Y, Z, L, a, b);292293out[3*w ] = saturate<uchar>(L * 255.f/100, roundf);294out[3*w + 1] = saturate<uchar>(a + 128, roundf);295out[3*w + 2] = saturate<uchar>(b + 128, roundf);296}297else if (LL_LUV == labluv)298{299float L, u, v;300f_xyz2luv(X, Y, Z, L, u, v);301302out[3*w ] = saturate<uchar>( L * 255.f/100, roundf);303out[3*w + 1] = saturate<uchar>((u + 134) * 255.f/354, roundf);304out[3*w + 2] = saturate<uchar>((v + 140) * 255.f/262, roundf);305}306else307CV_Error(cv::Error::StsBadArg, "unsupported color conversion");;308}309}310311GAPI_FLUID_KERNEL(GFluidRGB2Lab, cv::gapi::imgproc::GRGB2Lab, false)312{313static const int Window = 1;314315static void run(const View &src, Buffer &dst)316{317static const int blue = 2; // RGB: 0=red, 1=green, 2=blue318run_rgb2labluv<LL_Lab, blue>(dst, src);319}320};321322GAPI_FLUID_KERNEL(GFluidBGR2LUV, cv::gapi::imgproc::GBGR2LUV, false)323{324static const int Window = 1;325326static void run(const View &src, Buffer &dst)327{328static const int blue = 0; // BGR: 0=blue, 1=green, 2=red329run_rgb2labluv<LL_LUV, blue>(dst, src);330}331};332333//-------------------------------334//335// Fluid kernels: blur, boxFilter336//337//-------------------------------338339static const int maxKernelSize = 9;340341template<typename DST, typename SRC>342static void run_boxfilter(Buffer &dst, const View &src, const cv::Size &kernelSize,343const cv::Point& /* anchor */, bool normalize)344{345GAPI_Assert(kernelSize.width <= maxKernelSize);346GAPI_Assert(kernelSize.width == kernelSize.height);347348int kernel = kernelSize.width;349int border = (kernel - 1) / 2;350351const SRC *in[ maxKernelSize ];352DST *out;353354for (int i=0; i < kernel; i++)355{356in[i] = src.InLine<SRC>(i - border);357}358359out = dst.OutLine<DST>();360361int width = dst.length();362int chan = dst.meta().chan;363364GAPI_DbgAssert(chan <= 4);365366for (int w=0; w < width; w++)367{368float sum[4] = {0, 0, 0, 0};369370for (int i=0; i < kernel; i++)371{372for (int j=0; j < kernel; j++)373{374for (int c=0; c < chan; c++)375sum[c] += in[i][(w + j - border)*chan + c];376}377}378379for (int c=0; c < chan; c++)380{381float result = normalize? sum[c]/(kernel * kernel) : sum[c];382383out[w*chan + c] = saturate<DST>(result, rintf);384}385}386}387388GAPI_FLUID_KERNEL(GFluidBlur, cv::gapi::imgproc::GBlur, false)389{390static const int Window = 3;391392static void run(const View &src, const cv::Size& kernelSize, const cv::Point& anchor,393int /* borderType */, const cv::Scalar& /* borderValue */, Buffer &dst)394{395// TODO: support sizes 3, 5, 7, 9, ...396GAPI_Assert(kernelSize.width == 3 && kernelSize.height == 3);397398// TODO: suport non-trivial anchor399GAPI_Assert(anchor.x == -1 && anchor.y == -1);400401static const bool normalize = true;402403// DST SRC OP __VA_ARGS__404UNARY_(uchar , uchar , run_boxfilter, dst, src, kernelSize, anchor, normalize);405UNARY_(ushort, ushort, run_boxfilter, dst, src, kernelSize, anchor, normalize);406UNARY_( short, short, run_boxfilter, dst, src, kernelSize, anchor, normalize);407408CV_Error(cv::Error::StsBadArg, "unsupported combination of types");409}410411static Border getBorder(const cv::GMatDesc& /* src */,412const cv::Size & /* kernelSize */,413const cv::Point & /* anchor */,414int borderType,415const cv::Scalar & borderValue)416{417return { borderType, borderValue};418}419};420421GAPI_FLUID_KERNEL(GFluidBoxFilter, cv::gapi::imgproc::GBoxFilter, false)422{423static const int Window = 3;424425static void run(const View & src,426int /* ddepth */,427const cv::Size & kernelSize,428const cv::Point & anchor,429bool normalize,430int /* borderType */,431const cv::Scalar& /* borderValue */,432Buffer& dst)433{434// TODO: support sizes 3, 5, 7, 9, ...435GAPI_Assert(kernelSize.width == 3 && kernelSize.height == 3);436437// TODO: suport non-trivial anchor438GAPI_Assert(anchor.x == -1 && anchor.y == -1);439440// DST SRC OP __VA_ARGS__441UNARY_(uchar , uchar , run_boxfilter, dst, src, kernelSize, anchor, normalize);442UNARY_(ushort, ushort, run_boxfilter, dst, src, kernelSize, anchor, normalize);443UNARY_( short, short, run_boxfilter, dst, src, kernelSize, anchor, normalize);444UNARY_( float, uchar , run_boxfilter, dst, src, kernelSize, anchor, normalize);445UNARY_( float, ushort, run_boxfilter, dst, src, kernelSize, anchor, normalize);446UNARY_( float, short, run_boxfilter, dst, src, kernelSize, anchor, normalize);447448CV_Error(cv::Error::StsBadArg, "unsupported combination of types");449}450451static Border getBorder(const cv::GMatDesc& /* src */,452int /* ddepth */,453const cv::Size & /* kernelSize */,454const cv::Point & /* anchor */,455bool /* normalize */,456int borderType,457const cv::Scalar & borderValue)458{459return { borderType, borderValue};460}461};462463//-------------------------464//465// Fluid kernels: sepFilter466//467//-------------------------468469template<typename T>470static void getKernel(T k[], const cv::Mat& kernel)471{472GAPI_Assert(kernel.channels() == 1);473474int depth = CV_MAT_DEPTH(kernel.type());475int cols = kernel.cols;476int rows = kernel.rows;477478switch ( depth )479{480case CV_8U:481for (int h=0; h < rows; h++)482for (int w=0; w < cols; w++)483k[h*cols + w] = static_cast<T>( kernel.at<uchar>(h, w) );484break;485case CV_16U:486for (int h=0; h < rows; h++)487for (int w=0; w < cols; w++)488k[h*cols + w] = static_cast<T>( kernel.at<ushort>(h, w) );489break;490case CV_16S:491for (int h=0; h < rows; h++)492for (int w=0; w < cols; w++)493k[h*cols + w] = static_cast<T>( kernel.at<short>(h, w) );494break;495case CV_32F:496for (int h=0; h < rows; h++)497for (int w=0; w < cols; w++)498k[h*cols + w] = static_cast<T>( kernel.at<float>(h, w) );499break;500default: CV_Error(cv::Error::StsBadArg, "unsupported kernel type");501}502}503504template<typename DST, typename SRC>505static void run_sepfilter(Buffer& dst, const View& src,506const float kx[], int kxLen,507const float ky[], int kyLen,508const cv::Point& /* anchor */,509float delta=0)510{511static const int maxLines = 9;512GAPI_Assert(kyLen <= maxLines);513514const SRC *in[ maxLines ];515DST *out;516517int border = (kyLen - 1) / 2;518for (int i=0; i < kyLen; i++)519{520in[i] = src.InLine<SRC>(i - border);521}522523out = dst.OutLine<DST>();524525int width = dst.length();526int chan = dst.meta().chan;527528for (int w=0; w < width; w++)529{530// TODO: make this cycle innermost531for (int c=0; c < chan; c++)532{533float sum=0;534535for (int i=0; i < kyLen; i++)536{537float sumi=0;538539for (int j=0; j < kxLen; j++)540{541sumi += in[i][(w + j - border)*chan + c] * kx[j];542}543544sum += sumi * ky[i];545}546547float result = sum + delta;548549out[w*chan + c] = saturate<DST>(result, rintf);550}551}552}553554GAPI_FLUID_KERNEL(GFluidSepFilter, cv::gapi::imgproc::GSepFilter, true)555{556static const int Window = 3;557558static void run(const View& src,559int /* ddepth */,560const cv::Mat& kernX,561const cv::Mat& kernY,562const cv::Point& anchor,563const cv::Scalar& delta_,564int /* borderType */,565const cv::Scalar& /* borderValue */,566Buffer& dst,567Buffer& scratch)568{569// TODO: support non-trivial anchors570GAPI_Assert(anchor.x == -1 && anchor.y == -1);571572// TODO: support kernel heights 3, 5, 7, 9, ...573GAPI_Assert((kernY.rows == 1 || kernY.cols == 1) && (kernY.cols * kernY.rows == 3));574GAPI_Assert((kernX.rows == 1 || kernX.cols == 1));575576int kxLen = kernX.rows * kernX.cols;577int kyLen = kernY.rows * kernY.cols;578579float *kx = scratch.OutLine<float>();580float *ky = kx + kxLen;581582float delta = static_cast<float>(delta_[0]);583584// DST SRC OP __VA_ARGS__585UNARY_(uchar , uchar , run_sepfilter, dst, src, kx, kxLen, ky, kyLen, anchor, delta);586UNARY_(ushort, ushort, run_sepfilter, dst, src, kx, kxLen, ky, kyLen, anchor, delta);587UNARY_( short, short, run_sepfilter, dst, src, kx, kxLen, ky, kyLen, anchor, delta);588UNARY_( float, float, run_sepfilter, dst, src, kx, kxLen, ky, kyLen, anchor, delta);589590CV_Error(cv::Error::StsBadArg, "unsupported combination of types");591}592593static void initScratch(const GMatDesc& /* in */,594int /* ddepth */,595const Mat & kernX,596const Mat & kernY,597const Point & /* anchor */,598const Scalar & /* delta */,599int /* borderType */,600const Scalar & /* borderValue */,601Buffer & scratch)602{603int kxLen = kernX.rows * kernX.cols;604int kyLen = kernY.rows * kernY.cols;605606cv::gapi::own::Size bufsize(kxLen + kyLen, 1);607GMatDesc bufdesc = {CV_32F, 1, bufsize};608Buffer buffer(bufdesc);609scratch = std::move(buffer);610611// FIXME: move to resetScratch stage ?612float *kx = scratch.OutLine<float>();613float *ky = kx + kxLen;614getKernel(kx, kernX);615getKernel(ky, kernY);616}617618static void resetScratch(Buffer& /* scratch */)619{620}621622static Border getBorder(const cv::GMatDesc& /* src */,623int /* ddepth */,624const cv::Mat& /* kernX */,625const cv::Mat& /* kernY */,626const cv::Point& /* anchor */,627const cv::Scalar& /* delta */,628int borderType,629const cv::Scalar& borderValue)630{631return { borderType, borderValue};632}633};634635//----------------------------636//637// Fluid kernels: gaussianBlur638//639//----------------------------640641GAPI_FLUID_KERNEL(GFluidGaussBlur, cv::gapi::imgproc::GGaussBlur, true)642{643// TODO: support kernel height 3, 5, 7, 9, ...644static const int Window = 3;645646static void run(const View & src,647const cv::Size & ksize,648double /* sigmaX */,649double /* sigmaY */,650int /* borderType */,651const cv::Scalar& /* borderValue */,652Buffer& dst,653Buffer& scratch)654{655GAPI_Assert(ksize.height == 3);656657int kxsize = ksize.width;658int kysize = ksize.height;659660auto *kx = scratch.OutLine<float>(); // cached kernX data661auto *ky = kx + kxsize; // cached kernY data662663auto anchor = cv::Point(-1, -1);664float delta = 0.f;665666// DST SRC OP __VA_ARGS__667UNARY_(uchar , uchar , run_sepfilter, dst, src, kx, kxsize, ky, kysize, anchor, delta);668UNARY_(ushort, ushort, run_sepfilter, dst, src, kx, kxsize, ky, kysize, anchor, delta);669UNARY_( short, short, run_sepfilter, dst, src, kx, kxsize, ky, kysize, anchor, delta);670671CV_Error(cv::Error::StsBadArg, "unsupported combination of types");672}673674static void initScratch(const GMatDesc& /* in */,675const cv::Size & ksize,676double sigmaX,677double sigmaY,678int /* borderType */,679const cv::Scalar & /* borderValue */,680Buffer & scratch)681{682int kxsize = ksize.width;683int kysize = ksize.height;684685cv::gapi::own::Size bufsize(kxsize + kysize, 1);686GMatDesc bufdesc = {CV_32F, 1, bufsize};687Buffer buffer(bufdesc);688scratch = std::move(buffer);689690// FIXME: fill buffer at resetScratch stage?691692if (sigmaX == 0)693sigmaX = 0.3 * ((kxsize - 1)/2. - 1) + 0.8;694695if (sigmaY == 0)696sigmaY = sigmaX;697698Mat kernX = getGaussianKernel(kxsize, sigmaX, CV_32F);699700Mat kernY = kernX;701if (sigmaY != sigmaX)702kernY = getGaussianKernel(kysize, sigmaY, CV_32F);703704auto *kx = scratch.OutLine<float>();705auto *ky = kx + kxsize;706707getKernel(kx, kernX);708getKernel(ky, kernY);709}710711static void resetScratch(Buffer& /* scratch */)712{713}714715static Border getBorder(const cv::GMatDesc& /* src */,716const cv::Size & /* ksize */,717double /* sigmaX */,718double /* sigmaY */,719int borderType,720const cv::Scalar & borderValue)721{722return { borderType, borderValue};723}724};725726//---------------------727//728// Fluid kernels: Sobel729//730//---------------------731732template<typename DST, typename SRC>733static void run_sobel(Buffer& dst,734const View & src,735float kx[],736float ky[],737int ksize,738float scale=1,739float delta=0)740{741static const int kmax = 11;742GAPI_Assert(ksize <= kmax);743744const SRC *in[ kmax ];745DST *out;746747int border = (ksize - 1) / 2;748for (int i=0; i < ksize; i++)749{750in[i] = src.InLine<SRC>(i - border);751}752753out = dst.OutLine<DST>();754755int width = dst.length();756int chan = dst.meta().chan;757758for (int w=0; w < width; w++)759{760// TODO: make this cycle innermost761for (int c=0; c < chan; c++)762{763float sum=0;764765for (int i=0; i < ksize; i++)766{767float sumi=0;768769for (int j=0; j < ksize; j++)770{771sumi += in[i][(w + j - border)*chan + c] * kx[j];772}773774sum += sumi * ky[i];775}776777float result = sum*scale + delta;778779out[w*chan + c] = saturate<DST>(result, rintf);780}781}782}783784GAPI_FLUID_KERNEL(GFluidSobel, cv::gapi::imgproc::GSobel, true)785{786static const int Window = 3;787788static void run(const View & src,789int /* ddepth */,790int /* dx */,791int /* dy */,792int ksize,793double _scale,794double _delta,795int /* borderType */,796const cv::Scalar& /* borderValue */,797Buffer& dst,798Buffer& scratch)799{800// TODO: support kernel height 3, 5, 7, 9, ...801GAPI_Assert(ksize == 3 || ksize == CV_SCHARR);802803if (ksize == CV_SCHARR)804ksize = 3;805806auto *kx = scratch.OutLine<float>();807auto *ky = kx + ksize;808809auto scale = static_cast<float>(_scale);810auto delta = static_cast<float>(_delta);811812// DST SRC OP __VA_ARGS__813UNARY_(uchar , uchar , run_sobel, dst, src, kx, ky, ksize, scale, delta);814UNARY_(ushort, ushort, run_sobel, dst, src, kx, ky, ksize, scale, delta);815UNARY_( short, short, run_sobel, dst, src, kx, ky, ksize, scale, delta);816UNARY_( float, uchar , run_sobel, dst, src, kx, ky, ksize, scale, delta);817UNARY_( float, ushort, run_sobel, dst, src, kx, ky, ksize, scale, delta);818UNARY_( float, short, run_sobel, dst, src, kx, ky, ksize, scale, delta);819820CV_Error(cv::Error::StsBadArg, "unsupported combination of types");821}822823static void initScratch(const GMatDesc& /* in */,824int /* ddepth */,825int dx,826int dy,827int ksize,828double /* scale */,829double /* delta */,830int /* borderType */,831const Scalar & /* borderValue */,832Buffer & scratch)833{834cv::gapi::own::Size bufsize(ksize + ksize, 1);835GMatDesc bufdesc = {CV_32F, 1, bufsize};836Buffer buffer(bufdesc);837scratch = std::move(buffer);838839// FIXME: move to resetScratch stage ?840auto *kx = scratch.OutLine<float>();841auto *ky = kx + ksize;842Mat kxmat(1, ksize, CV_32FC1, kx);843Mat kymat(ksize, 1, CV_32FC1, ky);844getDerivKernels(kxmat, kymat, dx, dy, ksize);845}846847static void resetScratch(Buffer& /* scratch */)848{849}850851static Border getBorder(const cv::GMatDesc& /* src */,852int /* ddepth */,853int /* dx */,854int /* dy */,855int /* ksize */,856double /* scale */,857double /* delta */,858int borderType,859const cv::Scalar & borderValue)860{861return { borderType, borderValue};862}863};864865//------------------------866//867// Fluid kernels: filter2D868//869//------------------------870871template<typename DST, typename SRC>872static void run_filter2d(Buffer& dst, const View& src,873const float k[], int k_rows, int k_cols,874const cv::Point& /* anchor */,875float delta=0)876{877static const int maxLines = 9;878GAPI_Assert(k_rows <= maxLines);879880const SRC *in[ maxLines ];881DST *out;882883int border_x = (k_cols - 1) / 2;884int border_y = (k_rows - 1) / 2;885886for (int i=0; i < k_rows; i++)887{888in[i] = src.InLine<SRC>(i - border_y);889}890891out = dst.OutLine<DST>();892893int width = dst.length();894int chan = dst.meta().chan;895896for (int w=0; w < width; w++)897{898// TODO: make this cycle innermost899for (int c=0; c < chan; c++)900{901float sum = 0;902903for (int i=0; i < k_rows; i++)904for (int j=0; j < k_cols; j++)905{906sum += in[i][(w + j - border_x)*chan + c] * k[k_cols*i + j];907}908909float result = sum + delta;910911out[w*chan + c] = saturate<DST>(result, rintf);912}913}914}915916GAPI_FLUID_KERNEL(GFluidFilter2D, cv::gapi::imgproc::GFilter2D, true)917{918static const int Window = 3;919920static void run(const View & src,921int /* ddepth */,922const cv::Mat & kernel,923const cv::Point & anchor,924const cv::Scalar& delta_,925int /* borderType */,926const cv::Scalar& /* borderValue */,927Buffer& dst,928Buffer& scratch)929{930// TODO: support non-trivial anchors931GAPI_Assert(anchor.x == -1 && anchor.y == -1);932933// TODO: support kernel heights 3, 5, 7, 9, ...934GAPI_Assert(kernel.rows == 3 && kernel.cols == 3);935936float delta = static_cast<float>(delta_[0]);937938int k_rows = kernel.rows;939int k_cols = kernel.cols;940const float *k = scratch.OutLine<float>(); // copy of kernel.data941942// DST SRC OP __VA_ARGS__943UNARY_(uchar , uchar , run_filter2d, dst, src, k, k_rows, k_cols, anchor, delta);944UNARY_(ushort, ushort, run_filter2d, dst, src, k, k_rows, k_cols, anchor, delta);945UNARY_( short, short, run_filter2d, dst, src, k, k_rows, k_cols, anchor, delta);946UNARY_( float, uchar , run_filter2d, dst, src, k, k_rows, k_cols, anchor, delta);947UNARY_( float, ushort, run_filter2d, dst, src, k, k_rows, k_cols, anchor, delta);948UNARY_( float, short, run_filter2d, dst, src, k, k_rows, k_cols, anchor, delta);949UNARY_( float, float, run_filter2d, dst, src, k, k_rows, k_cols, anchor, delta);950951CV_Error(cv::Error::StsBadArg, "unsupported combination of types");952}953954static void initScratch(const cv::GMatDesc& /* in */,955int /* ddepth */,956const cv::Mat & kernel,957const cv::Point & /* anchor */,958const cv::Scalar & /* delta */,959int /* borderType */,960const cv::Scalar & /* borderValue */,961Buffer & scratch)962{963cv::gapi::own::Size bufsize(kernel.rows * kernel.cols, 1);964GMatDesc bufdesc = {CV_32F, 1, bufsize};965Buffer buffer(bufdesc);966scratch = std::move(buffer);967968// FIXME: move to resetScratch stage ?969float *data = scratch.OutLine<float>();970getKernel(data, kernel);971}972973static void resetScratch(Buffer& /* scratch */)974{975}976977static Border getBorder(const cv::GMatDesc& /* src */,978int /* ddepth */,979const cv::Mat& /* kernel */,980const cv::Point& /* anchor */,981const cv::Scalar& /* delta */,982int borderType,983const cv::Scalar& borderValue)984{985return { borderType, borderValue};986}987};988989//-----------------------------990//991// Fluid kernels: erode, dilate992//993//-----------------------------994995enum Morphology { M_ERODE, M_DILATE };996997template<typename DST, typename SRC>998static void run_morphology( Buffer& dst,999const View & src,1000const uchar k[],1001int k_rows,1002int k_cols,1003const cv::Point & /* anchor */,1004Morphology morphology)1005{1006static const int maxLines = 9;1007GAPI_Assert(k_rows <= maxLines);10081009const SRC *in[ maxLines ];1010DST *out;10111012int border_x = (k_cols - 1) / 2;1013int border_y = (k_rows - 1) / 2;10141015for (int i=0; i < k_rows; i++)1016{1017in[i] = src.InLine<SRC>(i - border_y);1018}10191020out = dst.OutLine<DST>();10211022int width = dst.length();1023int chan = dst.meta().chan;10241025for (int w=0; w < width; w++)1026{1027// TODO: make this cycle innermost1028for (int c=0; c < chan; c++)1029{1030SRC result=0;1031if (M_ERODE == morphology)1032{1033result = std::numeric_limits<SRC>::max();1034}1035else if (M_DILATE == morphology)1036{1037result = std::numeric_limits<SRC>::min();1038}1039else1040CV_Error(cv::Error::StsBadArg, "unsupported morphology operation");10411042for (int i=0; i < k_rows; i++)1043for (int j=0; j < k_cols; j++)1044{1045if ( k[k_cols*i + j] )1046{1047if (M_ERODE == morphology)1048{1049result = std::min(result, in[i][(w + j - border_x)*chan + c]);1050}1051else if (M_DILATE == morphology)1052{1053result = std::max(result, in[i][(w + j - border_x)*chan + c]);1054}1055else1056CV_Error(cv::Error::StsBadArg, "unsupported morphology operation");1057}1058}10591060out[w*chan + c] = saturate<DST>(result, rintf);1061}1062}1063}10641065GAPI_FLUID_KERNEL(GFluidErode, cv::gapi::imgproc::GErode, true)1066{1067static const int Window = 3;10681069static void run(const View & src,1070const cv::Mat & kernel,1071const cv::Point & anchor,1072int iterations,1073int /* borderType */,1074const cv::Scalar& /* borderValue */,1075Buffer& dst,1076Buffer& scratch)1077{1078// TODO: support non-trivial anchors1079GAPI_Assert(anchor.x == -1 && anchor.y == -1);10801081// TODO: support kernel heights 3, 5, 7, 9, ...1082GAPI_Assert(kernel.rows == 3 && kernel.cols == 3);10831084// TODO: support iterations > 11085GAPI_Assert(iterations == 1);10861087int k_rows = kernel.rows;1088int k_cols = kernel.cols;10891090auto *k = scratch.OutLine<uchar>(); // copy of kernel.data10911092// DST SRC OP __VA_ARGS__1093UNARY_(uchar , uchar , run_morphology, dst, src, k, k_rows, k_cols, anchor, M_ERODE);1094UNARY_(ushort, ushort, run_morphology, dst, src, k, k_rows, k_cols, anchor, M_ERODE);1095UNARY_( short, short, run_morphology, dst, src, k, k_rows, k_cols, anchor, M_ERODE);10961097CV_Error(cv::Error::StsBadArg, "unsupported combination of types");1098}10991100static void initScratch(const GMatDesc& /* in */,1101const Mat & kernel,1102const Point & /* anchor */,1103int /* iterations */,1104int /* borderType */,1105const cv::Scalar & /* borderValue */,1106Buffer & scratch)1107{1108int k_rows = kernel.rows;1109int k_cols = kernel.cols;11101111cv::gapi::own::Size bufsize(k_rows * k_cols, 1);1112GMatDesc bufdesc = {CV_8U, 1, bufsize};1113Buffer buffer(bufdesc);1114scratch = std::move(buffer);11151116// FIXME: move to resetScratch stage ?1117auto *k = scratch.OutLine<uchar>();1118getKernel(k, kernel);1119}11201121static void resetScratch(Buffer& /* scratch */)1122{1123}11241125static Border getBorder(const cv::GMatDesc& /* src */,1126const cv::Mat & /* kernel */,1127const cv::Point & /* anchor */,1128int /* iterations */,1129int borderType,1130const cv::Scalar& borderValue)1131{1132#if 11133// TODO: saturate borderValue to image type in general case (not only maximal border)1134GAPI_Assert(borderType == cv::BORDER_CONSTANT && borderValue[0] == DBL_MAX);1135return { borderType, cv::gapi::own::Scalar::all(INT_MAX) };1136#else1137return { borderType, borderValue };1138#endif1139}1140};11411142GAPI_FLUID_KERNEL(GFluidDilate, cv::gapi::imgproc::GDilate, true)1143{1144static const int Window = 3;11451146static void run(const View & src,1147const cv::Mat & kernel,1148const cv::Point & anchor,1149int iterations,1150int /* borderType */,1151const cv::Scalar& /* borderValue */,1152Buffer& dst,1153Buffer& scratch)1154{1155// TODO: support non-trivial anchors1156GAPI_Assert(anchor.x == -1 && anchor.y == -1);11571158// TODO: support kernel heights 3, 5, 7, 9, ...1159GAPI_Assert(kernel.rows == 3 && kernel.cols == 3);11601161// TODO: support iterations > 11162GAPI_Assert(iterations == 1);11631164int k_rows = kernel.rows;1165int k_cols = kernel.cols;11661167auto *k = scratch.OutLine<uchar>(); // copy of kernel.data11681169// DST SRC OP __VA_ARGS__1170UNARY_(uchar , uchar , run_morphology, dst, src, k, k_rows, k_cols, anchor, M_DILATE);1171UNARY_(ushort, ushort, run_morphology, dst, src, k, k_rows, k_cols, anchor, M_DILATE);1172UNARY_( short, short, run_morphology, dst, src, k, k_rows, k_cols, anchor, M_DILATE);11731174CV_Error(cv::Error::StsBadArg, "unsupported combination of types");1175}11761177static void initScratch(const GMatDesc& /* in */,1178const Mat & kernel,1179const Point & /* anchor */,1180int /* iterations */,1181int /* borderType */,1182const cv::Scalar & /* borderValue */,1183Buffer & scratch)1184{1185int k_rows = kernel.rows;1186int k_cols = kernel.cols;11871188cv::gapi::own::Size bufsize(k_rows * k_cols, 1);1189GMatDesc bufdesc = {CV_8U, 1, bufsize};1190Buffer buffer(bufdesc);1191scratch = std::move(buffer);11921193// FIXME: move to resetScratch stage ?1194auto *k = scratch.OutLine<uchar>();1195getKernel(k, kernel);1196}11971198static void resetScratch(Buffer& /* scratch */)1199{1200}12011202static Border getBorder(const cv::GMatDesc& /* src */,1203const cv::Mat & /* kernel */,1204const cv::Point & /* anchor */,1205int /* iterations */,1206int borderType,1207const cv::Scalar& borderValue)1208{1209#if 11210// TODO: fix borderValue for Dilate in general case (not only minimal border)1211GAPI_Assert(borderType == cv::BORDER_CONSTANT && borderValue[0] == DBL_MAX);1212return { borderType, cv::gapi::own::Scalar::all(INT_MIN) };1213#else1214return { borderType, borderValue };1215#endif1216}1217};12181219//--------------------------1220//1221// Fluid kernels: medianBlur1222//1223//--------------------------12241225template<typename DST, typename SRC>1226static void run_medianblur( Buffer& dst,1227const View & src,1228int ksize)1229{1230static const int kmax = 9;1231GAPI_Assert(ksize <= kmax);12321233const SRC *in[ kmax ];1234DST *out;12351236int border = (ksize - 1) / 2;12371238for (int i=0; i < ksize; i++)1239{1240in[i] = src.InLine<SRC>(i - border);1241}12421243out = dst.OutLine<DST>(0);12441245int width = dst.length();1246int chan = dst.meta().chan;12471248for (int w=0; w < width; w++)1249{1250// TODO: make this cycle innermost1251for (int c=0; c < chan; c++)1252{1253SRC neighbours[kmax * kmax];12541255for (int i=0; i < ksize; i++)1256for (int j=0; j < ksize; j++)1257{1258neighbours[i*ksize + j] = in[i][(w + j - border)*chan + c];1259}12601261int length = ksize * ksize;1262std::nth_element(neighbours, neighbours + length/2, neighbours + length);12631264out[w*chan + c] = saturate<DST>(neighbours[length/2], rintf);1265}1266}1267}12681269GAPI_FLUID_KERNEL(GFluidMedianBlur, cv::gapi::imgproc::GMedianBlur, false)1270{1271static const int Window = 3;12721273static void run(const View & src,1274int ksize,1275Buffer& dst)1276{1277// TODO: support kernel sizes: 3, 5, 7, 9, ...1278GAPI_Assert(ksize == 3);12791280// DST SRC OP __VA_ARGS__1281UNARY_(uchar , uchar , run_medianblur, dst, src, ksize);1282UNARY_(ushort, ushort, run_medianblur, dst, src, ksize);1283UNARY_( short, short, run_medianblur, dst, src, ksize);12841285CV_Error(cv::Error::StsBadArg, "unsupported combination of types");1286}12871288static Border getBorder(const cv::GMatDesc& /* src */,1289int /* ksize */)1290{1291int borderType = cv::BORDER_REPLICATE;1292auto borderValue = cv::Scalar();1293return { borderType, borderValue };1294}1295};12961297} // namespace fliud1298} // namespace gapi1299} // namespace cv13001301cv::gapi::GKernelPackage cv::gapi::imgproc::fluid::kernels()1302{1303using namespace cv::gapi::fluid;13041305return cv::gapi::kernels1306< GFluidBGR2Gray1307, GFluidRGB2Gray1308, GFluidRGB2GrayCustom1309, GFluidRGB2YUV1310, GFluidYUV2RGB1311, GFluidRGB2Lab1312, GFluidBGR2LUV1313, GFluidBlur1314, GFluidSepFilter1315, GFluidBoxFilter1316, GFluidFilter2D1317, GFluidErode1318, GFluidDilate1319, GFluidMedianBlur1320, GFluidGaussBlur1321, GFluidSobel1322#if 01323, GFluidCanny -- not fluid (?)1324, GFluidEqualizeHist -- not fluid1325#endif1326>();1327}13281329#endif // !defined(GAPI_STANDALONE)133013311332