im4java開發向導


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擴展名

 



    
        
        
        
       


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM