基本原理:
计算四个点的增长斜率,使用双线性插值实现像素填充。
废话也懒得说啦,自己看代码吧,我从一个地方抄袭+修改了一下
源来的代码,原因是原来的代码太乱了,也太让人费解了。
运行效果:
滤镜源代码:
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; } }
转载请务必注明