ImageMagick是个图片处理工具能够安装在绝大多数的平台上使用,Linux、Mac、Windows都没有问题。GraphicsMagick是在ImageMagick基础上的另外一个项目,大大提升了图片处理的性能,在linux平台上,能够使用命令行的形式处理图片。Im4java 和Jmagick 都是开源社区为上面两个工具开发的 Java API,性能和方便度上im4java是更好的选择。java
JMagick是一个开源API,利用JNI(Java Native Interface)技术实现了对ImageMagick API的Java访问接口,所以也将比纯Java实现的图片操做函数在速度上要快。JMagick只实现了ImageMagicAPI的一部分功能,它的发行遵循LGPL协议。linux
im4java是ImageMagick的另外一个Java开源接口。与JMagick不一样之处在于im4java只是生成与ImageMagick相对应的命令行,而后将生成的命令行传至选中的ImageCommand(使用java.lang.ProcessBuilder.start()实现)来执行相应的操做。它支持大部分ImageMagick命令,能够针对不一样组的图片屡次复用同一个命令行。git
im4java只是封装ImageMagick的命令。因此不须要依赖dll,也不存在64位系统调用32位dll的问题.并且im4java支持GraphicsMagick,GraphicsMagick是ImageMagick的分支。相对ImageMagick ,GraphicsMagick更稳定,消耗资源更少。最重要的是不依赖dll环境
因此使用 im4java 是更好的选择。github
这篇文章主要是 im4java 的使用,而 im4java 又是对 GraphicsMagick 命令的封装,GraphicsMagick 命令的使用能够看另外一篇文章 GraphicsMagick 1.3.23 经常使用命令canvas
先写个包含获取图片信息的简单工具类,以下。windows
public class Im4JavaUtils { private static Logger logger = LoggerFactory.getLogger(Im4JavaUtils.class); // 图片质量 public static final String IMAGE_QUALITY = "quality"; // 图片高度 public static final String IMAGE_HEIGHT = "height"; // 图片宽度 public static final String IMAGE_WIDTH = "width"; // 图片格式 public static final String IMAGE_SUFFIX = "suffix"; // 图片大小 public static final String IMAGE_SIZE = "size"; // 图片路径 public static final String IMAGE_PATH = "path"; /** * 是否使用 GraphicsMagick */ private static final boolean IS_USE_GRAPHICS_MAGICK = true; /** * ImageMagick安装路径,windows下使用 */ private static final String IMAGE_MAGICK_PATH = "D:\\software\\ImageMagick-6.2.7-Q8"; /** * gm 命令所在目录 */ private static final String GRAPHICS_MAGICK_PATH = "/usr/local/bin"; /** * 水印图片路径 */ private static final String watermarkImagePath = "/Users/gary/Documents/Job/ImageProcessTool/Im4java/Linux_logo.jpg"; /** * 水印图片 */ private static Image watermarkImage = null; static { try { watermarkImage = ImageIO.read(new File(watermarkImagePath)); } catch (Exception e) { e.printStackTrace(); } } /** * 命令类型 * */ private enum CommandType { convert("转换处理"), identify("图片信息"), compositecmd("图片合成"); private String name; CommandType(String name) { this.name = name; } } /** * 查询图片的基本信息:格式,质量,宽度,高度 * <p> * gm identify -format %w,%h,%d/%f,%Q,%b,%e /Users/gary/Documents/999999999/10005582/1.jpg * <p> * * @param imagePath * @return */ public static Map<String, String> getImageInfo(String imagePath) { long startTime = System.currentTimeMillis(); Map<String, String> imageInfo = new HashMap<>(); try { IMOperation op = new IMOperation(); op.format("%w,%h,%d/%f,%Q,%b,%e"); op.addImage(); ImageCommand identifyCmd = getImageCommand(CommandType.identify); ArrayListOutputConsumer output = new ArrayListOutputConsumer(); identifyCmd.setOutputConsumer(output); identifyCmd.run(op, imagePath); ArrayList<String> cmdOutput = output.getOutput(); String[] result = cmdOutput.get(0).split(","); if (result.length == 6) { imageInfo.put(IMAGE_WIDTH, result[0]); imageInfo.put(IMAGE_HEIGHT, result[1]); imageInfo.put(IMAGE_PATH, result[2]); imageInfo.put(IMAGE_QUALITY, result[3]); imageInfo.put(IMAGE_SIZE, result[4]); imageInfo.put(IMAGE_SUFFIX, result[5]); } } catch (Exception e) { // e.printStackTrace(); logger.error("图片工具获取图片基本信息异常" + e.getMessage(), e); } long endTime = System.currentTimeMillis(); // logger.info("take time: " + (endTime - startTime)); return imageInfo; } /** * 获取 ImageCommand * * @param command 命令类型 * @return */ private static ImageCommand getImageCommand(CommandType command) { ImageCommand cmd = null; switch (command) { case convert: cmd = new ConvertCmd(IS_USE_GRAPHICS_MAGICK); break; case identify: cmd = new IdentifyCmd(IS_USE_GRAPHICS_MAGICK); break; case compositecmd: cmd = new CompositeCmd(IS_USE_GRAPHICS_MAGICK); break; } if (cmd != null && System.getProperty("os.name").toLowerCase().indexOf("windows") == -1) { cmd.setSearchPath(IS_USE_GRAPHICS_MAGICK ? GRAPHICS_MAGICK_PATH : IMAGE_MAGICK_PATH); } return cmd; } }
上面的代码中要注意几个问题:api
获取图片信息的命令中有个参数 -format ,该参数可取的值以下。app
%b file size of image read in %c comment property %d directory component of path %e filename extension or suffix %f filename (including suffix) %g layer canvas page geometry ( = %Wx%H%X%Y ) %h current image height in pixels %i image filename (note: becomes output filename for "info:") %k number of unique colors %l label property %m image file format (file magic) %n exact number of images in current image sequence %o output filename (used for delegates) %p index of image in current image list %q quantum depth (compile-time constant) %r image class and colorspace %s scene number (from input unless re-assigned) %t filename without directory or extension (suffix) %u unique temporary filename (used for delegates) %w current width in pixels %x x resolution (density) %y y resolution (density) %z image depth (as read in unless modified, image save depth) %A image transparency channel enabled (true/false) %C image compression type %D image dispose method %G image size ( = %wx%h ) %H page (canvas) height %M Magick filename (original file exactly as given, including read mods) %O page (canvas) offset ( = %X%Y ) %P page (canvas) size ( = %Wx%H ) %Q image compression quality ( 0 = default ) %S ?? scenes ?? %T image time delay %W page (canvas) width %X page (canvas) x offset (including sign) %Y page (canvas) y offset (including sign) %Z unique filename (used for delegates) %@ bounding box %# signature %% a percent sign \n newline \r carriage return
压缩图片质量less
/** * 图片压缩 * <p> * 拼装命令示例: gm convert -quality 80 /apps/watch.jpg /apps/watch_compress.jpg * * @param srcImagePath * @param destImagePath * @param quality * @throws Exception */ public static void compressImage(String srcImagePath, String destImagePath, double quality) throws Exception { IMOperation op = new IMOperation(); op.quality(quality); op.addImage(); op.addImage(); ImageCommand cmd = getImageCommand(CommandType.convert); cmd.run(op, srcImagePath, destImagePath); }
裁剪图片ide
/** * 裁剪图片 * * @param imagePath 源图片路径 * @param newPath 处理后图片路径 * @param x 起始X坐标 * @param y 起始Y坐标 * @param width 裁剪宽度 * @param height 裁剪高度 * @return 返回true说明裁剪成功, 不然失败 */ public static boolean cutImage(String imagePath, String newPath, int x, int y, int width, int height) { boolean flag = false; try { IMOperation op = new IMOperation(); op.addImage(imagePath); /** width:裁剪的宽度 * height:裁剪的高度 * x:裁剪的横坐标 * y:裁剪纵坐标 */ op.crop(width, height, x, y); op.addImage(newPath); ConvertCmd convert = new ConvertCmd(true); convert.run(op); flag = true; } catch (IOException e) { System.out.println("文件读取错误!"); flag = false; } catch (InterruptedException e) { flag = false; } catch (IM4JavaException e) { flag = false; } finally { } return flag; }
缩放图片
/** * 根据尺寸缩放图片[等比例缩放:参数height为null,按宽度缩放比例缩放;参数width为null,按高度缩放比例缩放] * * @param imagePath 源图片路径 * @param newPath 处理后图片路径 * @param width 缩放后的图片宽度 * @param height 缩放后的图片高度 * @return 返回true说明缩放成功, 不然失败 */ public static boolean zoomImage(String imagePath, String newPath, Integer width, Integer height) { boolean flag; try { IMOperation op = new IMOperation(); op.addImage(imagePath); if (width == null) {// 根据高度缩放图片 op.resize(null, height); } else if (height == null) {// 根据宽度缩放图片 op.resize(width); } else { op.resize(width, height); } op.addImage(newPath); ConvertCmd convert = new ConvertCmd(true); convert.run(op); flag = true; } catch (IOException e) { System.out.println("文件读取错误!"); flag = false; } catch (InterruptedException e) { flag = false; } catch (IM4JavaException e) { flag = false; } return flag; }
图片旋转
/** * 图片旋转(顺时针旋转) * 拼装命令示例: gm convert -rotate 90 /apps/watch.jpg /apps/watch_compress.jpg * * @param imagePath 源图片路径 * @param newPath 处理后图片路径 * @param degree 旋转角度 */ public static boolean rotate(String imagePath, String newPath, double degree) { boolean flag; try { // 1.将角度转换到0-360度之间 degree = degree % 360; if (degree <= 0) { degree = 360 + degree; } IMOperation op = new IMOperation(); op.rotate(degree); op.addImage(imagePath); op.addImage(newPath); ConvertCmd cmd = new ConvertCmd(true); cmd.run(op); flag = true; } catch (Exception e) { flag = false; System.out.println("图片旋转失败!"); } return flag; }
添加文字水印
/** * 文字水印 * * @param srcImagePath 源图片路径 * @param destImagePath 目标图片路径 * @param content 文字内容(不支持汉字) * @throws Exception */ public static void addTextWatermark(String srcImagePath, String destImagePath, String content) throws Exception { IMOperation op = new IMOperation(); op.font("ArialBold"); // 文字方位-东南 op.gravity("southeast"); // 文字信息 op.pointsize(60).fill("#F2F2F2").draw("text 10,10 " + content); // 原图 op.addImage(srcImagePath); // 目标 op.addImage(destImagePath); ImageCommand cmd = getImageCommand(CommandType.convert); cmd.run(op); }
添加文字前要安装文字包,或直接指向 ttf 文字格式文件上。
OSX 安装
brew install ghostscript
添加中文水印会乱码,待解决。
/** * 图片水印 * * @param srcImagePath 源图片路径 * @param destImagePath 目标图片路径 * @param dissolve 透明度(0-100) * @throws Exception */ public static void addImgWatermark(String srcImagePath, String destImagePath, Integer dissolve) throws Exception { // 原始图片信息 BufferedImage buffimg = ImageIO.read(new File(srcImagePath)); int w = buffimg.getWidth(); int h = buffimg.getHeight(); IMOperation op = new IMOperation(); // 水印图片位置 op.geometry(watermarkImage.getWidth(null), watermarkImage.getHeight(null), w - watermarkImage.getWidth(null) - 10, h - watermarkImage.getHeight(null) - 10); // 水印透明度 op.dissolve(dissolve); // 水印 op.addImage(watermarkImagePath); // 原图 op.addImage(srcImagePath); // 目标 op.addImage(destImagePath); ImageCommand cmd = getImageCommand(CommandType.compositecmd); cmd.run(op); }
参考资料