dcm(dicom)医学影像android通过dcmtk解析

dcm(dicom)医学影像android通过dcmtk解析,第1张

概述DICOM是由美国放射学院(ACR)和美国国家电气制造商协会(NEMA)开发的标准。在全球范围内用于存储,交换和传输医学图像。DICOM在现代放射成像的发展中一直处于核心地位:DICOM结合了诸如放射成像,超声检查,计算机断层扫描(CT),磁共振成像(MRI)和放射治疗等成像方式的标准。DICOM包括用于图像

DICOM是由美国放射学院(ACR)和美国国家电气制造商协会(NEMA)开发的标准。在全球范围内用于存储,交换和传输医学图像。DICOM在现代放射成像的发展中一直处于核心地位:DICOM结合了诸如放射成像,超声检查,计算机断层扫描(CT),磁共振成像(MRI)和放射治疗等成像方式的标准。DICOM包括用于图像交换(例如,通过诸如DVD之类的便携式介质),图像压缩,3-D可视化,图像表示和结果报告的协议。更多信息可去维基百科查看。地址,
dcm图像类似于这样


首先说明下这是一个没有运行成功的demo,目前关于dcm查了国内外的网站,在androID上解析的文章太少。dicom有三个比较有名的开源框架(dcmtk、dcm4che、fo-dicom),参考博客,我自己试过两个框架dcm4che,dcmtk,但都没有成功。dcm4che比较简单,有jar包,也有demo,链接,但我运行文件没有成功。本文重点说说另一个开源框架,三个开源框架中评分最高的dcmtk。代码是c编写开源的,由于没有androID的参考代码,我参照windows平台参考代码的链接,ios平台的demo参考demo链接1参考demo2分别编写了编写了一次。但运行都只是读出了文件部分信息,读取图片信息没有成功。
由于dcmtk是开源,并且没有编译为linux可用的so,所以需要自己编译下,windows系统可用子系统 ubuntu(WSL)编译,参考博客。特别注意因为ndk编译时代码有用模拟器验证,如果ubuntu没有配置sdk,可将CMake/dcmtkUseandroidSDK.cmake文件中部分有关于模拟器的代码执行return(),还有return()需要大概注意下位置。我在编译时有过在function(DCMTK_SETUP_ANDROID_EMulATOR)中return()在if(NOT ANDROID_TEMPORARY_fileS_LOCATION)这个判断前导致报没有临时输出文件错误,如图


对应于那些地方需要用到return(),可参照错误日志类似于下图的地方需要模拟器就写上return()


这是针对于编译,编译后需要运行,由于AndroID的imagevIEw一般可展示的就是png,jpg,bitmap,我参考上述博客ios的demo,决定将文件读为int数组再去转bitmap在androID上显示,其实参考发现这种输出有点类似于opencv输出构造bitmap显示。具体代码参照windows代码如下,c++模式的jni

