Reputation: 737
dcm4che3 with jai_imageio-1.1 didn't work out for me. It won't let you convert jpegloss dicom images. Normally, it converts most of the images but it gives CLibJPEGImageReader error while reading the jpeg inside the dicom file. Anybody figured this out yet?
codecLib error
Well actually I use dcm4che2 together with the old jai_imageio-1.0_01 to solve this issue. You can still use the new dcm4che3 for your other purposes but watch out what sub-libraries you use for dcm4che3. Dcm2dcm or Dcm2jpg of dcm4che3 then it will download its jai_imageio-3.3 dependencies which will corrupt your conversions
If you are experiencing the same issues, here are the steps you need to follow.
1st step:
First make sure you use 32 bit JVM on your Spring Framework Java Application.
C:\Program Files (x86)\Java\jdk1.7.0\jre\bin
2nd step:
Let me repeat again, this is important: New dcmche3 api along with jai_1.1 doesn't work for all dcm files to convert to jpeg files. Some dcm files with JpegLoss merged images can not be converted into jpegs which gives codelLib error or com.sun.media.imageioimpl.plugins.jpeg.CLibJPEGImageReader error
Solution:
Please jai_image-1.0_01 version along with dcm4che-core-2.0.25, dcm4che-image-2.0.25 and dcm4che-imageio-2.0.25
you need the old jai libs
<dependency>
<groupId>com.sun.media</groupId>
<artifactId>jai_imageio</artifactId>
<version>1.0_01</version>
</dependency>
<dependency>
<groupId>javax.media</groupId>
<artifactId>jai_core</artifactId>
<version>1.1.2_01</version>
</dependency>
we need version 2.0.25 of dcm4che-core and dcm4che-imageio to convert jpeg images. dcm files with jpegloss images can't be converted with dcm 3.3.0, so we use old version 2.0.25
<dependency>
<groupId>dcm4che</groupId>
<artifactId>dcm4che-core</artifactId>
<version>2.0.25</version>
</dependency>
<dependency>
<groupId>dcm4che</groupId>
<artifactId>dcm4che-imageio</artifactId>
<version>2.0.25</version>
</dependency>
3rd Step: Now it is time to use a custom Dcm2Jpeg class which you can copy paste this code that belongs to Gunter Zeilinger long time ago to make your jpeg conversions. It uses the above mentioned 2.0.25 dcm4che libraries. It is old but it works like a charm
import java.awt.image.BufferedImage;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.util.Iterator;
import java.util.List;
import javax.imageio.ImageIO;
import javax.imageio.ImageReader;
import javax.imageio.stream.ImageInputStream;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.GnuParser;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.OptionBuilder;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.dcm4che2.data.DicomObject;
import org.dcm4che2.imageio.plugins.dcm.DicomImageReadParam;
import org.dcm4che2.io.DicomInputStream;
import org.dcm4che2.util.CloseUtils;
import com.sun.image.codec.jpeg.JPEGCodec;
import com.sun.image.codec.jpeg.JPEGImageEncoder;
/**
* @author Gunter Zeilinger <[email protected]>
* @version $Revision$ $Date$
* @since Jul 11, 2007
*/
public class Dcm2Jpeg {
private static final String USAGE =
"dcm2jpg [Options] <dcmfile> <jpegfile>\n" +
"or dcm2jpg [Options] <dcmfile>... <outdir>\n" +
"or dcm2jpg [Options] <indir>... <outdir>";
private static final String DESCRIPTION =
"Convert DICOM image(s) to JPEG(s)\nOptions:";
private static final String EXAMPLE = null;
private int frame = 1;
private float center;
private float width;
private String vlutFct;
private boolean autoWindowing;
private DicomObject prState;
private short[] pval2gray;
private String fileExt = ".jpg";
private void setFrameNumber(int frame) {
this.frame = frame;
}
private void setWindowCenter(float center) {
this.center = center;
}
private void setWindowWidth(float width) {
this.width = width;
}
public final void setVoiLutFunction(String vlutFct) {
this.vlutFct = vlutFct;
}
private final void setAutoWindowing(boolean autoWindowing) {
this.autoWindowing = autoWindowing;
}
private final void setPresentationState(DicomObject prState) {
this.prState = prState;
}
private final void setPValue2Gray(short[] pval2gray) {
this.pval2gray = pval2gray;
}
public final void setFileExt(String fileExt) {
this.fileExt = fileExt;
}
public void convert(File src, File dest) throws IOException {
Iterator<ImageReader> iter = ImageIO.getImageReadersByFormatName("DICOM");
ImageReader reader = iter.next();
DicomImageReadParam param =
(DicomImageReadParam) reader.getDefaultReadParam();
param.setWindowCenter(center);
param.setWindowWidth(width);
param.setVoiLutFunction(vlutFct);
param.setPresentationState(prState);
param.setPValue2Gray(pval2gray);
param.setAutoWindowing(autoWindowing);
ImageInputStream iis = ImageIO.createImageInputStream(src);
BufferedImage bi;
OutputStream out = null;
try {
reader.setInput(iis, false);
bi = reader.read(frame - 1, param);
if (bi == null) {
System.out.println("\nError: " + src + " - couldn't read!");
return;
}
out = new BufferedOutputStream(new FileOutputStream(dest));
JPEGImageEncoder enc = JPEGCodec.createJPEGEncoder(out);
enc.encode(bi);
} finally {
CloseUtils.safeClose(iis);
CloseUtils.safeClose(out);
}
//System.out.print('.');
}
public int mconvert(List<String> args, int optind, File destDir)
throws IOException {
int count = 0;
for (int i = optind, n = args.size() - 1; i < n; ++i) {
File src = new File(args.get(i));
count += mconvert(src, new File(destDir, src2dest(src)));
}
return count;
}
private String src2dest(File src) {
String srcname = src.getName();
return src.isFile() ? srcname + this.fileExt : srcname;
}
public int mconvert(File src, File dest) throws IOException {
if (!src.exists()) {
System.err.println("WARNING: No such file or directory: " + src
+ " - skipped.");
return 0;
}
if (src.isFile()) {
try {
convert(src, dest);
} catch (Exception e) {
System.err.println("WARNING: Failed to convert " + src + ":");
e.printStackTrace(System.err);
System.out.print('F');
return 0;
}
System.out.print('.');
return 1;
}
File[] files = src.listFiles();
if (files.length > 0 && !dest.exists()) {
dest.mkdirs();
}
int count = 0;
for (int i = 0; i < files.length; ++i) {
count += mconvert(files[i], new File(dest, src2dest(files[i])));
}
return count;
}
@SuppressWarnings("unchecked")
public static void main(String args[]) throws Exception {
CommandLine cl = parse(args);
Dcm2Jpeg dcm2jpg = new Dcm2Jpeg();
if (cl.hasOption("f")) {
dcm2jpg.setFrameNumber(
parseInt(cl.getOptionValue("f"),
"illegal argument of option -f",
1, Integer.MAX_VALUE));
}
if (cl.hasOption("p")) {
dcm2jpg.setPresentationState(loadDicomObject(
new File(cl.getOptionValue("p"))));
}
if (cl.hasOption("pv2gray")) {
dcm2jpg.setPValue2Gray(loadPVal2Gray(
new File(cl.getOptionValue("pv2gray"))));
}
if (cl.hasOption("c")) {
dcm2jpg.setWindowCenter(
parseFloat(cl.getOptionValue("c"),
"illegal argument of option -c"));
}
if (cl.hasOption("w")) {
dcm2jpg.setWindowWidth(
parseFloat(cl.getOptionValue("w"),
"illegal argument of option -w"));
}
if (cl.hasOption("sigmoid")) {
dcm2jpg.setVoiLutFunction(DicomImageReadParam.SIGMOID);
}
dcm2jpg.setAutoWindowing(!cl.hasOption("noauto"));
if (cl.hasOption("jpgext")) {
dcm2jpg.setFileExt(cl.getOptionValue("jpgext"));
}
final List<String> argList = cl.getArgList();
int argc = argList.size();
File dest = new File(argList.get(argc-1));
long t1 = System.currentTimeMillis();
int count = 1;
if (dest.isDirectory()) {
count = dcm2jpg.mconvert(argList, 0, dest);
} else {
File src = new File(argList.get(0));
if (argc > 2 || src.isDirectory()) {
exit("dcm2jpg: when converting several files, "
+ "last argument must be a directory\n");
}
dcm2jpg.convert(src, dest);
}
long t2 = System.currentTimeMillis();
System.out.println("\nconverted " + count + " files in " + (t2 - t1)
/ 1000f + " s.");
}
private static DicomObject loadDicomObject(File file) {
DicomInputStream in = null;
try {
in = new DicomInputStream(file);
return in.readDicomObject();
} catch (IOException e) {
exit(e.getMessage());
throw new RuntimeException();
} finally {
CloseUtils.safeClose(in);
}
}
private static short[] loadPVal2Gray(File file) {
BufferedReader r = null;
try {
r = new BufferedReader(new InputStreamReader(new FileInputStream(
file)));
short[] pval2gray = new short[256];
int n = 0;
String line;
while ((line = r.readLine()) != null) {
try {
int val = Integer.parseInt(line.trim());
if (n == pval2gray.length) {
if (n == 0x10000) {
exit("Number of entries in " + file + " > 2^16");
}
short[] tmp = pval2gray;
pval2gray = new short[n << 1];
System.arraycopy(tmp, 0, pval2gray, 0, n);
}
pval2gray[n++] = (short) val;
} catch (NumberFormatException nfe) {
// ignore lines where Integer.parseInt fails
}
}
if (n != pval2gray.length) {
exit("Number of entries in " + file + ": " + n
+ " != 2^[8..16]");
}
return pval2gray;
} catch (IOException e) {
exit(e.getMessage());
throw new RuntimeException();
} finally {
CloseUtils.safeClose(r);
}
}
private static CommandLine parse(String[] args) {
Options opts = new Options();
OptionBuilder.withArgName("frame");
OptionBuilder.hasArg();
OptionBuilder.withDescription(
"frame to convert, 1 (= first frame) by default");
opts.addOption(OptionBuilder.create("f"));
OptionBuilder.withArgName("prfile");
OptionBuilder.hasArg();
OptionBuilder.withDescription(
"file path of presentation state to apply");
opts.addOption(OptionBuilder.create("p"));
OptionBuilder.withArgName("center");
OptionBuilder.hasArg();
OptionBuilder.withDescription("Window Center");
opts.addOption(OptionBuilder.create("c"));
OptionBuilder.withArgName("width");
OptionBuilder.hasArg();
OptionBuilder.withDescription("Window Width");
opts.addOption(OptionBuilder.create("w"));
opts.addOption("sigmoid", false,
"apply sigmoid VOI LUT function with given Window Center/Width");
opts.addOption("noauto", false,
"disable auto-windowing for images w/o VOI attributes");
OptionBuilder.withArgName("file");
OptionBuilder.hasArg();
OptionBuilder.withDescription(
"file path of P-Value to gray value map");
opts.addOption(OptionBuilder.create("pv2gray"));
OptionBuilder.withArgName(".xxx");
OptionBuilder.hasArg();
OptionBuilder.withDescription(
"jpeg file extension used with destination directory argument,"
+ " default: '.jpg'.");
opts.addOption(OptionBuilder.create("jpgext"));
opts.addOption("h", "help", false, "print this message");
opts.addOption("V", "version", false,
"print the version information and exit");
CommandLine cl = null;
try {
cl = new GnuParser().parse(opts, args);
} catch (ParseException e) {
exit("dcm2jpg: " + e.getMessage());
throw new RuntimeException("unreachable");
}
if (cl.hasOption('V')) {
Package p = Dcm2Jpeg.class.getPackage();
System.out.println("dcm2jpg v" + p.getImplementationVersion());
System.exit(0);
}
if (cl.hasOption('h') || cl.getArgList().size() < 2) {
HelpFormatter formatter = new HelpFormatter();
formatter.printHelp(USAGE, DESCRIPTION, opts, EXAMPLE);
System.exit(0);
}
return cl;
}
private static int parseInt(String s, String errPrompt, int min, int max) {
try {
int i = Integer.parseInt(s);
if (i >= min && i <= max)
return i;
} catch (NumberFormatException e) {
// parameter is not a valid integer; fall through to exit
}
exit(errPrompt);
throw new RuntimeException();
}
private static float parseFloat(String s, String errPrompt) {
try {
return Float.parseFloat(s);
} catch (NumberFormatException e) {
exit(errPrompt);
throw new RuntimeException();
}
}
private static void exit(String msg) {
System.err.println(msg);
System.err.println("Try 'dcm2jpg -h' for more information.");
System.exit(1);
}
}
4th step: You can still use dcm4che-core-3.3.0, dcm4che-net-3.3.0, dcm4che-tool-storescp-3.3.0 etc. But do not use dcm4che-dcm2jpg-3.3.0 or dcm4che-dcm2dcm-3.3.0. Both of them will inlcude the dependencies cm4che-imageio-rle-3.3.0 and dcm4che-imageio-3.3.0 automatically and the image conversion will not work again for JpegLoss format dcom images.
As I mentioned before, we can still use dcm 3.3.0 libraries. For example we need dcm4che-tool-storescp-3.3.0 to receive dicoms in listener and dcm4che-core-3.3.0 to get the attributes of the dicom file
<dependency>
<groupId>org.dcm4che</groupId>
<artifactId>dcm4che-core</artifactId>
<version>3.3.0</version>
</dependency>
<dependency>
<groupId>org.dcm4che.tool</groupId>
<artifactId>dcm4che-tool-storescp</artifactId>
<version>3.3.0</version>
</dependency>
5th step: - If this exception occurs: No Image Reader of class com.sun.media.imageioimpl.plugins.jpeg.CLibJPEGImageReader available for format:jpeg
Then jai-imageio-1.0.01.jar of c-libs seem to be not available but in fact it is there which means The api (jai_imageio-xx.jar) is sometimes not sufficient to be seen on runtime by JVM. Copying the clib_jiio.dll, clib_jiio_sse2.dll and clib_jiio_util.dll into C:\Program Files (x86)\Java\jdk1.7.0\jre\bin didn't work out either for me.
Solution:
Download and run the jai_imageio-1_0_01-lib-windows-i586-jre.exe from here:
Note: Please select C:\Program Files (x86)\Java\jdk1.7.0\jre\bin to install. Actually it also copies the native libraries into bin directory but for some reasons, copying manually the dll files didn't work for me. Maybe it also copies the jar files.
6th step: Finally let's run the dcm to jpeg conversion:
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.Iterator;
import javax.imageio.ImageIO;
import javax.imageio.ImageReader;
import javax.imageio.stream.ImageInputStream;
import org.apache.log4j.Logger;
public class Dcm2JpgTest {
private final static Logger logger = Logger.getLogger(Dcm2JpgTest.class);
public static void main(String[] args) throws IOException {
try{
File src = new File
("C:/Temp/1.2.392.200036.9116.2.5.1.48.1221208977.1408947738.730238");
File dest = new File("C:/Temp/test.jpg");
Dcm2Jpeg dcm2jpg= new Dcm2Jpeg();
dcm2jpg.convert(src, dest);
} catch(IOException e){
logger.error(e.getMessage());
} catch(Exception e){
logger.error(e.getMessage());
}
}
}
Upvotes: 5
Views: 4276