一 glur是高斯blur的缩写
简单来说就是通过周围的像素点计算得到中间像素点的值,ffmpeg中gblur滤镜只考虑了上下左右四个点,然后有一个强度系数sigma。
二 代码走读
2.1 整体流程
把原画copy到申请的buffer -> 在buffer中完成gblur模糊,行上和列上面是分开做的->把buffer中blur好的画面copy到out帧中
if (s->depth == 8) {
for (y = 0; y < height; y++) {
for (x = 0; x < width; x++) {
bptr[x] = src[x];//先循环吧数据copy到buffer里面
}
bptr += width;
src += in->linesize[plane];
}
} else {
for (y = 0; y < height; y++) {
for (x = 0; x < width; x++) {
bptr[x] = src16[x];
}
bptr += width;
src16 += in->linesize[plane] / 2;
}
}
gaussianiir2d(ctx, plane);
bptr = s->buffer;
if (s->depth == 8) {
for (y = 0; y < height; y++) {
for (x = 0; x < width; x++) {
dst[x] = bptr[x];//吧数据从buffer里面copy到目标帧里面
}
bptr += width;
dst += out->linesize[plane];
}
} else {
for (y = 0; y < height; y++) {
for (x = 0; x < width; x++) {
dst16[x] = bptr[x];
}
bptr += width;
dst16 += out->linesize[plane] / 2;
}
}
三 行列上的blur
列上的blur,地址不是连续的不能使用SIMD
static int filter_vertically(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
{
GBlurContext *s = ctx->priv; //参数数据
ThreadData *td = arg; //线程上下文
const int height = td->height; //高度
const int width = td->width; //宽度
const int slice_start = (width * jobnr ) / nb_jobs; //起始列
const int slice_end = (width * (jobnr+1)) / nb_jobs;//结束lie
const float boundaryscale = s->boundaryscaleV;//输入参数
const int steps = s->steps;//步长
const float nu = s->nuV; //列强度
float *buffer = s->buffer;//缓存buffer
int aligned_end; //对其
aligned_end = slice_start + (((slice_end - slice_start) >> 3) << 3);
/* Filter vertically along columns (process 8 columns in each step) 对齐的列*/
do_vertical_columns(buffer, width, height, slice_start, aligned_end,
steps, nu, boundaryscale, 8); // 8列一组
/* Filter un-aligned columns one by one 对齐的列 */
do_vertical_columns(buffer, width, height, aligned_end, slice_end,
steps, nu, boundaryscale, 1); //一列一组
return 0;
}
static void do_vertical_columns(
float *buffer, // 缓存buffer,一个像素一个值
int width, //宽
int height, //高
int column_begin, //起始列
int column_end, //结束列
int steps, //窗口size
float nu, //nu系数
float boundaryscale, //强度scale
int column_step) //列step
{
const int numpixels = width * height; //像素树木
int i, x, k, step;
float *ptr;
for (x = column_begin; x < column_end;) { //从起始列循环到结束列
for (step = 0; step < steps; step++) { //循环遍历窗口
ptr = buffer + x;// 缓存地址起点
for (k = 0; k < column_step; k++) { //每间隔step 乘以系数boundaryscale ,step 每次8个一组
ptr[k] *= boundaryscale;
}
/* Filter downwards */
for (i = width; i < numpixels; i += width) { //滤波每一列,遍历每一个像素
for (k = 0; k < column_step; k++) {
ptr[i + k] += nu * ptr[i - width + k]; //周围点的带权求和
/*
* (参考点)
*(当前点)
当前点的值 = 上一个点 * 一定的系数
*/
}
}
i = numpixels - width; //最后一行
for (k = 0; k < column_step; k++) //column_step = 8
ptr[i + k] *= boundaryscale; //
/* Filter upwards 下一行的系数*/
for (; i > 0; i -= width) {
for (k = 0; k < column_step; k++)
ptr[i - width + k] += nu * ptr[i + k]; //当前点,下一个点
}
}
x += column_step;
}
}
行上的blur
static void horiz_slice_c(float *buffer, int width, int height, int steps,
float nu, float bscale)
{ //行上blur C语言版本
//还有intrisic版本
int step, x, y;
float *ptr;
for (y = 0; y < height; y++) {
for (step = 0; step < steps; step++) {
ptr = buffer + width * y; //先取到这一行的内存
ptr[0] *= bscale;
/* Filter rightwards */
for (x = 1; x < width; x++)
ptr[x] += nu * ptr[x - 1]; //行上当前点和前一个点,向右考虑
ptr[x = width - 1] *= bscale;
/* Filter leftwards */
for (; x > 0; x--) //循环,向左考虑
ptr[x - 1] += nu * ptr[x];//当前点的位置前一个点。
}
}
}