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