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); }
參考資料