JAI 多图片合成TIF格式

筅森魡賤 提交于 2019-12-10 16:27:12

  因为项目需要,所以要做这么个工具类,发现了一些问题,接下来一一说明。

  需要引入jai-codec-1.1.3.jar跟jai_code-1.1.3.jar。

  1.判断图片格式:

  JPEG (jpg),文件头:FFD8FF ,结尾:FFD9

  PNG (png),文件头:89504E47 

  GIF (gif),文件头:47494638 

  TIFF (tif),文件头:49492A00
  Windows Bitmap (bmp),文件头:424D

  -- 可以通过UltraEdit进行查看图片的十六进制内容

    /**
     * 判断图片格式
     * @param fis
     * @return
     */
    private static String getPicType(FileInputStream fis) {
        //读取文件的前几个字节来判断图片格式
        byte[] b = new byte[4];
        try {
            fis.read(b, 0, b.length);
            String type = bytesToHexString(b).toUpperCase();
            if (type.contains("FFD8FF")) {
                return "jpg";
            } else if (type.contains("89504E47")) {
                return "png";
            } else if (type.contains("47494638")) {
                return "gif";
            } else if (type.contains("424D")) {
                return "bmp";
            }else{
                return "unkown";
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if(fis != null){
                try {
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return null;
    }

    private static String bytesToHexString(byte[] src){
        StringBuilder stringBuilder = new StringBuilder();
        if (src == null || src.length <= 0) {
            return null;
        }
        for (int i = 0; i < src.length; i++) {
            int v = src[i] & 0xFF;
            String hv = Integer.toHexString(v);
            if (hv.length() < 2) {
                stringBuilder.append(0);
            }
            stringBuilder.append(hv);
        }
        return stringBuilder.toString();
    }

  2.多张图片合成TIF,因为合成之后可能文件会很大,所以采用了压缩模式(TIFFEncodeParam.COMPRESSION_JPEG_TTN2),但是压缩模式需要将图片都转为JPG格式

    /**
     *
     * @param imageFileList 图片路径列表 (图片为E:/xx/xx.jpg格式)
     * @param toPath tif文件所放路径
     * @param distFileName tif文件名
     * @param convertPath 转换图片存放路径
     * @param isCompress 是否压缩(压缩的时候是先将不是jpg的格式转为jpg,因为JAI可以设置JPEG的压缩模式。
     *                            但是png转jpg会有点失真,所以建议用非压缩的方式!!!)
     */
    public static String manyImgToTif(List<String> imageFileList,String toPath, String distFileName, String convertPath, boolean isCompress) {
        String tifFile = null;
        if(imageFileList != null && imageFileList.size()>0){
            List<File> fileArr = new ArrayList<File>();
            String fileName;
            String imageFile;
            String fileType = "";
            File tmpFile ;
            FileInputStream tmpIns = null;
            boolean isNeedTransfer = false;
            for (int i = 0; i < imageFileList.size(); i++){
                imageFile = imageFileList.get(i);
                String[] tempFile = imageFile.split("/");
                fileName = tempFile[tempFile.length-1];
                tmpFile = new File(imageFile);

                // 处理文件
                try {
                    // 以防是png格式的图片直接改后缀的情况
                    tmpIns = new FileInputStream(tmpFile);
                    fileType = getPicType(tmpIns);

                    if(isCompress){
                        File convertFile = new File(convertPath);
                        if (!convertFile.exists()) {
                            convertFile.mkdirs();
                        }

                        if ("bmp".equals(fileType)
                                || "jpg".equals(fileType)){
                            // 有时图片有损坏,但是能展示,就是用这个架包的时候会报错,
                            // 比较严格,所以需要捕获异常做一下转换
                            isNeedTransfer = false;
                            try{
                                // 需要重新获取流,因为上面判断格式已经读过流了.
                                tmpIns = new FileInputStream(tmpFile);
                                JPEGImageDecoder decoder = JPEGCodec.createJPEGDecoder(tmpIns);
                                decoder.decodeAsBufferedImage();
                            } catch (TruncatedFileException e){
                                e.printStackTrace();
                                logger.info("图片有损坏,需要做转换,image:"+imageFile);
                                isNeedTransfer = true;
                            }

                            if(isNeedTransfer){
                                fileArr.add(pngtoJpg(imageFile,convertPath,fileName));
                            } else {
                                fileArr.add(new File(imageFile));
                            }

                        } else if("gif".equals(fileType)){
                            fileArr.add(giftoJpg(imageFile,convertPath,fileName));
                        }else if("png".equals(fileType)){
                            fileArr.add(pngtoJpg(imageFile,convertPath,fileName));
                        }
                    } else {
                        if ("bmp".equals(fileType)
                                || "jpg".equals(fileType)
                                || "gif".equals(fileType)
                                || "png".equals(fileType)){
                            fileArr.add(new File(imageFile));
                        }
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    if(tmpIns != null){
                        try {
                            tmpIns.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }

            }

            if (fileArr.size() > 0) {
                try {
                    ArrayList pages = new ArrayList(fileArr.size() - 1);
                    FileSeekableStream[] stream = new FileSeekableStream[fileArr.size()];
                    for (int i = 0; i < fileArr.size(); i++) {
                        stream[i] = new FileSeekableStream(fileArr.get(i).getCanonicalPath());
                    }
                    ParameterBlock pb = (new ParameterBlock());
                    PlanarImage firstPage = JAI.create("stream", stream[0]);
                    for (int i = 1; i < fileArr.size(); i++) {
                        PlanarImage page = JAI.create("stream", stream[i]);
                        pages.add(page);
                    }
                    TIFFEncodeParam param = new TIFFEncodeParam();
                    if(isCompress){
                        param.setCompression(TIFFEncodeParam.COMPRESSION_JPEG_TTN2);
                    }

                    TIFFField[] extras = new TIFFField[4];
                    extras[0] = new TIFFField(262, TIFFField.TIFF_SHORT, 1, (Object) new short[] { 6 });
                    extras[1] = new TIFFField(282, 5, 1, (Object) new long[][]{{(long) 200, 1}, {0, 0}});
                    extras[2] = new TIFFField(283, 5, 1, (Object) new long[][]{{(long) 200, 1}, {0, 0}});
                    extras[3] = new TIFFField(258, TIFFField.TIFF_SHORT, 1, (Object) new char[] { 8 });
                    param.setExtraFields(extras);
                    param.setExtraImages(pages.iterator());

                    File f = new File(toPath);
                    if (!f.exists()) {
                        f.mkdirs();
                    }

                    tifFile = toPath + "\\"+ distFileName+".tif";
                    OutputStream os = new FileOutputStream(tifFile);
                    ImageEncoder enc = ImageCodec.createImageEncoder("tiff", os, param);

                    enc.encode(firstPage);
                    //关掉流
                    os.flush();
                    os.close();
                    for (int i = 0; i < fileArr.size(); i++) {
                        stream[i].close();
                    }

                    logger.info("====manyImgToTif done====");
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return tifFile;
    }

  3.遇到的问题:

java.lang.ArrayIndexOutOfBoundsException: 3
    at com.sun.media.jai.codec.JPEGEncodeParam.getHorizontalSubsampling(JPEGEncodeParam.java:104)

  这个是因为图片不都是JPG格式,但却采用了TIFFEncodeParam.COMPRESSION_JPEG_TTN2压缩模式导致的出错,转为JPG格式就好了。

 java.lang.RuntimeException: - Unable to render RenderedOp for this operation.
    at javax.media.jai.RenderedOp.createInstance(RenderedOp.java:827)
Caused by: com.sun.image.codec.jpeg.ImageFormatException: Not a JPEG file: starts with 0xff 0xd9

  这个是因为图片有损坏,例如JPG格式,文件头是FFD8FF ,但可能结尾不是FFD9,所以导致处理的时候会有问题,所以需要重读取流再转换一下就正常了。

  附录-两个转换方法(png->jpg,bmp->jpg)

     public static File pngtoJpg(String fromImg, String path , String fileName) {
            File outFile = new File(path+ File.separator+fileName+"_convert.jpg");
            try {
                FileOutputStream out = new FileOutputStream(outFile);
                File img = new File(fromImg);
                BufferedImage image = ImageIO.read(img);

                BufferedImage newBufferedImage = new BufferedImage(image.getWidth(),
                        image.getHeight(), BufferedImage.TYPE_INT_RGB);
                //TYPE_INT_RGB:创建一个RBG图像,24位深度,成功将32位图转化成24位
                newBufferedImage.createGraphics().drawImage(image, 0, 0, Color.WHITE, null);
                ImageIO.write(newBufferedImage,"jpg",outFile);

                out.flush();
                out.close();

            }catch (IOException e){
                e.printStackTrace();
            }
            return outFile;
        }

    public static File giftoJpg(String fromImg, String path, String fileName) {
        File outFile = new File(path+ File.separator+fileName+"_convert.jpg");
        try {
            File infile = new File(fromImg);
            BufferedImage src = null;
            src = ImageIO.read(infile);
            int wideth = src.getWidth(null);
            int height = src.getHeight(null);

            BufferedImage tag = new BufferedImage(wideth , height , BufferedImage.TYPE_INT_RGB);
            tag.getGraphics().drawImage(src, 0, 0, wideth , height , null);
            FileOutputStream out = new FileOutputStream(outFile);
            JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(out);
            encoder.encode(tag);
            out.flush();
            out.close();
        } catch (IOException e) {
            e.printStackTrace();
        }

        return outFile;
    }

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!