0、搜索ImageMagick下載安裝
1、Setting up the Environment
引入im4java到classpath
設置圖片處理引擎的command searchpath
三種方式:
設置系統環境變量 IM4JAVA_TOOLPATH
在程序中設置全局變量(將覆蓋上一層)
String myPath="C:\\Programs\\ImageMagick;C:\\Programs\\exiftool";
ProcessStarter.setGlobalSearchPath(myPath);
設置命令單獨變量(將覆蓋上兩層)
String imPath="C:\\Programs\\ImageMagick"; ConvertCmd cmd = new ConvertCmd(); cmd.setSearchPath(imPath);
2、Simple Use
ImageCommand 對象 e.g. ConvertCmd
ProcessStarter包裝了java.lang.ProcessBuilder,掌管輸入輸出流、支持asynchronous execution
ImageCommand是ProcessStarter的子類,持有一個通過ProcessStarter獲得的process ID,提供了reusing operations或dynamic operations等方法
所有的command類都是ImageCommand的子類
ImageCommand並不是無狀態類,同步使用時一般創建一次就可以了,並發處理除外
Operation 對象 e.g. IMOperation 每個操作創建一次
示例:
// create command ConvertCmd cmd = new ConvertCmd(); // create the operation, add images and operators/options IMOperation op = new IMOperation(); op.addImage("myimage.jpg"); op.resize(800,600); op.addImage("myimage_small.jpg"); // execute the operation cmd.run(op);
3、Using GraphicsMagick
GraphicsMagick是ImageMagick的一個分支、改進了IM的效率和命令行語法
在im4java中有三種方式使用GraphicsMagick
直接創建GM對象 GraphicsMagickCmd cmd = new GraphicsMagickCmd("convert");
使用包裝類 ConvertCmd cmd = new ConvertCmd(true);
運行時通過system-property im4java.useGM = true 決定
4、Reusing Operations
直接看代碼
public void resizeImages(String... pImageNames) { // create command ConvertCmd cmd = new ConvertCmd(); // create the operation, add images and operators/options IMOperation op = new IMOperation(); op.addImage();// 這里相當於插入了一個占位符,無參方法相當於op.addImage(Operation.IMG_PLACEHOLDER) //addImage方法還有兩個重載方法 //op.addImage(String... images) 支持修飾符 //如op.addImage("[300x200]"); //op.addImage(Operation.IMG_PLACEHOLDER+"[300x200]"); //op.addImage(int count) 一次性傳入占位符的數量 op.resize(800,600); op.addImage(); for (String srcImage:pImageNames) { int lastDot = srcImage.lastIndexOf('.'); String dstImage = srcImage.substring(1,lastDot-1)+"_small.jpg"; cmd.run(op,srcImage,dstImage);//在run的時候,逐一替換占位符,這樣相當於復用了Operation對象 //run方法的第二個參數實際是一個Object[]支持任意個數的替換 } }
第二種復用Operation的方式
IMOperation frame = new IMOperation(); frame.rotate("90"); frame.resize(640); frame.border(10,10); IMOperation row = new IMOperation(); row.addImages(3); row.add(frame); row.p_append();
子操作的概念
IMOperation frame = new IMOperation(); frame.openOperation(); frame.rotate("90"); frame.resize(640); frame.border(10,10); frame.closeOperation();
另一種寫法
IMOperation frame = new IMOperation(); frame.rotate("90"); frame.resize(640); frame.border(10,10); IMOperation row = new IMOperation(); row.addImages(3); row.addSubOperation(frame); row.p_append();
5、Dynamic Operations
op.addDynamicOperation(new DynamicOperation() { public Operation resolveOperation(Object... pImages) throws IM4JavaException { //封裝一些邏輯處理 //返回一個Operation對象或null } });
6、Capturing Output
默認的輸出會傳遞給stdout和stderr
可以設置Consumer改變默認行為
ImageCommand.setOutputConsumer(OutputConsumer oc)
ImageCommand.setErrorConsumer(ErrorConsumer ec)
提供了一個示例實現ArrayListOutputConsumer 把輸出轉為String[]
7、Piping
Pipe implements InputProvider, OutputConsumer, ErrorConsumer
后補詳情
8、Using BufferedImages
BufferedImage是一種java本地圖片對象
在作為輸入的時候,im4java可以透明的使用
IMOperation op = new IMOperation(); op.addImage(); // input op.blur(2.0).paint(10.0); op.addImage(); // output ConvertCmd convert = new ConvertCmd(); BufferedImage img = ...; String outfile = ...; ... convert.run(op,img,outfile);
作為輸出的時候要轉一下
IMOperation op = new IMOperation(); op.addImage(); // input .... op.addImage("png:-"); // output: stdout ... images = ...; // set up command ConvertCmd convert = new ConvertCmd(); Stream2BufferedImage s2b = new Stream2BufferedImage(); convert.setOutputConsumer(s2b); // run command and extract BufferedImage from OutputConsumer convert.run(op,(Object[]) images); BufferedImage img = s2b.getImage();
9、 Asynchronous Execution
ConvertCmd cmd = new ConvertCmd(); cmd.setAsyncMode(true);//fire-and-forget 方式 ... cmd.run(op);
如果需要線程執行的反饋,則實現org.im4java.process.ProcessEventListener接口
processInitiated()
processStarted()
processTerminated()
利用ProcessStarter的addProcessEventListener方法添加監聽(ImageCommand對象擴展自ProcessStarter)
如果要自己控制線程的執行
用java原生的執行器
ProcessTask pt = cmd.getProcessTask(op); ExecutorService exec = Executors.newSingleThreadExecutor(); exec.execute(pt); exec.shutdown();
上述異步執行方式有三個問題
問題一:處理大量圖片嚴重消耗系統資源
問題二:不知道每項處理何時完成
問題三:不知道每項任務執行成功還是失敗
解決問題一
java.util.concurrent.Executors以工廠的方式返回執行器對象,你可以控制線程排隊,線程數量、停止或銷毀線程
但有一個嚴重的缺點,一旦ImageCommand在線程中running,就不能被kill
使用org.im4java.process.ProcessExecutor(擴展自java.util.concurrent.ThreadPoolExecutor )
默認的構造器查詢系統的處理器能力限制並發處理量,也可以顯式的傳入一個整數控制,比如new ProcessExecutor(10)
// load images into an array, e.g. from a directoy ArrayList<String> images = load(myDir); // convert all images ProcessExecutor exec = new ProcessExecutor(); Operation op = ...; for (String img:images) { String outfile = ...; ConvertCmd cmd = new ConvertCmd(); ProcessTask pt = cmd.getProcessTask(op,img,outfile); exec.execute(pt); } exec.shutdown();
ProcessTask 對象擴展自java.util.concurrent.FutureTask
解決問題二
因為org.im4java.process.ProcessExecutor擴展自java.util.concurrent.ThreadPoolExecutor
可以調用ThreadPoolExecutor的awaitTermination方法,等候執行器執行完成
awaitTermination方法會阻塞線程直到發生以下情況之一:所有任務執行完成、任務執行超時、當前線程被interrupt
根據這個特點,可以在上面的代碼下加上一段
ProcessExecutor exec = new ProcessExecutor(); for (String img:images) { ... } exec.shutdown(); if (exec.awaitTermination(10,TimeUnit.SECONDS)) { System.err.println("processes terminated on their own"); } else { System.err.println("trying to cancel all running processes ..."); exec.shutdownNow(); }
另外可以擴展ProcessExecutor 的terminated方法,當所有任務執行完成時,這個方法將被調用
解決問題三
對於簡單的異步處理,可以用ProcessEventListener 的processTerminated(ProcessEvent pEvent)方法監聽到執行結果
對於大量的並發處理,則需要借助ProcessEvent,它提供了一系列定位線程的方法,比如getPID()
PID是ImageCommand內部的一個標識,可以在創建對象的時候手動設置
還可以通過ProcessEvent.getProcessStarter()的方法拿到整個ImageCommand對象(因為ImageCommand擴展自ProcessStarter)
10、Utilities
Image Information
通常查詢圖片完整信息使用IdentifyCmd這個命令對象,它封裝了IM的identify命令
另外使用Info對象可以更快速的查詢基本信息
Info imageInfo = new Info(filename,true); System.out.println("Format: " + imageInfo.getImageFormat()); System.out.println("Width: " + imageInfo.getImageWidth()); System.out.println("Height: " + imageInfo.getImageHeight()); System.out.println("Geometry: " + imageInfo.getImageGeometry()); System.out.println("Depth: " + imageInfo.getImageDepth()); System.out.println("Class: " + imageInfo.getImageClass());
第二個構造參數true代表查詢基本信息
對於像TIF或GIF這樣的支持多張圖片的格式(IM里稱之為scenes)
imageInfo.getImageWidth()//返回第一張圖片的寬度(基本信息模式) imageInfo.getProperty("Width")//返回最后張圖片的寬度(完整信息模式) imageInfo.getImageWidth(3) imageInfo.getProperty("Width",3)
完整信息模式中,getSceneCount()方法返回scene的數量
另外還有ExiftoolCmd對象
FilenameLoader
FilenameLoader對象的List<String> loadFilenames(String pDir)可以根據傳入的路徑參數加載所有的文件返回一個String List
ExtensionFilter filter = new ExtensionFilter("jpg"); filter.setRecursion(true); filter.ignoreDotDirs(true); FilenameLoader loader = new FilenameLoader(filter); List<String> files = loader.loadFilenames(mydir);
FilenamePatternResolver
直接看代碼
// define operation and command IMOperation op = new IMOperation(); op.addImage(); // input-file op.addImage(); // output-file ConvertCmd cmd = new ConvertCmd(); // load files ExtensionFilter filter = new ExtensionFilter("jpg"); FilenameLoader loader = new FilenameLoader(filter); List<String> files = loader.loadFilenames(mydir); // create the resolver FilenamePatternResolver resolver = new FilenamePatternResolver("%P/%f.tif"); // now iterate over all files for (String img:files) { cmd.run(op,img,resolver.createName(img)); }
%P: full pathname of source-image (i.e. the directory)
%p: last component of %P
%F: full filename without directory part
%f: filename without directory part and extension
%e: only the extension
%D: drive-letter (on windows systems). Not available for source-files with an UNC-name.
BatchConverter
org.im4java.utils.BatchConverter 是為客戶端應用提供的一個工具類,會並發調用所有可用的CPU資源完成轉換,不太適合Web應用使用
//首先獲得要操作的文件列表 ExtensionFilter filter = new ExtensionFilter("jpg"); filter.setRecursion(false); FilenameLoader loader = new FilenameLoader(filter); List<String> images=loader.loadFilenames(dir); 然后通過BatchConverter的run方法開始批量操作 // create a simple thumbnail operation op = new IMOperation(); op.size(80); op.addImage(); // placeholder input filename op.thumbnail(80); op.addImage(); // placeholder output filename // create a template for the output-files: // we put them in targetDir with the same filename as the original // images String template=targetDir+"%F"; // create instance of BatchConverter and convert images BatchConverter bc = new BatchConverter(BatchConverter.Mode.PARALLEL); bc.run(op,images,targetDir+"%F");
BatchConverter擴展自ProcessExecutor,可以使用awaitTermination方法控制線程終止
BatchConverter有三種模式
BatchConverter.SEQUENTIAL - 順序處理
BatchConverter.PARALLEL - 多核並發處理
BatchConverter.BATCH - 單核處理器可以用這個
另外使用terminated()完成線程終止時的回調邏輯,使用getFailedConversions()方法獲得處理出現錯誤的資源信息
Debugging
ImageCommand對象的createScript()方法,可以把執行腳本生成出來,供調試
IMOperation op = new IMOperation(); ... ConvertCmd cmd = new ConvertCmd(); cmd.createScript("myscript.sh",op);//Win下會自動添加.cmd擴展名