avfilter/vf_convolution: add horizontal/row mode

Signed-off-by: Paul B Mahol <onemda@gmail.com>
This commit is contained in:
Paul B Mahol 2018-05-03 10:55:10 +02:00
parent 0f0d468fbc
commit 3a96534ed9
2 changed files with 109 additions and 27 deletions

View File

@ -6408,7 +6408,7 @@ colorspace=smpte240m
@section convolution @section convolution
Apply convolution 3x3, 5x5 or 7x7 filter. Apply convolution of 3x3, 5x5, 7x7 or horizontal up to 49 elements.
The filter accepts the following options: The filter accepts the following options:
@ -6418,7 +6418,8 @@ The filter accepts the following options:
@item 2m @item 2m
@item 3m @item 3m
Set matrix for each plane. Set matrix for each plane.
Matrix is sequence of 9, 25 or 49 signed integers. Matrix is sequence of 9, 25 or 49 signed integers in @var{square} mode,
and from 1 to 49 odd number of signed integers in @var{row} mode.
@item 0rdiv @item 0rdiv
@item 1rdiv @item 1rdiv
@ -6433,6 +6434,13 @@ If unset or 0, it will be sum of all matrix elements.
@item 3bias @item 3bias
Set bias for each plane. This value is added to the result of the multiplication. Set bias for each plane. This value is added to the result of the multiplication.
Useful for making the overall image brighter or darker. Default is 0.0. Useful for making the overall image brighter or darker. Default is 0.0.
@item 0mode
@item 1mode
@item 2mode
@item 3mode
Set matrix mode for each plane. Can be @var{square} or @var{row}.
Default is @var{square}.
@end table @end table
@subsection Examples @subsection Examples

View File

