tift2dcm(tiff2Dicom、tiff轉dicom)

膠片掃描tiff文件轉dicom

	首先感謝您花時間閱讀這篇文章,在您爲tiff轉dicom文件而煩惱的時候,
	不防認真的看看這篇文章能否幫到您,也希望您對文章中有錯誤的地方請不吝賜教。先謝謝各位筆下留情!
	好了廢話不多說,直接上主題!

使用的語言 Java

使用的開源庫 Dcm4che

需要用到的jar包:
commons-cli-1.2.jar
dcm4che-core-5.12.0.jar
dcm4che-imageio-5.12.0.jar
dcm4che-tool-common-5.12.0.jar

額外使用到的jar ij.jar

dcm4che裏面有個工具類是dcm4che-tool-jpg2dcm,裏面有個類是Jpg2Dcm

Jpg2Dcm裏面的 代碼片.

    public void convert(File infile, File outfile) throws IOException {
        FileType fileType = null;
        try {
            fileType = FileType.valueOf(infile);
        } catch (IllegalArgumentException e) {
            throw new IllegalArgumentException(MessageFormat.format(rb.getString("invalid-file-ext"), infile));
        }

        fileLength = infile.length();
        if (fileLength > MAX_FILE_SIZE)
            throw new IllegalArgumentException(MessageFormat.format(rb.getString("file-too-large"), infile));

        try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(infile))) {
            if (!parseHeader(fileType, bis))
                throw new IOException(MessageFormat.format(rb.getString("failed-to-parse"), fileType, infile));

            int itemLen = (int) fileLength;
            try (DicomOutputStream dos = new DicomOutputStream(outfile)) {
                dos.writeDataset(metadata.createFileMetaInformation(fileType.getTransferSyntaxUID()), metadata);
                dos.writeHeader(Tag.PixelData, VR.OB, -1);
                dos.writeHeader(Tag.Item, null, 0);
                if (jpegHeader != null && noAPPn) {
                    int offset = jpegHeader.offsetAfterAPP();
                    itemLen -= offset - 3;
                    dos.writeHeader(Tag.Item, null, (itemLen + 1) & ~1);
                    dos.write((byte) -1);
                    dos.write((byte) JPEG.SOI);
                    dos.write((byte) -1);
                    dos.write(buffer, offset, headerLength - offset);
                } else {
                    dos.writeHeader(Tag.Item, null, (itemLen + 1) & ~1);
                    dos.write(buffer, 0, headerLength);
                }
                StreamUtils.copy(bis, dos, buffer);
                if ((itemLen & 1) != 0)
                    dos.write(0);
                dos.writeHeader(Tag.SequenceDelimitationItem, null, 0);
            }
        }
        System.out.println(MessageFormat.format(rb.getString("converted"), infile, outfile));
    }
 private enum FileType {
        jpeg(UID.SecondaryCaptureImageStorage, UID.JPEGBaseline1) {
            @Override
            boolean parseHeader(Jpg2Dcm main) {
                return (main.jpegHeader = new JPEGHeader(main.buffer, JPEG.SOS)).toAttributes(main.metadata) != null;
            }
        },
        mpeg(UID.VideoPhotographicImageStorage, UID.MPEG2) {
            @Override
            boolean parseHeader(Jpg2Dcm main) {
                return new MPEGHeader(main.buffer).toAttributes(main.metadata, main.fileLength) != null;
            }
        };

        private final String cuid;
        private final String tsuid;

        FileType(String cuid, String tsuid) {
            this.cuid = cuid;
            this.tsuid = tsuid;
        }

        public String getSOPClassUID() {
            return cuid;
        }

        public String getTransferSyntaxUID() {
            return tsuid;
        }

        abstract boolean parseHeader(Jpg2Dcm main);

        static FileType valueOf(File file) {
            String contentType = FileTypeMap.getDefaultFileTypeMap().getContentType(file);
            return valueOf(contentType.substring(contentType.lastIndexOf("/")+1));
        }
    }
  

以上就是Jpg2Dcm jpg轉dicom的源碼。當我用tiff的圖像去轉dicom的時候這裏就獲取不到 tiif文件的FileType。於是對源碼做了部分修改

 private enum FileType {
        jpeg(UID.SecondaryCaptureImageStorage, UID.JPEGBaseline1) {
            @Override
            boolean parseHeader(Jpg2Dcm main) {
                return (main.jpegHeader = new JPEGHeader(main.buffer, JPEG.SOS)).toAttributes(main.metadata) != null;
            }
        },
        
        tiff(UID.SecondaryCaptureImageStorage, UID.JPEGLossless) {
            @Override
            boolean parseHeader(Jpg2Dcm main) {
                return true;
            }
        },
        
        mpeg(UID.VideoPhotographicImageStorage, UID.MPEG2) {
            @Override
            boolean parseHeader(Jpg2Dcm main) {
                return new MPEGHeader(main.buffer).toAttributes(main.metadata, main.fileLength) != null;
            }
        };

        private final String cuid;
        private final String tsuid;

        FileType(String cuid, String tsuid) {
            this.cuid = cuid;
            this.tsuid = tsuid;
        }

        public String getSOPClassUID() {
            return cuid;
        }

        public String getTransferSyntaxUID() {
            return tsuid;
        }

        abstract boolean parseHeader(Jpg2Dcm main);

        static FileType valueOf(File file) {
            String contentType = FileTypeMap.getDefaultFileTypeMap().getContentType(file);
            return valueOf(contentType.substring(contentType.lastIndexOf("/")+1));
        }
    }

