PDA

View Full Version : How to do lens distortion on image?



xtal256
25th July 2011, 06:26
I want to make a lens distortion effect on an image. And i am wondering; first - how to do it, and second - how fast will it be?

The image in question is quite small, about 64x64 pixels, so i am hoping that the effect can be done in real-time. But there will be factors which will determine how fast it will be (i.e. if i use anti-aliasing to improve quality, that would slow down computation).

I guess i will have to implement the algorithm myself, in which case i will need to know what the algorithm is. Wikipedia has some formulas on the "Optical aberration" page, but some C/C++ code would be nice :).

Also, would it be better to use OpenGL for this sort of thing? I would probably have to do the calculations on the CPU (i don't know much about them fancy pixel shaders, and not all graphics cards support that), so i don't know if that would be any faster.

Basically, i am just trying to gauge whether this will be worth doing.

Santosh Reddy
25th July 2011, 06:47
Did you look at Vector Deformation Example (http://doc.qt.nokia.com/4.7/demos-deform.html)

xtal256
25th July 2011, 23:49
I forgot to mention (or perhaps it wasn't clear in my first post) - i wish to do this on pixel data, not vectors. Unless that example can somehow be applied to QPixmaps as well, i didn't read it that closely. I guess i should have at least looked at how it's doing it, i sort of stopped reading once i got to "what is rendered on screen is not pixel manipulation, but modified vector representations of the glyphs themselves"

SixDegrees
26th July 2011, 00:56
This doesn't seem difficult. A ray passing through the simulated lens is deflected toward the center of the lens; the deflection will be greater near the center. Figure out which pixel a ray would hit and color the screen pixel that color. You only have to do a quadrant (actually, only an octant, but quadrants are easier to work with) and for such a small image you can pre-compute the deflections once; the problem then reduces to a simple array lookup for each pixel.

You can try a linear deflection warp to start, but I'd guess a sinusoidal function might be more realistic.

xtal256
26th July 2011, 03:01
I have just been playing around with direct pixel manipulation using QImage::bits(), and it is quite fast. I can perform these operations in real-time in the paint event. Optimisations can be done, but for now it definitely seems possible.

@SixDegrees: Thanks. I will have to think about the maths a bit, and i don't understand what you mean about quadrants and pre-computing deflections, but i'm sure i'll get it.

xtal256
27th July 2011, 03:14
I'm getting there, but still needs work.

Here is what i have so far:

/* get the_image ... */

QImage originalImage = the_image;
QImage warpedImage = originalImage.copy();
const QRgb* oldPixels = (QRgb*)originalImage.bits();
QRgb* newPixels = (QRgb*)warpedImage.bits();
const int imgWidth = warpedImage.width();
const int imgHeight = warpedImage.height();
const int halfWidth = imgWidth / 2;
const int halfHeight = imgHeight / 2;

for (int y = 0; y < imgHeight; y++) {
for (int x = 0; x < imgWidth; x++) {
float cx = (float)(x - halfWidth);
float cy = (float)(y - halfHeight);
float radiusSquared = (cx * cx) + (cy * cy);
float f = 1 - (radiusSquared * 0.0005f);

int newX = (int)(f * cx) + halfWidth;
int newY = (int)(f * cy) + halfHeight;

if (newX >= 0 && newY >= 0 && newX < imgWidth && newY < imgHeight) {
*(newPixels + (y * imgWidth) + x) = *(oldPixels + (newY * imgWidth) + newX);
}
}
}

/* draw warpedImage ... */

The main thing is calculating the offsets (deflection) of each pixel. That is the line:
f = 1 - (radiusSquared * scaleFactor)

With that minus in there, the image curves out toward the edges (like this (http://paulbourke.net/miscellaneous/lenscorrection/m1.gif)). I want it to distort the other way, like a magnifying glass. But when i change the minus to a plus, the result is not quite right. I think it is curving the image correctly, but only the middle of the image, the outside is unaffected.
I will continue to play around with the algorithm until i get something that looks good.

Just one more question, is that algorithm above a linear function or sinusoidal? (i think it's linear, right?).

xtal256
27th July 2011, 23:45
Ok, i have something now that works quite well.
I scrapped that algorithm i posted above and instead just converted the x,y points to polar coordinates then scaled the radius appropriately and converted back to Cartesian.
Perhaps there is a quicker way of doing that, but i am pre-calculating and caching these values for each pixel (which i assume is what SixDegrees was talking about), so speed doesn't really matter there.

Now i just have to do anti-aliasing on the distorted image.