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