summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNiklas Haas <git@nand.wakku.to>2015-01-06 10:47:26 +0100
committerwm4 <wm4@nowhere>2015-01-09 03:18:21 +0100
commit286340d7d09f72f471d5d1bddcf4d242ed22f4ed (patch)
treee14517963f60820eb38a58dac0d97556dc3e50d0
parent33dd9147ae859b712c52eecfadc8ff97e3d07575 (diff)
downloadmpv-286340d7d09f72f471d5d1bddcf4d242ed22f4ed.tar.bz2
mpv-286340d7d09f72f471d5d1bddcf4d242ed22f4ed.tar.xz
video: Add sigmoidal upscaling to avoid ringing artifacts
This avoids issues when upscaling directly in linear light, and is the recommended way to upscale images according to imagemagick. The default slope of 6.5 offers a reasonable compromise between ringing artifacts eliminated and ringing artifacts introduced by sigmoid-upscaling. Same goes for the default center of 0.75.
-rw-r--r--DOCS/man/vo.rst14
-rw-r--r--video/out/gl_video.c43
-rw-r--r--video/out/gl_video.h3
-rw-r--r--video/out/gl_video_shaders.glsl11
4 files changed, 69 insertions, 2 deletions
diff --git a/DOCS/man/vo.rst b/DOCS/man/vo.rst
index c296e705be..0566fd6817 100644
--- a/DOCS/man/vo.rst
+++ b/DOCS/man/vo.rst
@@ -457,6 +457,18 @@ Available video output drivers are:
This is automatically disabled for anamorphic video, because this
feature doesn't work correctly with this.
+ ``sigmoid-upscaling``
+ When upscaling in linear light, use a sigmoidal color transform
+ to avoid emphasizing ringing artifacts.
+
+ ``sigmoid-center``
+ The center of the sigmoid curve used for ``sigmoid-upscaling``, must
+ be a float between 0.0 and 1.0. Defaults to 0.75 if not specified.
+
+ ``sigmoid-slope``
+ The slope of the sigmoid curve used for ``sigmoid-upscaling``, must
+ be a float between 1.0 and 20.0. Defaults to 6.5 if not specified.
+
``no-npot``
Force use of power-of-2 texture sizes. For debugging only.
Borders will be distorted due to filtering.
@@ -598,7 +610,7 @@ Available video output drivers are:
This is equivalent to::
- --vo=opengl:lscale=spline36:dither-depth=auto:fbo-format=rgba16:fancy-downscaling
+ --vo=opengl:lscale=spline36:dither-depth=auto:fbo-format=rgba16:fancy-downscaling:sigmoid-upscaling
Note that some cheaper LCDs do dithering that gravely interferes with
``opengl``'s dithering. Disabling dithering with ``dither-depth=no`` helps.
diff --git a/video/out/gl_video.c b/video/out/gl_video.c
index 5b3fe7b34d..c6077c6ad2 100644
--- a/video/out/gl_video.c
+++ b/video/out/gl_video.c
@@ -181,6 +181,9 @@ struct gl_video {
// state for luma (0) and chroma (1) scalers
struct scaler scalers[2];
+ // true if scaler is currently upscaling
+ bool upscaling;
+
struct mp_csp_equalizer video_eq;
struct mp_image_params image_params;
@@ -319,6 +322,8 @@ const struct gl_video_opts gl_video_opts_def = {
.dither_size = 6,
.fbo_format = GL_RGBA,
.scale_sep = 1,
+ .sigmoid_center = 0.75,
+ .sigmoid_slope = 6.5,
.scalers = { "bilinear", "bilinear" },
.scaler_params = {{NAN, NAN}, {NAN, NAN}},
.scaler_radius = {NAN, NAN},
@@ -333,6 +338,9 @@ const struct gl_video_opts gl_video_opts_hq_def = {
.fbo_format = GL_RGBA16,
.scale_sep = 1,
.fancy_downscaling = 1,
+ .sigmoid_center = 0.75,
+ .sigmoid_slope = 6.5,
+ .sigmoid_upscaling = 1,
.scalers = { "spline36", "bilinear" },
.scaler_params = {{NAN, NAN}, {NAN, NAN}},
.scaler_radius = {NAN, NAN},
@@ -364,6 +372,9 @@ const struct m_sub_options gl_video_conf = {
OPT_FLOATRANGE("cradius", scaler_radius[1], 0, 1.0, 32.0),
OPT_FLAG("scaler-resizes-only", scaler_resizes_only, 0),
OPT_FLAG("fancy-downscaling", fancy_downscaling, 0),
+ OPT_FLAG("sigmoid-upscaling", sigmoid_upscaling, 0),
+ OPT_FLOATRANGE("sigmoid-center", sigmoid_center, 0, 0.0, 1.0),
+ OPT_FLOATRANGE("sigmoid-slope", sigmoid_slope, 0, 1.0, 20.0),
OPT_FLAG("indirect", indirect, 0),
OPT_FLAG("scale-sep", scale_sep, 0),
OPT_CHOICE("fbo-format", fbo_format, 0,
@@ -405,6 +416,7 @@ static void uninit_rendering(struct gl_video *p);
static void delete_shaders(struct gl_video *p);
static void check_gl_features(struct gl_video *p);
static bool init_format(int fmt, struct gl_video *init);
+static double get_scale_factor(struct gl_video *p);
static const struct fmt_entry *find_tex_format(GL *gl, int bytes_per_comp,
int n_channels)
@@ -683,6 +695,21 @@ static void update_uniforms(struct gl_video *p, GLuint program)
gl->Uniform1f(gl->GetUniformLocation(program, "conv_gamma"),
p->conv_gamma);
+ // Coefficients for the sigmoidal transform are taken from the
+ // formula here: http://www.imagemagick.org/Usage/color_mods/#sigmoidal
+ float sig_center = p->opts.sigmoid_center;
+ float sig_slope = p->opts.sigmoid_slope;
+
+ // This function needs to go through (0,0) and (1,1) so we compute the
+ // values at 1 and 0, and then scale/shift them, respectively.
+ float sig_offset = 1.0/(1+expf(sig_slope * sig_center));
+ float sig_scale = 1.0/(1+expf(sig_slope * (sig_center-1))) - sig_offset;
+
+ gl->Uniform1f(gl->GetUniformLocation(program, "sig_center"), sig_center);
+ gl->Uniform1f(gl->GetUniformLocation(program, "sig_slope"), sig_slope);
+ gl->Uniform1f(gl->GetUniformLocation(program, "sig_scale"), sig_scale);
+ gl->Uniform1f(gl->GetUniformLocation(program, "sig_offset"), sig_offset);
+
float gamma = p->opts.gamma ? p->opts.gamma : 1.0;
gl->Uniform3f(gl->GetUniformLocation(program, "inv_gamma"),
1.0 / (cparams.rgamma * gamma),
@@ -1034,6 +1061,12 @@ static void compile_shaders(struct gl_video *p)
gamma_fun = MP_CSP_TRC_BT_2020_EXACT;
}
+ bool use_linear_light = gamma_fun != MP_CSP_TRC_NONE || p->is_linear_rgb;
+
+ // Optionally transform to sigmoidal color space if requested, but only
+ // when upscaling in linear light
+ bool use_sigmoid = p->opts.sigmoid_upscaling && use_linear_light && p->upscaling;
+
// Figure out the right color spaces we need to convert, if any
enum mp_csp_prim prim_src = p->image_params.primaries, prim_dest;
if (use_cms) {
@@ -1114,11 +1147,13 @@ static void compile_shaders(struct gl_video *p)
gamma_fun == MP_CSP_TRC_BT_2020_EXACT);
shader_def_opt(&header_conv, "USE_LINEAR_LIGHT_SRGB",
gamma_fun == MP_CSP_TRC_SRGB);
+ shader_def_opt(&header_conv, "USE_SIGMOID", use_sigmoid);
if (p->opts.alpha_mode > 0 && p->has_alpha && p->plane_count > 3)
shader_def(&header_conv, "USE_ALPHA_PLANE", "3");
if (p->opts.alpha_mode == 2 && p->has_alpha)
shader_def(&header_conv, "USE_ALPHA_BLEND", "1");
+ shader_def_opt(&header_final, "USE_SIGMOID_INV", use_sigmoid);
shader_def_opt(&header_final, "USE_GAMMA_POW", p->opts.gamma > 0);
shader_def_opt(&header_final, "USE_CMS_MATRIX", use_cms_matrix);
shader_def_opt(&header_final, "USE_3DLUT", p->use_lut_3d);
@@ -1146,7 +1181,7 @@ static void compile_shaders(struct gl_video *p)
// Don't sample from input video textures before converting the input to
// linear light.
- if (use_input_gamma || use_conv_gamma || gamma_fun != MP_CSP_TRC_NONE)
+ if (use_input_gamma || use_conv_gamma || use_linear_light)
use_indirect = true;
// It doesn't make sense to scale the chroma with cscale in the 1. scale
@@ -1849,6 +1884,12 @@ static void check_resize(struct gl_video *p)
if (strcmp(p->scalers[n].name, expected_scaler(p, n)) != 0)
need_scaler_reinit = true;
}
+ if (p->upscaling != (get_scale_factor(p) > 1.0)) {
+ p->upscaling = !p->upscaling;
+ // Switching between upscaling and downscaling also requires sigmoid
+ // to be toggled
+ need_scaler_reinit |= p->opts.sigmoid_upscaling;
+ }
if (need_scaler_reinit) {
reinit_rendering(p);
} else if (need_scaler_update) {
diff --git a/video/out/gl_video.h b/video/out/gl_video.h
index 1ee0ec9213..37c819ecdb 100644
--- a/video/out/gl_video.h
+++ b/video/out/gl_video.h
@@ -39,6 +39,9 @@ struct gl_video_opts {
int approx_gamma;
int scale_sep;
int fancy_downscaling;
+ int sigmoid_upscaling;
+ float sigmoid_center;
+ float sigmoid_slope;
int scaler_resizes_only;
int npot;
int pbo;
diff --git a/video/out/gl_video_shaders.glsl b/video/out/gl_video_shaders.glsl
index 2f5940c26f..4037e42449 100644
--- a/video/out/gl_video_shaders.glsl
+++ b/video/out/gl_video_shaders.glsl
@@ -179,6 +179,10 @@ uniform mat2 dither_trafo;
uniform vec3 inv_gamma;
uniform float input_gamma;
uniform float conv_gamma;
+uniform float sig_center;
+uniform float sig_slope;
+uniform float sig_scale;
+uniform float sig_offset;
uniform float dither_quantization;
uniform float dither_center;
uniform float filter_param1_l;
@@ -425,7 +429,14 @@ void main() {
// The BT.2020 specification says Yc = 0.2627*R + 0.6780*G + 0.0593*B
color.g = (color.g - 0.2627*color.r - 0.0593*color.b)/0.6780;
#endif
+#ifdef USE_SIGMOID
+ color = sig_center - log(1.0/(color * sig_scale + sig_offset) - 1.0)/sig_slope;
+#endif
// Image upscaling happens roughly here
+#ifdef USE_SIGMOID_INV
+ // Inverse of USE_SIGMOID
+ color = (1.0/(1.0 + exp(sig_slope * (sig_center - color))) - sig_offset) / sig_scale;
+#endif
#ifdef USE_GAMMA_POW
// User-defined gamma correction factor (via the gamma sub-option)
color = pow(color, inv_gamma);