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