mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-27 04:01:08 +00:00
video-converter: add dithering
Use the new dither object to perform dithering. Add option to select dithering method. Add option to quantize to a specific value
This commit is contained in:
parent
dfb202a117
commit
d03136f1ea
3 changed files with 133 additions and 115 deletions
|
@ -149,9 +149,8 @@ struct _GstVideoConverter
|
|||
gint current_bits;
|
||||
|
||||
GstStructure *config;
|
||||
GstVideoDitherMethod dither;
|
||||
|
||||
guint16 *errline;
|
||||
guint16 *tmpline;
|
||||
|
||||
gboolean fill_border;
|
||||
gpointer borderline;
|
||||
|
@ -159,7 +158,6 @@ struct _GstVideoConverter
|
|||
|
||||
void (*convert) (GstVideoConverter * convert, const GstVideoFrame * src,
|
||||
GstVideoFrame * dest);
|
||||
void (*dither16) (GstVideoConverter * convert, guint16 * pixels, int j);
|
||||
|
||||
/* data for unpack */
|
||||
GstLineCache *unpack_lines;
|
||||
|
@ -214,6 +212,10 @@ struct _GstVideoConverter
|
|||
guint down_n_lines;
|
||||
gint down_offset;
|
||||
|
||||
/* dither */
|
||||
GstLineCache *dither_lines;
|
||||
GstVideoDither *dither;
|
||||
|
||||
/* pack */
|
||||
GstLineCache *pack_lines;
|
||||
guint pack_nlines;
|
||||
|
@ -396,6 +398,8 @@ static gboolean do_vscale_lines (GstLineCache * cache, gint out_line,
|
|||
gint in_line, gpointer user_data);
|
||||
static gboolean do_hscale_lines (GstLineCache * cache, gint out_line,
|
||||
gint in_line, gpointer user_data);
|
||||
static gboolean do_dither_lines (GstLineCache * cache, gint out_line,
|
||||
gint in_line, gpointer user_data);
|
||||
|
||||
typedef struct
|
||||
{
|
||||
|
@ -530,6 +534,8 @@ get_opt_str (GstVideoConverter * convert, const gchar * opt, const gchar * def)
|
|||
#define DEFAULT_OPT_PRIMARIES_MODE "none"
|
||||
#define DEFAULT_OPT_RESAMPLER_METHOD GST_VIDEO_RESAMPLER_METHOD_CUBIC
|
||||
#define DEFAULT_OPT_RESAMPLER_TAPS 0
|
||||
#define DEFAULT_OPT_DITHER_METHOD GST_VIDEO_DITHER_BAYER
|
||||
#define DEFAULT_OPT_DITHER_QUANTIZATION 1
|
||||
|
||||
#define GET_OPT_FILL_BORDER(c) get_opt_bool(c, \
|
||||
GST_VIDEO_CONVERTER_OPT_FILL_BORDER, DEFAULT_OPT_FILL_BORDER)
|
||||
|
@ -546,6 +552,11 @@ get_opt_str (GstVideoConverter * convert, const gchar * opt, const gchar * def)
|
|||
DEFAULT_OPT_RESAMPLER_METHOD)
|
||||
#define GET_OPT_RESAMPLER_TAPS(c) get_opt_uint(c, \
|
||||
GST_VIDEO_CONVERTER_OPT_RESAMPLER_TAPS, DEFAULT_OPT_RESAMPLER_TAPS)
|
||||
#define GET_OPT_DITHER_METHOD(c) get_opt_enum(c, \
|
||||
GST_VIDEO_CONVERTER_OPT_DITHER_METHOD, GST_TYPE_VIDEO_DITHER_METHOD, \
|
||||
DEFAULT_OPT_DITHER_METHOD)
|
||||
#define GET_OPT_DITHER_QUANTIZATION(c) get_opt_uint(c, \
|
||||
GST_VIDEO_CONVERTER_OPT_DITHER_QUANTIZATION, DEFAULT_OPT_DITHER_QUANTIZATION)
|
||||
|
||||
#define CHECK_MATRIX_FULL(c) (!g_strcmp0(GET_OPT_MATRIX_MODE(c), "full"))
|
||||
#define CHECK_MATRIX_NO_YUV(c) (!g_strcmp0(GET_OPT_MATRIX_MODE(c), "no-yuv"))
|
||||
|
@ -1419,6 +1430,65 @@ chain_downsample (GstVideoConverter * convert, GstLineCache * prev)
|
|||
return prev;
|
||||
}
|
||||
|
||||
static GstLineCache *
|
||||
chain_dither (GstVideoConverter * convert, GstLineCache * prev)
|
||||
{
|
||||
gint i;
|
||||
gboolean do_dither = FALSE;
|
||||
GstVideoDitherFlags flags = 0;
|
||||
GstVideoDitherMethod method;
|
||||
guint quant[4], target_quant;
|
||||
|
||||
method = GET_OPT_DITHER_METHOD (convert);
|
||||
target_quant = GET_OPT_DITHER_QUANTIZATION (convert);
|
||||
GST_DEBUG ("method %d, target-quantization %d", method, target_quant);
|
||||
|
||||
if (convert->pack_pal) {
|
||||
quant[0] = 47;
|
||||
quant[1] = 47;
|
||||
quant[2] = 47;
|
||||
quant[3] = 1;
|
||||
do_dither = TRUE;
|
||||
} else {
|
||||
for (i = 0; i < GST_VIDEO_MAX_COMPONENTS; i++) {
|
||||
gint depth;
|
||||
|
||||
depth = convert->out_info.finfo->depth[i];
|
||||
|
||||
if (depth == 0) {
|
||||
quant[i] = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (convert->current_bits >= depth) {
|
||||
quant[i] = 1 << (convert->current_bits - depth);
|
||||
if (target_quant > quant[i]) {
|
||||
flags |= GST_VIDEO_DITHER_FLAG_QUANTIZE;
|
||||
quant[i] = target_quant;
|
||||
}
|
||||
do_dither = TRUE;
|
||||
} else {
|
||||
quant[i] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (do_dither) {
|
||||
GST_DEBUG ("chain dither");
|
||||
|
||||
convert->dither = gst_video_dither_new (method,
|
||||
flags, convert->pack_format, quant, convert->current_width);
|
||||
|
||||
prev = convert->dither_lines = gst_line_cache_new (prev);
|
||||
prev->write_input = TRUE;
|
||||
prev->pass_alloc = TRUE;
|
||||
prev->n_lines = 1;
|
||||
prev->stride = convert->current_pstride * convert->current_width;
|
||||
gst_line_cache_set_need_line_func (prev, do_dither_lines, convert, NULL);
|
||||
}
|
||||
return prev;
|
||||
}
|
||||
|
||||
static GstLineCache *
|
||||
chain_pack (GstVideoConverter * convert, GstLineCache * prev)
|
||||
{
|
||||
|
@ -1536,9 +1606,7 @@ gst_video_converter_new (GstVideoInfo * in_info, GstVideoInfo * out_info,
|
|||
convert->out_info = *out_info;
|
||||
|
||||
/* default config */
|
||||
convert->config = gst_structure_new ("GstVideoConverter",
|
||||
GST_VIDEO_CONVERTER_OPT_DITHER_METHOD, GST_TYPE_VIDEO_DITHER_METHOD,
|
||||
GST_VIDEO_DITHER_NONE, NULL);
|
||||
convert->config = gst_structure_new_empty ("GstVideoConverter");
|
||||
if (config)
|
||||
gst_video_converter_set_config (convert, config);
|
||||
|
||||
|
@ -1616,12 +1684,13 @@ gst_video_converter_new (GstVideoInfo * in_info, GstVideoInfo * out_info,
|
|||
prev = chain_convert_to_YUV (convert, prev);
|
||||
/* downsample chroma */
|
||||
prev = chain_downsample (convert, prev);
|
||||
/* dither */
|
||||
prev = chain_dither (convert, prev);
|
||||
/* pack into final format */
|
||||
convert->pack_lines = chain_pack (convert, prev);
|
||||
|
||||
width = MAX (convert->in_maxwidth, convert->out_maxwidth);
|
||||
width += convert->out_x;
|
||||
convert->errline = g_malloc0 (sizeof (guint16) * width * 4);
|
||||
|
||||
if (convert->fill_border && (convert->out_height < convert->out_maxheight ||
|
||||
convert->out_width < convert->out_maxwidth)) {
|
||||
|
@ -1711,10 +1780,13 @@ gst_video_converter_free (GstVideoConverter * convert)
|
|||
if (convert->downsample_lines)
|
||||
gst_line_cache_free (convert->downsample_lines);
|
||||
|
||||
if (convert->dither)
|
||||
gst_video_dither_free (convert->dither);
|
||||
|
||||
g_free (convert->gamma_dec.gamma_table);
|
||||
g_free (convert->gamma_enc.gamma_table);
|
||||
|
||||
g_free (convert->errline);
|
||||
g_free (convert->tmpline);
|
||||
g_free (convert->borderline);
|
||||
|
||||
if (convert->config)
|
||||
|
@ -1723,46 +1795,6 @@ gst_video_converter_free (GstVideoConverter * convert)
|
|||
g_slice_free (GstVideoConverter, convert);
|
||||
}
|
||||
|
||||
static void
|
||||
video_dither_verterr (GstVideoConverter * convert, guint16 * pixels, int j)
|
||||
{
|
||||
int i;
|
||||
guint16 *errline = convert->errline;
|
||||
unsigned int mask = 0xff;
|
||||
|
||||
for (i = 0; i < 4 * convert->in_width; i++) {
|
||||
int x = pixels[i] + errline[i];
|
||||
if (x > 65535)
|
||||
x = 65535;
|
||||
pixels[i] = x;
|
||||
errline[i] = x & mask;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
video_dither_halftone (GstVideoConverter * convert, guint16 * pixels, int j)
|
||||
{
|
||||
int i;
|
||||
static guint16 halftone[8][8] = {
|
||||
{0, 128, 32, 160, 8, 136, 40, 168},
|
||||
{192, 64, 224, 96, 200, 72, 232, 104},
|
||||
{48, 176, 16, 144, 56, 184, 24, 152},
|
||||
{240, 112, 208, 80, 248, 120, 216, 88},
|
||||
{12, 240, 44, 172, 4, 132, 36, 164},
|
||||
{204, 76, 236, 108, 196, 68, 228, 100},
|
||||
{60, 188, 28, 156, 52, 180, 20, 148},
|
||||
{252, 142, 220, 92, 244, 116, 212, 84}
|
||||
};
|
||||
|
||||
for (i = 0; i < convert->in_width * 4; i++) {
|
||||
int x;
|
||||
x = pixels[i] + halftone[(i >> 2) & 7][j & 7];
|
||||
if (x > 65535)
|
||||
x = 65535;
|
||||
pixels[i] = x;
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
copy_config (GQuark field_id, const GValue * value, gpointer user_data)
|
||||
{
|
||||
|
@ -1795,42 +1827,13 @@ gboolean
|
|||
gst_video_converter_set_config (GstVideoConverter * convert,
|
||||
GstStructure * config)
|
||||
{
|
||||
gint dither;
|
||||
gboolean res = TRUE;
|
||||
|
||||
g_return_val_if_fail (convert != NULL, FALSE);
|
||||
g_return_val_if_fail (config != NULL, FALSE);
|
||||
|
||||
if (gst_structure_get_enum (config, GST_VIDEO_CONVERTER_OPT_DITHER_METHOD,
|
||||
GST_TYPE_VIDEO_DITHER_METHOD, &dither)) {
|
||||
gboolean update = TRUE;
|
||||
|
||||
switch (dither) {
|
||||
case GST_VIDEO_DITHER_NONE:
|
||||
convert->dither16 = NULL;
|
||||
break;
|
||||
case GST_VIDEO_DITHER_VERTERR:
|
||||
convert->dither16 = video_dither_verterr;
|
||||
break;
|
||||
case GST_VIDEO_DITHER_HALFTONE:
|
||||
convert->dither16 = video_dither_halftone;
|
||||
break;
|
||||
default:
|
||||
update = FALSE;
|
||||
break;
|
||||
}
|
||||
if (update)
|
||||
gst_structure_set (convert->config, GST_VIDEO_CONVERTER_OPT_DITHER_METHOD,
|
||||
GST_TYPE_VIDEO_DITHER_METHOD, dither, NULL);
|
||||
else
|
||||
res = FALSE;
|
||||
}
|
||||
if (res)
|
||||
gst_structure_foreach (config, copy_config, convert);
|
||||
|
||||
gst_structure_free (config);
|
||||
|
||||
return res;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1964,7 +1967,6 @@ video_converter_compute_resample (GstVideoConverter * convert)
|
|||
src, 0, frame->data, frame->info.stride, \
|
||||
frame->info.chroma_site, line, width);
|
||||
|
||||
|
||||
static gpointer
|
||||
get_dest_line (GstLineCache * cache, gint idx, gpointer user_data)
|
||||
{
|
||||
|
@ -2151,9 +2153,8 @@ do_convert_lines (GstLineCache * cache, gint out_line, gint in_line,
|
|||
GST_DEBUG ("matrix line %d %p", in_line, srcline);
|
||||
data->matrix_func (data, srcline);
|
||||
}
|
||||
if (convert->dither16)
|
||||
convert->dither16 (convert, srcline, in_line);
|
||||
|
||||
/* FIXME, dither here */
|
||||
if (out_bits == 8) {
|
||||
GST_DEBUG ("16->8 line %d %p->%p", in_line, srcline, destline);
|
||||
video_orc_convert_u16_to_u8 (destline, srcline, width * 4);
|
||||
|
@ -2221,6 +2222,26 @@ do_downsample_lines (GstLineCache * cache, gint out_line, gint in_line,
|
|||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
do_dither_lines (GstLineCache * cache, gint out_line, gint in_line,
|
||||
gpointer user_data)
|
||||
{
|
||||
GstVideoConverter *convert = user_data;
|
||||
gpointer *lines, destline;
|
||||
|
||||
lines = gst_line_cache_get_lines (cache->prev, out_line, in_line, 1);
|
||||
destline = lines[0];
|
||||
|
||||
if (convert->dither) {
|
||||
GST_DEBUG ("Dither line %d %p", in_line, destline);
|
||||
gst_video_dither_line (convert->dither, destline, 0, out_line,
|
||||
convert->out_width);
|
||||
}
|
||||
gst_line_cache_add_line (cache, in_line, destline);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
video_converter_generic (GstVideoConverter * convert, const GstVideoFrame * src,
|
||||
GstVideoFrame * dest)
|
||||
|
@ -2338,8 +2359,8 @@ convert_I420_YUY2 (GstVideoConverter * convert, const GstVideoFrame * src,
|
|||
|
||||
/* now handle last line */
|
||||
if (height & 1) {
|
||||
UNPACK_FRAME (src, convert->errline, height - 1, convert->in_x, width);
|
||||
PACK_FRAME (dest, convert->errline, height - 1, width);
|
||||
UNPACK_FRAME (src, convert->tmpline, height - 1, convert->in_x, width);
|
||||
PACK_FRAME (dest, convert->tmpline, height - 1, width);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2366,8 +2387,8 @@ convert_I420_UYVY (GstVideoConverter * convert, const GstVideoFrame * src,
|
|||
|
||||
/* now handle last line */
|
||||
if (height & 1) {
|
||||
UNPACK_FRAME (src, convert->errline, height - 1, convert->in_x, width);
|
||||
PACK_FRAME (dest, convert->errline, height - 1, width);
|
||||
UNPACK_FRAME (src, convert->tmpline, height - 1, convert->in_x, width);
|
||||
PACK_FRAME (dest, convert->tmpline, height - 1, width);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2393,8 +2414,8 @@ convert_I420_AYUV (GstVideoConverter * convert, const GstVideoFrame * src,
|
|||
|
||||
/* now handle last line */
|
||||
if (height & 1) {
|
||||
UNPACK_FRAME (src, convert->errline, height - 1, convert->in_x, width);
|
||||
PACK_FRAME (dest, convert->errline, height - 1, width);
|
||||
UNPACK_FRAME (src, convert->tmpline, height - 1, convert->in_x, width);
|
||||
PACK_FRAME (dest, convert->tmpline, height - 1, width);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2443,8 +2464,8 @@ convert_I420_Y444 (GstVideoConverter * convert, const GstVideoFrame * src,
|
|||
|
||||
/* now handle last line */
|
||||
if (height & 1) {
|
||||
UNPACK_FRAME (src, convert->errline, height - 1, convert->in_x, width);
|
||||
PACK_FRAME (dest, convert->errline, height - 1, width);
|
||||
UNPACK_FRAME (src, convert->tmpline, height - 1, convert->in_x, width);
|
||||
PACK_FRAME (dest, convert->tmpline, height - 1, width);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2470,8 +2491,8 @@ convert_YUY2_I420 (GstVideoConverter * convert, const GstVideoFrame * src,
|
|||
|
||||
/* now handle last line */
|
||||
if (height & 1) {
|
||||
UNPACK_FRAME (src, convert->errline, height - 1, convert->in_x, width);
|
||||
PACK_FRAME (dest, convert->errline, height - 1, width);
|
||||
UNPACK_FRAME (src, convert->tmpline, height - 1, convert->in_x, width);
|
||||
PACK_FRAME (dest, convert->tmpline, height - 1, width);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2538,8 +2559,8 @@ convert_UYVY_I420 (GstVideoConverter * convert, const GstVideoFrame * src,
|
|||
|
||||
/* now handle last line */
|
||||
if (height & 1) {
|
||||
UNPACK_FRAME (src, convert->errline, height - 1, convert->in_x, width);
|
||||
PACK_FRAME (dest, convert->errline, height - 1, width);
|
||||
UNPACK_FRAME (src, convert->tmpline, height - 1, convert->in_x, width);
|
||||
PACK_FRAME (dest, convert->tmpline, height - 1, width);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2690,8 +2711,8 @@ convert_Y42B_I420 (GstVideoConverter * convert, const GstVideoFrame * src,
|
|||
|
||||
/* now handle last line */
|
||||
if (height & 1) {
|
||||
UNPACK_FRAME (src, convert->errline, height - 1, convert->in_x, width);
|
||||
PACK_FRAME (dest, convert->errline, height - 1, width);
|
||||
UNPACK_FRAME (src, convert->tmpline, height - 1, convert->in_x, width);
|
||||
PACK_FRAME (dest, convert->tmpline, height - 1, width);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2781,8 +2802,8 @@ convert_Y444_I420 (GstVideoConverter * convert, const GstVideoFrame * src,
|
|||
|
||||
/* now handle last line */
|
||||
if (height & 1) {
|
||||
UNPACK_FRAME (src, convert->errline, height - 1, convert->in_x, width);
|
||||
PACK_FRAME (dest, convert->errline, height - 1, width);
|
||||
UNPACK_FRAME (src, convert->tmpline, height - 1, convert->in_x, width);
|
||||
PACK_FRAME (dest, convert->tmpline, height - 1, width);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3070,6 +3091,9 @@ video_converter_lookup_fastpath (GstVideoConverter * convert)
|
|||
if (width != convert->out_width || height != convert->out_height)
|
||||
return FALSE;
|
||||
|
||||
if (GET_OPT_DITHER_QUANTIZATION (convert) != 1)
|
||||
return FALSE;
|
||||
|
||||
/* we don't do gamma conversion in fastpath */
|
||||
in_transf = convert->in_info.colorimetry.transfer;
|
||||
out_transf = convert->out_info.colorimetry.transfer;
|
||||
|
@ -3113,6 +3137,7 @@ video_converter_lookup_fastpath (GstVideoConverter * convert)
|
|||
if (transforms[i].needs_color_matrix)
|
||||
video_converter_compute_matrix (convert);
|
||||
convert->convert = transforms[i].convert;
|
||||
convert->tmpline = g_malloc0 (sizeof (guint16) * (width + 8) * 4);
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,22 +24,6 @@
|
|||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
/**
|
||||
* GstVideoDitherMethod:
|
||||
* @GST_VIDEO_DITHER_NONE: no dithering
|
||||
* @GST_VIDEO_DITHER_VERTERR: propagate rounding errors downwards
|
||||
* @GST_VIDEO_DITHER_HALFTONE: Dither with halftone pattern
|
||||
* @GST_VIDEO_DITHER_HORIZERR: propagate rounding errors right
|
||||
*
|
||||
* Different dithering methods to use.
|
||||
*/
|
||||
typedef enum {
|
||||
GST_VIDEO_DITHER_NONE,
|
||||
GST_VIDEO_DITHER_VERTERR,
|
||||
GST_VIDEO_DITHER_HALFTONE,
|
||||
GST_VIDEO_DITHER_HORIZERR
|
||||
} GstVideoDitherMethod;
|
||||
|
||||
/**
|
||||
* GST_VIDEO_CONVERTER_OPT_RESAMPLER_METHOD:
|
||||
*
|
||||
|
@ -65,6 +49,15 @@ typedef enum {
|
|||
*/
|
||||
#define GST_VIDEO_CONVERTER_OPT_DITHER_METHOD "GstVideoConverter.dither-method"
|
||||
|
||||
/**
|
||||
* GST_VIDEO_CONVERTER_OPT_DITHER_QUANTIZATION:
|
||||
*
|
||||
* #G_TYPE_UINT, The quantization amount to dither to. Components will be
|
||||
* quantized to multiples of this value.
|
||||
* Default is 1
|
||||
*/
|
||||
#define GST_VIDEO_CONVERTER_OPT_DITHER_QUANTIZATION "GstVideoConverter.dither-quantization"
|
||||
|
||||
/**
|
||||
* GST_VIDEO_CONVERTER_OPT_SRC_X:
|
||||
*
|
||||
|
|
|
@ -477,7 +477,7 @@ gst_video_dither_line (GstVideoDither * dither, gpointer line, guint x, guint y,
|
|||
guint width)
|
||||
{
|
||||
g_return_if_fail (dither != NULL);
|
||||
g_return_if_fail (x + width < dither->width);
|
||||
g_return_if_fail (x + width <= dither->width);
|
||||
|
||||
if (dither->func)
|
||||
dither->func (dither, line, x, y, width);
|
||||
|
|
Loading…
Reference in a new issue