diff --git a/DIDKit/App/Model/FileModel.cpp b/DIDKit/App/Model/FileModel.cpp index cb0ec30..0c1d66d 100644 --- a/DIDKit/App/Model/FileModel.cpp +++ b/DIDKit/App/Model/FileModel.cpp @@ -121,32 +121,44 @@ void FileModel::setModelData() std::map::iterator patientIter; for(patientIter = mPatientList->begin();patientIter != mPatientList->end(); ++patientIter) { - FileItem* patient = new FileItem( - FileItemDataPointer(new QVariant(QString("Patient: %1, Name: %2, Birth Date: %3") - .arg(patientIter->first.c_str(),patientIter->second->patient_name.c_str(),patientIter->second->birth_date.c_str()))),mRootItem); + FileItem* patient = new FileItem(FileItemDataPointer( + new QVariant( + QString("Patient: %1, Name: %2, Birth Date: %3") + .arg(patientIter->first.c_str(), + patientIter->second->patient_name.c_str(), + patientIter->second->birth_date.c_str()))) + ,mRootItem); //Study Node std::map::iterator studyIter; StudiesMapType* studyMap = patientIter->second->studies; for(studyIter = studyMap->begin();studyIter != studyMap->end(); ++studyIter) { - FileItem* study = new FileItem(FileItemDataPointer(new QVariant(QString("Study: %1, Date: %2") - .arg(studyIter->first.c_str(), studyIter->second->study_date.c_str()))),patient); + FileItem* study = new FileItem(FileItemDataPointer( + new QVariant(QString("Study: %1, Date: %2") + .arg(studyIter->first.c_str(), + studyIter->second->study_date.c_str()))),patient); //Series Node std::map::iterator seriesIter; SeriesMapType* seriesMap = studyIter->second->series; for(seriesIter = seriesMap->begin();seriesIter != seriesMap->end(); ++seriesIter) { - FileItem* series = new FileItem(FileItemDataPointer(new QVariant(QString("Series: ")+seriesIter->first.c_str())),study); + FileItem* series = new FileItem(FileItemDataPointer( + new QVariant(QString("Series: ")+seriesIter->first.c_str())),study); //Image Set Node std::map::iterator imageSetIter; ImageSetMapType* imageSetMap = seriesIter->second; - for(imageSetIter = imageSetMap->begin();imageSetIter != imageSetMap->end(); ++imageSetIter) + for(imageSetIter = imageSetMap->begin(); + imageSetIter != imageSetMap->end(); + ++imageSetIter) { ExtendMedicalImageProperties* property = imageSetIter->second; - FileItem* imageSet = new FileItem(FileItemDataPointer(new QVariant(QString("ImageSet: ")+imageSetIter->first.c_str() + QString(" ") - + QString::number(property->GetFileNames().size()))) - , series - , FileItemActionPointer(new FileItemAction(mTableModel,property))); + FileItem* imageSet = + new FileItem(FileItemDataPointer( + new QVariant(QString("ImageSet: ") + + imageSetIter->first.c_str() + QString(" ") + + QString::number(property->GetFileNames().size()))) + , series + , FileItemActionPointer(new FileItemAction(mTableModel,property))); } } diff --git a/DIDKit/App/Model/FileModel.h b/DIDKit/App/Model/FileModel.h index 737b166..0e7e204 100644 --- a/DIDKit/App/Model/FileModel.h +++ b/DIDKit/App/Model/FileModel.h @@ -14,14 +14,17 @@ public: explicit FileModel(PropertyTableModel* aTableModel, QObject *parent = nullptr); ~FileModel() override; + // override superclass method QVariant data(const QModelIndex &aIndex, int aRole) const override; -// Qt::ItemFlags flags(const QModelIndex &aIndex) const override; -// QVariant headerData(int aSection, Qt::Orientation orientation, -// int aRole = Qt::DisplayRole) const override; + QModelIndex index(int aRow, int aColumn, const QModelIndex &aParent = QModelIndex()) const override; + QModelIndex parent(const QModelIndex &aIndex) const override; + int rowCount(const QModelIndex &aParent = QModelIndex()) const override; + int columnCount(const QModelIndex &aParent = QModelIndex()) const override; + void setModelData(); void clearModelData(); void executeItemAction(const QModelIndex& aIndex); diff --git a/DIDKit/CMakeLists.txt b/DIDKit/CMakeLists.txt index 5e63af4..d97c025 100644 --- a/DIDKit/CMakeLists.txt +++ b/DIDKit/CMakeLists.txt @@ -1,23 +1,31 @@ project(DIDKit) +set (DIDKit_VERSION_MAJOR 1) +set (DIDKit_VERSION_MINOR 0) +set (DIDKit_VERSION_BUILD 0) +set(DIDKit_VERSION "${DIDKit_VERSION_MAJOR}.${DIDKit_VERSION_MINOR}.${DIDKit_VERSION_BUILD}") + file(GLOB_RECURSE DIDKit_IO_headers ../src/src/IO/*.h) file(GLOB_RECURSE DIDKit_IO_cpps ../src/src/IO/*.cpp) file(GLOB_RECURSE DIDKit_PACS_headers ../src/src/PACS/*.h) file(GLOB_RECURSE DIDKit_PACS_cpps ../src/src/PACS/*.cpp) +file(GLOB_RECURSE DIDKit_Network_headers ../thirdparty/*.h) +file(GLOB_RECURSE DIDKit_Network_cpps ./thirdparty/*.cpp) file(GLOB_RECURSE DIDKit_App_headers ./*/*.h) file(GLOB_RECURSE DIDKit_App_cpps ./*/*.cpp) -set(BUILD_SHARED_LIBS ON) -if (${BUILD_SHARED_LIBS}) +set(DIDKit_BUILD_SHARED_LIBS ON) +if (${DIDKit_BUILD_SHARED_LIBS}) include_directories(Defines/) endif() include_directories(../src/src/) include_directories(./App/) -add_library(DIDKit SHARED ${DIDKit_headers} ${DIDKit_cpps}) +add_library(DIDKit SHARED ${DIDKit_IO_headers} ${DIDKit_IO_cpps}) option(BUILD_DIDKit_APP "Build DIDKit App" ON) set(BUILD_DIDKit_APP ON) +set(CMAKE_INSTALL_PREFIX D:/Libs/binary/TestLib) find_package(DCMTK REQUIRED) include_directories(${DCMTK_INCLUDE_DIRS}) @@ -26,21 +34,75 @@ include_directories(${DCM_NETWORK_INCLUDE_DIRS}) find_package(VTK REQUIRED) include(${VTK_USE_FILE}) -include_directories(${DCM_NETWORK_INCLUDE_DIRS}) target_link_libraries(DIDKit ${DCMTK_LIBRARIES}) target_link_libraries(DIDKit ${VTK_LIBRARIES}) -target_link_libraries(DIDKit dcm_network) -add_dependencies(DIDKit dcm_network) -file(GLOB_RECURSE DIDKit_UI ./*/*.ui) -qt5_wrap_ui(DIDKit_UI_FILES ${DIDKit_UI}) if(${BUILD_DIDKit_APP}) - add_executable(DIDKitApp ${DIDKit_IO_headers} ${DIDKit_IO_cpps} ${DIDKit_App_headers} ${DIDKit_App_cpps} ${DIDKit_PACS_headers} ${DIDKit_PACS_cpps} ${DIDKit_UI_FILES}) + include_directories(${DCM_NETWORK_INCLUDE_DIRS}) + file(GLOB_RECURSE DIDKit_UI ./*/*.ui) + qt5_wrap_ui(DIDKit_UI_FILES ${DIDKit_UI}) + add_executable(DIDKitApp ${DIDKit_IO_headers} ${DIDKit_IO_cpps} ${DIDKit_App_headers} + ${project_res} ${DIDKit_App_cpps} ${DIDKit_PACS_headers} ${DIDKit_PACS_cpps} + ${DIDKit_Network_headers} ${DIDKit_Network_cpps} ${DIDKit_UI_FILES}) target_link_libraries(DIDKitApp ${DCMTK_LIBRARIES}) target_link_libraries(DIDKitApp ${VTK_LIBRARIES}) target_link_libraries(DIDKitApp dcm_network) add_dependencies(DIDKitApp dcm_network) target_link_libraries(DIDKitApp Qt5::Core Qt5::Widgets Qt5::Gui) endif() + + +set_property(TARGET DIDKit PROPERTY VERSION ${DIDKit_VERSION}) +set_property(TARGET DIDKit PROPERTY SOVERSION 1) +set_property(TARGET DIDKit PROPERTY + INTERFACE_DIDKit_MAJOR_VERSION 1) +set_property(TARGET DIDKit APPEND PROPERTY + COMPATIBLE_INTERFACE_STRING DIDKit_MAJOR_VERSION) + +install(TARGETS DIDKit EXPORT DIDKitTargets + RUNTIME DESTINATION bin + LIBRARY DESTINATION lib + ARCHIVE DESTINATION lib + ) + +install( + FILES + ${DIDKit_IO_headers} + DESTINATION + include/IO + COMPONENT + Devel +) + +include(CMakePackageConfigHelpers) +write_basic_package_version_file( + "${CMAKE_CURRENT_BINARY_DIR}/DIDKit/DIDKitConfigVersion.cmake" + VERSION ${DIDKit_VERSION} + COMPATIBILITY AnyNewerVersion +) + +configure_file( ${CMAKE_CURRENT_LIST_DIR}/cmake/DIDKitConfig.cmake.in + ${CMAKE_CURRENT_BINARY_DIR}/DIDKitConfig.cmake + @ONLY + ) + +set(ConfigPackageLocation lib/cmake/DIDKit) +install(EXPORT DIDKitTargets + FILE + DIDKitTargets.cmake + DESTINATION + ${ConfigPackageLocation} + ) + + +install( + FILES + "${CMAKE_CURRENT_BINARY_DIR}/DIDKitConfig.cmake" + "${CMAKE_CURRENT_BINARY_DIR}/DIDKit/DIDKitConfigVersion.cmake" + DESTINATION + ${ConfigPackageLocation} + COMPONENT + Devel +) \ No newline at end of file diff --git a/DIDKit/cmake/DIDKitConfig.cmake.in b/DIDKit/cmake/DIDKitConfig.cmake.in new file mode 100644 index 0000000..e88de66 --- /dev/null +++ b/DIDKit/cmake/DIDKitConfig.cmake.in @@ -0,0 +1,25 @@ +set(DIDKIT_LIBRARIES DIDKit) + +# The configuration options. +set(DIDKIT_BUILD_SHARED_LIBS "@BUILD_SHARED_LIBS@") + +get_filename_component(SELF_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH) +if(EXISTS ${SELF_DIR}/DIDKITTargets.cmake) + # This is an install tree + include(${SELF_DIR}/DIDKITTargets.cmake) + get_filename_component(DIDKIT_INCLUDE_ROOT "${SELF_DIR}/../../../include" ABSOLUTE) + set(DIDKIT_INCLUDE_DIRS ${DIDKIT_INCLUDE_ROOT}) +else() + if(EXISTS ${SELF_DIR}/DIDKITExports.cmake) + # This is a build tree + set( DIDKIT_INCLUDE_DIRS "@CMAKE_SOURCE_DIR@/include") + + include(${SELF_DIR}/DIDKITExports.cmake) + + else() + message(FATAL_ERROR "fail to load DIDKIT") + endif() +endif() + +# Backward compatible part: +set(DIDKIT_FOUND TRUE) \ No newline at end of file diff --git a/src/src/IO/Convert/ConverterBase.h b/src/src/IO/Convert/ConverterBase.h index 09cfe0b..fc85f1f 100644 --- a/src/src/IO/Convert/ConverterBase.h +++ b/src/src/IO/Convert/ConverterBase.h @@ -8,16 +8,57 @@ #include class vtkImageData; + +/** + * Converter base class + */ class ConverterBase { public: ConverterBase() = default; virtual ~ConverterBase(); + + /** + * 设置输出大小 + * @param height + * @param width + */ void setOutputSize(int height, int width); + + /** + * 设置完整输入文件绝对路径,带后缀 + * @param filename 输入文件绝对路径,带后缀 + */ void setInputDICOMFile(const char* filename); + + /** + * 设置完整输出文件绝对路径,带后缀 + * @param filename 输出文件绝对路径,带后缀 + */ void setFileName(const char* filename); + + /** + * 设置颜色采样值,一般为1或者3,默认为1 + * @param sample 颜色采样值 + */ void setColorImage(int sample); + + /** + * 设置DICOM的窗宽窗位 + * @param windowLevel + * @param windowWidth + */ void setWindow(double windowLevel, double windowWidth); + + /** + * 设置图像的frame索引值,默认为0 + * @param frame + */ void setFrame(int frame); + + /** + * 保存执行虚函数 + * @param filename 保存的绝对路径 + */ virtual void save(const char* filename){}; protected: void loadDICOMPixelData(); diff --git a/src/src/IO/Convert/DICOMToBMPConverter.h b/src/src/IO/Convert/DICOMToBMPConverter.h index bb2d0bb..d990bca 100644 --- a/src/src/IO/Convert/DICOMToBMPConverter.h +++ b/src/src/IO/Convert/DICOMToBMPConverter.h @@ -7,10 +7,17 @@ #include "ConverterBase.h" +/** + * DICOM to BMP 转换类 + */ class DICOMToBMPConverter:public ConverterBase { public: DICOMToBMPConverter(); ~DICOMToBMPConverter(); + /** + * 保存函数 + * @param filename 输出的绝对路径 + */ void save(const char* filename) override ; }; diff --git a/src/src/IO/Convert/DICOMToJPEGConverter.h b/src/src/IO/Convert/DICOMToJPEGConverter.h index 457512b..70bf224 100644 --- a/src/src/IO/Convert/DICOMToJPEGConverter.h +++ b/src/src/IO/Convert/DICOMToJPEGConverter.h @@ -6,11 +6,24 @@ #define OMEGAV_DICOMTOJPEGCONVERTER_H #include "ConverterBase.h" +/** + * DICOM to JPG 转换类 + */ class DICOMToJPEGConverter: public ConverterBase { public: DICOMToJPEGConverter(); ~DICOMToJPEGConverter(); + + /** + * 保存函数 + * @param filename 输出的绝对路径 + */ void save(const char* filename) override; + + /** + * 设置保存质量,0-100,默认值为100 + * @param quality 保存质量 + */ void setQuality(int quality){ if (quality<0) quality = 0; if (quality>100) quality = 100; diff --git a/src/src/IO/Convert/DICOMToPNGConverter.h b/src/src/IO/Convert/DICOMToPNGConverter.h index 371d573..ef68a67 100644 --- a/src/src/IO/Convert/DICOMToPNGConverter.h +++ b/src/src/IO/Convert/DICOMToPNGConverter.h @@ -7,16 +7,30 @@ #include "ConverterBase.h" +/** + *DICOM to PNG 转换类 + */ class DICOMToPNGConverter:public ConverterBase { public: - DICOMToPNGConverter(); - ~DICOMToPNGConverter(); - void save(const char* filename) override; - void setCompressLevel(int level){ - if (level<0) level = 0; - if (level>9) level = 9; - m_CompressionLevel = level; - } + DICOMToPNGConverter(); + + ~DICOMToPNGConverter(); + + /** + * 保存函数 + * @param filename 输出的绝对路径 + */ + void save(const char *filename) override; + + /** + * 设置压缩级别,0-9.默认值 5 + * @param level 压缩级别 + */ + void setCompressLevel(int level) { + if (level < 0) level = 0; + if (level > 9) level = 9; + m_CompressionLevel = level; + } private: int m_CompressionLevel = 5; diff --git a/src/src/IO/DICOM/DICOMHeaderHelper.h b/src/src/IO/DICOM/DICOMHeaderHelper.h index 90b0929..8d48382 100644 --- a/src/src/IO/DICOM/DICOMHeaderHelper.h +++ b/src/src/IO/DICOM/DICOMHeaderHelper.h @@ -74,38 +74,75 @@ typedef std::vector DICOMFileList; typedef std::unordered_map SeriesMap; typedef std::unordered_map SeriesFileMap; +/** + * DICOM 文件头信息读取帮助类 + */ class DICOMHeaderHelper { public: DICOMHeaderHelper() = default; - ~DICOMHeaderHelper() { Clear(); } + /** + * 设置需要进行检索的文件夹的绝对路径 + * @param dir 需要进行检索的文件夹的绝对路径 + */ void SetDirName(const char *dir) { Clear(); dirName = dir; } + /** + * 设置需要检索的文件的绝对路径 + * @attention 只有当为设置检索文件夹时此方法才有效 + * @param file + */ void SetFileName(const char *file) { Clear(); fileName = file; } + /** + * 清楚缓存的所有ExtendMedicalImageProperties对象 + */ void Clear(); + /** + * 进行文件检索和数据读取,并按照读取所得的内容生成ExtendMedicalImageProperties + */ void Update(); + /** + * 获取当前对象中缓存的ExtendMedicalImageProperties的个数。 + * @return ExtendMedicalImageProperties的个数 + */ int GetSeriesCount() { return SeriesCount; } + /** + * 获取当前对象中缓存的ExtendMedicalImageProperties的vector静态引用 + * @return 当前对象中缓存的ExtendMedicalImageProperties的vector静态引用 + */ const std::vector &GetSeriesProperties() { return seriesProperties; } + /** + * 按照idx获取对应的ExtendMedicalImageProperties对象指针, + * 当idx超限时,返回nullptr + * @param idx 索引值 + * @return idx对应的ExtendMedicalImageProperties对象指针 + */ ExtendMedicalImageProperties *GetSeries(int idx); + /** + * 按照seriesUID获取对应的ExtendMedicalImageProperties对象指针, + * 当按seriesUID找不到对应的对象时,返回nullptr + * @param seriesUID ExtendMedicalImageProperties的SeriesUID属性 + * @return 对应的ExtendMedicalImageProperties对象指针 + */ ExtendMedicalImageProperties *GetSeriesBySeriesUID(const char *seriesUID); private: diff --git a/src/src/IO/DICOM/DICOMPixelDataHelper.h b/src/src/IO/DICOM/DICOMPixelDataHelper.h index 6b3fd0d..bf7fffe 100644 --- a/src/src/IO/DICOM/DICOMPixelDataHelper.h +++ b/src/src/IO/DICOM/DICOMPixelDataHelper.h @@ -11,20 +11,58 @@ class ExtendMedicalImageProperties; class vtkImageData; /** * DICOM file Pixel Data read helper + * DICOM文件的像素数据读取帮助对象 */ class DICOMPixelDataHelper { public: DICOMPixelDataHelper() = default; - ~DICOMPixelDataHelper() { - } + ~DICOMPixelDataHelper() {} + + /** + * 获取缩略图数据,缩略图数据为一个void*的buffer,可以单通道的纯 + * 灰度图像,可以是3通道RGB颜色,带DICOM本身的Overlay,可直接用 + * 于VTK生成vtkImageData或用于QImage生成图像 + * @param property 输入的ImageSet的ExtendMedicalImageProperties + * @param data buffer指针引用,应传入一个空指针,方法会自己申请需要的内存 + * @param length data的长度,按8bit计算 + * @param sample 输出的通道数,灰度图通道数为1,彩图为3 + */ static void GetThumbnailData(ExtendMedicalImageProperties *property, void*& data, unsigned long& length,int& sample); + /** + * 获取已渲染图像至vtkImageData中,主要用于DICOM数据的导出, + * vtkImageData参数应在输入前被初始化完毕,方法在执行完毕后, + * vtkImageData数据中只有一幅图像。 + * @param path 需要渲染的DICOM文件绝对路径 + * @param data 渲染目标vtkImageData + * @param sample DICOM文件内含数据的通道数,灰度图通道数为1,彩图为3 + * @param windowLevel 渲染DICOM的窗位 + * @param windowWidth 渲染DICOM的窗宽 + * @return 是否执行成功 + */ static bool GetRenderedData(const char * path, vtkImageData* data, int sample, double windowLevel, double windowWidth); + + /** + * 从Multi-frame类型的DICOM文件中,获取已渲染图像 + * 至vtkImageData中,主要用于DICOM数据的导出, + * vtkImageData参数应在输入前被初始化完毕,方法在 + * 执行完毕后,vtkImageData数据中只有一幅图像。 + * @param path 需要渲染的DICOM文件绝对路径 + * @param data 渲染目标vtkImageData + * @param sample DICOM文件内含数据的通道数,灰度图通道数为1,彩图为3 + * @param frame 指定需要渲染的frame的索引值 + * @param windowLevel 渲染DICOM的窗位 + * @param windowWidth 渲染DICOM的窗宽 + * @return + */ static bool GetMultiFrameRenderedData(const char * path, vtkImageData* data, int sample,int frame, double windowLevel, double windowWidth); /** * Get Pixel Data with Rescaled, result is calculate as integer, and arrange as integer. + * + * 获取DICOM文件的Pixel data, 该Pixel Data已经按照RescaledSlope和RescaledIntercept经过变换, + * 变换时采用整型精度,故会导致小数点以下位数损失,输出也为整型精度。 * @param path dcm file absolute path * @param data output data buffer pointer * @param length output data length @@ -33,6 +71,8 @@ public: /** * Check whether FileObject with path in cache + * + * 判断FileObject是否已经被缓存 * @param path dcm file absolute path, and the cache key * @return is in cache */ @@ -41,6 +81,8 @@ public: /** * Create a DcmFileFormat object with input path, and * store it into the cache map. + * + * 缓存一个DcmFileFormat对象 * @param path dcm file absolute path, and the cache key * @return cache success */ @@ -48,11 +90,15 @@ public: /** * Clear all object cache in the store map + * + * 清除所有缓存 */ static void ClearFileObjectCache(); /** * Get multi-frame format dicom file Pixel Data + * + * 获取multi-frame格式DICOM文件的Pixel Data * @param path dcm file absolute path * @param data output data pointer, must have been allocated. * @param length output data length @@ -62,6 +108,9 @@ public: /** * Get Pixel Data with Rescaled, result is calculate as Float, and arrange as Float. + * + * 获取DICOM文件的Pixel data, 该Pixel Data已经按照RescaledSlope和RescaledIntercept经过变换, + * 变换时采用double精度,输出也为Float精度。 * @param path dcm file absolute path * @param data output data buffer pointer, must have been allocated. * @param length output data length @@ -70,6 +119,8 @@ public: /** * Get colorful Pixel Data as three component R,G,B color value. + * + * 获取DICOM文件的Pixel data, 获取为三通道RGB颜色值 * @param path dcm file absolute path * @param data output data buffer pointer, must have been allocated. * @param length output data length @@ -78,6 +129,8 @@ public: /** * Get Overlay data count + * + * 获取Overlay数量 * @param path dcm file absolute path * @return Overlay data count */ @@ -85,6 +138,8 @@ public: /** * Get Overlay data + * + * 获取Overlay数量 * @param path dcm file absolute path * @param data output data buffer pointer, must have been allocated. * @param width output overlay width, usually same as image columns @@ -95,14 +150,26 @@ public: /** * Init decompress Codecs, must be called before other method. + * + * 初始化解压缩解码器,必须在其他方法之前被调用 */ static void InitCodecs(); /** * Finalize decompress Codecs, must be called after other method have done. + * + * 析构解压缩解码器,必须在其他方法调用完毕之后调用。 */ static void FinalizeCodecs(); + /** + * 渲染DicomImage对象的数据到指定的vtkImageData + * @param data 目标结果vtkImageData + * @param sample 采用通道数,灰度图为1,彩图为3 + * @param windowLevel 窗位 + * @param windowWidth 窗宽 + * @param dcmImage 源DicomImage对象引用 + */ static void RenderToImageData(vtkImageData *data, int sample, double windowLevel, double windowWidth, DicomImage &dcmImage); }; diff --git a/src/src/IO/DICOM/DicomLoader.h b/src/src/IO/DICOM/DicomLoader.h index 09b2c54..a61229d 100644 --- a/src/src/IO/DICOM/DicomLoader.h +++ b/src/src/IO/DICOM/DicomLoader.h @@ -10,21 +10,58 @@ class ExtendMedicalImageProperties; class vtkImageData; +/** + * DICOM数据载入方法应用对象(静态类,只含有静态方法) + */ class DIDKIT_EXPORT DicomLoader { public: + + /** + * 初始化解压缩解码器 + */ static void InitCodecs(); + /** + * 析构解压缩解码器 + */ static void FinalizeCodecs(); + /** + * 从文件夹中读取DICOM并生成ExtendMedicalImageProperties + * @param dir 读取目标文件夹绝对路径 + * @param properties 输出ExtendMedicalImageProperties的vector引用对象 + * @param count 输出ExtendMedicalImageProperties数量 + */ static void readPropertiesFromDir(const std::string &dir, std::vector& properties, int& count); + /** + * 从文件中读取DICOM并生成ExtendMedicalImageProperties + * @param file 读取目标文件的绝对路径 + * @param properties 输出ExtendMedicalImageProperties的vector引用对象 + * @param count 输出ExtendMedicalImageProperties数量 + */ static void readPropertiesFromFile(const std::string &file, std::vector& properties, int& count); + /** + * 根据ExtendMedicalImageProperties直接生成对应数据的vtkImageData + * @param property 输入的ExtendMedicalImageProperties + * @param pixelData 输出DICOM的pixel data的对应vtkImageData智能指针引用 + * @param overlayData 输出DICOM的overlay data的对应vtkImageData智能指针引用 + */ static void getImageData(ExtendMedicalImageProperties* property, vtkSmartPointer& pixelData, vtkSmartPointer& overlayData); + /** + * 根据ExtendMedicalImageProperties获取缩略图数据, + * 缩略图数据为一个void*的buffer,可以单通道的纯灰度图 + * 像,可以是3通道RGB颜色,带DICOM本身的Overlay,可直 + * 接用于VTK生成vtkImageData或用于QImage生成图像 + * @param property 输入的ExtendMedicalImageProperties + * @param data buffer指针引用,应传入一个空指针,方法会自己申请需要的内存 + * @param sample 输出的通道数,灰度图通道数为1,彩图为3 + */ static void getThumbnailData(ExtendMedicalImageProperties* property,void*& data, int& sample); private: DicomLoader() = delete; diff --git a/src/src/IO/DICOM/ExtendMedicalImageProperties.h b/src/src/IO/DICOM/ExtendMedicalImageProperties.h index dadb0a2..8367c18 100644 --- a/src/src/IO/DICOM/ExtendMedicalImageProperties.h +++ b/src/src/IO/DICOM/ExtendMedicalImageProperties.h @@ -12,69 +12,176 @@ #include "IO/Defines/DIDKitExport.h" +/** + * ExtendMedicalImageProperties,扩展的医学影像数据属性对象, + * 比VTK自带的基类vtkMedicalImageProperties包含更多的医学影 + * 像相关属性。 + */ class DIDKIT_EXPORT ExtendMedicalImageProperties: public vtkMedicalImageProperties{ public: + //@{ + /** + * Static method for construction. + * 符合VTK库规范的静态构造方法 + */ static ExtendMedicalImageProperties *New(); vtkTypeMacro(ExtendMedicalImageProperties,vtkMedicalImageProperties); - + //@} /** - * Convenience method to reset all fields to an empty string/value + * 清除所有properties */ void Clear() override; - vtkGetStringMacro(StudyUID) - vtkSetStringMacro(StudyUID) + //@{ + /** + * Study Instance UID + * DICOM (0020,000d) + */ + vtkGetStringMacro(StudyUID); + vtkSetStringMacro(StudyUID); + //@} - vtkGetStringMacro(SeriesUID) - vtkSetStringMacro(SeriesUID) + //@{ + /** + * Series Instance UID + * DICOM (0020,000e) + */ + vtkGetStringMacro(SeriesUID); + vtkSetStringMacro(SeriesUID); + //@} - vtkGetMacro(AcquisitionNumber, long) - vtkSetMacro(AcquisitionNumber, long) + //@{ + /** + * Acquisition Number + * DICOM (0020,0012) + */ + vtkGetMacro(AcquisitionNumber, long); + vtkSetMacro(AcquisitionNumber, long); + //@} - vtkGetMacro(ImageSetNumber, long) - vtkSetMacro(ImageSetNumber, long) + //@{ + /** + * ImageSetNumber + */ + vtkGetMacro(ImageSetNumber, long); + vtkSetMacro(ImageSetNumber, long); + //@} - vtkGetMacro(Rows, long) - vtkSetMacro(Rows, long) + //@{ + /** + * Rows + * DICOM (0028,0010) + */ + vtkGetMacro(Rows, long); + vtkSetMacro(Rows, long); + //@} - vtkGetMacro(Columns, long) - vtkSetMacro(Columns, long) + //@{ + /** + * Columns + * DICOM (0028,0011) + */ + vtkGetMacro(Columns, long); + vtkSetMacro(Columns, long); + //@} - vtkGetMacro(RescaleSlope, double) - vtkSetMacro(RescaleSlope, double) + //@{ + /** + * Rescale Slope + * DICOM (0028,1053) + */ + vtkGetMacro(RescaleSlope, double); + vtkSetMacro(RescaleSlope, double); + //@} - vtkGetMacro(RescaleOffset, double) - vtkSetMacro(RescaleOffset, double) + //@{ + /** + * Rescale Intercept + * DICOM (0028,1052) + */ + vtkGetMacro(RescaleOffset, double); + vtkSetMacro(RescaleOffset, double); + //@} - vtkGetMacro(SamplePerPixel, unsigned short) - vtkSetMacro(SamplePerPixel, unsigned short) + //@{ + /** + * Rescale Intercept + * DICOM (0028,0002) + */ + vtkGetMacro(SamplePerPixel, unsigned short); + vtkSetMacro(SamplePerPixel, unsigned short); + //@} - vtkGetMacro(BitsAllocated, unsigned short) - vtkSetMacro(BitsAllocated, unsigned short) + //@{ + /** + * Bits Allocated + * DICOM (0028,0100) + */ + vtkGetMacro(BitsAllocated, unsigned short); + vtkSetMacro(BitsAllocated, unsigned short); + //@} - vtkGetMacro(PixelRepresentation, unsigned short) - vtkSetMacro(PixelRepresentation, unsigned short) + //@{ + /** + * Pixel Representation + * DICOM (0028,0103) + */ + vtkGetMacro(PixelRepresentation, unsigned short); + vtkSetMacro(PixelRepresentation, unsigned short); + //@} - vtkGetMacro(HasOverlay, bool) - vtkSetMacro(HasOverlay, bool) + //@{ + /** + * 数据是否含有Overlay Data + */ + vtkGetMacro(HasOverlay, bool); + vtkSetMacro(HasOverlay, bool); + //@} + //@{ + /** + * Pixel Spacing + * DICOM (0028,0030) or DICOM (0018,1164) + */ vtkSetVector3Macro(Spacing,double); vtkGetVector3Macro(Spacing,double); + //@} + //@{ + /** + * Image Orientation Patient + * DICOM (0020,0032) + */ vtkSetVector3Macro(Position,double); vtkGetVector3Macro(Position,double); + //@} + /** + * Z轴的World坐标系增长向量 + */ vtkGetVector3Macro(ZVector,double); + /** + * 获取本ImageSet对应的文件列表对象 + * @return + */ const std::vector>& GetFileNames(){ return FileNames; } + /** + * 设置本ImageSet对应的文件列表对象 + * @param files 本ImageSet对应的文件列表对象 + */ void SetFileNames(std::vector>& files){ FileNames = std::move(files); } + /** + * 获取SeriesNumber,long类型 + * @return SeriesNumber + */ long GetSeriesNumberAsLong(){ if (this->SeriesNumber){ return atol(this->SeriesNumber); @@ -82,41 +189,95 @@ public: return 0; } + /** + * 设置获取SeriesNumber + * @param value SeriesNumber + */ void SetSeriesNumber(long value){ char buffer [sizeof(long)*8+1]; const char* s = _ltoa(value, buffer, 10); vtkMedicalImageProperties::SetSeriesNumber(s); } + /** + * 获取本ImageSet的缩略图对应文件文件名 + * @return + */ const char* GetThumbnailFileName(){ if (FileNames.empty()) return nullptr; return FileNames[0].first.data(); } + + /** + * 获取层数 + * @return + */ unsigned long long GetSliceCount(){ return FileNames.size(); } + /** + * 获取本ImageSet的唯一标志编码 + * @return + */ const std::string& GetUniqueID(){ return uniqueID; } + + /** + * 设置ImageSet的唯一标志编码 + * @param id ImageSet的唯一标志编码 + */ void SetUniqueID(const std::string& id){ uniqueID = id; } + + /** + * 生成ImageSet的唯一标志编码 + */ void GenerateUniqueID(); + /** + * 获取Model to World的坐标系方向变换的4x4齐次矩阵 + * @return 坐标系方向变换的4x4齐次矩阵指针, + * 该指针指向本对象的内部成员 + */ vtkMatrix4x4* GetOrientationMatrix(){ return OrientationMatrix.Get(); } + + /** + * 获取World to Model的坐标系变换的4x4齐次矩阵 + * @return World to Model的坐标系变换的4x4齐次矩阵, + * 该指针指向本对象的内部成员 + */ vtkMatrix4x4* GetWorldToModelMatrix(){ return WorldToModelMatrix.Get(); } + /** + * 获取Model to World的坐标系变换的4x4齐次矩阵 + * @return Model to World的坐标系变换的4x4齐次矩阵, + * 该指针指向本对象的内部成员 + */ vtkMatrix4x4* GetModelToWorldMatrix(){ return ModelToWorldMatrix.Get(); } + + /** + * 计算线性变换矩阵,需要先设置Position和DirectionCosine + */ void ComputeTransformMatrix(); + /** + * 判断ImageSet对应的ImageData的数据类型是否为浮点类型 + * @return ImageSet对应的ImageData的数据类型是否为浮点类型 + */ bool RescaledImageDataIsFloat(); + /** + * 判断 ImageSet对应的ImageData的数据类型是否为有符号类型 + * @return ImageSet对应的ImageData的数据类型是否为有符号类型 + */ bool RescaledImageDataIsSigned(); protected: diff --git a/src/src/IO/DICOM/MultiframeDICOMSpliter.h b/src/src/IO/DICOM/MultiframeDICOMSpliter.h index a7b7c59..8a55d53 100644 --- a/src/src/IO/DICOM/MultiframeDICOMSpliter.h +++ b/src/src/IO/DICOM/MultiframeDICOMSpliter.h @@ -6,12 +6,35 @@ #define OMEGAV_MULTIFRAMEDICOMSPLITER_H #include - +/** + * Multiframe类型DICOM文件分割器, + * 可用于将多frame文件分割为多个单frame文件 + */ class MultiframeDICOMSpliter { public: + /** + * 设置输入文件的路径 + * @param filename 输入的多frame文件的路径 + */ void SetInputFileName(const char* filename); + + /** + * 设置输出文件的文件名格式,这里主要是前缀、后缀和变量 + * @example SetOutputFilePattern(“dicomfile_%d.dcm”), + * 其中dicomfile_为前缀,%d为变量按照顺序从0递增,.dcm为后缀 + * @param format + */ void SetOutputFilePattern(const char* format); + + /** + * 设置存放输出文件的文件夹的文件夹路径 + * @param dir 输出文件的文件夹路径 + */ void SetOutputDirPath(const char* dir); + + /** + * 执行分割,将数据写入目标位置 + */ void write(); private: diff --git a/src/src/IO/DICOM/vtkDCMTKImageReader.h b/src/src/IO/DICOM/vtkDCMTKImageReader.h index a0c7d22..2d19f58 100644 --- a/src/src/IO/DICOM/vtkDCMTKImageReader.h +++ b/src/src/IO/DICOM/vtkDCMTKImageReader.h @@ -14,41 +14,79 @@ class ExtendMedicalImageProperties; +/** + * vtkDCMTKImageReader,一个使用DCMTK作为数据读取后端的,符合VTK库ImageReader接口 + * 标准的新ImageReader,使用ExtendMedicalImageProperties作为输入。 + */ class DIDKIT_EXPORT vtkDCMTKImageReader: public vtkImageReader2 { public: //@{ /** * Static method for construction. + * 符合VTK库规范的静态构造方法 */ static vtkDCMTKImageReader *New(); vtkTypeMacro(vtkDCMTKImageReader, vtkImageReader2); //@} + /** + * 设置ExtendMedicalImageProperties作为Reader的输入 + * @param p ImageSet对应的ExtendMedicalImageProperties + */ void SetImageProperties(ExtendMedicalImageProperties* p){ this->properties = p; } + /** + * 获取像素间距 + * @return 像素间距数组,数组长度为3 + */ double* GetPixelSpacing(); + //@{ + /** + * 数据是否含有Overlay Data + */ vtkGetMacro(HasOverlay, bool); vtkSetMacro(HasOverlay, bool); + //@} + /** + * 获取Reader的Overlay Data的输出 + * @return 当HasOverlay为true时,返回vtkImageData*,否则返回nullptr + */ vtkImageData* GetOutputOverLayData(); protected: - + /** + * 设置Reader的OutputInformation, + * OutputInformation会应用于输出的vtkImageData的构建 + * @param num_slices 数据的层数数量 + */ void SetupOutputInformation(int num_slices); + + /** + * 重写了父类方法 + */ void ExecuteInformation() override; + + /** + * 重写了父类方法,需要注意本方法会自动调用DICOMPixelDataHelper的 + * 压缩解码器初始化InitCodecs(),并会在完成相关工作之后调用解码器的 + * 析构函数FinalizeCodecs() + * @param output + * @param outInfo + */ void ExecuteDataWithInformation(vtkDataObject *output,vtkInformation *outInfo) override ; - // - // Constructor - // + /** + * Constructor + */ vtkDCMTKImageReader(); - // - // Destructor - // + /** + * Destructor + */ ~vtkDCMTKImageReader() override; private: vtkDCMTKImageReader(const vtkDCMTKImageReader&) = delete; diff --git a/src/src/IO/Defines/DIDKitExport.h b/src/src/IO/Defines/DIDKitExport.h index e42b34e..f6f6940 100644 --- a/src/src/IO/Defines/DIDKitExport.h +++ b/src/src/IO/Defines/DIDKitExport.h @@ -4,7 +4,7 @@ #ifndef OMEGAV_DIDKITEXPORT_H #define OMEGAV_DIDKITEXPORT_H - +#define DIDKIT_SHARED #ifdef _WIN32 #ifdef DIDKIT_SHARED /* Defines needed for building DLLs on windows */ diff --git a/src/src/PACS/Common/dicomviewerhelper.cpp b/src/src/PACS/Common/dicomviewerhelper.cpp index 9b12314..ad67753 100644 --- a/src/src/PACS/Common/dicomviewerhelper.cpp +++ b/src/src/PACS/Common/dicomviewerhelper.cpp @@ -1,5 +1,7 @@ #include "dicomviewerhelper.h" +#include #include +#include #include @@ -18,11 +20,21 @@ bool DicomViewerProductCfg::loadcfg() if(m_bLoaded) return true; QString strProductFileName = DicomViewerHelper::applicationPath(); - strProductFileName += "/cfg/pacs.json"; + strProductFileName += "/cfg/pacs.json"; + qDebug()<setHelper(m_pCallbackHelper); cmove->set_cmove_callback(moveCallback); @@ -54,7 +56,7 @@ void MoveWorker::onMoveProgress(int progress, int total) emit notifyMoveProgress(progress, total); } -void MoveWorker::onMoveStoreProgress(int code, std::string filename) +void MoveWorker::onMoveStoreProgress(int code, QString filename) { emit notifyMoveStoreProgress(code, filename); } \ No newline at end of file diff --git a/src/src/PACS/Network/moveworker.h b/src/src/PACS/Network/moveworker.h index bc4ba1f..4fcf394 100644 --- a/src/src/PACS/Network/moveworker.h +++ b/src/src/PACS/Network/moveworker.h @@ -19,11 +19,11 @@ public: signals: void notifyMoveDone(int, QString dir); void notifyMoveProgress(int, int); - void notifyMoveStoreProgress(int, std::string); + void notifyMoveStoreProgress(int, QString); public slots: void onMoveProgress(int, int); - void onMoveStoreProgress(int, std::string); + void onMoveStoreProgress(int, QString); private: QString m_strPeerIP_; diff --git a/src/src/PACS/Widget/importwidget.cpp b/src/src/PACS/Widget/importwidget.cpp index 661af7a..e5b15bb 100644 --- a/src/src/PACS/Widget/importwidget.cpp +++ b/src/src/PACS/Widget/importwidget.cpp @@ -642,12 +642,10 @@ void ImportWidget::onSeriesDoubleClicked(const QModelIndex ¤t) std::string seriesUID = m_pSeriesModel->index(row, 6).data().toString().toStdString(); std::string patientName = m_pSeriesModel->index(row, 7).data().toString().toStdString(); std::string outDirectory = DicomViewerHelper::applicationPath().toStdString(); + outDirectory += "/"; + outDirectory += studyInstanceUID; outDirectory += "/"; - outDirectory += patientName; - outDirectory += "/"; - outDirectory += studyID; - outDirectory += "/"; - outDirectory += seriesNumber; + outDirectory += seriesUID; bool mkRet = false; QDir d(QString::fromStdString(outDirectory)); @@ -667,7 +665,7 @@ void ImportWidget::onSeriesDoubleClicked(const QModelIndex ¤t) m_pMoveWorker = new MoveWorker(QString::fromStdString(peerIP), peerPort, QString::fromStdString(peerTitle), ourPort, QString::fromStdString(ourTitle), QString::fromStdString(outDirectory)); QObject::connect(m_pMoveWorker, SIGNAL(notifyMoveDone(int, QString)), this, SLOT(moveDone(int, QString)), Qt::QueuedConnection); QObject::connect(m_pMoveWorker, SIGNAL(notifyMoveProgress(int, int)), this, SLOT(moveProgress(int, int)), Qt::QueuedConnection); - QObject::connect(m_pMoveWorker, SIGNAL(notifyMoveStoreProgress(int, std::string)), this, SLOT(moveStoreProgress(int, std::string)), Qt::QueuedConnection); + QObject::connect(m_pMoveWorker, SIGNAL(notifyMoveStoreProgress(int, QString)), this, SLOT(moveStoreProgress(int, QString)), Qt::QueuedConnection); } m_pMoveWorker->setOutputDirectory(QString::fromStdString(outDirectory)); m_pMoveWorker->moveToThread(m_pQueryWorkerThread); @@ -693,7 +691,7 @@ void ImportWidget::moveProgress(int completed, int total) } -void ImportWidget::moveStoreProgress(int, std::string) +void ImportWidget::moveStoreProgress(int, QString) { } diff --git a/src/src/PACS/Widget/importwidget.h b/src/src/PACS/Widget/importwidget.h index d0cce51..f7154d7 100644 --- a/src/src/PACS/Widget/importwidget.h +++ b/src/src/PACS/Widget/importwidget.h @@ -62,7 +62,7 @@ public slots: void onSeriesDoubleClicked(const QModelIndex &); void moveDone(int, QString); void moveProgress(int, int); - void moveStoreProgress(int, std::string); + void moveStoreProgress(int, QString); protected: virtual bool eventFilter(QObject *obj, QEvent *event); @@ -130,6 +130,9 @@ private: QThread* m_pMoveWorkerThread; MoveWorker* m_pMoveWorker; +// QThread* m_pStoreWorkerThread; +// StoreWorker* m_pStoreWorker; + private: struct {