在FileType的枚舉類中加了一個tiff 的枚舉。UID.JPEGLossless這裏是因爲我看分析了這家醫院的膠片掃描出來的tiff文件中的傳輸語法是JPEGLossless,所以這裏用的是UID.JPEGLossless,別家醫院是不是也是這個就要看拿到的tiff文件是什麼了。(鄙人也是第一次做這個東西,有很多還是有不懂的地方)也是經過反覆實驗才成功的。耐心點看,繼續…
當然只改了這FileType這個枚舉類確實是能夠生成dicom文件,但是生成的dicom文件是打開不了的。
這時候前面說的ij.jar的jar包就用上了。
接着往下說之前先說說這個ij.jar包的來歷================================
這裏要感謝我的領導老易同志,他找到這個網站https://sourceforge.net/projects/tifftodicom/,裏面直接有個tiff轉dicom的工具,但是很遺憾,我沒能成功的將tiff轉成dicom文件。我也嘗試去反編譯裏面應用的jar包來獲取源碼,打斷點發現這裏的ij.jar獲取的像素數據 和ImageIo或者通過File文件獲取的圖像的像素數組有所差別。於是我把注意力放在這個ij.jar包的身上。
但是試了很久也沒有成功,這時候又得感謝老易同志了,有款軟件叫 Sante DICOM Viewer FREE(請大家自行去下載)。這軟件居然能把tiff轉成dicom輸出,好傢伙!用了一年多這個軟件居然沒發現。你說是不是得感謝老易同志。但是有個奇怪的地方,原本是20多兆的tiff文件,經過這個軟件以後,dicom文件變成了60多兆。通過二進制查看,像素數據足足多了3倍!經過對比,不看不知道,一看下一跳。ij.jar獲取到tiff的像素數據,前10個像素數據 打個比方 FD FA F6 E6 C1 99 7A 4E 38 36。而 用ImageIo或者File類獲取到的像素前10個像素數據,90 00 0F 51 A9 00 0F CD 3F 00。這時候Sante DICOM Viewer FREE把tiff轉出來的dicom文件(60多兆的dicom)卻是FD FD FD FA FA FA F6 F6 F6 E6 E6 E6 C1 C1 C1 99 99 99 7A 7A 7A 4E 4E 4E 38 38 38 36
36 36。簡直就是像絕望中發現了生機一樣。
之前種種的錯誤試驗就不再說了,很辛苦 很絕望…
於是我就看上了這個ij.jar。
這就是爲什麼要說用到這個ij.jar的緣故。====================================
ij.jar可以從上面說的那個網站中下載TiffConvert.jar解壓出來就用了。如果你能用TiffConvert.jar這裏面的方法直接能將Tiff轉Dicom就不用往下看了。
到這裏也快結束了。再堅持一下,再修改Jpg2Dcm的源碼

