Separate dataloader module to DIDKit.

This commit is contained in:
Krad
2022-09-20 15:12:48 +08:00
parent 85c6052092
commit 9e9f4d37fc
15 changed files with 353 additions and 304 deletions

View File

@@ -0,0 +1,71 @@
//
// Created by Krad on 2022/9/20.
//
#include "OrientationHelper.h"
#define ORIEN_NUM 4
#define SINGLE_INSTANCE 1
OrientationMapType OrientationHelper::orien_map;
std::string OrientationHelper::StringFilter(char* str)
{
const char s[] = "\\ ";
char *token;
char *buftmp;
token = strtok_s(str, s, &buftmp);
std::string trim ="";
while (token != NULL) {
//printf("%s\n", token);
trim += token;
token = strtok_s(NULL, s, &buftmp);
}
return trim;
}
void OrientationHelper::init()
{
const std::string index[] = { "LH","LF","RH","RF" };
std::vector<std::string> indexList(std::begin(index), std::end(index));
const std::string lh[] = { "S" ,"L" ,"R" ,"I" };
const std::string lf[] = { "I" , "L" , "R" , "S" };
const std::string rh[] = { "S" , "R" , "L" , "I" };
const std::string rf[] = { "I" , "R" , "L" , "S" };
std::vector<std::string> v_lh(std::begin(lh), std::end(lh));
std::vector<std::string> v_lf(std::begin(lf), std::end(lf));
std::vector<std::string> v_rh(std::begin(rh), std::end(rh));
std::vector<std::string> v_rf(std::begin(rf), std::end(rf));
std::vector<std::vector<std::string>> retList;
retList.push_back(v_lh);
retList.push_back(v_lf);
retList.push_back(v_rh);
retList.push_back(v_rh);
for (int i = 0; i < ORIEN_NUM; i++)
{
OrientationHelper::orien_map.insert(std::pair<std::string, std::vector<std::string>>(indexList.at(i), retList.at(i)));
}
}
std::vector<std::string>* OrientationHelper::getOrientationStrList(const std::string &index)
{
const char* s = index.c_str();
int len = strlen(s);
char *hint = new char[len + 1];
strcpy_s(hint, len + 1, s);
std::string trim = StringFilter(hint);
OrientationMapType::iterator it = OrientationHelper::orien_map.find(trim);
delete hint;
if (it == orien_map.end())
{
return nullptr;
}
return &(it->second);
}

View File

@@ -0,0 +1,19 @@
//
// Created by Krad on 2022/9/20.
//
#ifndef OMEGAV_ORIENTATIONHELPER_H
#define OMEGAV_ORIENTATIONHELPER_H
#include "Common/QGlobals.h"
using namespace std;
typedef std::map<std::string,std::vector<std::string>> OrientationMapType;
class OrientationHelper
{
public:
static std::string StringFilter(char* str);
static void init();
static std::vector<std::string>* getOrientationStrList(const std::string &index);
static OrientationMapType orien_map;
};
#endif //OMEGAV_ORIENTATIONHELPER_H

View File

