1 /*
2 filter_xsharpen.c
3
4 Copyright (C) 1999-2000 Donald A. Graft
5 modified 2002 by Tilmann Bitterberg for use with transcode
6
7 This file is part of transcode, a video stream processing tool
8
9 Xsharpen Filter for VirtualDub -- sharpen by mapping pixels
10 to the closest of window max or min.
11
12 This program is free software; you can redistribute it and/or modify
13 it under the terms of the GNU General Public License as published by
14 the Free Software Foundation.
15
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU General Public License for more details.
20
21 You should have received a copy of the GNU General Public License
22 along with this program; if not, write to the Free Software
23 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24
25 The author can be contacted at:
26 Donald Graft
27 http://sauron.mordor.net/dgraft/
28 neuron2@home.com.
29 */
30
31 #define MOD_NAME "filter_xharpen.so"
32 #define MOD_VERSION "(1.0b2) (2003-02-12)"
33 #define MOD_CAP "VirtualDub's XSharpen Filter"
34 #define MOD_AUTHOR "Donald Graft, Tilmann Bitterberg"
35
36 #include "transcode.h"
37 #include "filter.h"
38 #include "libtc/libtc.h"
39 #include "libtc/optstr.h"
40 #include "aclib/imgconvert.h"
41
42 static vob_t *vob=NULL;
43
44 /* vdub compat */
45 typedef unsigned int Pixel;
46 typedef unsigned int Pixel32;
47 typedef unsigned char Pixel8;
48 typedef int PixCoord;
49 typedef int PixDim;
50 typedef int PixOffset;
51
52 ///////////////////////////////////////////////////////////////////////////
53
54 typedef struct MyFilterData {
55 Pixel32 *convertFrameIn;
56 Pixel32 *convertFrameOut;
57 int strength;
58 int strengthInv;
59 int threshold;
60 int srcPitch;
61 int dstPitch;
62 TCVHandle tcvhandle;
63 } MyFilterData;
64
65 static MyFilterData *mfd;
66
help_optstr(void)67 static void help_optstr(void)
68 {
69 tc_log_info (MOD_NAME, "(%s) help\n"
70 "* Overview\n"
71 " This filter performs a subtle but useful sharpening effect. The\n"
72 " result is a sharpening effect that not only avoids amplifying\n"
73 " noise, but also tends to reduce it. A welcome side effect is that\n"
74 " files processed with this filter tend to compress to smaller files.\n"
75 "\n"
76 "* Options\n"
77 " * Strength 'strength' (0-255) [200]\n"
78 " When this value is 255, mapped pixels are not blended with the\n"
79 " original pixel values, so a full-strength effect is obtained. As\n"
80 " the value is reduced, each mapped pixel is blended with more of the\n"
81 " original pixel. At a value of 0, the original pixels are passed\n"
82 " through and there is no sharpening effect.\n"
83 "\n"
84 " * Threshold 'threshold' (0-255) [255]\n"
85 " This value determines how close a pixel must be to the brightest or\n"
86 " dimmest pixel to be mapped. If a pixel is more than threshold away\n"
87 " from the brightest or dimmest pixel, it is not mapped. Thus, as\n"
88 " the threshold is reduced, pixels in the mid range start to be\n"
89 " spared.\n"
90 , MOD_CAP);
91 }
92
tc_filter(frame_list_t * ptr_,char * options)93 int tc_filter(frame_list_t *ptr_, char *options)
94 {
95 vframe_list_t *ptr = (vframe_list_t *)ptr_;
96
97 //----------------------------------
98 //
99 // filter init
100 //
101 //----------------------------------
102
103
104 if(ptr->tag & TC_FILTER_INIT) {
105
106 int width, height;
107
108 if((vob = tc_get_vob())==NULL) return(-1);
109
110 mfd = tc_malloc(sizeof(MyFilterData));
111
112 if (!mfd) {
113 tc_log_error(MOD_NAME, "No memory at %d!", __LINE__);
114 return (-1);
115 }
116
117 height = vob->ex_v_height;
118 width = vob->ex_v_width;
119
120 /* default values */
121 mfd->strength = 200; /* 255 is too much */
122 mfd->strengthInv = 255 - mfd->strength;
123 mfd->threshold = 255;
124 mfd->srcPitch = 0;
125 mfd->dstPitch = 0;
126
127 if (options != NULL) {
128
129 if(verbose) tc_log_info(MOD_NAME, "options=%s", options);
130
131 optstr_get (options, "strength", "%d", &mfd->strength);
132 optstr_get (options, "threshold", "%d", &mfd->threshold);
133
134 }
135
136 mfd->strengthInv = 255 - mfd->strength;
137
138 if (verbose > 1) {
139
140 tc_log_info (MOD_NAME, " XSharpen Filter Settings (%dx%d):", width,height);
141 tc_log_info (MOD_NAME, " strength = %d", mfd->strength);
142 tc_log_info (MOD_NAME, " threshold = %d", mfd->threshold);
143 }
144
145 if (options)
146 if ( optstr_lookup(options, "help") != NULL) {
147 help_optstr();
148 }
149
150 /* fetch memory */
151
152 mfd->convertFrameIn = tc_malloc (width*height*sizeof(Pixel32));
153 if (!mfd->convertFrameIn) {
154 tc_log_error(MOD_NAME, "No memory at %d!", __LINE__);
155 return (-1);
156 }
157 memset(mfd->convertFrameIn, 0, width*height*sizeof(Pixel32));
158
159 mfd->convertFrameOut = tc_malloc (width*height*sizeof(Pixel32));
160 if (!mfd->convertFrameOut) {
161 tc_log_error(MOD_NAME, "No memory at %d!", __LINE__);
162 return (-1);
163 }
164 memset(mfd->convertFrameOut, 0, width*height*sizeof(Pixel32));
165
166 mfd->tcvhandle = tcv_init();
167
168 // filter init ok.
169 if(verbose) tc_log_info(MOD_NAME, "%s %s", MOD_VERSION, MOD_CAP);
170
171 return 0;
172
173 } /* TC_FILTER_INIT */
174
175
176 if(ptr->tag & TC_FILTER_GET_CONFIG) {
177 if (options) {
178 char buf[256];
179 optstr_filter_desc (options, MOD_NAME, MOD_CAP, MOD_VERSION, MOD_AUTHOR, "VRYO", "1");
180 tc_snprintf (buf, sizeof(buf), "%d", mfd->strength);
181 optstr_param (options, "strength", "How much of the effect", "%d", buf, "0", "255");
182
183 tc_snprintf (buf, sizeof(buf), "%d", mfd->threshold);
184 optstr_param (options, "threshold",
185 "How close a pixel must be to the brightest or dimmest pixel to be mapped",
186 "%d", buf, "0", "255");
187 }
188 }
189
190
191 if(ptr->tag & TC_FILTER_CLOSE) {
192
193 if (mfd->convertFrameIn)
194 free (mfd->convertFrameIn);
195 mfd->convertFrameIn = NULL;
196
197 if (mfd->convertFrameOut)
198 free (mfd->convertFrameOut);
199 mfd->convertFrameOut = NULL;
200
201 tcv_free(mfd->tcvhandle);
202 mfd->tcvhandle = 0;
203
204 if (mfd)
205 free(mfd);
206 mfd = NULL;
207
208 return 0;
209
210 } /* TC_FILTER_CLOSE */
211
212 ///////////////////////////////////////////////////////////////////////////
213
214 if(ptr->tag & TC_POST_M_PROCESS && ptr->tag & TC_VIDEO && !(ptr->attributes & TC_FRAME_IS_SKIPPED)) {
215
216 if (vob->im_v_codec == CODEC_RGB) {
217 const PixDim width = ptr->v_width;
218 const PixDim height = ptr->v_height;
219 Pixel32 *src, *dst;
220 int x, y;
221 int r, g, b, R, G, B;
222 Pixel32 p, min=1000, max=-1;
223 int luma, lumac, lumamax, lumamin, mindiff, maxdiff;
224 const int srcpitch = ptr->v_width*sizeof(Pixel32);
225 const int dstpitch = ptr->v_width*sizeof(Pixel32);
226
227 Pixel32 * dst_buf;
228 Pixel32 * src_buf;
229
230 tcv_convert(mfd->tcvhandle, ptr->video_buf,
231 (uint8_t *)mfd->convertFrameIn,
232 ptr->v_width, ptr->v_height, IMG_RGB24, IMG_BGRA32);
233
234 src_buf = mfd->convertFrameIn;
235 dst_buf = mfd->convertFrameOut;
236
237 /* First copy through the four border lines. */
238 src = src_buf;
239 dst = dst_buf;
240 for (x = 0; x < width; x++)
241 {
242 dst[x] = src[x];
243 }
244 src = (Pixel *)((char *)src_buf + (height - 1) * srcpitch);
245 dst = (Pixel *)((char *)dst_buf + (height - 1) * dstpitch);
246 for (x = 0; x < width; x++)
247 {
248 dst[x] = src[x];
249 }
250 src = src_buf;
251 dst = dst_buf;
252 for (y = 0; y < height; y++)
253 {
254 dst[0] = src[0];
255 dst[width-1] = src[width-1];
256 src = (Pixel *)((char *)src + srcpitch);
257 dst = (Pixel *)((char *)dst + dstpitch);
258 }
259
260 /* Now calculate and store the pixel luminances for the remaining pixels. */
261 src = src_buf;
262 for (y = 0; y < height; y++)
263 {
264 for (x = 0; x < width; x++)
265 {
266 r = (src[x] >> 16) & 0xff;
267 g = (src[x] >> 8) & 0xff;
268 b = src[x] & 0xff;
269 luma = (55 * r + 182 * g + 19 * b) >> 8;
270 src[x] &= 0x00ffffff;
271 src[x] |= (luma << 24);
272 }
273 src = (Pixel *)((char *)src + srcpitch);
274 }
275
276 /* Finally run the 3x3 rank-order sharpening kernel over the pixels. */
277 src = (Pixel *)((char *)src_buf + srcpitch);
278 dst = (Pixel *)((char *)dst_buf + dstpitch);
279 for (y = 1; y < height - 1; y++)
280 {
281 for (x = 1; x < width - 1; x++)
282 {
283 /* Find the brightest and dimmest pixels in the 3x3 window
284 surrounding the current pixel. */
285 lumamax = -1;
286 lumamin = 1000;
287
288 p = ((Pixel32 *)((char *)src - srcpitch))[x-1];
289 luma = p >> 24;
290 if (luma > lumamax)
291 {
292 lumamax = luma;
293 max = p;
294 }
295 if (luma < lumamin)
296 {
297 lumamin = luma;
298 min = p;
299 }
300
301 p = ((Pixel32 *)((char *)src - srcpitch))[x];
302 luma = p >> 24;
303 if (luma > lumamax)
304 {
305 lumamax = luma;
306 max = p;
307 }
308 if (luma < lumamin)
309 {
310 lumamin = luma;
311 min = p;
312 }
313
314 p = ((Pixel32 *)((char *)src - srcpitch))[x+1];
315 luma = p >> 24;
316 if (luma > lumamax)
317 {
318 lumamax = luma;
319 max = p;
320 }
321 if (luma < lumamin)
322 {
323 lumamin = luma;
324 min = p;
325 }
326
327 p = src[x-1];
328 luma = p >> 24;
329 if (luma > lumamax)
330 {
331 lumamax = luma;
332 max = p;
333 }
334 if (luma < lumamin)
335 {
336 lumamin = luma;
337 min = p;
338 }
339
340 p = src[x];
341 lumac = luma = p >> 24;
342 if (luma > lumamax)
343 {
344 lumamax = luma;
345 max = p;
346 }
347 if (luma < lumamin)
348 {
349 lumamin = luma;
350 min = p;
351 }
352
353 p = src[x+1];
354 luma = p >> 24;
355 if (luma > lumamax)
356 {
357 lumamax = luma;
358 max = p;
359 }
360 if (luma < lumamin)
361 {
362 lumamin = luma;
363 min = p;
364 }
365
366 p = ((Pixel32 *)((char *)src + srcpitch))[x-1];
367 luma = p >> 24;
368 if (luma > lumamax)
369 {
370 lumamax = luma;
371 max = p;
372 }
373 if (luma < lumamin)
374 {
375 lumamin = luma;
376 min = p;
377 }
378
379 p = ((Pixel32 *)((char *)src + srcpitch))[x];
380 luma = p >> 24;
381 if (luma > lumamax)
382 {
383 lumamax = luma;
384 max = p;
385 }
386 if (luma < lumamin)
387 {
388 lumamin = luma;
389 min = p;
390 }
391
392 p = ((Pixel32 *)((char *)src + srcpitch))[x+1];
393 luma = p >> 24;
394 if (luma > lumamax)
395 {
396 lumamax = luma;
397 max = p;
398 }
399 if (luma < lumamin)
400 {
401 lumamin = luma;
402 min = p;
403 }
404
405 /* Determine whether the current pixel is closer to the
406 brightest or the dimmest pixel. Then compare the current
407 pixel to that closest pixel. If the difference is within
408 threshold, map the current pixel to the closest pixel;
409 otherwise pass it through. */
410 p = -1;
411 if (mfd->strength != 0)
412 {
413 mindiff = lumac - lumamin;
414 maxdiff = lumamax - lumac;
415 if (mindiff > maxdiff)
416 {
417 if (maxdiff < mfd->threshold)
418 {
419 p = max;
420 }
421 }
422 else
423 {
424 if (mindiff < mfd->threshold)
425 {
426 p = min;
427 }
428 }
429 }
430 if (p == -1)
431 {
432 dst[x] = src[x];
433 }
434 else
435 {
436
437 R = (src[x] >> 16) & 0xff;
438 G = (src[x] >> 8) & 0xff;
439 B = src[x] & 0xff;
440 r = (p >> 16) & 0xff;
441 g = (p >> 8) & 0xff;
442 b = p & 0xff;
443 r = (mfd->strength * r + mfd->strengthInv * R) / 255;
444 g = (mfd->strength * g + mfd->strengthInv * G) / 255;
445 b = (mfd->strength * b + mfd->strengthInv * B) / 255;
446 dst[x] = (r << 16) | (g << 8) | b;
447 }
448 }
449 src = (Pixel *)((char *)src + srcpitch);
450 dst = (Pixel *)((char *)dst + dstpitch);
451 }
452
453 tcv_convert(mfd->tcvhandle, (uint8_t *)mfd->convertFrameOut,
454 ptr->video_buf, ptr->v_width, ptr->v_height,
455 IMG_BGRA32, IMG_RGB24);
456
457 return 0;
458 }
459
460
461 if (vob->im_v_codec == CODEC_YUV) {
462
463 const PixDim width = ptr->v_width;
464 const PixDim height = ptr->v_height;
465 char *src, *dst;
466 int x, y;
467 int luma, lumac, lumamax, lumamin;
468 int p, mindiff, maxdiff;
469 const int srcpitch = ptr->v_width;
470 const int dstpitch = ptr->v_width;
471
472 char * src_buf = ptr->video_buf;
473 static char * dst_buf = NULL;
474
475 if (!dst_buf)
476 dst_buf = tc_malloc (width*height*3/2);
477
478 /* First copy through the four border lines. */
479 /* first */
480 src = src_buf;
481 dst = dst_buf;
482 ac_memcpy (dst, src, width);
483
484 /* last */
485 src = src_buf+srcpitch*(height-1);
486 dst = dst_buf+dstpitch*(height-1);
487 ac_memcpy (dst, src, width);
488
489 /* copy Cb and Cr */
490 ac_memcpy (dst_buf+dstpitch*height, src_buf+srcpitch*height, width*height>>1);
491
492 src = src_buf;
493 dst = dst_buf;
494 for (y = 0; y < height; y++)
495 {
496 *dst = *src;
497 *(dst+width-1) = *(src+width-1);
498 dst += dstpitch;
499 src += srcpitch;
500 }
501
502 src = src_buf+srcpitch;
503 dst = dst_buf+dstpitch;
504
505 /* Finally run the 3x3 rank-order sharpening kernel over the pixels. */
506 for (y = 1; y < height - 1; y++)
507 {
508 for (x = 1; x < width - 1; x++)
509 {
510 /* Find the brightest and dimmest pixels in the 3x3 window
511 surrounding the current pixel. */
512 lumamax = -1000;
513 lumamin = 1000;
514
515 luma = (src - srcpitch)[x-1] &0xff;
516 if (luma > lumamax)
517 lumamax = luma;
518 if (luma < lumamin)
519 lumamin = luma;
520
521 luma = (src - srcpitch)[x] &0xff;
522 if (luma > lumamax)
523 lumamax = luma;
524 if (luma < lumamin)
525 lumamin = luma;
526
527 luma = (src - srcpitch)[x+1] &0xff;
528 if (luma > lumamax)
529 lumamax = luma;
530 if (luma < lumamin)
531 lumamin = luma;
532
533 luma = src[x-1] &0xff;
534 if (luma > lumamax)
535 lumamax = luma;
536 if (luma < lumamin)
537 lumamin = luma;
538
539 luma = src[x] &0xff;
540 lumac = luma;
541 if (luma > lumamax)
542 lumamax = luma;
543 if (luma < lumamin)
544 lumamin = luma;
545
546 luma = src[x+1] &0xff;
547 if (luma > lumamax)
548 lumamax = luma;
549 if (luma < lumamin)
550 lumamin = luma;
551
552 luma = (src + srcpitch)[x-1] &0xff;
553 if (luma > lumamax)
554 lumamax = luma;
555 if (luma < lumamin)
556 lumamin = luma;
557
558 luma = (src + srcpitch)[x] &0xff;
559 if (luma > lumamax)
560 lumamax = luma;
561 if (luma < lumamin)
562 lumamin = luma;
563
564 luma = (src + srcpitch)[x+1] &0xff;
565 if (luma > lumamax)
566 lumamax = luma;
567 if (luma < lumamin)
568 lumamin = luma;
569
570 /* Determine whether the current pixel is closer to the
571 brightest or the dimmest pixel. Then compare the current
572 pixel to that closest pixel. If the difference is within
573 threshold, map the current pixel to the closest pixel;
574 otherwise pass it through. */
575
576 p = -1;
577 if (mfd->strength != 0)
578 {
579 mindiff = lumac - lumamin;
580 maxdiff = lumamax - lumac;
581 if (mindiff > maxdiff)
582 {
583 if (maxdiff < mfd->threshold)
584 p = lumamax&0xff;
585 }
586 else
587 {
588 if (mindiff < mfd->threshold)
589 p = lumamin&0xff;
590 }
591 }
592 if (p == -1)
593 {
594 dst[x] = src[x];
595 }
596 else
597 {
598 int t;
599 lumac = src[x] &0xff;
600 t = ((mfd->strength*p + mfd->strengthInv*lumac)/255) & 0xff;
601 if (t>240) t = 240;
602 if (t<16) t = 16;
603 dst[x] = t&0xff;
604
605 }
606 }
607 src += srcpitch;
608 dst += dstpitch;
609 }
610
611 ac_memcpy (ptr->video_buf, dst_buf, width*height*3/2);
612
613 return 0;
614
615 }
616 }
617 return 0;
618 }
619
620