graphicsimage-rendering

Eliminate skewing effect in combined track and zoom


I am writing for HTML5 Canvas, using it as a viewport on which I maintain a central origin and zoom level. I want to track and zoom to a region, but have encountered a naturally occurring effect that spoils the look I am trying to obtain. This question concerns how I might mitigate this while preserving a natural-looking effect.

Imagine I have a downwards-pointing camera mounted on the ceiling, and somewhere on the floor is a piece of paper. I move the camera at a constant speed along the ceiling until it is above the paper, while zooming at a linear rate so that the paper will eventually fill the rendered image. These actions occur simultaneously.

When doing this, an effect can be observed whereby the paper slides out of frame before it returns to centre. This is probably best explained visually, and kept to two dimensions:

img

This is not a good look. Consider the user requesting focus on something. Their conceptualization is that the entity is moving into view, and it is strange, then, that it appears to first move away before it comes closer.

The effect I wish for is of the target object only approaching the centre of the frame, and never retreating. I suppose the answer is to deform the rate of zoom somehow (or the rate of movement, though I would not prefer this) presumably as a function of the distances involved, or artificially adjust the zoom so the page is kept in frame. I would prefer help towards a solution along the lines of the former approach.


Solution

  • Ow so you got an SW implementation no HW camera involved ....

    I was referring to the real skew effect in English called Rolling shutter: if you are obtaining image by moving real HW scanline camera (without TDI technology) then a skew+blur effect is appearing sliding the scan lines ... try to make a photo from a side window of a moving car the image will get skewed.

    The effect you are describing has nothing to do with skew its just a mathematical "singularity" sorry I do not have any name therm for it.

    As your camera is SW only you can affect both pan and zoom to your liking so I would just compute the zoom from camera FOV and pan position. I see it like this:

    overview

    So:

     I. a = FOV / zoom
    II. tan(0.5*a) = (x1-x0) / h
    ------------------------
    zoom = 0.5*FOV/atan( (x1-x0) / h )
    

    Where h is the ceiling height, x0 is the camera (pan) position, x0 is paper edge position and FOV is field of view of your non zoomed camera...

    [edit1] sliding the paper in and animation...

    I ended up with this:

    animation

    Here the C++/VCL code for this:

    //---------------------------------------------------------------------------
    double x0,x1,w=50,h=200,FOVx=60.0*M_PI/180.0,zoom,pan;
    double t=0; // animation parameter <0,1>
    //---------------------------------------------------------------------------
    void TMain::draw()
        {
        if (!_redraw) return;
    
        // clear buffer
        bmp->Canvas->Brush->Color=clBlack;
        bmp->Canvas->FillRect(TRect(0,0,xs,ys));
    
        double y0,y1,dx;
    
        // position side view onto screen based on its size xs,ys
        y0=0.5*(ys-h);
        y1=y0+h;
        x0=0.5*xs;
        x1=x0+(h*tan(0.5*FOVx));
        // compute zoom,pan from t
        pan=(x1-x0+(0.5*w))*t;
        zoom=0.5*FOVx/atan(((1.0-t)*(x1-x0)+(0.5*w*t))/h);
        // scene
        bmp->Canvas->Pen->Color=clBlue;
        bmp->Canvas->MoveTo( 0,y0);
        bmp->Canvas->LineTo(xs,y0);
        bmp->Canvas->MoveTo( 0,y1);
        bmp->Canvas->LineTo(xs,y1);
        // paper
        bmp->Canvas->Pen->Color=clRed;
        bmp->Canvas->MoveTo(x1,y1);
        bmp->Canvas->LineTo(x1+w,y1);
        // FOVx
        dx=h*tan(0.5*FOVx/zoom);
        bmp->Canvas->Pen->Color=clAqua;
        bmp->Canvas->MoveTo(x0+pan-dx,y1);
        bmp->Canvas->LineTo(x0+pan,y0);
        bmp->Canvas->LineTo(x0+pan+dx,y1);
        // points
        dx=4;
        bmp->Canvas->Pen->Color=clAqua;
        bmp->Canvas->Brush->Color=clBlue;
        bmp->Canvas->Ellipse(x0-dx,y0-dx,x0+dx,y0+dx);
        bmp->Canvas->Ellipse(x0+pan-dx,y0-dx,x0+pan+dx,y0+dx);
        bmp->Canvas->Ellipse(x1-dx,y1-dx,x1+dx,y1+dx);
        bmp->Canvas->Font->Color=clYellow;
        bmp->Canvas->Brush->Style=bsClear;
        bmp->Canvas->TextOutA(x0,y0-20,"x0");
        bmp->Canvas->TextOutA(x0+pan+20,y0+5,"x0+pan");
        bmp->Canvas->TextOutA(x1,y1+5,"x1");
        bmp->Canvas->Brush->Style=bsSolid;
    
        // render backbuffer
        Main->Canvas->Draw(0,0,bmp);
        _redraw=false;
        }
    //---------------------------------------------------------------------------
    

    If you ignore the rendering stuff this is what is important for you:

    pan=(x1-x0+(0.5*w))*t;
    zoom=0.5*FOVx/atan(((1.0-t)*(x1-x0)+(0.5*w*t))/h);
    

    So I slightly changed the meaning of x1 (its the other edge of paper) and instead of x1 in the original equations above I used x1+w*t which means that the paper is not in view at start (t=0) but its touching it from outside and it is fully in view when (t=1). The rest is just the result of the substitution ...

    The w is the paper width, t=<0.0,1.0> is the animation parameter and all the other values did not change meaning...

    The animation is just incrementing the t by 0.02 in 150ms timer recorded by my GIF encoder (that is why its so choppy)