@@ -0,0 +1,90 @@
//
// Created by Krad on 2022/9/20.
//
#include "ImageSetStore.h"
#include "SeriesImageSet.h"
#include "IO/DICOM/DicomLoader.h"
#include "IO/DICOM/ExtendMedicalImageProperties.h"
SeriesImageSet *ImageSetStore::getSeriesImageSet(const string &uniqueID) {
//only user key to the series level
if (store.count(uniqueID)>0)
{
//use cache instead load
return store[uniqueID];
}
auto iter = std::find_if(imageProperties.begin(), imageProperties.end(),[&uniqueID](auto property){
return property->GetUniqueID()==uniqueID;
});
if (iter==imageProperties.end()) return nullptr;
vtkSmartPointer<vtkImageData> pixelData;
vtkSmartPointer<vtkImageData> overlayData;
DicomLoader::InitCodecs();
DicomLoader::getImageData(*iter, pixelData, overlayData);
SeriesImageSet* set = new SeriesImageSet(*iter,pixelData);
if (overlayData.Get()){
set->SetOverlayData(overlayData);
}
DicomLoader::FinalizeCodecs();
store[uniqueID] = set;
return set;
}
void ImageSetStore::reset() {
for(auto item: store){
delete item.second;
}
store.clear();
for(auto item: m_patients){
delete item.second;
}
m_patients.clear();
if (placeHolder)placeHolder->Delete();
placeHolder = vtkObject::New();
imageProperties.clear();
}
void ImageSetStore::addImageSet(ExtendMedicalImageProperties* property) {
std::string patient_name = property->GetPatientName();
std::string study_uid = property->GetStudyUID();
std::string series_uid = property->GetSeriesUID();
//patient level
PatientInfo_t* patient = nullptr;
//get from store
if (m_patients.count(patient_name)>0){
patient = m_patients[patient_name];
}
else{
//create new
patient = new PatientInfo_t();
patient->patient_name = property->GetPatientName();
patient->birth_date = property->GetPatientBirthDate();
patient->studies = new StudiesMapType();
m_patients[patient_name] = patient;
}
StudyInfo_t* study = nullptr;
//get from store
if (patient->studies->count(study_uid)>0){
study = patient->studies->at(study_uid);
}
else{
//create new
study = new StudyInfo_t();
patient->studies->insert({study_uid, study});
study->study_description = property->GetStudyDescription();
study->study_date = property->GetStudyDate();
study->study_time = property->GetStudyTime();
study->series = new SeriesMapType();
}
//TODO:need add Image set logic
if (study->series->count(series_uid)>0){
}
else {
study->series->insert({series_uid,property});
imageProperties.push_back(property);
}
}

View File

@@ -0,0 +1,41 @@
//
// Created by Krad on 2022/9/20.
//
#ifndef OMEGAV_IMAGESETSTORE_H
#define OMEGAV_IMAGESETSTORE_H
#include "Common/QGlobals.h"
class SeriesImageSet;
class ExtendMedicalImageProperties;
typedef std::map<std::string, SeriesImageSet*> StoreMap;
class ImageSetStore {
public:
static ImageSetStore *GetInstance(){
static ImageSetStore store;
return &store;
}
const PatientsMapType &getPatientsList()
{
return m_patients;
}
void addImageSet(ExtendMedicalImageProperties* property);
SeriesImageSet* getSeriesImageSet(const std::string& uniqueID);
std::vector<ExtendMedicalImageProperties*>& getProperties(){
return imageProperties;
}
void reset();
private:
std::vector<ExtendMedicalImageProperties*> imageProperties;
StoreMap store;
PatientsMapType m_patients;
vtkObject* placeHolder;
};
#endif //OMEGAV_IMAGESETSTORE_H

View File

