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文件的工具,直接打开用
共勉:在最好的年龄去干些最有意义的事情。