java项目抠图功能
项目中需要一个上传文字签名并且抠掉背景图的功能,当初第一次听到这个需求时,差点惊掉下巴,我压根都不会觉得java里能实现这功能。但是既然客户需要,那就照办吧。
经过这次功能的实现,我也更加坚定了一个想法,再奇葩的需求,也要先找找方法,不要果断拒绝,要相信Java天下第一
,天下第一能有解决不了的事吗?(其实我现在内心已经对学python
蠢蠢欲动了,嘿嘿~~~)
1.效果图
经过我一番的搜索加改造,与前端小伙伴做了下边的效果。(整的跟PS一样,顿时有了高大上的感觉)
功能比较简单,上传图片给后端,后端返回抠完图的路径。
原图如下:
上传这个图之后,先框选范围、旋转好方向,注意,框选、旋转图片都是前端的操作,这些前端都有方法,如下图:
当设置好之后,则点击抠图
,即可看到下图的效果,已经变成了透明的。在这一步操作上,抠图就调用了本次java提供的抠图接口。
2.扣图接口
以下是抠图接口,在该接口里前端将裁剪后的图片传送到后端,在该接口里,后端将会把前端传过来的图片传给PictureUtils.transApla(restore,169)
方法,在该方法里,将会进行抠图并再保存,保存后将该处理好的图片路径返回给前端,前端直接展示即可。详细步骤如下:
- 前端上传图片到面板上,此时并没有调用后端接口,仅由前端加载。
- 因为用户上传的图片大小方向都不固定,因此也要考虑一些旋转、裁剪功能,因此这里我们也加入了这些常用功能,经过旋转、裁剪,定好了范围。注意到这里都是前端自己完成。
- 当处理好之后,再调用裁剪接口,也就是下边的代码所写,重点只是
PictureUtils.transApla(restore,169)
方法。
如下是裁剪接口:
/**
* 签名图扣掉背景色
* @param request
* @return
*/
@RequestMapping(value = "/signHandle",method = RequestMethod.POST)
public Object getPsPictureSign(HttpServletRequest request){
MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
String fileName = null;
String msg = null;
String result = "";
try {
//获取文件
Map<String, MultipartFile> fileMap = multipartRequest.getFileMap();
if(fileMap == null || fileMap.isEmpty()){
return addResultMapMsg(false,"未上传签名图片");
}
MultipartFile multipartFile = null;//获取文件名
for (Map.Entry<String, MultipartFile> set : fileMap.entrySet()) {
multipartFile = set.getValue();// 文件名
String uploadfileName = multipartFile.getOriginalFilename();
String suffix = uploadfileName.substring(uploadfileName.lastIndexOf(".")+1).toLowerCase();
if(!uploadSuffix.contains(suffix)){
return addResultMapMsg(false,"只能上传 png、jpg 图片格式");
}
}
//先将文件保存到服务器中,然后操作文件
String fileOriginalName = multipartFile.getOriginalFilename();
String resultStr = File.separator + "pictureTmp" + File.separator + File.separator + fileOriginalName.substring(0,fileOriginalName.lastIndexOf(".")+1) +".png";
fileName = realPath + resultStr;
File restore = new File(fileName);
if(!restore.getParentFile().exists()){
restore.getParentFile().mkdirs();
}
//System.out.println(3+"=="+fileName);
multipartFile.transferTo(restore);
// 抠图工具类
PictureUtils.transApla(restore,169); // 169为灰色的rcb色值
result = resultStr;
} catch (Exception e) {
e.printStackTrace();
msg = "处理失败";
}
if(!StringUtils.isEmpty(msg)){ //说明上传有错
return addResultMapMsg(false,msg);
}else {
return addResultMapMsg(true,result.replace("\\","/"));
}
}
以下是封装好的工具类,可以直接用,另外,因为使用的方法java直接就有,不需要引用额外的jar包。
如下是工具类方法:
package znxd.lxynzl.controller.photoshop;
import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.regex.Pattern;
/**
* Created by lingsf on 2020/6/16.
*/
public class PictureUtils {
public static int color_range = 210;
public static Pattern pattern = Pattern.compile("[0-9]*");
public static boolean isNo(String str) {
return pattern.matcher(str).matches();
}
/**
*
* @param file 保存后要处理的png文件
* @param rcb 要扣掉的rcb色值
*/
public static void transApla(File file,int rcb){
InputStream is=null;
try {
is = new FileInputStream(file);
// 如果是MultipartFile类型,那么自身也有转换成流的方法:is = file.getInputStream();
BufferedImage bi = ImageIO.read(is);
Image image = (Image) bi;
ImageIcon imageIcon = new ImageIcon(image);
BufferedImage bufferedImage = new BufferedImage(imageIcon.getIconWidth(), imageIcon.getIconHeight(),
BufferedImage.TYPE_4BYTE_ABGR);
Graphics2D g2D = (Graphics2D) bufferedImage.getGraphics();
g2D.drawImage(imageIcon.getImage(), 0, 0, imageIcon.getImageObserver());
int alpha = 0;
color_range = 255 - rcb; // 0
for (int j1 = bufferedImage.getMinY(); j1 < bufferedImage
.getHeight(); j1++) {
for (int j2 = bufferedImage.getMinX(); j2 < bufferedImage
.getWidth(); j2++) {
int rgb = bufferedImage.getRGB(j2, j1);
if (colorInRange(rgb)) {
alpha = 0;
} else {
alpha = 255;
}
rgb = (alpha << 24) | (rgb & 0x00ffffff);
bufferedImage.setRGB(j2, j1, rgb);
}
}
g2D.drawImage(bufferedImage, 0, 0, imageIcon.getImageObserver());
ImageIO.write(bufferedImage, "png", file);// 直接输出文件
} catch (Exception e) {
e.printStackTrace();
} finally {
if(is!=null){
try{
is.close();
}catch (Exception e){}
}
}
}
public static boolean colorInRange(int color) {
int red = (color & 0xff0000) >> 16;
int green = (color & 0x00ff00) >> 8;
int blue = (color & 0x0000ff);
if (red >= color_range && green >= color_range && blue >= color_range) {
return true;
}
return false;
}
}
3.总结
整体功能还是比较简单,后端只提供了一个接口,主要的解决办法,使用了swing
的色素替换的方法,就是将固定的色值替换为透明色素
,因此功能图上提示用户,尽量用白底黑字
的签名上传。
当然你们也看到了,上边的我写的签名图,拍出来也并不是白底黑字
,照样也能抠图成功。
前端vue下载地址:百度网盘 提取码:nvt2