summaryrefslogtreecommitdiffstats
path: root/libhb
diff options
context:
space:
mode:
authorBradley Sepos <[email protected]>2017-05-27 08:46:11 -0400
committerBradley Sepos <[email protected]>2017-05-30 14:00:27 -0400
commit2b0cbe69a83848fb0b8012ed858fa3ab402889e3 (patch)
treebee9bd4d4c227f66fca893c047eca6ed5cfd401a /libhb
parentfcbc09c44a7ae5c43319df08bf449e097fe083b2 (diff)
libhb: Add LapSharp sharpening filter.
Diffstat (limited to 'libhb')
-rw-r--r--libhb/common.c4
-rw-r--r--libhb/common.h1
-rw-r--r--libhb/internal.h1
-rw-r--r--libhb/lapsharp.c282
-rw-r--r--libhb/param.c179
-rw-r--r--libhb/preset.c6
6 files changed, 472 insertions, 1 deletions
diff --git a/libhb/common.c b/libhb/common.c
index c8a2f22f7..71a8d0e26 100644
--- a/libhb/common.c
+++ b/libhb/common.c
@@ -3959,6 +3959,10 @@ hb_filter_object_t * hb_filter_get( int filter_id )
filter = &hb_filter_crop_scale;
break;
+ case HB_FILTER_LAPSHARP:
+ filter = &hb_filter_lapsharp;
+ break;
+
case HB_FILTER_UNSHARP:
filter = &hb_filter_unsharp;
break;
diff --git a/libhb/common.h b/libhb/common.h
index 562bbc362..a251365ac 100644
--- a/libhb/common.h
+++ b/libhb/common.h
@@ -1270,6 +1270,7 @@ enum
HB_FILTER_NLMEANS,
HB_FILTER_RENDER_SUB,
HB_FILTER_CROP_SCALE,
+ HB_FILTER_LAPSHARP,
HB_FILTER_UNSHARP,
HB_FILTER_ROTATE,
HB_FILTER_GRAYSCALE,
diff --git a/libhb/internal.h b/libhb/internal.h
index 0cffbdff4..4ffd6422a 100644
--- a/libhb/internal.h
+++ b/libhb/internal.h
@@ -465,6 +465,7 @@ extern hb_filter_object_t hb_filter_crop_scale;
extern hb_filter_object_t hb_filter_rotate;
extern hb_filter_object_t hb_filter_grayscale;
extern hb_filter_object_t hb_filter_pad;
+extern hb_filter_object_t hb_filter_lapsharp;
extern hb_filter_object_t hb_filter_unsharp;
extern hb_filter_object_t hb_filter_avfilter;
diff --git a/libhb/lapsharp.c b/libhb/lapsharp.c
new file mode 100644
index 000000000..2170247bb
--- /dev/null
+++ b/libhb/lapsharp.c
@@ -0,0 +1,282 @@
+/* lapsharp.c
+
+ Copyright (c) 2003-2017 HandBrake Team
+ This file is part of the HandBrake source code
+ Homepage: <http://handbrake.fr/>.
+ It may be used under the terms of the GNU General Public License v2.
+ For full terms see the file COPYING file or visit http://www.gnu.org/licenses/gpl-2.0.html
+ */
+
+#include "hb.h"
+
+#define LAPSHARP_STRENGTH_LUMA_DEFAULT 0.2
+#define LAPSHARP_STRENGTH_CHROMA_DEFAULT 0.2
+
+#define LAPSHARP_KERNELS 3
+#define LAPSHARP_KERNEL_LUMA_DEFAULT 2
+#define LAPSHARP_KERNEL_CHROMA_DEFAULT 2
+
+typedef struct
+{
+ double strength; // strength
+ int kernel; // which kernel to use; lapsharp_kernels[kernel]
+} lapsharp_plane_context_t;
+
+typedef struct {
+ const int *mem;
+ const double coef;
+ const int size;
+} lapsharp_kernel_t;
+
+// 4-neighbor laplacian kernel (lap)
+// Sharpens vertical and horizontal edges, less effective on diagonals
+static const int lapsharp_kernel_lap[] =
+{
+ 0, -1, 0,
+-1, 5, -1,
+ 0, -1, 0
+};
+
+// Isotropic laplacian kernel (isolap)
+// Minimial directionality, sharpens all edges similarly
+static const int lapsharp_kernel_isolap[] =
+{
+-1, -4, -1,
+-4, 25, -4,
+-1, -4, -1
+};
+
+// Laplacian of gaussian kernel (log)
+// Slightly better at noise rejection
+static const int lapsharp_kernel_log[] =
+{
+ 0, 0, -1, 0, 0,
+ 0, -1, -2, -1, 0,
+-1, -2, 21, -2, -1,
+ 0, -1, -2, -1, 0,
+ 0, 0, -1, 0, 0
+};
+
+static lapsharp_kernel_t lapsharp_kernels[] =
+{
+ { lapsharp_kernel_lap, (1.0 / 1), 3 },
+ { lapsharp_kernel_isolap, (1.0 / 5), 3 },
+ { lapsharp_kernel_log, (1.0 / 5), 5 }
+};
+
+struct hb_filter_private_s
+{
+ lapsharp_plane_context_t plane_ctx[3];
+};
+
+static int hb_lapsharp_init(hb_filter_object_t *filter,
+ hb_filter_init_t *init);
+
+static int hb_lapsharp_work(hb_filter_object_t *filter,
+ hb_buffer_t ** buf_in,
+ hb_buffer_t ** buf_out);
+
+static void hb_lapsharp_close(hb_filter_object_t *filter);
+
+static const char hb_lapsharp_template[] =
+ "y-strength=^"HB_FLOAT_REG"$:y-kernel=^"HB_ALL_REG"$:"
+ "cb-strength=^"HB_FLOAT_REG"$:cb-kernel=^"HB_ALL_REG"$:"
+ "cr-strength=^"HB_FLOAT_REG"$:cr-kernel=^"HB_ALL_REG"$";
+
+hb_filter_object_t hb_filter_lapsharp =
+{
+ .id = HB_FILTER_LAPSHARP,
+ .enforce_order = 1,
+ .name = "Sharpen (lapsharp)",
+ .settings = NULL,
+ .init = hb_lapsharp_init,
+ .work = hb_lapsharp_work,
+ .close = hb_lapsharp_close,
+ .settings_template = hb_lapsharp_template,
+};
+
+static void hb_lapsharp(const uint8_t *src,
+ uint8_t *dst,
+ const int width,
+ const int height,
+ const int stride,
+ lapsharp_plane_context_t * ctx)
+{
+ const lapsharp_kernel_t *kernel = &lapsharp_kernels[ctx->kernel];
+
+ // Sharpen using selected kernel
+ const int offset_min = -((kernel->size - 1) / 2);
+ const int offset_max = (kernel->size + 1) / 2;
+ const int stride_border = (stride - width) / 2;
+ int16_t pixel;
+ for (int y = 0; y < height; y++)
+ {
+ for (int x = 0; x < width; x++)
+ {
+ if ((y < offset_max) ||
+ (y > height - offset_max) ||
+ (x < stride_border + offset_max) ||
+ (x > width + stride_border - offset_max))
+ {
+ *(dst + stride*y + x) = *(src + stride*y + x);
+ continue;
+ }
+ pixel = 0;
+ for (int k = offset_min; k < offset_max; k++)
+ {
+ for (int j = offset_min; j < offset_max; j++)
+ {
+ pixel += kernel->mem[((j - offset_min) * kernel->size) + k - offset_min] * *(src + stride*(y + j) + (x + k));
+ }
+ }
+ pixel = (int16_t)(((pixel * kernel->coef) - *(src + stride*y + x)) * ctx->strength) + *(src + stride*y + x);
+ pixel = pixel < 0 ? 0 : pixel;
+ pixel = pixel > 255 ? 255 : pixel;
+ *(dst + stride*y + x) = (uint8_t)(pixel);
+ }
+ }
+}
+
+static int hb_lapsharp_init(hb_filter_object_t *filter,
+ hb_filter_init_t *init)
+{
+ filter->private_data = calloc(sizeof(struct hb_filter_private_s), 1);
+ hb_filter_private_t * pv = filter->private_data;
+
+ char *kernel_string[3];
+
+ // Mark parameters unset
+ for (int c = 0; c < 3; c++)
+ {
+ pv->plane_ctx[c].strength = -1;
+ pv->plane_ctx[c].kernel = -1;
+ kernel_string[c] = NULL;
+ }
+
+ // Read user parameters
+ if (filter->settings != NULL)
+ {
+ hb_dict_t * dict = filter->settings;
+ hb_dict_extract_double(&pv->plane_ctx[0].strength, dict, "y-strength");
+ hb_dict_extract_string(&kernel_string[0], dict, "y-kernel");
+
+ hb_dict_extract_double(&pv->plane_ctx[1].strength, dict, "cb-strength");
+ hb_dict_extract_string(&kernel_string[1], dict, "cb-kernel");
+
+ hb_dict_extract_double(&pv->plane_ctx[2].strength, dict, "cr-strength");
+ hb_dict_extract_string(&kernel_string[2], dict, "cr-kernel");
+ }
+
+ // Convert kernel user string to internal id
+ for (int c = 0; c < 3; c++)
+ {
+ lapsharp_plane_context_t * ctx = &pv->plane_ctx[c];
+
+ ctx->kernel = -1;
+
+ if (kernel_string[c] == NULL)
+ {
+ continue;
+ }
+
+ if (!strcasecmp(kernel_string[c], "lap"))
+ {
+ ctx->kernel = 0;
+ }
+ else if (!strcasecmp(kernel_string[c], "isolap"))
+ {
+ ctx->kernel = 1;
+ }
+ else if (!strcasecmp(kernel_string[c], "log"))
+ {
+ ctx->kernel = 2;
+ }
+
+ free(kernel_string[c]);
+ }
+
+ // Cascade values
+ // Cr not set; inherit Cb. Cb not set; inherit Y. Y not set; defaults.
+ for (int c = 1; c < 3; c++)
+ {
+ lapsharp_plane_context_t * prev_ctx = &pv->plane_ctx[c - 1];
+ lapsharp_plane_context_t * ctx = &pv->plane_ctx[c];
+
+ if (ctx->strength == -1) ctx->strength = prev_ctx->strength;
+ if (ctx->kernel == -1) ctx->kernel = prev_ctx->kernel;
+ }
+
+ for (int c = 0; c < 3; c++)
+ {
+ lapsharp_plane_context_t * ctx = &pv->plane_ctx[c];
+
+ // Replace unset values with defaults
+ if (ctx->strength == -1)
+ {
+ ctx->strength = c ? LAPSHARP_STRENGTH_CHROMA_DEFAULT :
+ LAPSHARP_STRENGTH_LUMA_DEFAULT;
+ }
+ if (ctx->kernel == -1)
+ {
+ ctx->kernel = c ? LAPSHARP_KERNEL_CHROMA_DEFAULT :
+ LAPSHARP_KERNEL_LUMA_DEFAULT;
+ }
+
+ // Sanitize
+ if (ctx->strength < 0) ctx->strength = 0;
+ if (ctx->strength > 1.5) ctx->strength = 1.5;
+ if ((ctx->kernel < 0) || (ctx->kernel >= LAPSHARP_KERNELS))
+ {
+ ctx->kernel = c ? LAPSHARP_KERNEL_CHROMA_DEFAULT : LAPSHARP_KERNEL_LUMA_DEFAULT;
+ }
+ }
+
+ return 0;
+}
+
+static void hb_lapsharp_close(hb_filter_object_t * filter)
+{
+ hb_filter_private_t *pv = filter->private_data;
+
+ if (pv == NULL)
+ {
+ return;
+ }
+
+ free(pv);
+ filter->private_data = NULL;
+}
+
+static int hb_lapsharp_work(hb_filter_object_t *filter,
+ hb_buffer_t ** buf_in,
+ hb_buffer_t ** buf_out)
+{
+ hb_filter_private_t *pv = filter->private_data;
+ hb_buffer_t *in = *buf_in, *out;
+
+ if (in->s.flags & HB_BUF_FLAG_EOF)
+ {
+ *buf_out = in;
+ *buf_in = NULL;
+ return HB_FILTER_DONE;
+ }
+
+ out = hb_frame_buffer_init(in->f.fmt, in->f.width, in->f.height);
+
+ int c;
+ for (c = 0; c < 3; c++)
+ {
+ lapsharp_plane_context_t * ctx = &pv->plane_ctx[c];
+ hb_lapsharp(in->plane[c].data,
+ out->plane[c].data,
+ in->plane[c].width,
+ in->plane[c].height,
+ in->plane[c].stride,
+ ctx);
+ }
+
+ out->s = in->s;
+ *buf_out = out;
+
+ return HB_FILTER_OK;
+}
diff --git a/libhb/param.c b/libhb/param.c
index ae8d99618..92c972ed5 100644
--- a/libhb/param.c
+++ b/libhb/param.c
@@ -90,6 +90,27 @@ static hb_filter_param_t unsharp_tunes[] =
{ 0, NULL, NULL, NULL }
};
+static hb_filter_param_t lapsharp_presets[] =
+{
+ { 1, "Custom", "custom", NULL },
+ { 2, "Ultralight", "ultralight", NULL },
+ { 3, "Light", "light", NULL },
+ { 4, "Medium", "medium", NULL },
+ { 5, "Strong", "strong", NULL },
+ { 6, "Stronger", "stronger", NULL },
+ { 0, NULL, NULL, NULL }
+};
+
+static hb_filter_param_t lapsharp_tunes[] =
+{
+ { 0, "None", "none", NULL },
+ { 1, "Film", "film", NULL },
+ { 2, "Grain", "grain", NULL },
+ { 3, "Animation", "animation", NULL },
+ { 4, "Sprite", "sprite", NULL },
+ { 0, NULL, NULL, NULL }
+};
+
static hb_filter_param_t detelecine_presets[] =
{
{ 0, "Off", "off", "disable=1" },
@@ -164,6 +185,9 @@ static filter_param_map_t param_map[] =
{ HB_FILTER_UNSHARP, unsharp_presets, unsharp_tunes,
sizeof(unsharp_presets) / sizeof(hb_filter_param_t) },
+ { HB_FILTER_LAPSHARP, lapsharp_presets, lapsharp_tunes,
+ sizeof(lapsharp_presets) / sizeof(hb_filter_param_t) },
+
{ HB_FILTER_DETELECINE, detelecine_presets, NULL,
sizeof(detelecine_presets) / sizeof(hb_filter_param_t) },
@@ -562,6 +586,158 @@ static hb_dict_t * generate_unsharp_settings(const char *preset,
return settings;
}
+static hb_dict_t * generate_lapsharp_settings(const char *preset,
+ const char *tune,
+ const char *custom)
+{
+ hb_dict_t * settings;
+
+ if (preset == NULL)
+ return NULL;
+
+ if (preset == NULL || !strcasecmp(preset, "custom"))
+ {
+ return hb_parse_filter_settings(custom);
+ }
+ if (!strcasecmp(preset, "ultralight") ||
+ !strcasecmp(preset, "light") ||
+ !strcasecmp(preset, "medium") ||
+ !strcasecmp(preset, "strong") ||
+ !strcasecmp(preset, "stronger"))
+ {
+ double strength[2];
+ const char *kernel_string[2];
+
+ if (tune == NULL || !strcasecmp(tune, "none"))
+ {
+ strength[0] = strength[1] = 0.2;
+ kernel_string[0] = kernel_string[1] = "isolap";
+ if (!strcasecmp(preset, "ultralight"))
+ {
+ strength[0] = strength[1] = 0.05;
+ }
+ else if (!strcasecmp(preset, "light"))
+ {
+ strength[0] = strength[1] = 0.1;
+ }
+ else if (!strcasecmp(preset, "strong"))
+ {
+ strength[0] = strength[1] = 0.3;
+ }
+ else if (!strcasecmp(preset, "stronger"))
+ {
+ strength[0] = strength[1] = 0.5;
+ }
+ }
+ else if (!strcasecmp(tune, "film"))
+ {
+ strength[0] = 0.2; strength[1] = 0.12;
+ kernel_string[0] = kernel_string[1] = "isolap";
+ if (!strcasecmp(preset, "ultralight"))
+ {
+ strength[0] = 0.05; strength[1] = 0.03;
+ }
+ else if (!strcasecmp(preset, "light"))
+ {
+ strength[0] = 0.1; strength[1] = 0.06;
+ }
+ else if (!strcasecmp(preset, "strong"))
+ {
+ strength[0] = 0.3; strength[1] = 0.2;
+ }
+ else if (!strcasecmp(preset, "stronger"))
+ {
+ strength[0] = 0.5; strength[1] = 0.3;
+ }
+ }
+ else if (!strcasecmp(tune, "grain"))
+ {
+ strength[0] = 0.2; strength[1] = 0.1;
+ kernel_string[0] = kernel_string[1] = "log";
+ if (!strcasecmp(preset, "ultralight"))
+ {
+ strength[0] = 0.05; strength[1] = 0.025;
+ }
+ else if (!strcasecmp(preset, "light"))
+ {
+ strength[0] = 0.1; strength[1] = 0.05;
+ }
+ else if (!strcasecmp(preset, "strong"))
+ {
+ strength[0] = 0.3; strength[1] = 0.15;
+ }
+ else if (!strcasecmp(preset, "stronger"))
+ {
+ strength[0] = 0.5; strength[1] = 0.25;
+ }
+ }
+ else if (!strcasecmp(tune, "animation"))
+ {
+ strength[0] = 0.15; strength[1] = 0.09;
+ kernel_string[0] = kernel_string[1] = "isolap";
+ if (!strcasecmp(preset, "ultralight"))
+ {
+ strength[0] = 0.0375; strength[1] = 0.0225;
+ }
+ else if (!strcasecmp(preset, "light"))
+ {
+ strength[0] = 0.075; strength[1] = 0.05625;
+ }
+ else if (!strcasecmp(preset, "strong"))
+ {
+ strength[0] = 0.225; strength[1] = 0.15;
+ }
+ else if (!strcasecmp(preset, "stronger"))
+ {
+ strength[0] = 0.375; strength[1] = 0.225;
+ }
+ }
+ else if (!strcasecmp(tune, "sprite"))
+ {
+ strength[0] = strength[1] = 0.15;
+ kernel_string[0] = kernel_string[1] = "lap";
+ if (!strcasecmp(preset, "ultralight"))
+ {
+ strength[0] = strength[1] = 0.0375;
+ }
+ else if (!strcasecmp(preset, "light"))
+ {
+ strength[0] = strength[1] = 0.075;
+ }
+ else if (!strcasecmp(preset, "strong"))
+ {
+ strength[0] = strength[1] = 0.225;
+ }
+ else if (!strcasecmp(preset, "stronger"))
+ {
+ strength[0] = strength[1] = 0.375;
+ }
+ }
+ else
+ {
+ fprintf(stderr, "Unrecognized lapsharp tune (%s).\n", tune);
+ return NULL;
+ }
+
+ settings = hb_dict_init();
+ hb_dict_set(settings, "y-strength", hb_value_double(strength[0]));
+ hb_dict_set(settings, "y-kernel", hb_value_string(kernel_string[0]));
+
+ hb_dict_set(settings, "cb-strength", hb_value_double(strength[1]));
+ hb_dict_set(settings, "cb-kernel", hb_value_string(kernel_string[1]));
+ }
+ else
+ {
+ settings = hb_parse_filter_settings(preset);
+ if (tune != NULL)
+ {
+ fprintf(stderr, "Custom lapsharp parameters specified; ignoring lapsharp tune (%s).\n", tune);
+ }
+ }
+
+ return settings;
+}
+
int hb_validate_param_string(const char *regex_pattern, const char *param_string)
{
regex_t regex_temp;
@@ -822,6 +998,9 @@ hb_generate_filter_settings(int filter_id, const char *preset, const char *tune,
case HB_FILTER_NLMEANS:
settings = generate_nlmeans_settings(preset, tune, custom);
break;
+ case HB_FILTER_LAPSHARP:
+ settings = generate_lapsharp_settings(preset, tune, custom);
+ break;
case HB_FILTER_UNSHARP:
settings = generate_unsharp_settings(preset, tune, custom);
break;
diff --git a/libhb/preset.c b/libhb/preset.c
index 08f7d186c..6c32eb9f6 100644
--- a/libhb/preset.c
+++ b/libhb/preset.c
@@ -1385,7 +1385,11 @@ int hb_preset_apply_filters(const hb_dict_t *preset, hb_dict_t *job_dict)
strcasecmp(sharpen_filter, "off"))
{
int filter_id;
- if (!strcasecmp(sharpen_filter, "unsharp"))
+ if (!strcasecmp(sharpen_filter, "lapsharp"))
+ {
+ filter_id = HB_FILTER_LAPSHARP;
+ }
+ else if (!strcasecmp(sharpen_filter, "unsharp"))
{
filter_id = HB_FILTER_UNSHARP;
}