1 /*
2 filter_msharpen.c
3
4 Copyright (C) 1999-2000 Donal A. Graft
5 modified 2003 by William Hawkins for use with transcode
6
7 MSharpen Filter for VirtualDub -- performs sharpening
8 limited to edge areas of the frame.
9 Copyright (C) 1999-2000 Donald A. Graft
10
11 This program is free software; you can redistribute it and/or modify
12 it under the terms of the GNU General Public License as published by
13 the Free Software Foundation.
14
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
19
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23
24 The author can be contacted at:
25 Donald Graft
26 neuron2@home.com.
27 */
28
29 #define MOD_NAME "filter_msharpen.so"
30 #define MOD_VERSION "(1.0) (2003-07-17)"
31 #define MOD_CAP "VirtualDub's MSharpen Filter"
32 #define MOD_AUTHOR "Donald Graft, William Hawkins"
33
34 #include "transcode.h"
35 #include "filter.h"
36 #include "libtc/libtc.h"
37 #include "libtc/optstr.h"
38
39 #include "libtcvideo/tcvideo.h"
40
41 static vob_t *vob=NULL;
42
43 ///////////////////////////////////////////////////////////////////////////
44
45 typedef struct MyFilterData {
46 uint8_t *convertFrameIn;
47 uint8_t *convertFrameOut;
48 unsigned char *blur;
49 unsigned char *work;
50 int strength;
51 int threshold;
52 int mask;
53 int highq;
54 TCVHandle tcvhandle;
55 } MyFilterData;
56
57 static MyFilterData *mfd;
58
help_optstr(void)59 static void help_optstr(void)
60 {
61 tc_log_info(MOD_NAME, "(%s) help\n"
62 "* Overview\n"
63 " This plugin implements an unusual concept in spatial sharpening.\n"
64 " Although designed specifically for anime, it also works well with\n"
65 " normal video. The filter is very effective at sharpening important\n"
66 " edges without amplifying noise.\n"
67 "\n"
68 "* Options\n"
69 " * Strength 'strength' (0-255) [100]\n"
70 " This is the strength of the sharpening to be applied to the edge\n"
71 " detail areas. It is applied only to the edge detail areas as\n"
72 " determined by the 'threshold' parameter. Strength 255 is the\n"
73 " strongest sharpening.\n"
74 "\n"
75 " * Threshold 'threshold' (0-255) [10]\n"
76 " This parameter determines what is detected as edge detail and\n"
77 " thus sharpened. To see what edge detail areas will be sharpened,\n"
78 " use the 'mask' parameter.\n"
79 "\n"
80 " * Mask 'mask' (0-1) [0]\n"
81 " When set to true, the areas to be sharpened are shown in white\n"
82 " against a black background. Use this to set the level of detail to\n"
83 " be sharpened. This function also makes a basic edge detection filter.\n"
84 "\n"
85 " * HighQ 'highq' (0-1) [1]\n"
86 " This parameter lets you tradeoff speed for quality of detail\n"
87 " detection. Set it to true for the best detail detection. Set it to\n"
88 " false for maximum speed.\n"
89 , MOD_CAP);
90 }
91
tc_filter(frame_list_t * ptr_,char * options)92 int tc_filter(frame_list_t *ptr_, char *options)
93 {
94 vframe_list_t *ptr = (vframe_list_t *)ptr_;
95
96 //----------------------------------
97 //
98 // filter init
99 //
100 //----------------------------------
101
102
103 if(ptr->tag & TC_FILTER_INIT) {
104
105 int width, height;
106
107 if((vob = tc_get_vob())==NULL) return(-1);
108
109 mfd = tc_malloc(sizeof(MyFilterData));
110
111 if (!mfd) {
112 tc_log_error(MOD_NAME, "No memory at %d!\n", __LINE__);
113 return (-1);
114 }
115
116 height = vob->ex_v_height;
117 width = vob->ex_v_width;
118
119 /* default values */
120 mfd->strength = 100; /* A little bird told me this was a good value */
121 mfd->threshold = 10;
122 mfd->mask = TC_FALSE; /* not sure what this does at the moment */
123 mfd->highq = TC_TRUE; /* high Q or not? */
124
125 if (options != NULL) {
126
127 if(verbose) tc_log_info(MOD_NAME, "options=%s", options);
128
129 optstr_get (options, "strength", "%d", &mfd->strength);
130 optstr_get (options, "threshold", "%d", &mfd->threshold);
131 optstr_get (options, "highq", "%d", &mfd->highq);
132 optstr_get (options, "mask", "%d", &mfd->mask);
133
134 }
135
136 if (verbose > 1) {
137
138 tc_log_info (MOD_NAME, " MSharpen Filter Settings (%dx%d):", width,height);
139 tc_log_info (MOD_NAME, " strength = %d", mfd->strength);
140 tc_log_info (MOD_NAME, " threshold = %d", mfd->threshold);
141 tc_log_info (MOD_NAME, " highq = %d", mfd->highq);
142 tc_log_info (MOD_NAME, " mask = %d", mfd->mask);
143 }
144
145 if (options)
146 if ( optstr_lookup(options, "help") != NULL) {
147 help_optstr();
148 }
149
150 /* fetch memory */
151
152 mfd->blur = tc_malloc(4 * width * height);
153 if (!mfd->blur){
154 tc_log_error(MOD_NAME, "No memory at %d!\n", __LINE__);
155 return (-1);
156 }
157 mfd->work = tc_malloc(4 * width * height);
158 if (!mfd->work){
159 tc_log_error(MOD_NAME, "No memory at %d!\n", __LINE__);
160 return (-1);
161 }
162 mfd->convertFrameIn = tc_zalloc (width*height*4);
163 if (!mfd->convertFrameIn) {
164 tc_log_error(MOD_NAME, "No memory at %d!\n", __LINE__);
165 return (-1);
166 }
167
168 mfd->convertFrameOut = tc_zalloc (width*height*4);
169 if (!mfd->convertFrameOut) {
170 tc_log_error(MOD_NAME, "No memory at %d!\n", __LINE__);
171 return (-1);
172 }
173
174 mfd->tcvhandle = tcv_init();
175
176 // filter init ok.
177 if(verbose) tc_log_info(MOD_NAME, "%s %s", MOD_VERSION, MOD_CAP);
178
179 return 0;
180
181 } /* TC_FILTER_INIT */
182
183 if(ptr->tag & TC_FILTER_GET_CONFIG) {
184 if (options) {
185 char buf[256];
186 optstr_filter_desc (options, MOD_NAME, MOD_CAP, MOD_VERSION, MOD_AUTHOR, "VRYO", "1");
187 tc_snprintf (buf, sizeof(buf), "%d", mfd->strength);
188 optstr_param (options, "strength", "How much of the effect", "%d", buf, "0", "255");
189
190 tc_snprintf (buf, sizeof(buf), "%d", mfd->threshold);
191 optstr_param (options, "threshold",
192 "How close a pixel must be to the brightest or dimmest pixel to be mapped",
193 "%d", buf, "0", "255");
194 tc_snprintf (buf, sizeof(buf), "%d", mfd->highq);
195 optstr_param (options, "highq", "Tradeoff speed for quality of detail detection",
196 "%d", buf, "0", "1");
197 tc_snprintf (buf, sizeof(buf), "%d", mfd->mask);
198 optstr_param (options, "mask", "Areas to be sharpened are shown in white",
199 "%d", buf, "0", "1");
200
201 }
202 }
203
204
205 if(ptr->tag & TC_FILTER_CLOSE) {
206
207 if (mfd->convertFrameIn)
208 free (mfd->convertFrameIn);
209 mfd->convertFrameIn = NULL;
210
211 if (mfd->convertFrameOut)
212 free (mfd->convertFrameOut);
213 mfd->convertFrameOut = NULL;
214
215 if (mfd->blur)
216 free ( mfd->blur);
217 mfd->blur = NULL;
218 if (mfd->work)
219 free(mfd->work);
220 mfd->work = NULL;
221
222 tcv_free(mfd->tcvhandle);
223 mfd->tcvhandle = 0;
224
225 if (mfd)
226 free(mfd);
227 mfd = NULL;
228
229 return 0;
230
231 } /* TC_FILTER_CLOSE */
232
233 ///////////////////////////////////////////////////////////////////////////
234 if(ptr->tag & TC_POST_M_PROCESS && ptr->tag & TC_VIDEO) {
235
236
237 const int width = ptr->v_width;
238 const int height = ptr->v_height;
239 const long pitch = ptr->v_width*4;
240 int bwidth = 4 * width;
241 uint8_t *src;
242 uint8_t *dst;
243 uint8_t *srcpp, *srcp, *srcpn, *workp, *blurp, *blurpn, *dstp;
244 int r1, r2, r3, r4, g1, g2, g3, g4, b1, b2, b3, b4;
245 int x, y, max;
246 int strength = mfd->strength, invstrength = 255 - strength;
247 int threshold = mfd->threshold;
248 // const int srcpitch = ptr->v_width*4;
249 const int dstpitch = ptr->v_width*4;
250
251 tcv_convert(mfd->tcvhandle, ptr->video_buf, mfd->convertFrameIn,
252 ptr->v_width, ptr->v_height,
253 vob->im_v_codec==CODEC_YUV ? IMG_YUV_DEFAULT : IMG_RGB24,
254 IMG_BGRA32);
255
256 src = mfd->convertFrameIn;
257 dst = mfd->convertFrameOut;
258
259 /* Blur the source image prior to detail detection. Separate
260 dimensions for speed. */
261 /* Vertical. */
262 srcpp = src;
263 srcp = srcpp + pitch;
264 srcpn = srcp + pitch;
265 workp = mfd->work + bwidth;
266 for (y = 1; y < height - 1; y++)
267 {
268 for (x = 0; x < bwidth; x++)
269 {
270 workp[x] = (srcpp[x] + srcp[x] + srcpn[x]) / 3;
271 }
272 srcpp += pitch;
273 srcp += pitch;
274 srcpn += pitch;
275 workp += bwidth;
276 }
277
278 /* Horizontal. */
279 workp = mfd->work;
280 blurp = mfd->blur;
281 for (y = 0; y < height; y++)
282 {
283 for (x = 4; x < bwidth - 4; x++)
284 {
285 blurp[x] = (workp[x-4] + workp[x] + workp[x+4]) / 3;
286 }
287 workp += bwidth;
288 blurp += bwidth;
289 }
290
291 /* Fix up blur frame borders. */
292 srcp = src;
293 blurp = mfd->blur;
294 ac_memcpy(blurp, srcp, bwidth);
295 ac_memcpy(blurp + (height-1)*bwidth, srcp + (height-1)*pitch, bwidth);
296 for (y = 0; y < height; y++)
297 {
298 *((unsigned int *)(&blurp[0])) = *((unsigned int *)(&srcp[0]));
299 *((unsigned int *)(&blurp[bwidth-4])) = *((unsigned int *)(&srcp[bwidth-4]));
300 srcp += pitch;
301 blurp += bwidth;
302 }
303
304 /* Diagonal detail detection. */
305 blurp = mfd->blur;
306 blurpn = blurp + bwidth;
307 workp = mfd->work;
308 for (y = 0; y < height - 1; y++)
309 {
310 b1 = blurp[0];
311 g1 = blurp[1];
312 r1 = blurp[2];
313 b3 = blurpn[0];
314 g3 = blurpn[1];
315 r3 = blurpn[2];
316 for (x = 0; x < bwidth - 4; x+=4)
317 {
318 b2 = blurp[x+4];
319 g2 = blurp[x+5];
320 r2 = blurp[x+6];
321 b4 = blurpn[x+4];
322 g4 = blurpn[x+5];
323 r4 = blurpn[x+6];
324 if ((abs(b1 - b4) >= threshold) || (abs(g1 - g4) >= threshold) || (abs(r1 - r4) >= threshold) ||
325 (abs(b2 - b3) >= threshold) || (abs(g2 - g3) >= threshold) || (abs(g2 - g3) >= threshold))
326 {
327 *((unsigned int *)(&workp[x])) = 0xffffffff;
328 }
329 else
330 {
331 *((unsigned int *)(&workp[x])) = 0x0;
332 }
333 b1 = b2; b3 = b4;
334 g1 = g2; g3 = g4;
335 r1 = r2; r3 = r4;
336 }
337 workp += bwidth;
338 blurp += bwidth;
339 blurpn += bwidth;
340 }
341
342 if (mfd->highq == TC_TRUE)
343 // if (1)
344 {
345 /* Vertical detail detection. */
346 for (x = 0; x < bwidth; x+=4)
347 {
348 blurp = mfd->blur;
349 blurpn = blurp + bwidth;
350 workp = mfd->work;
351 b1 = blurp[x];
352 g1 = blurp[x+1];
353 r1 = blurp[x+2];
354 for (y = 0; y < height - 1; y++)
355 {
356 b2 = blurpn[x];
357 g2 = blurpn[x+1];
358 r2 = blurpn[x+2];
359 if (abs(b1 - b2) >= threshold || abs(g1 - g2) >= threshold || abs(r1 - r2) >= threshold)
360 {
361 *((unsigned int *)(&workp[x])) = 0xffffffff;
362 }
363 b1 = b2;
364 g1 = g2;
365 r1 = r2;
366 workp += bwidth;
367 blurp += bwidth;
368 blurpn += bwidth;
369 }
370 }
371
372 /* Horizontal detail detection. */
373 blurp = mfd->blur;
374 workp = mfd->work;
375 for (y = 0; y < height; y++)
376 {
377 b1 = blurp[0];
378 g1 = blurp[1];
379 r1 = blurp[2];
380 for (x = 0; x < bwidth - 4; x+=4)
381 {
382 b2 = blurp[x+4];
383 g2 = blurp[x+5];
384 r2 = blurp[x+6];
385 if (abs(b1 - b2) >= threshold || abs(g1 - g2) >= threshold || abs(r1 - r2) >= threshold)
386 {
387 *((unsigned int *)(&workp[x])) = 0xffffffff;
388 }
389 b1 = b2;
390 g1 = g2;
391 r1 = r2;
392 }
393 workp += bwidth;
394 blurp += bwidth;
395 }
396 }
397
398 /* Fix up detail map borders. */
399 memset(mfd->work + (height-1)*bwidth, 0, bwidth);
400 workp = mfd->work;
401 for (y = 0; y < height; y++)
402 {
403 *((unsigned int *)(&workp[bwidth-4])) = 0;
404 workp += bwidth;
405 }
406
407 if (mfd->mask == TC_TRUE)
408 {
409 workp = mfd->work;
410 dstp = dst;
411 for (y = 0; y < height; y++)
412 {
413 for (x = 0; x < bwidth; x++)
414 {
415 dstp[x] = workp[x];
416 }
417 workp += bwidth;
418 dstp = dstp + dstpitch;
419 }
420 return 0;
421 }
422
423 /* Fix up output frame borders. */
424 srcp = src;
425 dstp = dst;
426 ac_memcpy(dstp, srcp, bwidth);
427 ac_memcpy(dstp + (height-1)*pitch, srcp + (height-1)*pitch, bwidth);
428 for (y = 0; y < height; y++)
429 {
430 *((unsigned int *)(&dstp[0])) = *((unsigned int *)(&srcp[0]));
431 *((unsigned int *)(&dstp[bwidth-4])) = *((unsigned int *)(&srcp[bwidth-4]));
432 srcp += pitch;
433 dstp += pitch;
434 }
435
436 /* Now sharpen the edge areas and we're done! */
437 srcp = src + pitch;
438 dstp = dst + pitch;
439 workp = mfd->work + bwidth;
440 blurp = mfd->blur + bwidth;
441 for (y = 1; y < height - 1; y++)
442 {
443 for (x = 4; x < bwidth - 4; x+=4)
444 {
445 int xplus1 = x + 1, xplus2 = x + 2;
446
447 if (workp[x])
448 {
449 b4 = (4*(int)srcp[x] - 3*blurp[x]);
450 g4 = (4*(int)srcp[x+1] - 3*blurp[x+1]);
451 r4 = (4*(int)srcp[x+2] - 3*blurp[x+2]);
452
453 if (b4 < 0) b4 = 0;
454 if (g4 < 0) g4 = 0;
455 if (r4 < 0) r4 = 0;
456 max = b4;
457 if (g4 > max) max = g4;
458 if (r4 > max) max = r4;
459 if (max > 255)
460 {
461 b4 = (b4 * 255) / max;
462 g4 = (g4 * 255) / max;
463 r4 = (r4 * 255) / max;
464 }
465 dstp[x] = (strength * b4 + invstrength * srcp[x]) >> 8;
466 dstp[xplus1] = (strength * g4 + invstrength * srcp[xplus1]) >> 8;
467 dstp[xplus2] = (strength * r4 + invstrength * srcp[xplus2]) >> 8;
468 }
469 else
470 {
471 dstp[x] = srcp[x];
472 dstp[xplus1] = srcp[xplus1];
473 dstp[xplus2] = srcp[xplus2];
474 }
475 }
476 srcp += pitch;
477 dstp += pitch;
478 workp += bwidth;
479 blurp += bwidth;
480 }
481
482 tcv_convert(mfd->tcvhandle, mfd->convertFrameOut, ptr->video_buf,
483 ptr->v_width, ptr->v_height, IMG_BGRA32,
484 vob->im_v_codec==CODEC_YUV ? IMG_YUV_DEFAULT : IMG_RGB24);
485
486 return 0;
487 }
488 return 0;
489 }
490
491