public void convert(File infile, File outfile) throws IOException {
        FileType fileType = null;
        try {
            fileType = FileType.valueOf(infile);
        } catch (IllegalArgumentException e) {
            throw new IllegalArgumentException(MessageFormat.format(rb.getString("invalid-file-ext"), infile));
        }

        fileLength = infile.length();
        if (fileLength > MAX_FILE_SIZE)
            throw new IllegalArgumentException(MessageFormat.format(rb.getString("file-too-large"), infile));
        FileInfo fileInfo = null;
		try (BufferedInputStream bis = new BufferedInputStream(
		        new FileInputStream(infile))) {
			if (fileType == FileType.tiff) {
				supplementMissingValue(metadata, Tag.SOPClassUID, fileType.getSOPClassUID());
				//用ij的包去獲取圖像的像素
				Opener opener = new Opener();
		        ImagePlus img = opener.openTiff(infile.getParentFile().getAbsolutePath(), infile.getName());
		        fileInfo = img.getFileInfo();
		        Object obj = fileInfo.pixels;
		        buffer = (byte[]) obj;
				byte[] bufNew = new byte[buffer.length * 3];
				int index = 0;
				for (int i = 0; i < buffer.length; i++) {
					for (int j = 0; j < 3; j++) {
						bufNew[index++] = buffer[i];
					}
				}
				buffer = bufNew;
				fileLength = buffer.length;
        	}else{
        		 if (!parseHeader(fileType, bis))
                     throw new IOException(MessageFormat.format(rb.getString("failed-to-parse"), fileType, infile));
        	}
           
            int itemLen = (int) fileLength;
            try (DicomOutputStream dos = new DicomOutputStream(outfile)) {
            	if(fileType == FileType.tiff){
            		Attributes fileMetaInformation = metadata.createFileMetaInformation("1.2.840.10008.1.2.1");
            		Attributes img = new Attributes();
            		img.setInt(Tag.Rows, VR.US, fileInfo.height);
            		img.setInt(Tag.Columns, VR.US, fileInfo.width);
            		img.setInt(Tag.BitsAllocated, VR.US, 8);
            		img.setInt(Tag.BitsStored, VR.US, 8);
            		img.setInt(Tag.HighBit, VR.US, 7);
            		img.setInt(Tag.SamplesPerPixel, VR.US, 3);
            		img.setString(Tag.PhotometricInterpretation, VR.CS, "RGB");
            		img.setInt(Tag.PlanarConfiguration, VR.US, 0);
            		img.setBytes(Tag.PixelData, VR.OB, buffer);
            		metadata.addAll(img);
            		MsgEmity me = MyDicomUtil.writeAttrIntoFile(fileMetaInformation, metadata, outfile);
            		if(!me.isSuccess()){
            			System.out.println(me.getMsg());
            		}
            		return;
            	}else{
            		dos.writeDataset(metadata.createFileMetaInformation(fileType.getTransferSyntaxUID()), metadata);
            	}
                dos.writeHeader(Tag.PixelData, VR.OB, -1);
                dos.writeHeader(Tag.Item, null, 0);
                if (jpegHeader != null && noAPPn) {
                    int offset = jpegHeader.offsetAfterAPP();
                    itemLen -= offset - 3;
                    dos.writeHeader(Tag.Item, null, (itemLen + 1) & ~1);
                    dos.write((byte) -1);
                    dos.write((byte) JPEG.SOI);
                    dos.write((byte) -1);
                    dos.write(buffer, offset, headerLength - offset);
				} else {
					dos.writeHeader(Tag.Item, null, (itemLen + 1) & ~1);
					dos.write(buffer, 0, headerLength);
				}
                StreamUtils.copy(bis, dos, buffer);
                if ((itemLen & 1) != 0)
                    dos.write(0);
                dos.writeHeader(Tag.SequenceDelimitationItem, null, 0);
            }
        }
        System.out.println(MessageFormat.format(rb.getString("converted"), infile, outfile));
    }

//用ij的包去獲取圖像的像素
Opener opener = new Opener();
ImagePlus img = opener.openTiff(infile.getParentFile().getAbsolutePath(), infile.getName());
fileInfo = img.getFileInfo();
Object obj = fileInfo.pixels;
buffer = (byte[]) obj;
byte[] bufNew = new byte[buffer.length * 3];
int index = 0;
for (int i = 0; i < buffer.length; i++) {
for (int j = 0; j < 3; j++) {
bufNew[index++] = buffer[i];
}
}
buffer = bufNew;
fileLength = buffer.length;
所以這段代碼就是獲取像素然後把每個像素複製3邊,再賦值內存中的buffer數組即可。至於爲什麼每個像素會翻了3倍目前確實無法得知。
後面還有一段是
if(fileType == FileType.tiff){
Attributes fileMetaInformation = metadata.createFileMetaInformation(“1.2.840.10008.1.2.1”);
Attributes img = new Attributes();
img.setInt(Tag.Rows, VR.US, fileInfo.height);
img.setInt(Tag.Columns, VR.US, fileInfo.width);
img.setInt(Tag.BitsAllocated, VR.US, 8);
img.setInt(Tag.BitsStored, VR.US, 8);
img.setInt(Tag.HighBit, VR.US, 7);
img.setInt(Tag.SamplesPerPixel, VR.US, 3);
img.setString(Tag.PhotometricInterpretation, VR.CS, “RGB”);
img.setInt(Tag.PlanarConfiguration, VR.US, 0);
img.setBytes(Tag.PixelData, VR.OB, buffer);
metadata.addAll(img);
MsgEmity me = MyDicomUtil.writeAttrIntoFile(fileMetaInformation, metadata, outfile);
if(!me.isSuccess()){
System.out.println(me.getMsg());
}
return;
}
將圖片中的信息賦值到 Attributes 中,再輸出到文件即可。
MyDicomUtil.writeAttrIntoFile這個只是封裝了dem4che裏面的方法 將Attributes 輸出到文件的,並不是我自己寫的方法。
到這裏就結束了,最後也可以將tiff轉成dicom文件。最後原諒我不能上傳轉換以後的dicom文件,因爲都是醫院病人的真實圖像!
網上也有很多tiff轉dicom的工具,但是這個還是比較簡單的。還有的是把20多兆壓縮成7兆左右,就是前面提到的JPEGLossless壓縮語法,這個算法出來的dicom只有7兆左右,但是本人能力不足資歷尚淺,也沒有那麼多的時間和精力去處理,能搞定這個60多兆的已經是最大的幸運了。項目6月底上線。也算是功德圓滿了!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章