extern "C" JNIEXPORT jobjectJNICALL Java_com_lsp_myapplication_MainActivity_getIntFromDcm(jnienv *env, Jstring inputPath_) {       // __androID_log_print(ANDROID_LOG_ERROR, "tag", "inputPath_:%s",inputPath_);        const char *input_pa_name=  "/storage/emulated/0/Download/aa.dcm";        //const char *input_pa_name=  env->GetStringUTFChars(inputPath_, 0);        //chardata= inputPath_ ? env->GetStringUTFChars(inputPath_, NulL) : NulL;         //env->ReleaseStringUTFChars(filename, chardata);        //const char *chardata =  env->GetStringUTFChars(filename, JNI_FALSE);        OFfilename *file=new OFfilename(input_pa_name,false);        DcmfileFormat *dcmfileFormat = new DcmfileFormat();        OFCondition status = dcmfileFormat->loadfile(*file);        if (status.good()) {          __androID_log_print(ANDROID_LOG_ERROR, "tag", "6啦");            OFString patIEntname;            DcmDataset *dcmDataset = dcmfileFormat->getDataset();            OFCondition condition = dcmDataset->findAndGetoFString(DCM_PatIEntname,patIEntname);            if (condition.good()) {                __androID_log_print(ANDROID_LOG_ERROR, "tag", "condition.good = %s", patIEntname.c_str());               //LOGE(patIEntname.c_str());            } else {             __androID_log_print(ANDROID_LOG_ERROR, "tag", "condition. BAD");                //LOGE("condition. BAD");            }            const char *transferSyntax;            DcmMetaInfo *dcmMetaInfo = dcmfileFormat->getMetaInfo();            OFCondition transferSyntaxOfCondition = dcmMetaInfo->findAndGetString(DCM_TransferSyntaxUID, transferSyntax);            __androID_log_print(ANDROID_LOG_ERROR,"tag", "transferSyntaxOfCondition  %s", transferSyntaxOfCondition.text());            __androID_log_print(ANDROID_LOG_ERROR,"tag", "transferSyntax  %s", transferSyntax);            // 获得当前的窗宽 窗位            float64 windowCenter;            dcmDataset->findAndGetfloat64(DCM_WindowCenter, windowCenter);            __androID_log_print(ANDROID_LOG_ERROR,"tag", "windowCenter %f",windowCenter);            float64 windowWIDth;            dcmDataset->findAndGetfloat64(DCM_WindowWIDth, windowWIDth);            __androID_log_print(ANDROID_LOG_ERROR,"tag", "windowWIDth %f",windowWIDth);            E_TransferSyntax xfer = dcmDataset->getoriginalXfer();            __androID_log_print(ANDROID_LOG_ERROR,"tag", "E_TransferSyntax %d", xfer);            const char * model;            dcmDataset->findAndGetString(DCM_Modality, model);            __androID_log_print(ANDROID_LOG_ERROR,"tag", "-------------Model: %s",model);            std::string losslesstransUID = "1.2.840.10008.1.2.4.70";            std::string losstransUID = "1.2.840.10008.1.2.4.51";            std::string losslessp14 = "1.2.840.10008.1.2.4.57";            std::string lossyP1 = "1.2.840.10008.1.2.4.50";            std::string lossyRLE = "1.2.840.10008.1.2.5";            bool isYaSuo = 0;            DicomImage *dcmImage = NulL;            //todo            if (transferSyntax == NulL){                isYaSuo = false;// 无压缩            }else {            		if (transferSyntax == losslesstransUID || transferSyntax == losstransUID ||            			transferSyntax == losslessp14 || transferSyntax == lossyP1)            		{            			//对压缩的图像像素进行解压            			DJDecoderRegistration::registerCodecs();            			dcmDataset->chooseRepresentation(EXS_littleEndianExplicit, NulL);            			DJDecoderRegistration::cleanup();            			isYaSuo = true;// 有压缩            		}            		else if (transferSyntax == lossyRLE)            		{            			DcmRLEDecoderRegistration::registerCodecs();            			dcmDataset->chooseRepresentation(EXS_littleEndianExplicit, NulL);            			DcmRLEDecoderRegistration::cleanup();            			isYaSuo = true;// 有压缩            		}            		else            		{            			isYaSuo = false;// 无压缩            		}            	}                long imageHeight = 0;                long imageWIDth = 0;            	if (isYaSuo){            	 //dcmImage = new DicomImage(input_pa_name);            		dcmImage = new DicomImage((DcmObject*)dcmDataset, dcmDataset->getoriginalXfer(), CIF_TakeOverExternalDataset);            		if (dcmImage->getStatus() == EIS_normal) {            			imageWIDth = dcmImage->getWIDth();            			imageHeight = dcmImage->getHeight();            			if (windowWIDth < 1) {            				// 设置窗宽窗位            				dcmImage->setRoiWindow(0, 0, imageWIDth, imageHeight);            				// 重新对winCenter, winWIDth赋值            				dcmImage->getwindow(windowCenter, windowWIDth);            			}            		}            	}else{            		dcmImage = new DicomImage(dcmMetaInfo, dcmDataset->getoriginalXfer(), CIF_TakeOverExternalDataset);            		if (dcmImage->getStatus() == EIS_normal) {            			imageWIDth = dcmImage->getWIDth();            			imageHeight = dcmImage->getHeight();            			if (windowWIDth < 1) {            				dcmImage->setRoiWindow(0, 0, imageWIDth, imageHeight);            				dcmImage->getwindow(windowCenter, windowWIDth);            			}            		}            	}            //NSArray *paths = NSSearchPathForDirectorIEsInDomains(NSCachesDirectory,NSUserDomainMask,YES);            //Nsstring *pathCache = [paths objectAtIndex:0];           //LOGE("----------Cache:---%@",pathCache);            //todo            long depth = dcmImage->getDepth();            long size = dcmImage->getoutputDataSize(8);            //dcmImage->setwindow(windowCenter, windowWIDth);            __androID_log_print(ANDROID_LOG_ERROR,"tag", "png height %ld ", imageHeight);            __androID_log_print(ANDROID_LOG_ERROR,"tag", "png wIDth %ld ", imageWIDth);            __androID_log_print(ANDROID_LOG_ERROR,"tag", "png depth %ld ", depth);            __androID_log_print(ANDROID_LOG_ERROR,"tag", "png size %ld ", size);            __androID_log_print(ANDROID_LOG_ERROR,"tag", "int size %ld",sizeof(int));            unsigned char *pixelData = (unsigned char *) (dcmImage->getoutputData(8, 0, 0));            long size1 = imageHeight * imageWIDth;            unsigned char temp = NulL;            jint * p = (int *)malloc(imageWIDth * imageHeight * sizeof(int));    //        int *p = new int[size1];           if(strcmp(model,"SC") == 0){            __androID_log_print(ANDROID_LOG_ERROR,"tag", "-------执行SC------");                unsigned char r = NulL;                unsigned char g = NulL;                unsigned char b = NulL;                for (int j = 0; j < size1; ++j) {                    r = pixelData[j * 3] ;                    g = pixelData[j * 3 + 1] ;                    b = pixelData[j * 3 + 2] ;                    p[j] = r  | g << 8 | b << 16 | 0xff000000;                }           }else{          __androID_log_print(ANDROID_LOG_ERROR,"tag", "-------执行elseSC------");               for (int i = 0; i < size1; ++i) {                   temp = pixelData[i];                   p[i] = temp | (temp << 8) | (temp << 16) | 0xff000000;               }           }            if (pixelData != NulL) {                __androID_log_print(ANDROID_LOG_ERROR,"tag", "pixelData not null");            }            jintArray jntarray = env->NewIntArray( size1);             env->ReleaseIntArrayElements(jntarray, p, 0);             jclass myClass = env->FindClass("com/lsp/myapplication/DcmBean");             // 获取类的构造函数,记住这里是调用无参的构造函数             jmethodID ID = env->getmethodID(myClass, "<init>", "()V");             // 创建一个新的对象             jobject dcmBean_ = env->NewObject(myClass, ID);             jfIEldID w = env->GetFIEldID(myClass, "wIDth", "J");             jfIEldID h = env->GetFIEldID(myClass, "height", "J");             jfIEldID dcm = env->GetFIEldID(myClass, "dcm", "[I");             env->SetLongFIEld(dcmBean_, w, imageWIDth);             env->SetLongFIEld(dcmBean_, h, imageHeight);             env->SetobjectFIEld(dcmBean_, dcm, jntarray);            free(pixelData);            free(p);            delete dcmImage;            delete dcmfileFormat;            return myClass;     }    return NulL;}
参照ios平台的代码展示如下,c++模式的jni```cppextern "C" JNIEXPORT jobjectJNICALL Java_com_lsp_myapplication_MainActivity_getIntFromDcm(jnienv *env, Jstring inputPath_) {        __androID_log_print(ANDROID_LOG_ERROR, "tag", "inputPath_:%s",inputPath_);        __androID_log_print(ANDROID_LOG_ERROR, "tag", "1啦");        const char *chardata=  "/storage/emulated/0/Download/Imagefilename002.dcm";        //chardata= inputPath_ ? env->GetStringUTFChars(inputPath_, NulL) : NulL;         //env->ReleaseStringUTFChars(filename, chardata);        //const char *chardata =  env->GetStringUTFChars(filename, JNI_FALSE);        OFfilename *file=new OFfilename(chardata,false);        DcmfileFormat *dcmfileFormat = new DcmfileFormat();        OFCondition status = dcmfileFormat->loadfile(*file);        if (status.good()) {            OFString patIEntname;            DcmDataset *dcmDataset = dcmfileFormat->getDataset();            OFCondition condition = dcmDataset->findAndGetoFString(DCM_PatIEntname,patIEntname);            if (condition.good()) {                __androID_log_print(ANDROID_LOG_ERROR, "tag", "condition.good = %s", patIEntname.c_str());               //LOGE(patIEntname.c_str());            } else {             __androID_log_print(ANDROID_LOG_ERROR, "tag", "condition. BAD");                //LOGE("condition. BAD");            }            const char *transferSyntax;            DcmMetaInfo *dcmMetaInfo = dcmfileFormat->getMetaInfo();            OFCondition transferSyntaxOfCondition = dcmMetaInfo->findAndGetString(                                                                                  DCM_TransferSyntaxUID, transferSyntax);            __androID_log_print(ANDROID_LOG_ERROR,"tag", "transferSyntaxOfCondition  %s", transferSyntaxOfCondition.text());            __androID_log_print(ANDROID_LOG_ERROR,"tag", "transferSyntax  %s", transferSyntax);            // 获得当前的窗宽 窗位            float64 windowCenter;            dcmDataset->findAndGetfloat64(DCM_WindowCenter, windowCenter);           __androID_log_print(ANDROID_LOG_ERROR,"tag", "windowCenter %f",windowCenter);            float64 windowWIDth;            dcmDataset->findAndGetfloat64(DCM_WindowWIDth, windowWIDth);            __androID_log_print(ANDROID_LOG_ERROR,"tag", "windowWIDth %f",windowWIDth);            E_TransferSyntax xfer = dcmDataset->getoriginalXfer();            __androID_log_print(ANDROID_LOG_ERROR,"tag", "E_TransferSyntax %d", xfer);             const char * model;            dcmDataset->findAndGetString(DCM_Modality, model);            __androID_log_print(ANDROID_LOG_ERROR,"tag", "-------------Model: %s",model);            //NSArray *paths = NSSearchPathForDirectorIEsInDomains(NSCachesDirectory,NSUserDomainMask,YES);            //Nsstring *pathCache = [paths objectAtIndex:0];           //LOGE("----------Cache:---%@",pathCache);            // Dicom            DicomImage *m_dcmImage = NulL;            if (strcmp(transferSyntax, "1.2.840.10008.1.2.4.70") == 0) {                 __androID_log_print(ANDROID_LOG_ERROR,"tag", "-------ct解析------");                DJDecoderRegistration::registerCodecs();                OFCondition chooSEOfCondition = dcmDataset->chooseRepresentation( EXS_littleEndianExplicit, NulL);                m_dcmImage = new DicomImage((DcmObject *) dcmDataset,                                            xfer); //利用dataset生成DicomImage,需要上面的解压方法;                DJDecoderRegistration::cleanup();            }else if (strcmp(transferSyntax, "1.2.840.10008.1.2.4.80") == 0){                // LS 解码器                DJLSDecoderRegistration::registerCodecs();                OFCondition chooSEOfCondition = dcmDataset->chooseRepresentation(EXS_littleEndianExplicit, NulL);    //            if (dcmDataset->canWriteXfer(EXS_littleEndianExplicit)) {    //                OFCondition ofCondition = dcmfileFormat->savefile(filenameSave,    //                                                                  EXS_littleEndianExplicit);    //    //                if (ofCondition.good()) {    //                   LOGE("---------------保存成功----------------");    //                   LOGE("---------------------保存成功时间----%@",[self getTimeNow]);    //                }else{    //                   LOGE("-------------------Save Fail ------------------");    //                }    //            }                m_dcmImage = new DicomImage((DcmObject *) dcmDataset,                                            xfer); //利用dataset生成DicomImage,需要上面的解压方法;                DJLSDecoderRegistration::cleanup();            }else{    //            // LS 解码器    //            DJLSDecoderRegistration::registerCodecs();    //            OFCondition chooSEOfCondition = dcmDataset->chooseRepresentation(    //                                                                             EXS_littleEndianExplicit, NulL);                m_dcmImage = new DicomImage((DcmObject *) dcmDataset,                                            xfer); //利用dataset生成DicomImage,需要上面的解压方法;    //            DJLSDecoderRegistration::cleanup();            }            long height = m_dcmImage->getHeight();            long wIDth = m_dcmImage->getWIDth();            long depth = m_dcmImage->getDepth();            long size = m_dcmImage->getoutputDataSize(8);            m_dcmImage->setwindow(windowCenter, windowWIDth);            __androID_log_print(ANDROID_LOG_ERROR,"tag", "png height %ld ", height);            __androID_log_print(ANDROID_LOG_ERROR,"tag", "png wIDth %ld ", wIDth);            __androID_log_print(ANDROID_LOG_ERROR,"tag", "png depth %ld ", depth);            __androID_log_print(ANDROID_LOG_ERROR,"tag", "png size %ld ", size);            __androID_log_print(ANDROID_LOG_ERROR,"tag", "int size %ld",sizeof(int));            unsigned char *pixelData = (unsigned char *) (m_dcmImage->getoutputData(8, 0, 0));            long size1 = height * wIDth;            unsigned char temp = NulL;            jint * p = (int *)malloc(wIDth * height * sizeof(int));    //        int *p = new int[size1];           if(strcmp(model,"SC") == 0){            __androID_log_print(ANDROID_LOG_ERROR,"tag", "-------执行SC------");                unsigned char r = NulL;                unsigned char g = NulL;                unsigned char b = NulL;                for (int j = 0; j < size1; ++j) {                    r = pixelData[j * 3] ;                    g = pixelData[j * 3 + 1] ;                    b = pixelData[j * 3 + 2] ;                    p[j] = r  | g << 8 | b << 16 | 0xff000000;                }           }else{          __androID_log_print(ANDROID_LOG_ERROR,"tag", "-------执行elseSC------");               for (int i = 0; i < size1; ++i) {                   temp = pixelData[i];                   p[i] = temp | (temp << 8) | (temp << 16) | 0xff000000;               }           }            if (pixelData != NulL) {                __androID_log_print(ANDROID_LOG_ERROR,"tag", "pixelData not null");            }            jintArray jntarray = env->NewIntArray( size1);             env->ReleaseIntArrayElements(jntarray, p, 0);             jclass myClass = env->FindClass("com/lsp/myapplication/DcmBean");             // 获取类的构造函数,记住这里是调用无参的构造函数             jmethodID ID = env->getmethodID(myClass, "<init>", "()V");             // 创建一个新的对象             jobject dcmBean_ = env->NewObject(myClass, ID);             jfIEldID w = env->GetFIEldID(myClass, "wIDth", "J");             jfIEldID h = env->GetFIEldID(myClass, "height", "J");             jfIEldID dcm = env->GetFIEldID(myClass, "dcm", "[I");             env->SetLongFIEld(dcmBean_, w, wIDth);             env->SetLongFIEld(dcmBean_, h, height);             env->SetobjectFIEld(dcmBean_, dcm, jntarray);            free(pixelData);            free(p);            delete m_dcmImage;            delete dcmfileFormat;            return myClass;     }    return NulL;}

后记:具体的dcm图像可以去https://dicomlibrary.com这个网站下载,代码是一个参照ios和windows平台的写的,但运行起来只读出了部分信息,图片信息没有读取成功,欢迎读者完善和交流,只是自己找遍了国内国外都没找到dcmtk在androID的实例代码,可能是这个应用的领域只是医学显得小众不像ffmpeg一样可以有更多参考,希望能起个抛砖引玉的效果,带来多些实例代码,另外目前只编译了arm64-v8a架构后续补上其他的。

最后留下一个我的代码下载地址

总结

以上是内存溢出为你收集整理的dcm(dicom)医学影像android通过dcmtk解析全部内容,希望文章能够帮你解决dcm(dicom)医学影像android通过dcmtk解析所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: https://www.outofmemory.cn/web/1059323.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-25
下一篇 2022-05-25

发表评论

登录后才能评论

评论列表(0条)

保存