KFB格式的全视野数字病理切片(Whole slide images, WSIs)是国内一种病理切片扫描仪扫描出来的私有格式,该扫描仪是宁波江丰生物信息技术有限公司的一款产品。
然而,在实际开发中我们是无法直接使用该格式的文件。另外kfb文件大小不一,小的几十MB,大的甚至几个G,这么大的文件打开都是问题。
网上搜索各种资料,各种尝试,甚至AI都找了,AI写的代码是各种问题,不是找不见maven依赖就是代码执行各种报错。真的是各种屡试不爽,前前后后折腾了一个礼拜。最终经过本人多次研究尝试,找到了解决方案(转jpg)。
1. 按照网上说的最多的方法来进行第一步操作(kfb转tif)
相关代码如下:
package com.lonzh.utils;
import lombok.SneakyThrows;
import java.io.File;
import java.math.BigDecimal;
import java.math.RoundingMode;
/**
* Kfb转成其他格式
*
* @author DaiHaijiao
*/
public class Kfb2Other {
public static final String TIF = ".tif";
public static final String SVS = ".svs";
/**
* 转换(性能一般的SSD磁盘 转换200Mb的文件大约需要20s)
*
* @param exePath
* @param kfbPath
* @param extensionName 拓展名
*/
public static void convert(String exePath, String kfbPath, String extensionName) {
new Thread() {
@SneakyThrows
@Override
public void run() {
BigDecimal decimal = new BigDecimal(1024);
//文件大小,单位:MB
BigDecimal fileSize = new BigDecimal(new File(kfbPath).length()).divide(decimal, 2, RoundingMode.HALF_UP).divide(decimal, 2, RoundingMode.HALF_UP);
String tifPath = kfbPath.substring(0, kfbPath.lastIndexOf(".")) + extensionName;
String[] cmd = new String[]{"cmd", "/c", exePath + " \"" + kfbPath + "\" \"" + tifPath + "\" 3"};
// 执行自动备份任务
Process process = Runtime.getRuntime().exec(cmd);
if (process.waitFor() == 0) {
process.destroy();
Tif2Jpg.tif2Jpg(tifPath);
}
}
}.start();
}
public static void main(String[] args) {
String exePtah = "C:\\病理教学软件\\kfb2tif\\KFB转Tif或SVS工具2.0\\x86\\KFbioConverter.exe";
convert(exePtah, "D:\\pathology-teaching\\file\\顶级目录\\骨/BL-01-01_骨骼肌萎缩-202306160841479.kfb", Kfb2Other.TIF);
}
}
代码中有使用到“KFbioConverter.exe”,假如你有C币,请移步传送门进行下载。假如你没有C币,就自行百度吧(就是花点时间的事)。
注意:“KFbioConverter.exe”不要安装,是通过代码调用执行的文件类型转换!!!
上述代码中是kfb转tif,假如你需要转svs,也可转svs,后续的转jpg是基于tif格式进行的转换。
2. tif转jpg
关键代码
package com.lonzh.utils;
import com.sun.media.jai.codec.*;
import javax.media.jai.JAI;
import javax.media.jai.RenderedOp;
import java.awt.image.RenderedImage;
import java.awt.image.renderable.ParameterBlock;
import java.io.*;
/**
* Tif装图片
*
* @author DaiHaijiao
*/
public class Tif2Jpg {
/**
* @param fileAbsolutePath
* @throws InterruptedException
*/
public static void tif2Jpg(String fileAbsolutePath) {
if (fileAbsolutePath == null || "".equals(fileAbsolutePath.trim())) {
return;
}
if (!new File(fileAbsolutePath).exists()) {
System.out.println("系统找不到指定文件【" + fileAbsolutePath + "】");
return;
}
FileSeekableStream fileSeekStream = null;
try {
fileSeekStream = new FileSeekableStream(fileAbsolutePath);
TIFFEncodeParam tiffEncodeParam = new TIFFEncodeParam();
JPEGEncodeParam jpegEncodeParam = new JPEGEncodeParam();
ImageDecoder dec = ImageCodec.createImageDecoder("tiff", fileSeekStream, null);
int count = dec.getNumPages();
tiffEncodeParam.setCompression(TIFFEncodeParam.COMPRESSION_GROUP4);
tiffEncodeParam.setLittleEndian(false);
System.out.println("该tif文件共有" + count + "页");
String filePathPrefix = fileAbsolutePath.substring(0, fileAbsolutePath.lastIndexOf("."));
for (int i = 0; i < count; i++) {
RenderedImage renderedImage = dec.decodeAsRenderedImage(i);
String str = "";
if (i == 0) {
//最大的一张图
str = "_large_img";
} else if (i == 4) {
//玻璃片上的标签
str = "_slide_label";
} else if (i == 5) {
//玻璃片
str = "_slide";
} else if (i == 1) {
//缩略图 i=1 和 i=3 图大小几乎无异,像素都是一样的,此处取略小的作为缩略图
str = "_small_img";
} else if (i == 2) {
//大小和像素都介于最大图和缩略图之间的图
str = "_medium_img";
} else {
continue;
}
File imgFile = new File(filePathPrefix + str + ".jpg");
System.out.println("第" + i + "页保存至: " + imgFile.getCanonicalPath());
ParameterBlock pb = new ParameterBlock();
pb.addSource(renderedImage);
pb.add(imgFile.toString());
pb.add("JPEG");
pb.add(jpegEncodeParam);
RenderedOp renderedOp = JAI.create("filestore", pb);
renderedOp.dispose();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fileSeekStream != null) {
try {
fileSeekStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
public static void jpg2Tif(String fileAbsolutePath) {
OutputStream outputStream = null;
try {
RenderedOp renderOp = JAI.create("fileload", fileAbsolutePath);
String tifFilePath = fileAbsolutePath.substring(0, fileAbsolutePath.lastIndexOf(".")) + ".tif";
outputStream = new FileOutputStream(tifFilePath);
TIFFEncodeParam tiffParam = new TIFFEncodeParam();
ImageEncoder imageEncoder = ImageCodec.createImageEncoder("TIFF", outputStream, tiffParam);
imageEncoder.encode(renderOp);
System.out.println("jpg2Tif 保存至: " + tifFilePath);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (outputStream != null) {
try {
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
// public static void main(String args[]) throws InterruptedException {
// tif2Jpg("C:\\pathology-teaching\\file\\顶级目录\\心/BL-15-04_肝包虫病-202306131413433.tif");
// jpg2Tif("D:/upload/2022/02/21/222.jpg");
// }
}
tif中可能会包含多张图片,我这边所使用的kfb转换成的tif中有5张图片。代码对于你们所用的业务可能不够完善,自行完善。上述代码是满足我这边业务的,我代码中就是那个代码。
注意:当tif图文件过大时,获取tif中最大的那一张图会报错!!!
针对于获取最大的那一张图报错问题,后面的帖子会继续讲述关于那一张最大的图的处理办法!
上述代码需要在pom文件中引入相关依赖
<dependency>
<groupId>javax.media</groupId>
<artifactId>jai_codec</artifactId>
<version>1.1.3</version>
</dependency>
<dependency>
<groupId>javax.media</groupId>
<artifactId>jai_core</artifactId>
<version>1.1.3</version>
</dependency>
有C币的朋友可直接移步jar文件下载传送门(jar引入方式见:本地Maven仓库导入外部jar)
到此,kfb转jpg已经完成!