JAI 多图片合成TIF格式
因为项目需要,所以要做这么个工具类,发现了一些问题,接下来一一说明。
需要引入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;
}
JAI 多图片合成TIF格式的更多相关文章
- C# 图片的裁剪,两个图片合成一个图片
图片的裁剪,两个图片合成一个图片(这是从网上摘的) /// <summary> /// 图片裁剪,生成新图,保存在同一目录下,名字加_new,格式1.png 新图1_ne ...
- 【开源java游戏框架libgdx专题】-12-开发工具-图片合成
TexturePackerGui工具: 1.工具使用: 首先看到texturepacker的界面 界面介绍: New pack:创建项目按钮,单击后输入文件名称,创建文件. Input directo ...
- php 图片合成时文字颜色丢失
最近在做图片合成的时候无意间发现文字颜色丢失了,仔细找了以后才发现原来是因为图片格式的原因 当图片是png图片时文字的颜色就变成了白色的,So.........去你妹的png,用jpg吧! $dest ...
- canvas图片合成中的坑
需求 要用代码来实现多张外部图片和文字的合并而且要上传到七牛云,再将图片链接通过客户端分享出去.图片背景需要支持用户自定义更换. 实现方案 在一个canvas上多次调用drawImage函数,分别绘制 ...
- pyhton图片合成模块-PIL
文章链接:https://www.cnblogs.com/lilinwei340/p/6474170.html python PIL实现图片合成 在项目中需要将两张图片合在一起.遇到两种情况,一种 ...
- python PIL实现图片合成
在项目中需要将两张图片合在一起.遇到两种情况,一种就是两张非透明图片的合成, 一种是涉及到透明png的合成. 相关API见 http://pillow.readthedocs.io/en/latest ...
- CATIA 使用技巧--转换出轻巧的tif格式文件
问题描述: 我们在与客户和供应商打交道的过程中经常需要TIF格式2D图纸文件,而默认的CATIA设置保存出来TIF文件非常大,不利于保存和传送.对于该问题,我们可以通过修改CATIA的默认设置选项,将 ...
- jpg/png格式图片转eps格式的方法总结
jpg/png格式图片转eps格式的方法总结 转自http://blog.sina.com.cn/s/blog_5410e7b50101lme2.html 用latex写论文的筒子应该遇到这样的问题: ...
- Android PNG透明图片转JPG格式背景变黑
Android PNG透明图片转JPG格式背景变黑 在上传图片是,需要把PNG格式转换成JPG格式的,但是在遇上透明背景时,转过来就变成黑色底图了! 原因是PNG支持透明图而 JPG格式不支持透明底 ...
- html5 图片转为base64格式异步上传
因为有这个需求(移动端),所以就研究了一下,发现还挺不错的.这个主要是用了html5的API,不需要其他的JS插件,不过只有支持html5的浏览器才行,就现在而言应该大部份都支持的.<!DOCT ...
随机推荐
- CF1404D 题解
题意 传送门 给定 \(2n\) 个数 \(1,2,\dots,2n\),A 和 B 进行交互,如下规则: A 需要将元素分成 \(n\) 组 \(\texttt{pair}\): B 从每组 \(\ ...
- 多点DLT (Direct Linear Transformation) 算法
阅读前可以先参看上一篇代数视觉博客: 四点DLT (Dierct Linear Transformation) 算法 对于大于4个点的数据点来进行 DLT 算法变换, 如果数据点的标注都十分准确,那么 ...
- antdVue 重置select和input的样式 去掉蓝色换成灰色
代码实现: <template> <div> <a-select mode="tags" style="width: 200px" ...
- vue实现随机生成图形验证码
效果展示 安装插件 npm i identify 定义组件 verificationCode.vue <template> <!-- 图形验证码 --> <div cla ...
- k3s|如何将k8s集群的node节点设置不可调度或删除node节点?
k3s|如何将k8s集群的node节点设置不可调度或删除node节点? k3s是由 Rancher 公司开发的轻量级Kubernetes,是经CNCF一致性认证的Kubernetes发行版,专为物联网 ...
- 蓝牙mesh组网实践(厂商透传模型介绍)
目录 CH582的官方EVT中,除了代理节点例程和天猫精灵例程外都提供了厂商定义的透传模型. 模型位于蓝牙mesh网络协议中的最上层,负责标准化用户应用场景的实例,比如说开关模型.亮度模型.风速模型. ...
- Sql Sugar 使用
sql sugar orm文档地址: SqlSugar ORM 5.X 官网 .文档.教程 - SqlSugar 5x - .NET果糖网 (donet5.com) sql sugar 基本用法大全, ...
- react框架-知识点(ref,, setState)
react的思想:无必要勿增实体 1. ref 使用 myRef = React.createRef() <input ref={this.myRef}></input> 2. ...
- Web安全与渗透测试笔记
Web安全与渗透测试笔记 @author: lamaper 一.基本网络知识 (一)网络是怎样联通的 TCP/IP协议 Internet Http协议 (二)Http协议 http请求 一个完整的Ht ...
- Java笔记_方法重载
/** * @ClassName OverLoadExercise * @Description TODO * @Author Orange * @Date 2021/4/19 8:29 * @Ver ...