近日想找一找java裁剪图片的方法,结果各种方法层出不穷,其中这几种方法占大多数:
用图片流来处理,还用到了Iterator迭代器?? 而且使用结构还不规范,正确:Iterator<ImageReader>
还有一种,用复杂的坐标来计算
然而在BufferedImage 里有个getSubimage方法可以直接用来裁剪图片,难到这群人从来都不看源码的么
/**
* Returns a subimage defined by a specified rectangular region.
* The returned <code>BufferedImage</code> shares the same
* data array as the original image.
* @param x the X coordinate of the upper-left corner of the
* specified rectangular region
* @param y the Y coordinate of the upper-left corner of the
* specified rectangular region
* @param w the width of the specified rectangular region
* @param h the height of the specified rectangular region
* @return a <code>BufferedImage</code> that is the subimage of this
* <code>BufferedImage</code>.
* @exception RasterFormatException if the specified
* area is not contained within this <code>BufferedImage</code>.
*/
public BufferedImage getSubimage (int x, int y, int w, int h) {
return new BufferedImage (colorModel,
raster.createWritableChild(x, y, w, h,
0, 0, null),
colorModel.isAlphaPremultiplied(),
properties);
}
所以裁剪图片的代码就精简成了
public static BufferedImage getCuttedImage(BufferedImage bufferedImage, int x, int y, int length, int width) {
return bufferedImage.getSubimage(x, y, length, width);
}
******************************************************************************************************************************************
下面进入正题,先说需求:给定一张图片,从图片中裁剪下来一个随机位置指定大小的小图,并把在原图中把裁下来的区域做变色处理,最后返回这两张图片的base64和被裁区域左上角顶点的xy坐标,先贴代码
/**
* 图片处理工具类
*
* @author xuLiang
* @since 1.0.0
*/
@SuppressWarnings("restriction")
@Component
public class VerifyImageUtil {
/**
* 用来裁剪到滑动的方块
*/
public static BufferedImage getCuttedImage(BufferedImage bufferedImage, int x, int y, int length, int width) {
return bufferedImage.getSubimage(x, y, length, width);
}
/**
* 被抠滑块的坐标集合
*
* @param targetLength
* 原图的长度
* @param targetWidth
* 原图的宽度
* @param x
* 裁剪区域的x坐标
* @param y
* 裁剪区域的y坐标
* @param length
* 抠图的长度
* @param width
* 抠图的宽度
* @return 被抠滑块的坐标
*/
public static int[][] getCutAreaData(int targetLength, int targetWidth, int x, int y, int length, int width) {
int[][] data = new int[targetLength][targetWidth];
for (int i = 0; i < targetLength; i++) {
for (int j = 0; j < targetWidth; j++) {
if (i < x + length && i >= x && j < y + width && j > y) {
data[i][j] = 1;
} else {
data[i][j] = 0;
}
}
}
return data;
}
/**
* 得到抠掉滑块后的图并加阴影
*
* @param oriImage
* @param templateImage
*
*/
public static void cutByTemplate(BufferedImage oriImage, int[][] templateImage) {
for (int i = 0; i < oriImage.getWidth(); i++) {
for (int j = 0; j < oriImage.getHeight(); j++) {
int rgb = templateImage[i][j];
if (rgb == 1) {
oriImage.setRGB(i, j, -1); // 原图对应位置颜色变化
}
}
}
}
/**
* 图片转Base64
*
* @param image
* @return Base64
* @throws Exception
*/
public static String imageToBase64(BufferedImage image) throws Exception {
byte[] imagedata = null;
ByteArrayOutputStream bao = new ByteArrayOutputStream();
imagedata = bao.toByteArray();
BASE64Encoder encoder = new BASE64Encoder();
String BASE64IMAGE = encoder.encodeBuffer(imagedata).trim();
BASE64IMAGE = BASE64IMAGE.replaceAll("\n", "").replaceAll("\r", "");// 删除 \r\n
return BASE64IMAGE;
}
/**
* Base64转图片
*
* @param base64String
* @return image
* @throws Exception
*/
public static BufferedImage base64StringToImage(String base64String) {
try {
BASE64Decoder decoder = new BASE64Decoder();
byte[] bytes1 = decoder.decodeBuffer(base64String);
ByteArrayInputStream bais = new ByteArrayInputStream(bytes1);
return ImageIO.read(bais);
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}
service中:
/**
* 获取图片和坐标值
*
* @param originImgX
* 原图的长
* @param originImgY
* 原图的宽
* @param cuttedImgX
* 裁出来的方块的长
* @param cuttedImgY
* 裁出来的方块的宽
* @param path
* 原图文件路径
* @return VerificationCodeResp
* @author xuLiang
* @since 1.0.0
* @throws Exception
*/
@Override
public VerificationCodeResp getImage(int originImgX, int originImgY, int cuttedImgX, int cuttedImgY, String path)
throws Exception {
int x = new Random().nextInt(originImgX - cuttedImgX);
int y = new Random().nextInt(originImgY - cuttedImgY);
BufferedImage bufferedImage = ImageIO.read(new FileInputStream(path));
BufferedImage cuttedImg = VerifyImageUtil.getCuttedImage(bufferedImage, x, y, cuttedImgX, cuttedImgY);// 用来裁剪到滑动的方块
int[][] cuttedOriginImgCoordinate = VerifyImageUtil.getCutAreaData(originImgX, originImgY, x, y, cuttedImgX,
cuttedImgY);// 被抠滑块的坐标集合
VerifyImageUtil.cutByTemplate(bufferedImage, cuttedOriginImgCoordinate);// 得到抠掉滑块后的图并加阴影
VerificationCodeResp response = new VerificationCodeResp();
response.setCuttedOriginImgBase64(VerifyImageUtil.imageToBase64(bufferedImage));
response.setCuttedImgBase64(VerifyImageUtil.imageToBase64(cuttedImg));
response.setXCoordinate(x + "");
response.setYCoordinate(y + "");
return response;
}
/**
* 获取验证图片响应体
*
* @author xuLiang
* @since 1.0.0
*/
public class VerificationCodeResp {
private String cuttedImgBase64;
private String cuttedOriginImgBase64;
private String XCoordinate;
private String YCoordinate;
@Override
public String toString() {
return "VerificationCodeResp [cuttedImgBase64=" + cuttedImgBase64 + ", cuttedOriginImgBase64="
+ cuttedOriginImgBase64 + ", XCoordinate=" + XCoordinate + ", YCoordinate=" + YCoordinate + "]";
}
public String getCuttedImgBase64() {
return cuttedImgBase64;
}
public void setCuttedImgBase64(String cuttedImgBase64) {
this.cuttedImgBase64 = cuttedImgBase64;
}
public String getCuttedOriginImgBase64() {
return cuttedOriginImgBase64;
}
public void setCuttedOriginImgBase64(String cuttedOriginImgBase64) {
this.cuttedOriginImgBase64 = cuttedOriginImgBase64;
}
public String getXCoordinate() {
return XCoordinate;
}
public void setXCoordinate(String xCoordinate) {
XCoordinate = xCoordinate;
}
public String getYCoordinate() {
return YCoordinate;
}
public void setYCoordinate(String yCoordinate) {
YCoordinate = yCoordinate;
}
}
代码看似一切正常,但是在测试的时候发现一个很奇怪的bug,进行裁剪的图片变成了已经经过变色处理后的图片,而不是源图,在经过漫长的debug后发现了问题所在,
简单来说,就是图片处理方法中的参数bufferedImage指向了和处理过后的图片相同的地址值 ,返回的图片也没有开辟新的内存来存放,做如下改动
public static BufferedImage getCuttedImage(BufferedImage bufferedImage, int x, int y, int length, int width) {
BufferedImage bi = bufferedImage.getSubimage(x, y, length, width);
return bi;
}
@Override
public VerificationCodeResp getImage(int originImgX, int originImgY, int cuttedImgX, int cuttedImgY, String path)
throws Exception {
VerificationCodeResp response = new VerificationCodeResp();
int x = new Random().nextInt(originImgX - cuttedImgX);
int y = new Random().nextInt(originImgY - cuttedImgY);
response.setXCoordinate(x + "");
response.setYCoordinate(y + "");
BufferedImage bufferedImage = ImageIO.read(new FileInputStream(path));
BufferedImage cuttedImg = VerifyImageUtil.getCuttedImage(bufferedImage, x, y, cuttedImgX, cuttedImgY);// 用来裁剪到滑动的方块
response.setCuttedImgBase64(VerifyImageUtil.imageToBase64(cuttedImg));
int[][] cuttedOriginImgCoordinate = VerifyImageUtil.getCutAreaData(originImgX, originImgY, x, y, cuttedImgX,
cuttedImgY);// 被抠滑块的坐标集合
VerifyImageUtil.cutByTemplate(bufferedImage, cuttedOriginImgCoordinate);// 得到抠掉滑块后的图并加阴影
response.setCuttedOriginImgBase64(VerifyImageUtil.imageToBase64(bufferedImage));
return response;
}
经测试,改动后的代码就能得到预期结果了。