@ -29,12 +29,19 @@
#include "internal.h" #include "internal.h"
#include "video.h" #include "video.h"
enum MatrixMode {
MATRIX_SQUARE,
MATRIX_ROW,
MATRIX_NBMODES,
};
typedef struct ConvolutionContext { typedef struct ConvolutionContext {
const AVClass *class; const AVClass *class;
char *matrix_str[4]; char *matrix_str[4];
float rdiv[4]; float rdiv[4];
float bias[4]; float bias[4];
int mode[4];
float scale; float scale;
float delta; float delta;
int planes; int planes;
@ -51,11 +58,11 @@ typedef struct ConvolutionContext {
int matrix_length[4]; int matrix_length[4];
int copy[4]; int copy[4];
void (*setup[4])(const uint8_t *c[], const uint8_t *src, int stride, void (*setup[4])(int radius, const uint8_t *c[], const uint8_t *src, int stride,
int x, int width, int y, int height, int bpc); int x, int width, int y, int height, int bpc);
void (*filter[4])(uint8_t *dst, const uint8_t *src, int width, void (*filter[4])(uint8_t *dst, const uint8_t *src, int width,
float rdiv, float bias, const int *const matrix, float rdiv, float bias, const int *const matrix,
const uint8_t *c[], int peak); const uint8_t *c[], int peak, int radius);
} ConvolutionContext; } ConvolutionContext;
#define OFFSET(x) offsetof(ConvolutionContext, x) #define OFFSET(x) offsetof(ConvolutionContext, x)
@ -74,6 +81,12 @@ static const AVOption convolution_options[] = {
{ "1bias", "set bias for 2nd plane", OFFSET(bias[1]), AV_OPT_TYPE_FLOAT, {.dbl=0.0}, 0.0, INT_MAX, FLAGS}, { "1bias", "set bias for 2nd plane", OFFSET(bias[1]), AV_OPT_TYPE_FLOAT, {.dbl=0.0}, 0.0, INT_MAX, FLAGS},
{ "2bias", "set bias for 3rd plane", OFFSET(bias[2]), AV_OPT_TYPE_FLOAT, {.dbl=0.0}, 0.0, INT_MAX, FLAGS}, { "2bias", "set bias for 3rd plane", OFFSET(bias[2]), AV_OPT_TYPE_FLOAT, {.dbl=0.0}, 0.0, INT_MAX, FLAGS},
{ "3bias", "set bias for 4th plane", OFFSET(bias[3]), AV_OPT_TYPE_FLOAT, {.dbl=0.0}, 0.0, INT_MAX, FLAGS}, { "3bias", "set bias for 4th plane", OFFSET(bias[3]), AV_OPT_TYPE_FLOAT, {.dbl=0.0}, 0.0, INT_MAX, FLAGS},
{ "0mode", "set matrix mode for 1st plane", OFFSET(mode[0]), AV_OPT_TYPE_INT, {.i64=MATRIX_SQUARE}, 0, MATRIX_NBMODES-1, FLAGS, "mode" },
{ "1mode", "set matrix mode for 2nd plane", OFFSET(mode[1]), AV_OPT_TYPE_INT, {.i64=MATRIX_SQUARE}, 0, MATRIX_NBMODES-1, FLAGS, "mode" },
{ "2mode", "set matrix mode for 3rd plane", OFFSET(mode[2]), AV_OPT_TYPE_INT, {.i64=MATRIX_SQUARE}, 0, MATRIX_NBMODES-1, FLAGS, "mode" },
{ "3mode", "set matrix mode for 4th plane", OFFSET(mode[3]), AV_OPT_TYPE_INT, {.i64=MATRIX_SQUARE}, 0, MATRIX_NBMODES-1, FLAGS, "mode" },
{ "square", "square matrix", 0, AV_OPT_TYPE_CONST, {.i64=MATRIX_SQUARE}, 0, 0, FLAGS, "mode" },
{ "row", "single row matrix", 0, AV_OPT_TYPE_CONST, {.i64=MATRIX_ROW} , 0, 0, FLAGS, "mode" },
{ NULL } { NULL }
}; };
@ -129,7 +142,7 @@ typedef struct ThreadData {
static void filter16_prewitt(uint8_t *dstp, const uint8_t *src, int width, static void filter16_prewitt(uint8_t *dstp, const uint8_t *src, int width,
float scale, float delta, const int *const matrix, float scale, float delta, const int *const matrix,
const uint8_t *c[], int peak) const uint8_t *c[], int peak, int radius)
{ {
uint16_t *dst = (uint16_t *)dstp; uint16_t *dst = (uint16_t *)dstp;
int x; int x;
@ -146,7 +159,7 @@ static void filter16_prewitt(uint8_t *dstp, const uint8_t *src, int width,
static void filter16_roberts(uint8_t *dstp, const uint8_t *src, int width, static void filter16_roberts(uint8_t *dstp, const uint8_t *src, int width,
float scale, float delta, const int *const matrix, float scale, float delta, const int *const matrix,
const uint8_t *c[], int peak) const uint8_t *c[], int peak, int radius)
{ {
uint16_t *dst = (uint16_t *)dstp; uint16_t *dst = (uint16_t *)dstp;
int x; int x;
@ -161,7 +174,7 @@ static void filter16_roberts(uint8_t *dstp, const uint8_t *src, int width,
static void filter16_sobel(uint8_t *dstp, const uint8_t *src, int width, static void filter16_sobel(uint8_t *dstp, const uint8_t *src, int width,
float scale, float delta, const int *const matrix, float scale, float delta, const int *const matrix,
const uint8_t *c[], int peak) const uint8_t *c[], int peak, int radius)
{ {
uint16_t *dst = (uint16_t *)dstp; uint16_t *dst = (uint16_t *)dstp;
int x; int x;
@ -178,7 +191,7 @@ static void filter16_sobel(uint8_t *dstp, const uint8_t *src, int width,
static void filter_prewitt(uint8_t *dst, const uint8_t *src, int width, static void filter_prewitt(uint8_t *dst, const uint8_t *src, int width,
float scale, float delta, const int *const matrix, float scale, float delta, const int *const matrix,
const uint8_t *c[], int peak) const uint8_t *c[], int peak, int radius)
{ {
const uint8_t *c0 = c[0], *c1 = c[1], *c2 = c[2]; const uint8_t *c0 = c[0], *c1 = c[1], *c2 = c[2];
const uint8_t *c3 = c[3], *c5 = c[5]; const uint8_t *c3 = c[3], *c5 = c[5];
@ -197,7 +210,7 @@ static void filter_prewitt(uint8_t *dst, const uint8_t *src, int width,
static void filter_roberts(uint8_t *dst, const uint8_t *src, int width, static void filter_roberts(uint8_t *dst, const uint8_t *src, int width,
float scale, float delta, const int *const matrix, float scale, float delta, const int *const matrix,
const uint8_t *c[], int peak) const uint8_t *c[], int peak, int radius)
{ {
int x; int x;
@ -211,7 +224,7 @@ static void filter_roberts(uint8_t *dst, const uint8_t *src, int width,
static void filter_sobel(uint8_t *dst, const uint8_t *src, int width, static void filter_sobel(uint8_t *dst, const uint8_t *src, int width,
float scale, float delta, const int *const matrix, float scale, float delta, const int *const matrix,
const uint8_t *c[], int peak) const uint8_t *c[], int peak, int radius)
{ {
const uint8_t *c0 = c[0], *c1 = c[1], *c2 = c[2]; const uint8_t *c0 = c[0], *c1 = c[1], *c2 = c[2];
const uint8_t *c3 = c[3], *c5 = c[5]; const uint8_t *c3 = c[3], *c5 = c[5];
@ -230,7 +243,7 @@ static void filter_sobel(uint8_t *dst, const uint8_t *src, int width,
static void filter16_3x3(uint8_t *dstp, const uint8_t *src, int width, static void filter16_3x3(uint8_t *dstp, const uint8_t *src, int width,
float rdiv, float bias, const int *const matrix, float rdiv, float bias, const int *const matrix,
const uint8_t *c[], int peak) const uint8_t *c[], int peak, int radius)
{ {
uint16_t *dst = (uint16_t *)dstp; uint16_t *dst = (uint16_t *)dstp;
int x; int x;
@ -252,7 +265,7 @@ static void filter16_3x3(uint8_t *dstp, const uint8_t *src, int width,
static void filter16_5x5(uint8_t *dstp, const uint8_t *src, int width, static void filter16_5x5(uint8_t *dstp, const uint8_t *src, int width,
float rdiv, float bias, const int *const matrix, float rdiv, float bias, const int *const matrix,
const uint8_t *c[], int peak) const uint8_t *c[], int peak, int radius)
{ {
uint16_t *dst = (uint16_t *)dstp; uint16_t *dst = (uint16_t *)dstp;
int x; int x;
@ -270,7 +283,7 @@ static void filter16_5x5(uint8_t *dstp, const uint8_t *src, int width,
static void filter16_7x7(uint8_t *dstp, const uint8_t *src, int width, static void filter16_7x7(uint8_t *dstp, const uint8_t *src, int width,
float rdiv, float bias, const int *const matrix, float rdiv, float bias, const int *const matrix,
const uint8_t *c[], int peak) const uint8_t *c[], int peak, int radius)
{ {
uint16_t *dst = (uint16_t *)dstp; uint16_t *dst = (uint16_t *)dstp;
int x; int x;
@ -286,9 +299,27 @@ static void filter16_7x7(uint8_t *dstp, const uint8_t *src, int width,
} }
} }
static void filter16_row(uint8_t *dstp, const uint8_t *src, int width,
float rdiv, float bias, const int *const matrix,
const uint8_t *c[], int peak, int radius)
{
uint16_t *dst = (uint16_t *)dstp;
int x;
for (x = 0; x < width; x++) {
int i, sum = 0;
for (i = 0; i < 2 * radius + 1; i++)
sum += AV_RN16A(&c[i][2 * x]) * matrix[i];
sum = (int)(sum * rdiv + bias + 0.5f);
dst[x] = av_clip(sum, 0, peak);
}
}
static void filter_7x7(uint8_t *dst, const uint8_t *src, int width, static void filter_7x7(uint8_t *dst, const uint8_t *src, int width,
float rdiv, float bias, const int *const matrix, float rdiv, float bias, const int *const matrix,
const uint8_t *c[], int peak) const uint8_t *c[], int peak, int radius)
{ {
int x; int x;
@ -305,7 +336,7 @@ static void filter_7x7(uint8_t *dst, const uint8_t *src, int width,
static void filter_5x5(uint8_t *dst, const uint8_t *src, int width, static void filter_5x5(uint8_t *dst, const uint8_t *src, int width,
float rdiv, float bias, const int *const matrix, float rdiv, float bias, const int *const matrix,
const uint8_t *c[], int peak) const uint8_t *c[], int peak, int radius)
{ {
int x; int x;
@ -322,7 +353,7 @@ static void filter_5x5(uint8_t *dst, const uint8_t *src, int width,
static void filter_3x3(uint8_t *dst, const uint8_t *src, int width, static void filter_3x3(uint8_t *dst, const uint8_t *src, int width,
float rdiv, float bias, const int *const matrix, float rdiv, float bias, const int *const matrix,
const uint8_t *c[], int peak) const uint8_t *c[], int peak, int radius)
{ {
const uint8_t *c0 = c[0], *c1 = c[1], *c2 = c[2]; const uint8_t *c0 = c[0], *c1 = c[1], *c2 = c[2];
const uint8_t *c3 = c[3], *c4 = c[4], *c5 = c[5]; const uint8_t *c3 = c[3], *c4 = c[4], *c5 = c[5];
@ -338,7 +369,24 @@ static void filter_3x3(uint8_t *dst, const uint8_t *src, int width,
} }
} }
static void setup_3x3(const uint8_t *c[], const uint8_t *src, int stride, static void filter_row(uint8_t *dst, const uint8_t *src, int width,
float rdiv, float bias, const int *const matrix,
const uint8_t *c[], int peak, int radius)
{
int x;
for (x = 0; x < width; x++) {
int i, sum = 0;
for (i = 0; i < 2 * radius + 1; i++)
sum += c[i][x] * matrix[i];
sum = (int)(sum * rdiv + bias + 0.5f);
dst[x] = av_clip_uint8(sum);
}
}
static void setup_3x3(int radius, const uint8_t *c[], const uint8_t *src, int stride,
int x, int w, int y, int h, int bpc) int x, int w, int y, int h, int bpc)
{ {
int i; int i;
@ -354,7 +402,7 @@ static void setup_3x3(const uint8_t *c[], const uint8_t *src, int stride,
} }
} }
static void setup_5x5(const uint8_t *c[], const uint8_t *src, int stride, static void setup_5x5(int radius, const uint8_t *c[], const uint8_t *src, int stride,
int x, int w, int y, int h, int bpc) int x, int w, int y, int h, int bpc)
{ {
int i; int i;
@ -370,7 +418,7 @@ static void setup_5x5(const uint8_t *c[], const uint8_t *src, int stride,
} }
} }
static void setup_7x7(const uint8_t *c[], const uint8_t *src, int stride, static void setup_7x7(int radius, const uint8_t *c[], const uint8_t *src, int stride,
int x, int w, int y, int h, int bpc) int x, int w, int y, int h, int bpc)
{ {
int i; int i;
@ -386,6 +434,20 @@ static void setup_7x7(const uint8_t *c[], const uint8_t *src, int stride,
} }
} }
static void setup_row(int radius, const uint8_t *c[], const uint8_t *src, int stride,
int x, int w, int y, int h, int bpc)
{
int i;
for (i = 0; i < radius * 2 + 1; i++) {
int xoff = FFABS(x + i - radius);
xoff = xoff >= w ? 2 * w - 1 - xoff : xoff;
c[i] = src + xoff * bpc + y * stride;
}
}
static int filter_slice(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) static int filter_slice(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
{ {
ConvolutionContext *s = ctx->priv; ConvolutionContext *s = ctx->priv;
@ -419,15 +481,17 @@ static int filter_slice(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
for (y = slice_start; y < slice_end; y++) { for (y = slice_start; y < slice_end; y++) {
for (x = 0; x < radius; x++) { for (x = 0; x < radius; x++) {
s->setup[plane](c, src, stride, x, width, y, height, bpc); s->setup[plane](radius, c, src, stride, x, width, y, height, bpc);
s->filter[plane](dst + x * bpc, src, 1, rdiv, bias, matrix, c, s->max); s->filter[plane](dst + x * bpc, src, 1, rdiv,
bias, matrix, c, s->max, radius);
} }
s->setup[plane](c, src, stride, radius, width, y, height, bpc); s->setup[plane](radius, c, src, stride, radius, width, y, height, bpc);
s->filter[plane](dst + radius * bpc, src, width - 2 * radius, s->filter[plane](dst + radius * bpc, src, width - 2 * radius,
rdiv, bias, matrix, c, s->max); rdiv, bias, matrix, c, s->max, radius);
for (x = width - radius; x < width; x++) { for (x = width - radius; x < width; x++) {
s->setup[plane](c, src, stride, x, width, y, height, bpc); s->setup[plane](radius, c, src, stride, x, width, y, height, bpc);
s->filter[plane](dst + x * bpc, src, 1, rdiv, bias, matrix, c, s->max); s->filter[plane](dst + x * bpc, src, 1, rdiv,
bias, matrix, c, s->max, radius);
} }
dst += dstride; dst += dstride;
} }
@ -458,7 +522,9 @@ static int config_input(AVFilterLink *inlink)
if (!strcmp(ctx->filter->name, "convolution")) { if (!strcmp(ctx->filter->name, "convolution")) {
if (s->depth > 8) { if (s->depth > 8) {
for (p = 0; p < s->nb_planes; p++) { for (p = 0; p < s->nb_planes; p++) {
if (s->size[p] == 3) if (s->mode[p] == MATRIX_ROW)
s->filter[p] = filter16_row;
else if (s->size[p] == 3)
s->filter[p] = filter16_3x3; s->filter[p] = filter16_3x3;
else if (s->size[p] == 5) else if (s->size[p] == 5)
s->filter[p] = filter16_5x5; s->filter[p] = filter16_5x5;
@ -528,7 +594,15 @@ static av_cold int init(AVFilterContext *ctx)
s->matrix_length[i]++; s->matrix_length[i]++;
} }
if (s->matrix_length[i] == 9) { if (!(s->matrix_length[i] & 1)) {
av_log(ctx, AV_LOG_ERROR, "number of matrix elements must be odd\n");
return AVERROR(EINVAL);
}
if (s->mode[i] == MATRIX_ROW) {
s->filter[i] = filter_row;
s->setup[i] = setup_row;
s->size[i] = s->matrix_length[i];
} else if (s->matrix_length[i] == 9) {
s->size[i] = 3; s->size[i] = 3;
if (!memcmp(matrix, same3x3, sizeof(same3x3))) if (!memcmp(matrix, same3x3, sizeof(same3x3)))
s->copy[i] = 1; s->copy[i] = 1;