opencv/modules/gapi/test/gapi_fluid_test_kernels.cpp
Alexey Smirnov 8a7e74673e Merge pull request #15312 from smirnov-alexey:gapi_fluid_garray
* Support GArray as input in fluid kernels

* Create tests on GArray input in fluid

* Some fixes to fully support GArray

* Refactor code and change the kernel according to review

* Add histogram calculation as a G-API kernel

Add assert that input GArgs in fluid contain at least one GMat
2019-09-02 15:51:58 +03:00

628 lines
19 KiB
C++

// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Copyright (C) 2018 Intel Corporation
#include "test_precomp.hpp"
#include <iomanip>
#include <vector>
#include "gapi_fluid_test_kernels.hpp"
#include <opencv2/gapi/core.hpp>
#include <opencv2/gapi/own/saturate.hpp>
namespace cv
{
namespace gapi_test_kernels
{
GAPI_FLUID_KERNEL(FAddSimple, TAddSimple, false)
{
static const int Window = 1;
static void run(const cv::gapi::fluid::View &a,
const cv::gapi::fluid::View &b,
cv::gapi::fluid::Buffer &o)
{
// std::cout << "AddSimple {{{\n";
// std::cout << " a - "; a.debug(std::cout);
// std::cout << " b - "; b.debug(std::cout);
// std::cout << " o - "; o.debug(std::cout);
const uint8_t* in1 = a.InLine<uint8_t>(0);
const uint8_t* in2 = b.InLine<uint8_t>(0);
uint8_t* out = o.OutLine<uint8_t>();
// std::cout << "a: ";
// for (int i = 0, w = a.length(); i < w; i++)
// {
// std::cout << std::setw(4) << int(in1[i]);
// }
// std::cout << "\n";
// std::cout << "b: ";
// for (int i = 0, w = a.length(); i < w; i++)
// {
// std::cout << std::setw(4) << int(in2[i]);
// }
// std::cout << "\n";
for (int i = 0, w = a.length(); i < w; i++)
{
out[i] = in1[i] + in2[i];
}
// std::cout << "}}} " << std::endl;;
}
};
GAPI_FLUID_KERNEL(FAddCSimple, TAddCSimple, false)
{
static const int Window = 1;
static const int LPI = 2;
static void run(const cv::gapi::fluid::View &in,
const int cval,
cv::gapi::fluid::Buffer &out)
{
for (int l = 0, lpi = out.lpi(); l < lpi; l++)
{
const uint8_t* in_row = in .InLine <uint8_t>(l);
uint8_t* out_row = out.OutLine<uint8_t>(l);
//std::cout << "l=" << l << ": ";
for (int i = 0, w = in.length(); i < w; i++)
{
//std::cout << std::setw(4) << int(in_row[i]);
//FIXME: it seems that over kernels might need it as well
out_row[i] = cv::gapi::own::saturate<uint8_t>(in_row[i] + cval);
}
//std::cout << std::endl;
}
}
};
GAPI_FLUID_KERNEL(FAddScalar, TAddScalar, false)
{
static const int Window = 1;
static const int LPI = 2;
static void run(const cv::gapi::fluid::View &in,
const cv::Scalar &cval,
cv::gapi::fluid::Buffer &out)
{
for (int l = 0, lpi = out.lpi(); l < lpi; l++)
{
const uint8_t* in_row = in .InLine <uint8_t>(l);
uint8_t* out_row = out.OutLine<uint8_t>(l);
std::cout << "l=" << l << ": ";
for (int i = 0, w = in.length(); i < w; i++)
{
std::cout << std::setw(4) << int(in_row[i]);
out_row[i] = static_cast<uint8_t>(in_row[i] + cval[0]);
}
std::cout << std::endl;
}
}
};
GAPI_FLUID_KERNEL(FAddScalarToMat, TAddScalarToMat, false)
{
static const int Window = 1;
static const int LPI = 2;
static void run(const cv::Scalar &cval,
const cv::gapi::fluid::View &in,
cv::gapi::fluid::Buffer &out)
{
for (int l = 0, lpi = out.lpi(); l < lpi; l++)
{
const uint8_t* in_row = in .InLine <uint8_t>(l);
uint8_t* out_row = out.OutLine<uint8_t>(l);
std::cout << "l=" << l << ": ";
for (int i = 0, w = in.length(); i < w; i++)
{
std::cout << std::setw(4) << int(in_row[i]);
out_row[i] = static_cast<uint8_t>(in_row[i] + cval[0]);
}
std::cout << std::endl;
}
}
};
template<int kernelSize, int lpi = 1>
static void runBlur(const cv::gapi::fluid::View& src, cv::gapi::fluid::Buffer& dst)
{
const auto borderSize = (kernelSize - 1) / 2;
const unsigned char* ins[kernelSize];
for (int l = 0; l < lpi; l++)
{
for (int i = 0; i < kernelSize; i++)
{
ins[i] = src.InLine<unsigned char>(i - borderSize + l);
}
auto out = dst.OutLine<unsigned char>(l);
const auto width = dst.length();
for (int w = 0; w < width; w++)
{
float res = 0.0f;
for (int i = 0; i < kernelSize; i++)
{
for (int j = -borderSize; j < borderSize + 1; j++)
{
res += ins[i][w+j];
}
}
out[w] = static_cast<unsigned char>(std::rint(res / (kernelSize * kernelSize)));
}
}
}
GAPI_FLUID_KERNEL(FBlur1x1, TBlur1x1, false)
{
static const int Window = 1;
static void run(const cv::gapi::fluid::View &src, int /*borderType*/,
cv::Scalar /*borderValue*/, cv::gapi::fluid::Buffer &dst)
{
runBlur<Window>(src, dst);
}
};
GAPI_FLUID_KERNEL(FBlur3x3, TBlur3x3, false)
{
static const int Window = 3;
static void run(const cv::gapi::fluid::View &src, int /*borderType*/,
cv::Scalar /*borderValue*/, cv::gapi::fluid::Buffer &dst)
{
runBlur<Window>(src, dst);
}
static cv::gapi::fluid::Border getBorder(const cv::GMatDesc &/*src*/, int borderType, cv::Scalar borderValue)
{
return { borderType, to_own(borderValue)};
}
};
GAPI_FLUID_KERNEL(FBlur5x5, TBlur5x5, false)
{
static const int Window = 5;
static void run(const cv::gapi::fluid::View &src, int /*borderType*/,
cv::Scalar /*borderValue*/, cv::gapi::fluid::Buffer &dst)
{
runBlur<Window>(src, dst);
}
static cv::gapi::fluid::Border getBorder(const cv::GMatDesc &/*src*/, int borderType, cv::Scalar borderValue)
{
return { borderType, to_own(borderValue)};
}
};
GAPI_FLUID_KERNEL(FBlur3x3_2lpi, TBlur3x3_2lpi, false)
{
static const int Window = 3;
static const int LPI = 2;
static void run(const cv::gapi::fluid::View &src, int /*borderType*/,
cv::Scalar /*borderValue*/, cv::gapi::fluid::Buffer &dst)
{
runBlur<Window, LPI>(src, dst);
}
static cv::gapi::fluid::Border getBorder(const cv::GMatDesc &/*src*/, int borderType, cv::Scalar borderValue)
{
return { borderType, to_own(borderValue)};
}
};
GAPI_FLUID_KERNEL(FBlur5x5_2lpi, TBlur5x5_2lpi, false)
{
static const int Window = 5;
static const int LPI = 2;
static void run(const cv::gapi::fluid::View &src, int /*borderType*/,
cv::Scalar /*borderValue*/, cv::gapi::fluid::Buffer &dst)
{
runBlur<Window, LPI>(src, dst);
}
static cv::gapi::fluid::Border getBorder(const cv::GMatDesc &/*src*/, int borderType, cv::Scalar borderValue)
{
return { borderType, to_own(borderValue )};
}
};
GAPI_FLUID_KERNEL(FIdentity, TId, false)
{
static const int Window = 3;
static void run(const cv::gapi::fluid::View &a,
cv::gapi::fluid::Buffer &o)
{
const uint8_t* in[3] = {
a.InLine<uint8_t>(-1),
a.InLine<uint8_t>( 0),
a.InLine<uint8_t>(+1)
};
uint8_t* out = o.OutLine<uint8_t>();
// ReadFunction3x3(in, a.length());
for (int i = 0, w = a.length(); i < w; i++)
{
out[i] = in[1][i];
}
}
static gapi::fluid::Border getBorder(const cv::GMatDesc &)
{
return { cv::BORDER_REPLICATE, cv::gapi::own::Scalar{} };
}
};
GAPI_FLUID_KERNEL(FId7x7, TId7x7, false)
{
static const int Window = 7;
static const int LPI = 2;
static void run(const cv::gapi::fluid::View &a,
cv::gapi::fluid::Buffer &o)
{
for (int l = 0, lpi = o.lpi(); l < lpi; l++)
{
const uint8_t* in[Window] = {
a.InLine<uint8_t>(-3 + l),
a.InLine<uint8_t>(-2 + l),
a.InLine<uint8_t>(-1 + l),
a.InLine<uint8_t>( 0 + l),
a.InLine<uint8_t>(+1 + l),
a.InLine<uint8_t>(+2 + l),
a.InLine<uint8_t>(+3 + l),
};
uint8_t* out = o.OutLine<uint8_t>(l);
// std::cout << "Id7x7 " << l << " of " << lpi << " {{{\n";
// std::cout << " a - "; a.debug(std::cout);
// std::cout << " o - "; o.debug(std::cout);
// std::cout << "}}} " << std::endl;;
// // std::cout << "Id7x7 at " << a.y() << "/L" << l << " {{{" << std::endl;
// for (int j = 0; j < Window; j++)
// {
// // std::cout << std::setw(2) << j-(Window-1)/2 << ": ";
// for (int i = 0, w = a.length(); i < w; i++)
// std::cout << std::setw(4) << int(in[j][i]);
// std::cout << std::endl;
// }
// std::cout << "}}}" << std::endl;
for (int i = 0, w = a.length(); i < w; i++)
out[i] = in[(Window-1)/2][i];
}
}
static cv::gapi::fluid::Border getBorder(const cv::GMatDesc&/* src*/)
{
return { cv::BORDER_REPLICATE, cv::gapi::own::Scalar{} };
}
};
GAPI_FLUID_KERNEL(FPlusRow0, TPlusRow0, true)
{
static const int Window = 1;
static void initScratch(const cv::GMatDesc &in,
cv::gapi::fluid::Buffer &scratch)
{
cv::Size scratch_size{in.size.width, 1};
cv::gapi::fluid::Buffer buffer(in.withSize(scratch_size));
scratch = std::move(buffer);
}
static void resetScratch(cv::gapi::fluid::Buffer &scratch)
{
// FIXME: only 1 line can be used!
uint8_t* out_row = scratch.OutLine<uint8_t>();
for (int i = 0, w = scratch.length(); i < w; i++)
{
out_row[i] = 0;
}
}
static void run(const cv::gapi::fluid::View &in,
cv::gapi::fluid::Buffer &out,
cv::gapi::fluid::Buffer &scratch)
{
const uint8_t* in_row = in .InLine <uint8_t>(0);
uint8_t* out_row = out .OutLine<uint8_t>();
uint8_t* tmp_row = scratch.OutLine<uint8_t>();
if (in.y() == 0)
{
// Copy 1st row to scratch buffer
for (int i = 0, w = in.length(); i < w; i++)
{
out_row[i] = in_row[i];
tmp_row[i] = in_row[i];
}
}
else
{
// Output is 1st row + in
for (int i = 0, w = in.length(); i < w; i++)
{
out_row[i] = in_row[i] + tmp_row[i];
}
}
}
};
static void split3Row(const cv::gapi::fluid::View &in,
cv::gapi::fluid::Buffer &o1,
cv::gapi::fluid::Buffer &o2,
cv::gapi::fluid::Buffer &o3)
{
for (int l = 0; l < o1.lpi(); l++)
{
// std::cout << "Split3 {{{\n";
// std::cout << " a - "; in.debug(std::cout);
// std::cout << " 1 - "; o1.debug(std::cout);
// std::cout << " 2 - "; o2.debug(std::cout);
// std::cout << " 3 - "; o3.debug(std::cout);
// std::cout << "}}} " << std::endl;;
const uint8_t* in_rgb = in.InLine<uint8_t>(l);
uint8_t* out_r = o1.OutLine<uint8_t>(l);
uint8_t* out_g = o2.OutLine<uint8_t>(l);
uint8_t* out_b = o3.OutLine<uint8_t>(l);
for (int i = 0, w = in.length(); i < w; i++)
{
out_r[i] = in_rgb[3*i];
out_g[i] = in_rgb[3*i+1];
out_b[i] = in_rgb[3*i+2];
}
}
}
GAPI_FLUID_KERNEL(FTestSplit3, cv::gapi::core::GSplit3, false)
{
static const int Window = 1;
static void run(const cv::gapi::fluid::View &in,
cv::gapi::fluid::Buffer &o1,
cv::gapi::fluid::Buffer &o2,
cv::gapi::fluid::Buffer &o3)
{
split3Row(in, o1, o2, o3);
}
};
GAPI_FLUID_KERNEL(FTestSplit3_4lpi, TSplit3_4lpi, false)
{
static const int Window = 1;
static const int LPI = 4;
static void run(const cv::gapi::fluid::View &in,
cv::gapi::fluid::Buffer &o1,
cv::gapi::fluid::Buffer &o2,
cv::gapi::fluid::Buffer &o3)
{
split3Row(in, o1, o2, o3);
}
};
std::tuple<GMat, GMat, GMat> split3_4lpi(const GMat& src)
{
return TSplit3_4lpi::on(src);
}
GAPI_FLUID_KERNEL(FSum2MatsAndScalar, TSum2MatsAndScalar, false)
{
static const int Window = 1;
static const int LPI = 2;
static void run(const cv::gapi::fluid::View &a,
const cv::Scalar &cval,
const cv::gapi::fluid::View &b,
cv::gapi::fluid::Buffer &out)
{
for (int l = 0, lpi = out.lpi(); l < lpi; l++)
{
const uint8_t* in_row1 = a .InLine <uint8_t>(l);
const uint8_t* in_row2 = b .InLine <uint8_t>(l);
uint8_t* out_row = out.OutLine<uint8_t>(l);
std::cout << "l=" << l << ": ";
for (int i = 0, w = a.length(); i < w; i++)
{
std::cout << std::setw(4) << int(in_row1[i]);
std::cout << std::setw(4) << int(in_row2[i]);
out_row[i] = static_cast<uint8_t>(in_row1[i] + in_row2[i] + cval[0]);
}
std::cout << std::endl;
}
}
};
GAPI_FLUID_KERNEL(FEqualizeHist, TEqualizeHist, false)
{
static const int Window = 1;
static const int LPI = 2;
static void run(const cv::gapi::fluid::View &mat,
const std::vector<int> &arr,
cv::gapi::fluid::Buffer &out)
{
for (int l = 0, lpi = out.lpi(); l < lpi; l++)
{
const uint8_t* in_row = mat.InLine <uint8_t>(l);
uint8_t* out_row = out.OutLine<uint8_t>(l);
for (int i = 0, w = mat.length(); i < w; i++)
{
out_row[i] = static_cast<uint8_t>(arr[in_row[i]]);
}
}
}
};
GAPI_OCV_KERNEL(OCVCalcHist, TCalcHist)
{
static void run(const cv::Mat& in, std::vector<int>& out)
{
out = std::vector<int>(256, 0);
// Calculate normalized accumulated integral transformation array for gapi
for(int i = 0; i < in.rows; ++i)
for(int j = 0; j < in.cols; ++j)
++out[in.at<uint8_t>(i, j)];
for(unsigned int i = 1; i < out.size(); ++i)
out[i] += out[i-1];
int size = in.size().width * in.size().height;
int min = size;
for(unsigned int i = 0; i < out.size(); ++i)
if(out[i] != 0 && out[i] < min)
min = out[i];
for(auto & el : out)
{
// General histogram equalization formula
el = cvRound(((float)(el - min) / (float)(size - min))*255);
}
}
};
static const int ITUR_BT_601_CY = 1220542;
static const int ITUR_BT_601_CUB = 2116026;
static const int ITUR_BT_601_CUG = -409993;
static const int ITUR_BT_601_CVG = -852492;
static const int ITUR_BT_601_CVR = 1673527;
static const int ITUR_BT_601_SHIFT = 20;
static inline void uvToRGBuv(const uchar u, const uchar v, int& ruv, int& guv, int& buv)
{
int uu, vv;
uu = int(u) - 128;
vv = int(v) - 128;
ruv = (1 << (ITUR_BT_601_SHIFT - 1)) + ITUR_BT_601_CVR * vv;
guv = (1 << (ITUR_BT_601_SHIFT - 1)) + ITUR_BT_601_CVG * vv + ITUR_BT_601_CUG * uu;
buv = (1 << (ITUR_BT_601_SHIFT - 1)) + ITUR_BT_601_CUB * uu;
}
static inline void yRGBuvToRGB(const uchar vy, const int ruv, const int guv, const int buv,
uchar& r, uchar& g, uchar& b)
{
int y = std::max(0, vy - 16) * ITUR_BT_601_CY;
r = saturate_cast<uchar>((y + ruv) >> ITUR_BT_601_SHIFT);
g = saturate_cast<uchar>((y + guv) >> ITUR_BT_601_SHIFT);
b = saturate_cast<uchar>((y + buv) >> ITUR_BT_601_SHIFT);
}
GAPI_FLUID_KERNEL(FNV12toRGB, cv::gapi::imgproc::GNV12toRGB, false)
{
static const int Window = 1;
static const int LPI = 2;
static const auto Kind = GFluidKernel::Kind::NV12toRGB;
static void run(const cv::gapi::fluid::View &in1,
const cv::gapi::fluid::View &in2,
cv::gapi::fluid::Buffer &out)
{
const auto w = out.length();
GAPI_Assert(w % 2 == 0);
GAPI_Assert(out.lpi() == 2);
const uchar* uv_row = in2.InLineB(0);
const uchar* y_rows[] = {in1. InLineB(0), in1. InLineB(1)};
uchar* out_rows[] = {out.OutLineB(0), out.OutLineB(1)};
for (int i = 0; i < w/2; i++)
{
uchar u = uv_row[2*i];
uchar v = uv_row[2*i + 1];
int ruv, guv, buv;
uvToRGBuv(u, v, ruv, guv, buv);
for (int y = 0; y < 2; y++)
{
for (int x = 0; x < 2; x++)
{
uchar vy = y_rows[y][2*i + x];
uchar r, g, b;
yRGBuvToRGB(vy, ruv, guv, buv, r, g, b);
out_rows[y][3*(2*i + x)] = r;
out_rows[y][3*(2*i + x) + 1] = g;
out_rows[y][3*(2*i + x) + 2] = b;
}
}
}
}
};
GAPI_FLUID_KERNEL(FMerge3_4lpi, TMerge3_4lpi, false)
{
static const int Window = 1;
static const int LPI = 4;
static void run(const cv::gapi::fluid::View &src1,
const cv::gapi::fluid::View &src2,
const cv::gapi::fluid::View &src3,
cv::gapi::fluid::Buffer &dst)
{
for (int l = 0; l < dst.lpi(); l++)
{
const auto *in1 = src1.InLine<uchar>(l);
const auto *in2 = src2.InLine<uchar>(l);
const auto *in3 = src3.InLine<uchar>(l);
auto *out = dst.OutLine<uchar>(l);
for (int w = 0; w < dst.length(); w++)
{
out[3*w ] = in1[w];
out[3*w + 1] = in2[w];
out[3*w + 2] = in3[w];
}
}
}
};
GMat merge3_4lpi(const GMat& src1, const GMat& src2, const GMat& src3)
{
return TMerge3_4lpi::on(src1, src2, src3);
}
cv::gapi::GKernelPackage fluidTestPackage = cv::gapi::kernels
<FAddSimple
,FAddCSimple
,FAddScalar
,FAddScalarToMat
,FBlur1x1
,FBlur3x3
,FBlur5x5
,FBlur3x3_2lpi
,FBlur5x5_2lpi
,FIdentity
,FId7x7
,FMerge3_4lpi
,FNV12toRGB
,FPlusRow0
,FSum2MatsAndScalar
,FTestSplit3
,FTestSplit3_4lpi
,FEqualizeHist
,OCVCalcHist
>();
} // namespace gapi_test_kernels
} // namespace cv