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

 



    
        
        
        
       

你可能感兴趣的:(java开发)