/*
Phaneron - Clustered, accelerated and cloud-fit video server, pre-assembled and in kit form.
Copyright (C) 2020 Streampunk Media Ltd.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
https://www.streampunk.media/ mailto:furnace@streampunk.media
14 Ormiscaig, Aultbea, Achnasheen, IV22 2JJ U.K.
*/
/*
* Based on https://github.com/FFmpeg/FFmpeg/blob/8e50215b5e02074b0773dfcf55867654ee59c179/libavfilter/vf_yadif_cuda.cu
*/
import { ProcessImpl } from './imageProcess'
import { KernelParams } from 'nodencl'
const yadifKernel = `
__constant sampler_t sampler1 =
CLK_NORMALIZED_COORDS_FALSE
| CLK_ADDRESS_CLAMP_TO_EDGE
| CLK_FILTER_NEAREST;
float4 spatial_predictor(
float4 a, float4 b, float4 c, float4 d,
float4 e, float4 f, float4 g, float4 h,
float4 i, float4 j, float4 k, float4 l,
float4 m, float4 n
) {
float4 spatialPred = (d + k) / 2.0f;
float4 spatialScore = fabs(c - j) + fabs(d - k) + fabs(e - l);
float4 score = fabs(b - k) + fabs(c - l) + fabs(d - m);
int4 compareScore = score < spatialScore;
spatialPred = compareScore ? (c + l) / 2.0f : spatialPred;
spatialScore = compareScore ? score : spatialScore;
score = compareScore ? fabs(a - l) + fabs(b - m) + fabs(c - n) : score;
compareScore = compareScore && (score < spatialScore);
spatialPred = compareScore ? (b + m) / 2.0f : spatialPred;
spatialScore = compareScore ? score : spatialScore;
score = fabs(d - i) + fabs(e - j) + fabs(f - k);
compareScore = score < spatialScore;
spatialPred = compareScore ? (e + j) / 2.0f : spatialPred;
spatialScore = compareScore ? score : spatialScore;
score = compareScore ? fabs(e - h) + fabs(f - i) + fabs(g - j) : score;
compareScore = compareScore && (score < spatialScore);
spatialPred = compareScore ? (f + i) / 2.0f : spatialPred;
spatialScore = compareScore ? score : spatialScore;
return spatialPred;
}
float4 fmax3(float4 a, float4 b, float4 c) {
return fmax(fmax(a, b), c);
}
float4 fmin3(float4 a, float4 b, float4 c) {
return fmin(fmin(a, b), c);
}
float4 temporal_predictor(
float4 A, float4 B, float4 C, float4 D,
float4 E, float4 F, float4 G, float4 H,
float4 I, float4 J, float4 K, float4 L,
float4 spatialPred, int skipCheck
) {
float4 p0 = (C + H) / 2.0f;
float4 p1 = F;
float4 p2 = (D + I) / 2.0f;
float4 p3 = G;
float4 p4 = (E + J) / 2.0f;
float4 tdiff0 = fabs(D - I);
float4 tdiff1 = (fabs(A - F) + fabs(B - G)) / 2.0f;
float4 tdiff2 = (fabs(K - F) + fabs(G - L)) / 2.0f;
float4 diff = fmax3(tdiff0, tdiff1, tdiff2);
if (!skipCheck) {
float4 p2mp3 = p2 - p3;
float4 p2mp1 = p2 - p1;
float4 p0mp1 = p0 - p1;
float4 p4mp3 = p4 - p3;
float4 maxi = fmax3(p2mp3, p2mp1, fmin(p0mp1, p4mp3));
float4 mini = fmin3(p2mp3, p2mp1, fmax(p0mp1, p4mp3));
diff = fmax3(diff, mini, -maxi);
}
spatialPred = (spatialPred > (p2 + diff)) ? p2 + diff : spatialPred;
spatialPred = (spatialPred < (p2 - diff)) ? p2 - diff : spatialPred;
return spatialPred;
}
__kernel void yadif(
__read_only image2d_t prev,
__read_only image2d_t cur,
__read_only image2d_t next,
__private int parity,
__private int tff,
__private int skipSpatial,
__write_only image2d_t output) {
int xo = get_global_id(0);
int yo = get_global_id(1);
// Don't modify the primary field
if (yo % 2 == parity) {
write_imagef(output, (int2)(xo, yo), read_imagef(cur, sampler1, (int2) (xo, yo)));
return;
}
// Calculate spatial prediction
float4 a = read_imagef(cur, sampler1, (int2) (xo - 3, yo - 1));
float4 b = read_imagef(cur, sampler1, (int2) (xo - 2, yo - 1));
float4 c = read_imagef(cur, sampler1, (int2) (xo - 1, yo - 1));
float4 d = read_imagef(cur, sampler1, (int2) (xo - 0, yo - 1));
float4 e = read_imagef(cur, sampler1, (int2) (xo + 1, yo - 1));
float4 f = read_imagef(cur, sampler1, (int2) (xo + 2, yo - 1));
float4 g = read_imagef(cur, sampler1, (int2) (xo + 3, yo - 1));
float4 h = read_imagef(cur, sampler1, (int2) (xo - 3, yo + 1));
float4 i = read_imagef(cur, sampler1, (int2) (xo - 2, yo + 1));
float4 j = read_imagef(cur, sampler1, (int2) (xo - 1, yo + 1));
float4 k = read_imagef(cur, sampler1, (int2) (xo - 0, yo + 1));
float4 l = read_imagef(cur, sampler1, (int2) (xo + 1, yo + 1));
float4 m = read_imagef(cur, sampler1, (int2) (xo + 2, yo + 1));
float4 n = read_imagef(cur, sampler1, (int2) (xo + 3, yo + 1));
float4 spatialPred =
spatial_predictor(a, b, c, d, e, f, g, h, i, j, k, l, m, n);
// Calculate temporal prediction
int isSecondField = !(parity ^ tff);
float4 A = read_imagef(prev, sampler1, (int2) (xo, yo - 1));
float4 B = read_imagef(prev, sampler1, (int2) (xo, yo + 1));
float4 C = isSecondField ? read_imagef(cur, sampler1, (int2) (xo, yo - 2)) : read_imagef(prev, sampler1, (int2) (xo, yo - 2));
float4 D = isSecondField ? read_imagef(cur, sampler1, (int2) (xo, yo + 0)) : read_imagef(prev, sampler1, (int2) (xo, yo + 0));
float4 E = isSecondField ? read_imagef(cur, sampler1, (int2) (xo, yo + 2)) : read_imagef(prev, sampler1, (int2) (xo, yo + 2));
float4 F = read_imagef(cur, sampler1, (int2) (xo, yo - 1));
float4 G = read_imagef(cur, sampler1, (int2) (xo, yo + 1));
float4 H = isSecondField ? read_imagef(next, sampler1, (int2) (xo, yo - 2)) : read_imagef(cur, sampler1, (int2) (xo, yo - 2));
float4 I = isSecondField ? read_imagef(next, sampler1, (int2) (xo, yo + 0)) : read_imagef(cur, sampler1, (int2) (xo, yo + 0));
float4 J = isSecondField ? read_imagef(next, sampler1, (int2) (xo, yo + 2)) : read_imagef(cur, sampler1, (int2) (xo, yo + 2));
float4 K = read_imagef(next, sampler1, (int2) (xo, yo - 1));
float4 L = read_imagef(next, sampler1, (int2) (xo, yo + 1));
spatialPred = temporal_predictor(
A, B, C, D, E, F, G, H, I, J, K, L,
spatialPred, skipSpatial
);
// Reset Alpha
spatialPred.s3 = read_imagef(cur, sampler1, (int2) (xo, yo)).s3;
write_imagef(output, (int2)(xo, yo), spatialPred);
};
`
export default class YadifCL extends ProcessImpl {
constructor(width: number, height: number) {
super('yadif', width, height, yadifKernel, 'yadif')
}
async init(): Promise {
return Promise.resolve()
}
async getKernelParams(params: KernelParams): Promise {
return Promise.resolve({
prev: params.prev,
cur: params.cur,
next: params.next,
parity: params.parity,
tff: (params.tff as boolean) ? 1 : 0,
skipSpatial: (params.skipSpatial as boolean) ? 1 : 0,
output: params.output
})
}
releaseRefs(): void {
return
}
}