android解析并显示dicom文件的数据和图像

Dicom全称是医学数字图像与通讯,这里android程序代码解析diocm格式文件并显示dicom的图片和数据。

这里解析dicom使用的是dcm4che3的jar包来解析,还要另外导入一个slf的jar包。

程序效果:

程序实现数据的基本显示,和图片可放大缩小效果。
但是也看到程序中有些dicom文件没有图片显示,是因为不能读取压缩的图片的dicom文件的图片数据的读取!

程序代码:
布局文件就不显示了,上面多个TextView,下面是一个Photoview。
java代码:

package com.liwenzhi.dcm;

import android.app.Activity;
import android.graphics.Bitmap;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.TextView;
import com.liwenzhi.dcm.photoview.PhotoView;
import org.dcm4che3.android.Raster;
import org.dcm4che3.android.RasterUtil;
import org.dcm4che3.android.imageio.dicom.DicomImageReader;
import org.dcm4che3.data.Attributes;
import org.dcm4che3.data.Tag;
import org.dcm4che3.data.VR;
import org.dcm4che3.io.DicomInputStream;

import java.io.*;

/**
 * 解析并显示dicom文件的示例
 */
public class MyActivity extends Activity {

    PhotoView iv;
    TextView tv_picture;
    TextView tv_name;
    TextView tv_birthday;
    TextView tv_institution;
    TextView tv_station;
    TextView tv_StudyDescription;
    TextView tv_SeriesDescription;
    TextView tv_manufacturerModelName;
    TextView tv_manufacturer;
    TextView tv_StudyDate;
    int number = 1;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        initView();
        initEvent();
    }

    private void initView() {
        iv = (PhotoView) findViewById(R.id.iv);
        tv_picture = (TextView) findViewById(R.id.tv_picture);
        tv_name = (TextView) findViewById(R.id.tv_name);
        tv_birthday = (TextView) findViewById(R.id.tv_birthday);
        tv_institution = (TextView) findViewById(R.id.tv_institution);
        tv_station = (TextView) findViewById(R.id.tv_station);
        tv_StudyDescription = (TextView) findViewById(R.id.tv_StudyDescription);
        tv_SeriesDescription = (TextView) findViewById(R.id.tv_SeriesDescription);
        tv_manufacturer = (TextView) findViewById(R.id.tv_manufacturer);
        tv_manufacturerModelName = (TextView) findViewById(R.id.tv_manufacturerModelName);
        tv_StudyDate = (TextView) findViewById(R.id.tv_StudyDate);

    }


    private void initEvent() {


    }


    public void next(View view) {
        //清空数据
        tv_name.setText("");
        tv_birthday.setText("");
        tv_institution.setText("");
        tv_station.setText("");
        tv_StudyDescription.setText("");
        tv_manufacturer.setText("");
        tv_manufacturerModelName.setText("");
        tv_StudyDate.setText("");
        tv_SeriesDescription.setText("");
        iv.setImageResource(R.drawable.ic_launcher);

        String fileName = "test" + number + ".dcm";
        tv_picture.setText(fileName);
        //加载图片
        btnLoadClicked(fileName);

        number++;
        if (number == 10)
            number = 1;

    }


    void btnLoadClicked(String fileName) {
        String testFileName = this.getCacheDir().getAbsolutePath() + "/" + fileName;
        File file = new File(testFileName);
        if (file.exists())
            file.delete();
        InputStream is;
        try {
            is = getAssets().open(fileName);
            copyFile(is, file);      //从assets中复制到本地路径
            readFile(testFileName); //读取本地路径中的dicom文件
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    /**
     * 复制文件的代码
     */
    void copyFile(InputStream is, File dstFile) {
        try {

            BufferedInputStream bis = new BufferedInputStream(is);
            BufferedOutputStream bos = new BufferedOutputStream(
                    new FileOutputStream(dstFile), 1024);
            byte buf[] = new byte[1024];
            int c = 0;
            c = bis.read(buf);
            while (c > 0) {
                bos.write(buf, 0, c);
                c = bis.read(buf);
            }
            bis.close();
            bos.close();

        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    /**
     * 读取文件数据
     */
    public void readFile(String filePath) {
        DicomImageReader dr = new DicomImageReader();
        try {
            File file = new File(filePath);
            //dcm文件输入流
            DicomInputStream dcmInputStream = new DicomInputStream(file);
            //属性对象
            Attributes attrs = dcmInputStream.readDataset(-1, -1);
            //输出所有属性信息
            Log.e("TAG", "输出所有属性信息1:" + attrs);

            //获取行
            int row = attrs.getInt(Tag.Rows, 1);
            //获取列
            int columns = attrs.getInt(Tag.Columns, 1);
            //窗宽窗位
            float win_center = attrs.getFloat(Tag.WindowCenter, 1);
            float win_width = attrs.getFloat(Tag.WindowWidth, 1);
            Log.e("TAG", "" + "row=" + row + ",columns=" + row + "row*columns = " + row * columns);

            Log.e("TAG", "" + "win_center=" + win_center + ",win_width=" + win_width);
            //获取像素数据 ,这个像素数据不知道怎么用!!!,得到的是图片像素的两倍的长度
            //后面那个 raster.getByteData()是图片的像素数据
            byte[] b = attrs.getSafeBytes(Tag.PixelData);
            if (b != null) {
                Log.e("TAG", "" + "b.length=" + b.length);
            } else {
                Log.e("TAG", "" + "b==null");
            }

            //修改默认字符集为GB18030
            attrs.setString(Tag.SpecificCharacterSet, VR.CS, "GB18030");//解决中文乱码问题

            Log.e("TAG", "输出所有属性信息2:" + attrs);
            String patientName = attrs.getString(Tag.PatientName, "");
            tv_name.setText("姓名:" + patientName);

            //生日
            String patientBirthDate = attrs.getString(Tag.PatientBirthDate, "");
            tv_birthday.setText("生日:" + patientBirthDate);

            //机构
            String institution = attrs.getString(Tag.InstitutionName, "");
            tv_institution.setText("机构:" + institution);

            //站点
            String station = attrs.getString(Tag.StationName, "");
            tv_station.setText("站点:" + station);

            //制造商
            String Manufacturer = attrs.getString(Tag.Manufacturer, "");
            tv_manufacturer.setText("制造商:" + Manufacturer);

            //制造商模型
            String ManufacturerModelName = attrs.getString(Tag.ManufacturerModelName, "");
            tv_manufacturerModelName.setText("制造商模型:" + ManufacturerModelName);


            //描述--心房
            String description = attrs.getString(Tag.StudyDescription, "");
            tv_StudyDescription.setText("描述--心房:" + description);
            //描述--具体
            String SeriesDescription = attrs.getString(Tag.SeriesDescription, "");
            tv_SeriesDescription.setText("描述--具体:" + SeriesDescription);

            //描述时间
            String studyData = attrs.getString(Tag.StudyDate, "");
            tv_StudyDate.setText("描述时间:" + studyData);


            dr.open(file);
//            Attributes ds = dr.getAttributes();
//            String wc = ds.getString(Tag.WindowCenter);
//            String ww = ds.getString(Tag.WindowWidth);
//            Log.e("TAG", "" + "wc=" + wc + ",ww=" + ww);
            Raster raster = dr.applyWindowCenter(0, (int) win_width, (int) win_center);
//            Log.e("TAG", "" + "raster.getWidth()=" + raster.getWidth() + ",raster.getHeight()=" + raster.getHeight());
//            Log.e("TAG", "" + "raster.getByteData().length=" + raster.getByteData().length);

//            Bitmap bmp = RasterUtil.gray8ToBitmap(raster.getWidth(), raster.getHeight(), raster.getByteData());
//            Log.e("TAG", "b==raster.getByteData()" + (b == raster.getByteData()));
            Bitmap bmp = RasterUtil.gray8ToBitmap(columns, row, raster.getByteData());
            iv.setImageBitmap(bmp);

        } catch (Exception e) {
            Log.e("TAG", "" + e);
        }

    }


}

上面代码中有些注释了的代码,说的是使用另一种方法获取WindowCenter和WindowWidth。

上面还有一句重要的代码:

//修改默认字符集为GB18030
  attrs.setString(Tag.SpecificCharacterSet, VR.CS, "GB18030");//解决中文乱码问题

默认情况下,有些dicom文件没有设置编码方式,如果有中文字符出现,获取会得到乱码,这里编码方式只能设置成“GB08030”,才能成功解析中文数据,不能设置”GBK“或”UTF-8“。

解析没有压缩图片的dicom文件,使用dcm4che3来解析是没有问题的。
但是解析有压缩图片的dicom文件就不行了,
目前也是没有任何框架或代码能实现直接用代码解析压缩图片的dicom文件。

网上有些是在java服务器端解压dicom后再读取,或使用一些工具解压dicom,
反正目前用代码只能读取到没有压缩图片的dicom文件。

我的代码和jar包,dicom文件等数据请看我的项目。
地址:https://github.com/liwenzhi/dcm4che3Demo

其中,dicom还有很多其他的tag数据,这个具体要看dicom文件详细解析了。

下面是某些tag,以及tag对应的VR。

string getVR(string tag)
{
    switch (tag)
    {
        case "0002,0000"://文件元信息长度
            return "UL";
            break;
        case "0002,0010"://传输语法
            return "UI";
            break;
        case "0002,0013"://文件生成程序的标题
            return "SH";
            break;
        case "0008,0005"://文本编码
            return "CS";
            break;
        case "0008,0008":
            return "CS";
            break;
        case "0008,1032"://成像时间
            return "SQ";
            break;
        case "0008,1111":
            return "SQ";
            break;
        case "0008,0020"://检查日期
            return "DA";
            break;
        case "0008,0060"://成像仪器
            return "CS";
            break;
        case "0008,0070"://成像仪厂商
            return "LO";
            break;
        case "0008,0080":
            return "LO";
            break;
        case "0010,0010"://病人姓名
            return "PN";
            break;
        case "0010,0020"://病人id
            return "LO";
            break;
        case "0010,0030"://病人生日
            return "DA";
            break;
        case "0018,0060"://电压
            return "DS";
            break;
        case "0018,1030"://协议名
            return "LO";
            break;
        case "0018,1151":
            return "IS";
            break;
        case "0020,0010"://检查ID
            return "SH";
            break;
        case "0020,0011"://序列
            return "IS";
            break;
        case "0020,0012"://成像编号
            return "IS";
            break;
        case "0020,0013"://影像编号
            return "IS";
            break;
        case "0028,0002"://像素采样1为灰度3为彩色
            return "US";
            break;
        case "0028,0004"://图像模式MONOCHROME2为灰度
            return "CS";
            break;
        case "0028,0010"://row高
            return "US";
            break;
        case "0028,0011"://col宽
            return "US";
            break;
        case "0028,0100"://单个采样数据长度
            return "US";
            break;
        case "0028,0101"://实际长度
            return "US";
            break;
        case "0028,0102"://采样最大值
            return "US";
            break;
        case "0028,1050"://窗位
            return "DS";
            break;
        case "0028,1051"://窗宽
            return "DS";
            break;
        case "0028,1052":
            return "DS";
            break;
        case "0028,1053":
            return "DS";
            break;
        case "0040,0008"://文件夹标签
            return "SQ";
            break;
        case "0040,0260"://文件夹标签
            return "SQ";
            break;
        case "0040,0275"://文件夹标签
            return "SQ";
            break;
        case "7fe0,0010"://像素数据开始处
            return "OW";
            break;
        default:
            return "UN";
            break;
    }
}

上面程序的后台其实是有打Log数据的,

如图:


上面图说的tag包含的数据,其实就是整个dataElement中的数据值。

可以根据输出的dicom数据,看看你需要那个tag信息,再用代码获取,也是可以的。

dicom传输的相关知识也只能介绍到这里,很多知识还没有理解透彻,只是把这些知识罗列出来,给大家参考一下!

dicom文件解析知识的其他地址:

1.dicom文件详解


2.dicom文件的值类型VR详解


3.dicom文件tag详解


4.android 解析并显示dicom文件的数据和图像


5.java代码使用ImageJ解析dicom文件成图片


前面5个是我自己写的,后面是一些我自己看过的相关资料:

6.Dicom文件解析


7.使用dcm4che3获取Dicom的bmp格式缩略图


8.使用dcm4che3解析DICOM中,中文乱码问题


9.使用dcm4che3对jpeg压缩的dcm文件进行解压


10.DICOM的常用Tag分类和说明


11.dicom的大牛zssure的博客,几十篇文章


12.dicom协议中文文档下载


13.Sante DICOM Editor 4,查看dicom文件的工具,直接打开用


共勉:在最好的年龄去干些最有意义的事情。