1 /* lapsharp.c
2
3 Copyright (c) 2003-2021 HandBrake Team
4 This file is part of the HandBrake source code
5 Homepage: <http://handbrake.fr/>.
6 It may be used under the terms of the GNU General Public License v2.
7 For full terms see the file COPYING file or visit http://www.gnu.org/licenses/gpl-2.0.html
8 */
9
10 #include "handbrake/handbrake.h"
11
12 #define LAPSHARP_STRENGTH_LUMA_DEFAULT 0.2
13 #define LAPSHARP_STRENGTH_CHROMA_DEFAULT 0.2
14
15 #define LAPSHARP_KERNELS 4
16 #define LAPSHARP_KERNEL_LUMA_DEFAULT 2
17 #define LAPSHARP_KERNEL_CHROMA_DEFAULT 2
18
19 typedef struct
20 {
21 double strength; // strength
22 int kernel; // which kernel to use; kernels[kernel]
23 } lapsharp_plane_context_t;
24
25 typedef struct {
26 const int *mem;
27 const int size;
28 const double coef;
29 } kernel_t;
30
31 // 4-neighbor Laplacian kernel (lap)
32 // Sharpens vertical and horizontal edges, less effective on diagonals
33 // size = 3, coef = 1.0
34 static const int kernel_lap[] =
35 {
36 0, -1, 0,
37 -1, 5, -1,
38 0, -1, 0
39 };
40
41 // Isotropic Laplacian kernel (isolap)
42 // Minimal directionality, sharpens all edges similarly
43 // size = 3, coef = 1.0 / 5
44 static const int kernel_isolap[] =
45 {
46 -1, -4, -1,
47 -4, 25, -4,
48 -1, -4, -1
49 };
50
51 // Laplacian of Gaussian kernel (log)
52 // Slight noise and grain rejection
53 // σ ~= 1
54 // size = 5, coef = 1.0 / 5
55 static const int kernel_log[] =
56 {
57 0, 0, -1, 0, 0,
58 0, -1, -2, -1, 0,
59 -1, -2, 21, -2, -1,
60 0, -1, -2, -1, 0,
61 0, 0, -1, 0, 0
62 };
63
64 // Isotropic Laplacian of Gaussian kernel (isolog)
65 // Minimal directionality, plus noise and grain rejection
66 // σ ~= 1.2
67 // size = 5, coef = 1.0 / 15
68 static const int kernel_isolog[] =
69 {
70 0, -1, -1, -1, 0,
71 -1, -3, -4, -3, -1,
72 -1, -4, 55, -4, -1,
73 -1, -3, -4, -3, -1,
74 0, -1, -1, -1, 0
75 };
76
77 static kernel_t kernels[] =
78 {
79 { kernel_lap, 3, 1.0 },
80 { kernel_isolap, 3, 1.0 / 5 },
81 { kernel_log, 5, 1.0 / 5 },
82 { kernel_isolog, 5, 1.0 / 15 }
83 };
84
85 struct hb_filter_private_s
86 {
87 lapsharp_plane_context_t plane_ctx[3];
88
89 hb_filter_init_t input;
90 hb_filter_init_t output;
91 };
92
93 static int hb_lapsharp_init(hb_filter_object_t *filter,
94 hb_filter_init_t *init);
95
96 static int hb_lapsharp_work(hb_filter_object_t *filter,
97 hb_buffer_t ** buf_in,
98 hb_buffer_t ** buf_out);
99
100 static void hb_lapsharp_close(hb_filter_object_t *filter);
101
102 static const char hb_lapsharp_template[] =
103 "y-strength=^"HB_FLOAT_REG"$:y-kernel=^"HB_ALL_REG"$:"
104 "cb-strength=^"HB_FLOAT_REG"$:cb-kernel=^"HB_ALL_REG"$:"
105 "cr-strength=^"HB_FLOAT_REG"$:cr-kernel=^"HB_ALL_REG"$";
106
107 hb_filter_object_t hb_filter_lapsharp =
108 {
109 .id = HB_FILTER_LAPSHARP,
110 .enforce_order = 1,
111 .name = "Sharpen (lapsharp)",
112 .settings = NULL,
113 .init = hb_lapsharp_init,
114 .work = hb_lapsharp_work,
115 .close = hb_lapsharp_close,
116 .settings_template = hb_lapsharp_template,
117 };
118
hb_lapsharp(const uint8_t * src,uint8_t * dst,const int width,const int height,const int stride,lapsharp_plane_context_t * ctx)119 static void hb_lapsharp(const uint8_t *src,
120 uint8_t *dst,
121 const int width,
122 const int height,
123 const int stride,
124 lapsharp_plane_context_t * ctx)
125 {
126 const kernel_t *kernel = &kernels[ctx->kernel];
127
128 // Sharpen using selected kernel
129 const int offset_min = -((kernel->size - 1) / 2);
130 const int offset_max = (kernel->size + 1) / 2;
131 const int stride_border = (stride - width) / 2;
132 int16_t pixel;
133 for (int y = 0; y < height; y++)
134 {
135 for (int x = 0; x < width; x++)
136 {
137 if ((y < offset_max) ||
138 (y > height - offset_max) ||
139 (x < stride_border + offset_max) ||
140 (x > width + stride_border - offset_max))
141 {
142 *(dst + stride*y + x) = *(src + stride*y + x);
143 continue;
144 }
145 pixel = 0;
146 for (int k = offset_min; k < offset_max; k++)
147 {
148 for (int j = offset_min; j < offset_max; j++)
149 {
150 pixel += kernel->mem[((j - offset_min) * kernel->size) + k - offset_min] * *(src + stride*(y + j) + (x + k));
151 }
152 }
153 pixel = (int16_t)(((pixel * kernel->coef) - *(src + stride*y + x)) * ctx->strength) + *(src + stride*y + x);
154 pixel = pixel < 0 ? 0 : pixel;
155 pixel = pixel > 255 ? 255 : pixel;
156 *(dst + stride*y + x) = (uint8_t)(pixel);
157 }
158 }
159 }
160
hb_lapsharp_init(hb_filter_object_t * filter,hb_filter_init_t * init)161 static int hb_lapsharp_init(hb_filter_object_t *filter,
162 hb_filter_init_t *init)
163 {
164 filter->private_data = calloc(sizeof(struct hb_filter_private_s), 1);
165 hb_filter_private_t * pv = filter->private_data;
166
167 char *kernel_string[3];
168
169 pv->input = *init;
170
171 // Mark parameters unset
172 for (int c = 0; c < 3; c++)
173 {
174 pv->plane_ctx[c].strength = -1;
175 pv->plane_ctx[c].kernel = -1;
176 kernel_string[c] = NULL;
177 }
178
179 // Read user parameters
180 if (filter->settings != NULL)
181 {
182 hb_dict_t * dict = filter->settings;
183 hb_dict_extract_double(&pv->plane_ctx[0].strength, dict, "y-strength");
184 hb_dict_extract_string(&kernel_string[0], dict, "y-kernel");
185
186 hb_dict_extract_double(&pv->plane_ctx[1].strength, dict, "cb-strength");
187 hb_dict_extract_string(&kernel_string[1], dict, "cb-kernel");
188
189 hb_dict_extract_double(&pv->plane_ctx[2].strength, dict, "cr-strength");
190 hb_dict_extract_string(&kernel_string[2], dict, "cr-kernel");
191 }
192
193 // Convert kernel user string to internal id
194 for (int c = 0; c < 3; c++)
195 {
196 lapsharp_plane_context_t * ctx = &pv->plane_ctx[c];
197
198 ctx->kernel = -1;
199
200 if (kernel_string[c] == NULL)
201 {
202 continue;
203 }
204
205 if (!strcasecmp(kernel_string[c], "lap"))
206 {
207 ctx->kernel = 0;
208 }
209 else if (!strcasecmp(kernel_string[c], "isolap"))
210 {
211 ctx->kernel = 1;
212 }
213 else if (!strcasecmp(kernel_string[c], "log"))
214 {
215 ctx->kernel = 2;
216 }
217 else if (!strcasecmp(kernel_string[c], "isolog"))
218 {
219 ctx->kernel = 3;
220 }
221
222 free(kernel_string[c]);
223 }
224
225 // Cascade values
226 // Cr not set; inherit Cb. Cb not set; inherit Y. Y not set; defaults.
227 for (int c = 1; c < 3; c++)
228 {
229 lapsharp_plane_context_t * prev_ctx = &pv->plane_ctx[c - 1];
230 lapsharp_plane_context_t * ctx = &pv->plane_ctx[c];
231
232 if (ctx->strength == -1) ctx->strength = prev_ctx->strength;
233 if (ctx->kernel == -1) ctx->kernel = prev_ctx->kernel;
234 }
235
236 for (int c = 0; c < 3; c++)
237 {
238 lapsharp_plane_context_t * ctx = &pv->plane_ctx[c];
239
240 // Replace unset values with defaults
241 if (ctx->strength == -1)
242 {
243 ctx->strength = c ? LAPSHARP_STRENGTH_CHROMA_DEFAULT :
244 LAPSHARP_STRENGTH_LUMA_DEFAULT;
245 }
246 if (ctx->kernel == -1)
247 {
248 ctx->kernel = c ? LAPSHARP_KERNEL_CHROMA_DEFAULT :
249 LAPSHARP_KERNEL_LUMA_DEFAULT;
250 }
251
252 // Sanitize
253 if (ctx->strength < 0) ctx->strength = 0;
254 if (ctx->strength > 1.5) ctx->strength = 1.5;
255 if ((ctx->kernel < 0) || (ctx->kernel >= LAPSHARP_KERNELS))
256 {
257 ctx->kernel = c ? LAPSHARP_KERNEL_CHROMA_DEFAULT : LAPSHARP_KERNEL_LUMA_DEFAULT;
258 }
259 }
260 pv->output = *init;
261
262 return 0;
263 }
264
hb_lapsharp_close(hb_filter_object_t * filter)265 static void hb_lapsharp_close(hb_filter_object_t * filter)
266 {
267 hb_filter_private_t *pv = filter->private_data;
268
269 if (pv == NULL)
270 {
271 return;
272 }
273
274 free(pv);
275 filter->private_data = NULL;
276 }
277
hb_lapsharp_work(hb_filter_object_t * filter,hb_buffer_t ** buf_in,hb_buffer_t ** buf_out)278 static int hb_lapsharp_work(hb_filter_object_t *filter,
279 hb_buffer_t ** buf_in,
280 hb_buffer_t ** buf_out)
281 {
282 hb_filter_private_t *pv = filter->private_data;
283 hb_buffer_t *in = *buf_in, *out;
284
285 if (in->s.flags & HB_BUF_FLAG_EOF)
286 {
287 *buf_out = in;
288 *buf_in = NULL;
289 return HB_FILTER_DONE;
290 }
291
292 hb_frame_buffer_mirror_stride(in);
293 out = hb_frame_buffer_init(pv->output.pix_fmt, in->f.width, in->f.height);
294 out->f.color_prim = pv->output.color_prim;
295 out->f.color_transfer = pv->output.color_transfer;
296 out->f.color_matrix = pv->output.color_matrix;
297 out->f.color_range = pv->output.color_range ;
298
299 int c;
300 for (c = 0; c < 3; c++)
301 {
302 lapsharp_plane_context_t * ctx = &pv->plane_ctx[c];
303 hb_lapsharp(in->plane[c].data,
304 out->plane[c].data,
305 in->plane[c].width,
306 in->plane[c].height,
307 in->plane[c].stride,
308 ctx);
309 }
310
311 out->s = in->s;
312 *buf_out = out;
313
314 return HB_FILTER_OK;
315 }
316