On Sun, 27 Mar 2005, Steven Boswell II wrote:
> >the 'dark' criteria. If you'd like a copy to > >laugh at or use as a template/startingpoint I could certainly do that. > > Sure, I'd like to see it. Attached. Building should be as simple as cc `pkg-config --cflags mjpegtools` y4mjunk.c `pkg-config --libs mjpegtools` put it inline before 'yuvplay' - something like: smil2yuv ... | y4mjunk -D | yuvplay > I'm seeing this artifact more in my VideoCDs than > in my DVDs. It almost looks like the quantization > floor is set really high for MPEG-1 video or something. "something" ;) You've only got ~1Mb/s so the effective "q" has to be raised quite a lot by the encoder - from the one vcd test I ran tonight (trying to get an assertion to trigger - so far it hasn't): INFO: [mpeg2enc] Frame 10403 10402 B q=10.53 sum act=701487.00841 INFO: [mpeg2enc] Frame 10404 10406 P q=9.95 sum act=701555.08122 > What about using an edge-detection algorithm to > find the boundaries of large areas, then testing Hmmm, hadn't though of that. > if the large area is low-intensity before setting > it all to black? I haven't looked into That will look terrible - you'll end up with splotches of 'pure black' next to areas that are just a tiny bit outside the threshold. That was the naive approach that 'y4mblackfix' took (and why the program was withdrawn ;)). Once a dark area is found my thought was that fairly aggressive medianfiltering (or other algorithm) could be applied. The other thing which I've seen to be quite effective is to desaturate the dark areas by removing a settable amount of the color information. Have fun. Steven Schultz
/* * $Id: y4mhist.c,v 1.3 2004/05/12 03:12:57 sms Exp $ * * Simple program to calculate a crude histogram of the Y' values for frames * in a YUV4MPEG2 file. For each frame a check is made if a specified * percentage of the pixels are less than the specified threshold. */ #include <stdio.h> #include <sys/types.h> #include <stdlib.h> #include <unistd.h> #define HAVE_STDINT_H #include <mjpegtools/yuv4mpeg.h> static void usage(void); struct filter_args { int width; int height; int radius; int blocksize; int percent; int limit; int debug; int output; }; void filter_1(struct filter_args *, int, int, u_char *); void filter_2(struct filter_args *, long *, u_char *, u_char *); void debug_bright(struct filter_args *, u_char *); main(int argc, char **argv) { int i, j, fdin, fdout, frameno, interlace; int plane0_l, plane1_l, plane2_l; u_char *yuv[3]; y4m_stream_info_t istream, ostream; y4m_frame_info_t iframe; struct filter_args params; memset(¶ms, 0, sizeof (params)); while ((i = getopt(argc, argv, "DOt:p:r:s:")) != EOF) { switch (i) { case 'O': params.output = 1; break; case 'D': params.debug = 1; params.output = 1; break; case 'p': params.percent = atoi(optarg); break; case 't': params.limit = atoi(optarg); break; case 'r': params.radius = atoi(optarg); break; case 's': params.blocksize = atoi(optarg); break; case '?': default: usage(); } } if (params.percent == 0) params.percent = 50; if (params.limit == 0) params.limit = 40; if (params.blocksize == 0) params.blocksize = 8; if (params.radius == 0) params.radius = 2; fdin = fileno(stdin); fdout = fileno(stdout); y4m_accept_extensions(1); y4m_init_stream_info(&istream); y4m_init_stream_info(&ostream); y4m_init_frame_info(&iframe); if (y4m_read_stream_header(fdin, &istream) != Y4M_OK) mjpeg_error_exit1("%s: stream header error"); if (y4m_si_get_plane_count(&istream) != 3) mjpeg_error_exit1("Only 3 plane formats supported"); params.width = y4m_si_get_width(&istream); params.height = y4m_si_get_height(&istream); i = y4m_si_get_interlace(&istream); switch (i) { case Y4M_ILACE_NONE: interlace = 0; break; case Y4M_ILACE_BOTTOM_FIRST: case Y4M_ILACE_TOP_FIRST: interlace = 1; break; default: mjpeg_warn("Unsupported interlacing '%d', assuming non-interlaced", i); interlace = 0; break; } plane0_l = y4m_si_get_plane_length(&istream, 0); plane1_l = y4m_si_get_plane_length(&istream, 1); plane2_l = y4m_si_get_plane_length(&istream, 2); yuv[0] = malloc(plane0_l); if (yuv[0] == NULL) mjpeg_error_exit1("malloc(%d) plane 0", plane0_l); yuv[1] = malloc(plane1_l); if (yuv[1] == NULL) mjpeg_error_exit1("malloc(%d) plane 1", plane1_l); yuv[2] = malloc(plane2_l); if (yuv[2] == NULL) mjpeg_error_exit1("malloc(%d) plane 2", plane2_l); y4m_copy_stream_info(&ostream, &istream); y4m_write_stream_header(fdout, &ostream); frameno = 0; while (y4m_read_frame(fdin,&istream,&iframe,yuv) == Y4M_OK) { if (interlace) { filter_1(¶ms, 2, 0, yuv[0]); filter_1(¶ms, 2, 1, yuv[0]); } else { filter_1(¶ms, 1, 0, yuv[0]); } frameno++; if (params.output) y4m_write_frame(fdout, &ostream, &iframe, yuv); } y4m_fini_frame_info(&iframe); y4m_fini_stream_info(&istream); y4m_fini_stream_info(&ostream); exit(0); } void filter_1(struct filter_args *params, int stride, int field, u_char *buf) { int i, j, row, col, pix_num, sum; int dark_number_threshold; long ystats[256]; u_char *cp, *inbuf, *outbuf; inbuf = (u_char *)malloc(params->blocksize * params->blocksize); outbuf = (u_char *)malloc(params->blocksize * params->blocksize); dark_number_threshold = (params->blocksize * params->blocksize * params->percent) / 100; for (row = field; row < params->height; row += (params->blocksize * stride)) { for (col = 0; col < params->width; col += params->blocksize) { cp = inbuf; memset(ystats, 0, sizeof (ystats)); for (i = 0; i < params->blocksize; i++) { for (j = 0; j < params->blocksize; j++) { pix_num = ((row + (stride * i)) * params->width) + col + j; /* * Count the pixel and at the same time copy it into the blocksizeXblocksize * buffer. A separate buffer is used so that filters further on do not have * to be concerned at all with the interlacing or frame dimensions - all they * need to know is the blocksize and contiguous pixels. */ ystats[buf[pix_num]]++; *cp++ = buf[pix_num]; } } for (sum = i = 0; i < params->limit; i++) { sum += ystats[i]; } if (sum >= dark_number_threshold) { /* * The filter_2 routine can either do the filtering itself or use the provided * information (radius, threshold, etc.) to formulate calls to other filters. * Pass along the statistics in case the filters want to make decisions based * on the Y' histogram. */ filter_2(params, ystats, inbuf, outbuf); /* * Now put the modified (outbuf) pixels back into the original frame after the * median/averaging/whatever filter has processed 'inbuf' and placed the new * data in 'outbuf'. This is not as inefficient as one might think at first * glance since this only happens for blocks that are being filtered - in a * movie most frames/blocks will not get here. */ cp = outbuf; for (i = 0; i < params->blocksize; i++) { for (j = 0; j < params->blocksize; j++) { pix_num = ((row + (stride * i)) * params->width) + col + j; buf[pix_num] = *cp++; } } } } } free(outbuf); free(inbuf); } void debug_bright(struct filter_args *params, u_char *outbuf) { memset(outbuf, 235, params->blocksize * params->blocksize); } void filter_2(struct filter_args *params, long *ystats, u_char *inbuf, u_char *outbuf) { if (params->debug) debug_bright(params, outbuf); } static void usage(void) { mjpeg_error_exit1("usage: -t threshold -p percentage -r blocksize"); }