图像处理之错切变换
一:基本数学知识:
图像错切变换在图像几何形变方面非常有用,常见的错切变换分为X方向与Y方向的
错切变换。对应的数学矩阵分别如下:
根据上述矩阵假设P(x1, y1)为错切变换之前的像素点,则错切变换以后对应的像素
P’(x2, y2)当X方向错切变换时:
当Y方向错切变换时:
二:程序实现基本思路
实现图像错切变换时,必须考虑图像将目标像素点坐标变为源相点坐标时小数部分对
像素值的影响,这里通过临近点插值算法实现了目标像素值的计算。根据目标像素计
算源像素的公式可以根据上面的数学公式运算以后分别求的x1,y1的值。由于错切以
后图像会在宽或者高上比原图像大,多出来的这些背景像素默认填充颜色为黑色。
类ShearFilter实现了图像水平或者垂直方向的错切变换,支持角度与背景颜色参数
设置。
三:编程关键点解析
Ø 计算错切以后图像的宽与高
double angleValue = (angle/180.0d) * Math.PI; outh = vertical ? (int)(height + width * Math.tan(angleValue)) : height; outw = vertical ? width : (int)(width + height * Math.tan(angleValue)); System.out.println("after shear, new width : " + outw); System.out.println("after shear, new height: " + outh);
Ø 根据目标像素点坐标计算源像素点坐标
double prow = vertical ? row + Math.tan(angleValue) * (col - width) : row; double pcol = vertical ? col : col + Math.tan(angleValue) * (row - height); int[] rgb = getPixel(inPixels, width, height, prow, pcol);
Ø 临近点插值计算目标像素点像素值
private int[] getPixel(int[] input, int width, int height, double prow, double pcol) { double row = Math.floor(prow); double col = Math.floor(pcol); if(row < 0 || row >= height) { return new int[]{backgroundColor.getRed(), backgroundColor.getGreen(), backgroundColor.getBlue()}; } if(col < 0 || col >= width) { return new int[]{backgroundColor.getRed(), backgroundColor.getGreen(), backgroundColor.getBlue()}; } double u = vertical ? (prow - row) : pcol - col; int nextCol = (int)(col + 1); int nextRow = (int)(row + 1); if((col + 1) >= width) { nextCol = (int)col; } if((row + 1) >= height) { nextRow = (int)row; } int index1 = (int)(row * width + col); int index2 = vertical ? (int)(nextRow * width + col) : (int)(row * width + nextCol); int tr1, tr2; int tg1, tg2; int tb1, tb2; tr1 = (input[index1] >> 16) & 0xff; tg1 = (input[index1] >> 8) & 0xff; tb1 = input[index1] & 0xff; tr2 = (input[index2] >> 16) & 0xff; tg2 = (input[index2] >> 8) & 0xff; tb2 = input[index2] & 0xff; int tr = (int)(tr1 * (1-u) + tr2 * u); int tg = (int)(tg1 * (1-u) + tg2 * u); int tb = (int)(tb1 * (1-u) + tb2 * u); return new int[]{tr, tg, tb}; }
四:运行效果
五:类ShearFilter完整代码
package com.gloomyfish.filter.study; import java.awt.Color; import java.awt.image.BufferedImage; import java.awt.image.ColorModel; public class ShearFilter extends AbstractBufferedImageOp { private int outw; private int outh; private double angle; private Color backgroundColor; private boolean vertical; public void setVertical(boolean vertical) { this.vertical = vertical; } public ShearFilter() { backgroundColor = Color.BLACK; vertical = false; this.angle = 20; } public int getOutw() { return outw; } public void setOutw(int outw) { this.outw = outw; } public int getOuth() { return outh; } public void setOuth(int outh) { this.outh = outh; } public double getAngle() { return angle; } public void setAngle(double angle) { this.angle = angle; } public Color getBackgroundColor() { return backgroundColor; } public void setBackgroundColor(Color backgroundColor) { this.backgroundColor = backgroundColor; } @Override public BufferedImage filter(BufferedImage src, BufferedImage dest) { int width = src.getWidth(); int height = src.getHeight(); double angleValue = (angle/180.0d) * Math.PI; outh = vertical ? (int)(height + width * Math.tan(angleValue)) : height; outw = vertical ? width : (int)(width + height * Math.tan(angleValue)); System.out.println("after shear, new width : " + outw); System.out.println("after shear, new height: " + outh); int[] inPixels = new int[width*height]; int[] outPixels = new int[outh*outw]; getRGB( src, 0, 0, width, height, inPixels ); int index = 0; for(int row=0; row<outh; row++) { int ta = 0; for(int col=0; col<outw; col++) { double prow = vertical ? row + Math.tan(angleValue) * (col - width) : row; double pcol = vertical ? col : col + Math.tan(angleValue) * (row - height); int[] rgb = getPixel(inPixels, width, height, prow, pcol); index = row * outw + col; outPixels[index] = (ta << 24) | (rgb[0] << 16) | (rgb[1] << 8) | rgb[2]; } } if ( dest == null ) dest = createCompatibleDestImage( src, null ); setRGB( dest, 0, 0, outw, outh, outPixels ); return dest; } private int[] getPixel(int[] input, int width, int height, double prow, double pcol) { double row = Math.floor(prow); double col = Math.floor(pcol); if(row < 0 || row >= height) { return new int[]{backgroundColor.getRed(), backgroundColor.getGreen(), backgroundColor.getBlue()}; } if(col < 0 || col >= width) { return new int[]{backgroundColor.getRed(), backgroundColor.getGreen(), backgroundColor.getBlue()}; } double u = vertical ? (prow - row) : pcol - col; int nextCol = (int)(col + 1); int nextRow = (int)(row + 1); if((col + 1) >= width) { nextCol = (int)col; } if((row + 1) >= height) { nextRow = (int)row; } int index1 = (int)(row * width + col); int index2 = vertical ? (int)(nextRow * width + col) : (int)(row * width + nextCol); int tr1, tr2; int tg1, tg2; int tb1, tb2; tr1 = (input[index1] >> 16) & 0xff; tg1 = (input[index1] >> 8) & 0xff; tb1 = input[index1] & 0xff; tr2 = (input[index2] >> 16) & 0xff; tg2 = (input[index2] >> 8) & 0xff; tb2 = input[index2] & 0xff; int tr = (int)(tr1 * (1-u) + tr2 * u); int tg = (int)(tg1 * (1-u) + tg2 * u); int tb = (int)(tb1 * (1-u) + tb2 * u); return new int[]{tr, tg, tb}; } public BufferedImage createCompatibleDestImage(BufferedImage src, ColorModel dstCM) { if ( dstCM == null ) dstCM = src.getColorModel(); return new BufferedImage(dstCM, dstCM.createCompatibleWritableRaster(outw, outh), dstCM.isAlphaPremultiplied(), null); } }
下半年事情比较多,博客一直没有更新,感谢众多网友的关注与留言
我会继续努力的!再次声明一下:请不要向我索取源码!谢谢!
代码我在整理中,最终我会开源让大家自己下载,请耐心等待!