基本原理:
计算四个点的增长斜率,使用双线性插值实现像素填充。
废话也懒得说啦,自己看代码吧,我从一个地方抄袭+修改了一下
源来的代码,原因是原来的代码太乱了,也太让人费解了。
运行效果:
滤镜源代码:
package com.gloomyfish.filter.study;
import java.awt.Rectangle;
import java.awt.image.BufferedImage;
/**
*
* @author gloomy-fish 2013-04-25
*
*/
public class PerspectiveFilter extends AbstractBufferedImageOp {
private float x0, y0, x1, y1, x2, y2, x3, y3;
private float dx1, dy1, dx2, dy2, dx3, dy3;
private float A, B, C, D, E, F, G, H, I;
private float a11, a12, a13, a21, a22, a23, a31, a32, a33;
private boolean scaled;
private int width;
private int height;
@Override
public BufferedImage filter(BufferedImage src, BufferedImage dest) {
width = src.getWidth();
height = src.getHeight();
if ( dest == null )
dest = createCompatibleDestImage( src, null );
A = a22*a33 - a32*a23;
B = a31*a23 - a21*a33;
C = a21*a32 - a31*a22;
D = a32*a13 - a12*a33;
E = a11*a33 - a31*a13;
F = a31*a12 - a11*a32;
G = a12*a23 - a22*a13;
H = a21*a13 - a11*a23;
I = a11*a22 - a21*a12;
if ( !scaled ) {
float invWidth = 1.0f/width;
float invHeight = 1.0f/height;
A *= invWidth;
D *= invWidth;
G *= invWidth;
B *= invHeight;
E *= invHeight;
H *= invHeight;
}
int[] inPixels = getRGB( src, 0, 0, width, height, null );
int srcWidth = width;
int srcHeight = height;
int srcWidth1 = width-1;
int srcHeight1 = height-1;
int outX=0, outY=0;
Rectangle transformedSpace = new Rectangle(0, 0, width, height);
transformSpace(transformedSpace);
outX = transformedSpace.x;
outY = transformedSpace.y;
int outWidth = transformedSpace.width;
int outHeight = transformedSpace.height;
// int index = 0;
int[] outPixels = new int[transformedSpace.width];
float[] out = new float[2];
for (int y = 0; y < outHeight; y++) {
for (int x = 0; x < outWidth; x++) {
transformInverse(outX+x, outY+y, out);
int srcX = (int)Math.floor( out[0] );
int srcY = (int)Math.floor( out[1] );
float xWeight = out[0]-srcX;
float yWeight = out[1]-srcY;
int nw, ne, sw, se;
if ( srcX >= 0 && srcX < srcWidth1 && srcY >= 0 && srcY < srcHeight1) {
// Easy case, all corners are in the image
int i = srcWidth*srcY + srcX;
nw = inPixels[i];
ne = inPixels[i+1];
sw = inPixels[i+srcWidth];
se = inPixels[i+srcWidth+1];
} else {
// Some of the corners are off the image
nw = getPixel( inPixels, srcX, srcY, srcWidth, srcHeight );
ne = getPixel( inPixels, srcX+1, srcY, srcWidth, srcHeight );
sw = getPixel( inPixels, srcX, srcY+1, srcWidth, srcHeight );
se = getPixel( inPixels, srcX+1, srcY+1, srcWidth, srcHeight );
}
outPixels[x] = ImageMath.bilinearInterpolate(xWeight, yWeight, nw, ne, sw, se);
}
setRGB( dest, 0, y, transformedSpace.width, 1, outPixels );
}
return dest;
}
protected void transformSpace( Rectangle rect ) {
if ( scaled ) {
rect.x = (int)Math.min( Math.min( x0, x1 ), Math.min( x2, x3 ) );
rect.y = (int)Math.min( Math.min( y0, y1 ), Math.min( y2, y3 ) );
rect.width = (int)Math.max( Math.max( x0, x1 ), Math.max( x2, x3 ) ) - rect.x;
rect.height = (int)Math.max( Math.max( y0, y1 ), Math.max( y2, y3 ) ) - rect.y;
return;
}
}
final private int getPixel( int[] pixels, int x, int y, int width, int height ) {
if (x < 0 || x >= width || y < 0 || y >= height) {
return pixels[(ImageMath.clamp(y, 0, height-1) * width) + ImageMath.clamp(x, 0, width-1)] & 0x00ffffff;
}
return pixels[ y*width+x ];
}
public PerspectiveFilter() {
this( 0, 0, 1, 0, 1, 1, 0, 1);
}
public PerspectiveFilter(float x0, float y0, float x1, float y1, float x2, float y2, float x3, float y3) {
unitSquareToQuad(x0, y0, x1, y1, x2, y2, x3, y3);
}
protected void transformInverse( int x, int y, float[] out ) {
out[0] = width * (A*x+B*y+C)/(G*x+H*y+I);
out[1] = height * (D*x+E*y+F)/(G*x+H*y+I);
}
public void unitSquareToQuad( float x0, float y0, float x1, float y1, float x2, float y2, float x3, float y3 ) {
this.x0 = x0;
this.y0 = y0;
this.x1 = x1;
this.y1 = y1;
this.x2 = x2;
this.y2 = y2;
this.x3 = x3;
this.y3 = y3;
dx1 = x1-x2;
dy1 = y1-y2;
dx2 = x3-x2;
dy2 = y3-y2;
dx3 = x0-x1+x2-x3;
dy3 = y0-y1+y2-y3;
if (dx3 == 0 && dy3 == 0) {
a11 = x1-x0;
a21 = x2-x1;
a31 = x0;
a12 = y1-y0;
a22 = y2-y1;
a32 = y0;
a13 = a23 = 0;
} else {
a13 = (dx3*dy2-dx2*dy3)/(dx1*dy2-dy1*dx2);
a23 = (dx1*dy3-dy1*dx3)/(dx1*dy2-dy1*dx2);
a11 = x1-x0+a13*x1;
a21 = x3-x0+a23*x3;
a31 = x0;
a12 = y1-y0+a13*y1;
a22 = y3-y0+a23*y3;
a32 = y0;
}
a33 = 1;
scaled = false;
}
public void setCorners(float x0, float y0, float x1, float y1, float x2, float y2, float x3, float y3) {
unitSquareToQuad( x0, y0, x1, y1, x2, y2, x3, y3 );
scaled = true;
}
}
转载请务必注明