diff options
author | Bradley Sepos <[email protected]> | 2017-05-27 08:46:11 -0400 |
---|---|---|
committer | Bradley Sepos <[email protected]> | 2017-05-30 14:00:27 -0400 |
commit | 2b0cbe69a83848fb0b8012ed858fa3ab402889e3 (patch) | |
tree | bee9bd4d4c227f66fca893c047eca6ed5cfd401a /libhb | |
parent | fcbc09c44a7ae5c43319df08bf449e097fe083b2 (diff) |
libhb: Add LapSharp sharpening filter.
Diffstat (limited to 'libhb')
-rw-r--r-- | libhb/common.c | 4 | ||||
-rw-r--r-- | libhb/common.h | 1 | ||||
-rw-r--r-- | libhb/internal.h | 1 | ||||
-rw-r--r-- | libhb/lapsharp.c | 282 | ||||
-rw-r--r-- | libhb/param.c | 179 | ||||
-rw-r--r-- | libhb/preset.c | 6 |
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; } |