[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[OpenDivX] smart doubling



I have worked for some times on the issue of image doubling, and I have a
small piece of code that may be of interest.
It was originally coded by me for inclusion into dvdview, an excellent
mpeg2 player by Dirk Farin. The logic behind all is this:
Sometimes it is impossible to code a video source when there is a
constraint on bandwidth; it is better to scale the image to a lower
resolution (usually 352x288, that's VCD resolution) to avoid too much
artifacting. In fact, most TV sets are unable to reach anything over
512x350 full screen, due to the kerr effect; so it's useless to code much
data that can be seen or captured from a TV set.
So, what's the best way to do this, preserving a decent quality?
You can prefilter with a perfect or near-perfect filter the incoming
frame, using Sinc or Lanczos filters, and use a reconstruction filter in
the exit.
That's what this filter does: a doubling using a Lanczos filter, corrected
to avoid the use of an additional buffer, redone using only integer math
and (most important) the use of a limiter to avoid overshooting when
enlarging moire-like structures.
here it is:
(m_width and m_height are the original image sizes; width and height the
scaled image, that is 2xm_*; spy,spu,spv are the original YUV buffers,
dpy,dpu,dpv are the destinations).
Some examples can be found at http://www.conecta.it/linux/scaler/
the reduced1 and 3 are the 1/4 size frames, reduced[1-3]t are bilinear
filtered, reduced[1-3]z are filtered with this program.
Hope you can put this to use, I think it can improve playback sgnificantly
with a limited effort of CPU (the best thing would be an mmx
implementation, thiugh...)
ciao
							Carlo Daffara

/* doubling filter, based on Lanczos 3, by Carlo Daffara 2000     
GPL code, use it where you want :-))))		          
Differences with standard filters, eg. the one in ImageMagick: 
we avoid using a separate buffer using three stages, horiz. vert. and
diagonal. The diagonal is approximated, the end point (the one that
requires an external buffer to avoid corruption) is approximated using a
bilinear interpolation from the two nearest pixels of which we know the
value. The error is limited, anyway...
The other important part is the CLAMP we do to avoid over and undershoot;
sinc and lanczos tend to exhibit a distinct Gibbs effect; to avoid that we
make sure that it is not possible for an interpolated pixel to be lighter
than the lighter of the two, or darker than the darker of the two. */  

#define CLAMP(v,l,h) ((v)<(l) ? (l) : (v) > (h) ? (h) : v)
typedef unsigned char   Pixel;
#define WHITE_PIXEL     (255)
#define BLACK_PIXEL     (0)

    {
      for (int y=0;y<dimg->m_height;y++)
       for (int x=0;x<dimg->m_width;x++)
	if ((y<4)||(x<4)||(x>(dimg->m_width-4))||(y>(dimg->m_height-4)))
      {
	dpy[y*2][x*2]=spy[y][x];
	dpy[y*2+1][x*2]=spy[y][x];
	dpy[y*2][x*2+1]=spy[y][x];
	dpy[y*2+1][x*2+1]=spy[y][x];
      }
	else
      {
	dpy[y*2][x*2]=spy[y][x];
	dpy[y*2][x*2+1]=(Pixel)CLAMP((155*(spy[y][x+1]+spy[y][x])-34*(spy[y][x-1]+spy[y][x+2])+6*(spy[y][x-2]+spy[y][x+3]))>>8,BLACK_PIXEL,WHITE_PIXEL);
	dpy[y*2][x*2+1]=(Pixel)CLAMP(dpy[y*2][x*2+1],min(spy[y][x],spy[y][x+1]),max(spy[y][x],spy[y][x+1]));
	dpy[y*2+1][x*2]=(Pixel)CLAMP((155*(spy[y+1][x]+spy[y][x])-34*(spy[y-1][x]+spy[y+2][x])+6*(spy[y-2][x]+spy[y+3][x]))>>8,BLACK_PIXEL,WHITE_PIXEL);
	dpy[y*2+1][x*2]=(Pixel)CLAMP(dpy[y*2+1][x*2],min(spy[y+1][x],spy[y][x]),max(spy[y+1][x],spy[y][x]));
	dpy[y*2+1][x*2+1]=(Pixel)CLAMP((155*(spy[y+1][x+1]+spy[y][x])-34*(spy[y-1][x-1]+spy[y+2][x+2])+6*(spy[y-2][x-2]+spy[y+3][x+3]))>>8,BLACK_PIXEL,WHITE_PIXEL);
      };
      for (int y=0;y<oldch;y++)
       for (int x=0;x<oldcw;x++)
	if ((y<4)||(x<4)||(x>oldcw-4)||(y>(oldch-4)))
      {
	dpu[y*2][x*2]=spu[y][x];
	dpv[y*2][x*2]=spv[y][x];
	dpu[y*2+1][x*2]=spu[y][x];
	dpv[y*2+1][x*2]=spv[y][x];
	dpu[y*2][x*2+1]=spu[y][x];
	dpv[y*2][x*2+1]=spv[y][x];
	dpu[y*2+1][x*2+1]=spu[y][x];
	dpv[y*2+1][x*2+1]=spv[y][x];
      }
	else
      {
	dpu[y*2][x*2]=spu[y][x];
	dpu[y*2][x*2+1]=(Pixel)CLAMP((155*(spu[y][x+1]+spu[y][x])-34*(spu[y][x-1]+spu[y][x+2])+6*(spu[y][x-2]+spu[y][x+3]))>>8,BLACK_PIXEL,WHITE_PIXEL);
	dpu[y*2+1][x*2]=(Pixel)CLAMP((155*(spu[y+1][x]+spu[y][x])-34*(spu[y-1][x]+spu[y+2][x])+6*(spu[y-2][x]+spu[y+3][x]))>>8,BLACK_PIXEL,WHITE_PIXEL);
	dpu[y*2+1][x*2+1]=(spu[y][x]+spu[y+1][x+1])>>1;
	dpv[y*2][x*2]=spv[y][x];
	dpv[y*2][x*2+1]=(Pixel)CLAMP((155*(spv[y][x+1]+spv[y][x])-34*(spv[y][x-1]+spv[y][x+2])+6*(spv[y][x-2]+spv[y][x+3]))>>8,BLACK_PIXEL,WHITE_PIXEL);
	dpv[y*2+1][x*2]=(Pixel)CLAMP((155*(spv[y+1][x]+spv[y][x])-34*(spv[y-1][x]+spv[y+2][x])+6*(spv[y-2][x]+spv[y+3][x]))>>8,BLACK_PIXEL,WHITE_PIXEL);
	dpv[y*2+1][x*2+1]=(spv[y][x]+spv[y+1][x+1])>>1;

      }
      }




_______________________________________________
OpenDivX mailing list
[email protected]
http://lists.projectmayo.com/mailman/listinfo/opendivx


Reply To Poster

Local References / HOW-TO / FAQs