@@ -10,9 +10,9 @@
//----------------------------------------------------------------
SeriesImageSet::SeriesImageSet(ExtendMedicalImageProperties* property, vtkImageData* imagedata)
SeriesImageSet::SeriesImageSet(ExtendMedicalImageProperties* property, vtkImageData* imagedata):QObject(nullptr)
,m_image(imagedata)
{
m_image = imagedata;
m_image->SetSpacing(property->GetSpacing());
m_property = property;
m_pUniqueID.append(property->GetPatientName());

View File

@@ -38,9 +38,6 @@ ExtendMedicalImageProperties *DICOMHeaderHelper::GetSeriesBySeriesUID(const char
void DICOMHeaderHelper::Clear() {
dirName.clear();
fileName.clear();
for (auto property : seriesProperties) {
property->Delete();
}
seriesProperties.clear();
SeriesCount = 0;
}

View File

@@ -1,241 +1,59 @@
#include "DicomLoader.h"
#include <map>
#include <algorithm>
#include <vtkStringArray.h>
#include <QDebug>
#include <string.h>
#include <stdio.h>
#include <vtkNew.h>
#include <vtkImageData.h>
#include "Common/SeriesImageSet.h"
#include "DICOMHeaderHelper.h"
#include "DICOMPixelDataHelper.h"
#include "vtkDCMTKImageReader.h"
#define ORIEN_NUM 4
#define SINGLE_INSTANCE 1
OrientationMapType orientationHelper::orien_map;
/************************************************************************
* [Q]Is there anything different between these two?
* instance->m_image = m_itkConnector->GetOutput();
* instance->m_image->DeepCopy(m_itkConnector->GetOutput());
* [Q]I have no idea why it works just fine not using deep copy,
while the vtkdata is replaced in the Qfusion Project using exatly the same workflow!
/************************************************************************/
std::string orientationHelper::StringFilter(char* str)
{
const char s[] = "\\ ";
char *token;
char *buftmp;
token = strtok_s(str, s, &buftmp);
std::string trim ="";
while (token != NULL) {
//printf("%s\n", token);
trim += token;
token = strtok_s(NULL, s, &buftmp);
}
return trim;
}
void orientationHelper::init()
{
const std::string index[] = { "LH","LF","RH","RF" };
std::vector<std::string> indexList(std::begin(index), std::end(index));
const std::string lh[] = { "S" ,"L" ,"R" ,"I" };
const std::string lf[] = { "I" , "L" , "R" , "S" };
const std::string rh[] = { "S" , "R" , "L" , "I" };
const std::string rf[] = { "I" , "R" , "L" , "S" };
std::vector<std::string> v_lh(std::begin(lh), std::end(lh));
std::vector<std::string> v_lf(std::begin(lf), std::end(lf));
std::vector<std::string> v_rh(std::begin(rh), std::end(rh));
std::vector<std::string> v_rf(std::begin(rf), std::end(rf));
std::vector<std::vector<std::string>> retList;
retList.push_back(v_lh);
retList.push_back(v_lf);
retList.push_back(v_rh);
retList.push_back(v_rh);
for (int i = 0; i < ORIEN_NUM; i++)
{
orientationHelper::orien_map.insert(std::pair<std::string, std::vector<std::string>>(indexList.at(i), retList.at(i)));
}
}
std::vector<std::string>* orientationHelper::getOrientationStrList(const std::string &index)
{
const char* s = index.c_str();
int len = strlen(s);
char *hint = new char[len + 1];
strcpy_s(hint, len + 1, s);
std::string trim = StringFilter(hint);
OrientationMapType::iterator it = orientationHelper::orien_map.find(trim);
delete hint;
if (it == orien_map.end())
{
return nullptr;
}
return &(it->second);
}
DicomLoader* DicomLoader::instance = new DicomLoader();
DicomLoader* DicomLoader::GetInstance() {
//lazy man
//if (!instance) {
// instance = new DicomLoader();
//}
//hungry man
return instance;
}
DicomLoader::DicomLoader(){
// m_itkSeriesReader = SeriesReaderType::New();
// m_itkConnector = ConnectorType::New();
// m_gdcmIO = ImageIOType::New();
// m_inputNames = InputNamesGeneratorType::New();
reader = nullptr;
//transfer to series after!
m_patients.clear();
placeHolder = vtkObject::New();
defaultUniqueID = "";
}
DicomLoader::~DicomLoader() {
placeHolder->Delete();
imageProperties.clear();
}
SeriesImageSet* DicomLoader::getSeriesImageSet(const std::string& uniqueID)//, DicomTagInfo_t* tag_info)
{
//only user key to the series level
if (store.count(uniqueID)>0)
{
//use cache instead load
return store[uniqueID];
}
if (reader) reader->Delete();
reader = vtkDCMTKImageReader::New();
auto iter = std::find_if(imageProperties.begin(), imageProperties.end(),[&uniqueID](auto property){
return property->GetUniqueID()==uniqueID;
});
if (iter==imageProperties.end()) return nullptr;
// reader->SetFileNames((*iter)->GetFileNames());
reader->SetImageProperties((*iter));
reader->Update();
auto imageData = reader->GetOutput();
SeriesImageSet* result = new SeriesImageSet((*iter),imageData);
if (reader->GetHasOverlay()){
result->SetOverlayData(reader->GetOutputOverLayData());
}
// imageData->Register(placeHolder);
reader->Delete();
reader = nullptr;
store[uniqueID] = result;
return result;
}
void DicomLoader::readTags(const std::string &dir, SeriesOpenMode openMode)
{
void DicomLoader::readPropertiesFromDir(const std::string &dir,
std::vector<ExtendMedicalImageProperties*>& properties, int& count) {
DICOMHeaderHelper DICOMHelper;
if (openMode == FILE_OPEN_MODE)
{
//m_itkSeriesReader->SetFileName(m_dicomName.toStdString());
DICOMHelper.SetFileName(dir.c_str());
}
if (openMode == DIR_OPEN_MODE)
{
DICOMHelper.SetDirName(dir.c_str());
}
DICOMHelper.Update();
DICOMHelper.SetDirName(dir.c_str());
DICOMHelper.Update();
count = DICOMHelper.GetSeriesCount();
if ( DICOMHelper.GetSeriesCount()>0){
for (auto item : DICOMHelper.GetSeriesProperties()) {
item->Register(placeHolder);
imageProperties.push_back(item);
properties.push_back(item);
}
}
if ( imageProperties.size() > 0 ) {
currentImageProperty = imageProperties[0];
defaultUniqueID = currentImageProperty->GetUniqueID();
readSeries();
}
}
void DicomLoader::readSeries() {
for (ExtendMedicalImageProperties* property: imageProperties){
std::string patient_name = property->GetPatientName();
std::string study_uid = property->GetStudyUID();
std::string series_uid = property->GetSeriesUID();
//patient level
PatientInfo_t* patient = nullptr;
//get from store
if (m_patients.count(patient_name)>0){
patient = m_patients[patient_name];
void DicomLoader::readPropertiesFromFile(const std::string &file,
std::vector<ExtendMedicalImageProperties*>& properties, int& count) {
DICOMHeaderHelper DICOMHelper;
DICOMHelper.SetFileName(file.c_str());
DICOMHelper.Update();
count = DICOMHelper.GetSeriesCount();
if ( DICOMHelper.GetSeriesCount()>0){
for (auto item : DICOMHelper.GetSeriesProperties()) {
properties.push_back(item);
}
else{
//create new
patient = new PatientInfo_t();
patient->patient_name = property->GetPatientName();
patient->birth_date = property->GetPatientBirthDate();
patient->studies = new StudiesMapType();
m_patients[patient_name] = patient;
}
StudyInfo_t* study = nullptr;
//get from store
if (patient->studies->count(study_uid)>0){
study = patient->studies->at(study_uid);
}
else{
//create new
study = new StudyInfo_t();
patient->studies->insert({study_uid, study});
study->study_description = property->GetStudyDescription();
study->study_date = property->GetStudyDate();
study->study_time = property->GetStudyTime();
study->series = new SeriesMapType();
}
if (study->series->count(series_uid)>0){
// series = study->series->at(series_uid);
//TODO:need to add override logic!!
}
else {
study->series->insert({series_uid,property});
// double *wlww = property->GetNthWindowLevelPreset(0);
// int ww = wlww ? ((int) wlww[0]) : (512);
// int wl = wlww ? ((int) wlww[1]) : (256);
}
}
}
void DicomLoader::reset() {
currentImageProperty = nullptr;
reader = nullptr;
for(auto item: store){
delete item.second;
}
store.clear();
for(auto item: m_patients){
delete item.second;
}
m_patients.clear();
placeHolder->Delete();
placeHolder = vtkObject::New();
imageProperties.clear();
void DicomLoader::getImageData(ExtendMedicalImageProperties *property,
vtkSmartPointer<vtkImageData>& pixelData, vtkSmartPointer<vtkImageData>& overlayData) {
vtkNew<vtkDCMTKImageReader> reader;
reader->SetImageProperties(property);
reader->Update();
pixelData = reader->GetOutput();
overlayData = reader->GetOutputOverLayData();
}
void DicomLoader::InitCodecs() {
DICOMPixelDataHelper::InitCodecs();
}
void DicomLoader::FinalizeCodecs() {
DICOMPixelDataHelper::FinalizeCodecs();
}
void DicomLoader::getThumbnailData(ExtendMedicalImageProperties* property,void*& data, int& sample) {
unsigned long l ;
DICOMPixelDataHelper::GetThumbnailData(property, data,l,sample);
}

View File

@@ -1,77 +1,32 @@
#ifndef OMEGAV_DICOMLOADER_H
#define OMEGAV_DICOMLOADER_H
#pragma once
#include <vector>
#include <string>
#include <vtkSmartPointer.h>
#include "Common/QGlobals.h"
using namespace std;
typedef std::map<std::string,std::vector<std::string>> OrientationMapType;
class orientationHelper
{
public:
static std::string StringFilter(char* str);
static void init();
static std::vector<std::string>* getOrientationStrList(const std::string &index);
static OrientationMapType orien_map;
};
class SeriesImageSet;
class DICOMHeaderHelper;
class ExtendMedicalImageProperties;
class vtkDCMTKImageReader;
typedef std::map<std::string, SeriesImageSet*> ImageSetStore;
class vtkImageData;
class DicomLoader {
public:
static void InitCodecs();
static DicomLoader *GetInstance();
static void FinalizeCodecs();
/**
* 读取数据Tag
* @param dir
* @param openMode
*/
void readTags(const std::string &dir, SeriesOpenMode openMode);
static void readPropertiesFromDir(const std::string &dir,
std::vector<ExtendMedicalImageProperties*>& properties, int& count);
const std::string& getDefaultUniqueID(){
return defaultUniqueID;
}
static void readPropertiesFromFile(const std::string &file,
std::vector<ExtendMedicalImageProperties*>& properties, int& count);
const PatientsMapType &getPatientsList()
{
return m_patients;
}
SeriesImageSet* getSeriesImageSet(const std::string& uniqueID);
void reset();
static void getImageData(ExtendMedicalImageProperties* property,
vtkSmartPointer<vtkImageData>& pixelData, vtkSmartPointer<vtkImageData>& overlayData);
static void getThumbnailData(ExtendMedicalImageProperties* property,void*& data, int& sample);
private:
explicit DicomLoader();
~DicomLoader();
/**
* 本函数根据DICOM文件TagValue来构建一个树状的PSSI结构
* 即Patient-Study-Series-Image
* 主要用途是为左侧的thumbnail bar 做数据支撑
* TODO目前Image层暂缺
*/
void readSeries();
static DicomLoader *instance;
//once
ExtendMedicalImageProperties * currentImageProperty = nullptr;
std::vector<ExtendMedicalImageProperties*> imageProperties;
vtkDCMTKImageReader * reader = nullptr;
ImageSetStore store;
std::string defaultUniqueID;
PatientsMapType m_patients;
vtkObject* placeHolder;
DicomLoader() = delete;
~DicomLoader() = delete;
};
#endif OMEGAV_DICOMLOADER_H

View File

@@ -0,0 +1,19 @@
//
// Created by Krad on 2022/9/19.
//
#ifndef OMEGAV_DIDKITEXPORT_H
#define OMEGAV_DIDKITEXPORT_H
#ifdef _WIN32
#ifdef DIDKIT_SHARED
/* Defines needed for building DLLs on windows */
#define DIDKIT_EXPORT __declspec(dllexport)
#elif USE_DIDKIT
/* Defines needed for use DLLs on windows */
#define DIDKIT_EXPORT __declspec(dllimport)
#else
#define DIDKIT_EXPORT
#endif
#endif
#endif //OMEGAV_DIDKITEXPORT_H

View File

@@ -9,7 +9,7 @@
#include <QDebug>
#include <QVTKOpenGLNativeWidget.h>
#include "IO/DICOM/DicomLoader.h"
#include "Common/ImageSetStore.h"
#include "Interaction/ActorDraggableInteractorStyle.h"
#include "Rendering/Measure/MeasureFactory.h"
#include "UI/Widget/ImageView/ViewContainerWidget.h"
@@ -359,7 +359,7 @@ void ImageViewManager::viewEndWindowLevel(DicomImageView *src, double level, dou
void ImageViewManager::viewReload(const std::string &unique_info) {
if (!currentView) return;
currentView->unloadFusion();
DicomLoader *helper = DicomLoader::GetInstance();
ImageSetStore *helper = ImageSetStore::GetInstance();
currentView->loadSeries(helper->getSeriesImageSet(unique_info));
currentView->render();
reloadCurrentView(currentView);

View File

@@ -3,7 +3,7 @@
#include <QImage>
#include "qstyleoption.h"
#include "qpainter.h"
#include "IO/DICOM/DICOMPixelDataHelper.h"
#include "IO/DICOM/DicomLoader.h"
static QString unpick_style = "*{background-color:#7f7f7f;}"
"QLabel#m_descri{color:white}";
@@ -134,9 +134,8 @@ thumbnailImage::~thumbnailImage()
void thumbnailImage::drawThumbnail() {
if (m_Data) free(m_Data);
m_Data = nullptr;
unsigned long length = 0;
int sample = 0;
DICOMPixelDataHelper::GetThumbnailData(seriesProperty, m_Data, length, sample);
DicomLoader::getThumbnailData(seriesProperty, m_Data, sample);
QImage image( (uchar*)m_Data, seriesProperty->GetColumns(), seriesProperty->GetRows(),
sample == 1 ? QImage::Format_Grayscale8 : QImage::Format_RGB888);//使用8位深度的灰度图做输出
image = image.scaledToHeight(100);

View File

@@ -5,8 +5,8 @@
#include <qstyleoption.h>
#include <qpainter.h>
#include "Common/ImageSetStore.h"
#include "IO/DICOM/DicomLoader.h"
#include "IO/DICOM/DICOMPixelDataHelper.h"
#include "Common/SeriesImageSet.h"
#include "UI/Widget/ImageView/dicomimageview.h"
@@ -84,7 +84,7 @@ void ThumbnailBarWidget::updateThumbnailBar()
}
clear();
bool firstThumbnail = true;
DicomLoader *helper = DicomLoader::GetInstance();
ImageSetStore *helper = ImageSetStore::GetInstance();
//获取Patient
const PatientsMapType & all_patients = helper->getPatientsList();
for (PatientsMapType::const_iterator it_pa = all_patients.cbegin(); it_pa != all_patients.cend(); it_pa++) {
@@ -116,7 +116,7 @@ void ThumbnailBarWidget::updateThumbnailBar()
SeriesMapType *series = it_st->second->series;
DICOMPixelDataHelper::InitCodecs();
DicomLoader::InitCodecs();
for (SeriesMapType::const_iterator it_se = series->cbegin(); it_se != series->cend(); it_se++) {
thumbnailImage *thumbnail = createThumbnailImage(seriesPanel, it_se->second);
@@ -128,7 +128,7 @@ void ThumbnailBarWidget::updateThumbnailBar()
//save all thumbnail in one list
LabelList << thumbnail;
}
DICOMPixelDataHelper::FinalizeCodecs();
DicomLoader::FinalizeCodecs();
}
}
}

View File

@@ -7,6 +7,9 @@
#include <QButtonGroup>
#include "Common/Helper/OrientationHelper.h"
#include "Common/ImageSetStore.h"
#include "IO/DICOM/DicomLoader.h"
#include "Interaction/ActorDraggableInteractorStyle.h"
#include "UI/Manager/ImageViewManager.h"
#include "UI/Widget/Component/gridpopwidget.h"
@@ -39,7 +42,7 @@ QDicomViewer::QDicomViewer(QWidget *parent) : QMainWindow(parent),
displayThumbnailBar(false);
//TODO:目前为方向是写死的正交方向
orientationHelper::init();
OrientationHelper::init();
}
QDicomViewer::~QDicomViewer() {
@@ -272,14 +275,24 @@ void QDicomViewer::openDICOM(const std::string &dicomName, SeriesOpenMode openMo
//必须首先重置成1个窗口的布局
ui->viewContainer->resetLayoutToSingle();
//数据读取
DicomLoader *helper = DicomLoader::GetInstance();
helper->reset();
//load image and tag
helper->readTags(dicomName, openMode);
auto unique = helper->getDefaultUniqueID();
ui->viewContainer->getViewManager()->viewReload(unique);
//reset store
ImageSetStore *helper = ImageSetStore::GetInstance();
helper->reset();
int count = 0;
std::vector<ExtendMedicalImageProperties*> vector;
if (openMode == SeriesOpenMode::DIR_OPEN_MODE){
DicomLoader::readPropertiesFromDir(dicomName, vector,count);
}else{
DicomLoader::readPropertiesFromFile(dicomName, vector, count);
}
for (int i = 0; i < count; ++i) {
helper->addImageSet(vector[i]);
}
if (count > 0){
ui->viewContainer->getViewManager()->viewReload(vector[0]->GetUniqueID());
}
ui->thumbnailBar->updateThumbnailBar();
}