Browse Source

current version

master
dahuang 1 week ago
commit
7356703c73
100 changed files with 13109 additions and 0 deletions
  1. +21
    -0
      LICENSE
  2. BIN
      Libs/Lib_LabelAnalysis/bin/LabelAnalysis.dll
  3. BIN
      Libs/Lib_LabelAnalysis/bin/LabelAnalysis.pdb
  4. +112
    -0
      Libs/Lib_LabelAnalysis/include/LabelAnalysis.h
  5. BIN
      Libs/Lib_LabelAnalysis/lib/LabelAnalysis.lib
  6. +45
    -0
      UltimageTK.sln
  7. +30
    -0
      UltimageTK/AboutDlg.cpp
  8. +24
    -0
      UltimageTK/AboutDlg.h
  9. +84
    -0
      UltimageTK/AboutDlg.ui
  10. +105
    -0
      UltimageTK/AllSettings.cpp
  11. +60
    -0
      UltimageTK/AllSettings.h
  12. +70
    -0
      UltimageTK/ConfigHelper.cpp
  13. +66
    -0
      UltimageTK/ConfigHelper.h
  14. +612
    -0
      UltimageTK/DataLoad.cpp
  15. +61
    -0
      UltimageTK/DataLoad.h
  16. +38
    -0
      UltimageTK/DrawBase.cpp
  17. +89
    -0
      UltimageTK/DrawBase.h
  18. +817
    -0
      UltimageTK/DrawPen.cpp
  19. +97
    -0
      UltimageTK/DrawPen.h
  20. +553
    -0
      UltimageTK/FormatConvert.cpp
  21. +63
    -0
      UltimageTK/FormatConvert.h
  22. +124
    -0
      UltimageTK/FormatConvert.ui
  23. +66
    -0
      UltimageTK/GlobalDef.h
  24. +378
    -0
      UltimageTK/HistogramMatching.cpp
  25. +50
    -0
      UltimageTK/HistogramMatching.h
  26. +122
    -0
      UltimageTK/HistogramMatching.ui
  27. +940
    -0
      UltimageTK/ImageDataManager.cpp
  28. +104
    -0
      UltimageTK/ImageDataManager.h
  29. +1106
    -0
      UltimageTK/ImageWidget.cpp
  30. +109
    -0
      UltimageTK/ImageWidget.h
  31. +247
    -0
      UltimageTK/Rebuild3D.cpp
  32. +72
    -0
      UltimageTK/Rebuild3D.h
  33. +4
    -0
      UltimageTK/Rebuild3D.qrc
  34. +57
    -0
      UltimageTK/Rebuild3D.ui
  35. +523
    -0
      UltimageTK/Registration.cpp
  36. +49
    -0
      UltimageTK/Registration.h
  37. +324
    -0
      UltimageTK/Registration.ui
  38. BIN
      UltimageTK/Resources/icon.ico
  39. BIN
      UltimageTK/Resources/loading.gif
  40. +320
    -0
      UltimageTK/SaveThread.cpp
  41. +32
    -0
      UltimageTK/SaveThread.h
  42. +126
    -0
      UltimageTK/SettingColorItem.cpp
  43. +41
    -0
      UltimageTK/SettingColorItem.h
  44. +104
    -0
      UltimageTK/SettingColorItem.ui
  45. +206
    -0
      UltimageTK/SettingUI.cpp
  46. +47
    -0
      UltimageTK/SettingUI.h
  47. +161
    -0
      UltimageTK/SettingUI.ui
  48. BIN
      UltimageTK/UltimageTK.aps
  49. +1073
    -0
      UltimageTK/UltimageTK.cpp
  50. +81
    -0
      UltimageTK/UltimageTK.h
  51. +6
    -0
      UltimageTK/UltimageTK.qrc
  52. BIN
      UltimageTK/UltimageTK.rc
  53. +1292
    -0
      UltimageTK/UltimageTK.ui
  54. +16
    -0
      UltimageTK/UltimageTK.vcxproj.user
  55. +57
    -0
      UltimageTK/main.cpp
  56. +16
    -0
      UltimageTK/resource.h
  57. +888
    -0
      UltimageTK/ultimagetk_en.ts
  58. +888
    -0
      UltimageTK/ultimagetk_zh.ts
  59. +0
    -0
      docs/.nojekyll
  60. +18
    -0
      docs/README.md
  61. +2
    -0
      docs/_navbar.md
  62. +14
    -0
      docs/_sidebar.md
  63. +104
    -0
      docs/analysisApi.md
  64. +9
    -0
      docs/buildModel.md
  65. +35
    -0
      docs/changeLog.md
  66. BIN
      docs/favicon.ico
  67. +46
    -0
      docs/featureExtract.md
  68. +84
    -0
      docs/featureSelect.md
  69. +75
    -0
      docs/imageBrowsing.md
  70. +34
    -0
      docs/imageSegmentation.md
  71. BIN
      docs/imgs/boruta.png
  72. BIN
      docs/imgs/correlation_coefficient.png
  73. BIN
      docs/imgs/feature_extract.png
  74. BIN
      docs/imgs/feature_extract_label_name.png
  75. BIN
      docs/imgs/feature_select.png
  76. BIN
      docs/imgs/lasso.png
  77. BIN
      docs/imgs/logo.png
  78. BIN
      docs/imgs/logo32_32.png
  79. BIN
      docs/imgs/open_files.png
  80. BIN
      docs/imgs/pipeline.png
  81. BIN
      docs/imgs/pipeline_1.png
  82. BIN
      docs/imgs/pipeline_2.png
  83. BIN
      docs/imgs/pipeline_3.png
  84. BIN
      docs/imgs/pipeline_4.png
  85. BIN
      docs/imgs/pipeline_5.png
  86. BIN
      docs/imgs/pos_value.png
  87. BIN
      docs/imgs/pre_histogram_matching.png
  88. BIN
      docs/imgs/pre_regist.png
  89. BIN
      docs/imgs/pro_format.png
  90. BIN
      docs/imgs/read_single.png
  91. BIN
      docs/imgs/save_btn.png
  92. BIN
      docs/imgs/setting_color.png
  93. BIN
      docs/imgs/single_btn.png
  94. BIN
      docs/imgs/software_home.png
  95. BIN
      docs/imgs/transparancy.png
  96. BIN
      docs/imgs/window_level.png
  97. +85
    -0
      docs/index.html
  98. +25
    -0
      docs/introduction.md
  99. +1
    -0
      docs/libs/disqus.min.js
  100. +1
    -0
      docs/libs/docsify.min.js

+ 21
- 0
LICENSE View File

@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2020 UltimageTK

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

BIN
Libs/Lib_LabelAnalysis/bin/LabelAnalysis.dll View File


BIN
Libs/Lib_LabelAnalysis/bin/LabelAnalysis.pdb View File


+ 112
- 0
Libs/Lib_LabelAnalysis/include/LabelAnalysis.h View File

@@ -0,0 +1,112 @@
/******************************************************************************
* Copyright (C), 2017-2019,Advanced Technology Institute of Suzhou.
* THIS IS AN UNPUBLISHED WORK CONTAINING CONFIDENTIAL AND PROPRIETARY
*
*******************************************************************************
* File Name: LabelAnalysis.h
* Author: qh.zhang@atisz.ac.cn
* Version: 1.0.0
* Date: 2020-3-23
* Description: LRS结构是UltImageTK为了方便保存和读取已标注文件而定义的一种序列化到本地的格式
* History:
******************************************************************************/
#pragma once
#include <string.h>
#include <vector>
#include <list>
#include <map>

#ifdef LABELANALYSIS_EXPORTS
#define EXPORTS_LABEL _declspec(dllexport)
#else
#define EXPORTS_LABEL _declspec(dllimport)
#endif


#ifdef __cplusplus

extern "C"
{
namespace LabelAnalysis
{

/********************************************************
* @struct : Vertex
* @brief : 一个点的信息
* @details : 一个点的位置和物理值(HU)
*********************************************************/
struct Vertex
{
float fX; //在当前视图下的x坐标
float fY; //在当前视图下的y坐标
int nValue; //HU值
};

/********************************************************
* @struct : Target
* @brief : 每一个目标的信息
* @details : 标记好的每一个目标ROI的信息
*********************************************************/
struct Target
{
std::string strTargetName; //目标类型-标签值
std::string strTargetDisc; //目标描述-标签描述
int nTargetID; //目标在当前视图当前层的ID
int nTargetType; //目标几何形状类型
std::list<Vertex> lstVertex; //目标轮廓点集
};

/********************************************************
* @struct : FileInfo
* @brief : 源图像文件的信息
* @details : 源文件中一些相对比较重要的信息
*********************************************************/
struct FileInfo
{
std::string strFilePath; //源文件路径,或文件夹路径
std::string strPatientName; //患者名
std::string strPatientAge; //患者年龄
std::string strPatientSex; //患者性别
int nFileType; //文件类型
int nWidth; //宽
int nHeight; //高
int nThickness; //层数
float fSpacing; //间隔比例
};

/********************************************************
* @struct : AllLabelInfo
* @brief : 一个文件或图像序列的所有信息
* @details : 包含源图像文件的信息,已标注的标签信息,以及标注在各个视图面上的ROI信息
*********************************************************/
struct AllLabelInfo
{
int nCurVersion; //本版
FileInfo stFileInfo; //读取的文件信息
std::map<std::string, int> mapLabelProperty; //label的颜色和定义
std::map<int, std::map<int, Target>> mapSPTargets; //矢状面,帧号和目标列表
std::map<int, std::map<int, Target>> mapCPTargets; //冠状面,帧号和目标列表
std::map<int, std::map<int, Target>> mapTPTargets; //横断面,帧号和目标列表
};

/********************************************************
* @function : ReadLabelFile
* @brief : 读取pPath路径下的lrs文件
* @input : pPath 读路径
* @output : stAllLabelInfo LRS数据结构
* @return : bool 成功与否
*********************************************************/
EXPORTS_LABEL bool ReadLabelFile(char* pPath, AllLabelInfo &stAllLabelInfo);

/********************************************************
* @function : WriteLabelFile
* @brief : 往pPath路径写lrs文件
* @input : pPath 写路径
* @input : stAllLabelInfo LRS数据结构
* @return : bool 成功与否
*********************************************************/
EXPORTS_LABEL bool WriteLabelFile(char* pPath, AllLabelInfo stAllLabelInfo);
}
}

#endif

BIN
Libs/Lib_LabelAnalysis/lib/LabelAnalysis.lib View File


+ 45
- 0
UltimageTK.sln View File

@@ -0,0 +1,45 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.28307.572
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "UltimageTK", "UltimageTK\UltimageTK.vcxproj", "{B12702AD-ABFB-343A-A199-8E24837244A3}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "LabelAnalysis", "LabelAnalysis\LabelAnalysis.vcxproj", "{3371EEF4-BFB6-4EFB-A512-5975258FB4EE}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|Any CPU = Release|Any CPU
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{B12702AD-ABFB-343A-A199-8E24837244A3}.Debug|Any CPU.ActiveCfg = Debug|x64
{B12702AD-ABFB-343A-A199-8E24837244A3}.Debug|x64.ActiveCfg = Debug|x64
{B12702AD-ABFB-343A-A199-8E24837244A3}.Debug|x64.Build.0 = Debug|x64
{B12702AD-ABFB-343A-A199-8E24837244A3}.Debug|x86.ActiveCfg = Debug|x64
{B12702AD-ABFB-343A-A199-8E24837244A3}.Release|Any CPU.ActiveCfg = Release|x64
{B12702AD-ABFB-343A-A199-8E24837244A3}.Release|x64.ActiveCfg = Release|x64
{B12702AD-ABFB-343A-A199-8E24837244A3}.Release|x64.Build.0 = Release|x64
{B12702AD-ABFB-343A-A199-8E24837244A3}.Release|x86.ActiveCfg = Release|x64
{3371EEF4-BFB6-4EFB-A512-5975258FB4EE}.Debug|Any CPU.ActiveCfg = Debug|Win32
{3371EEF4-BFB6-4EFB-A512-5975258FB4EE}.Debug|x64.ActiveCfg = Debug|x64
{3371EEF4-BFB6-4EFB-A512-5975258FB4EE}.Debug|x64.Build.0 = Debug|x64
{3371EEF4-BFB6-4EFB-A512-5975258FB4EE}.Debug|x86.ActiveCfg = Debug|Win32
{3371EEF4-BFB6-4EFB-A512-5975258FB4EE}.Debug|x86.Build.0 = Debug|Win32
{3371EEF4-BFB6-4EFB-A512-5975258FB4EE}.Release|Any CPU.ActiveCfg = Release|Win32
{3371EEF4-BFB6-4EFB-A512-5975258FB4EE}.Release|x64.ActiveCfg = Release|x64
{3371EEF4-BFB6-4EFB-A512-5975258FB4EE}.Release|x64.Build.0 = Release|x64
{3371EEF4-BFB6-4EFB-A512-5975258FB4EE}.Release|x86.ActiveCfg = Release|Win32
{3371EEF4-BFB6-4EFB-A512-5975258FB4EE}.Release|x86.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {A6DBC197-F496-4D38-923E-17611289B7BF}
EndGlobalSection
EndGlobal

+ 30
- 0
UltimageTK/AboutDlg.cpp View File

@@ -0,0 +1,30 @@
/********************************************************
* @file : AboutDlg.cpp
* @brief : brief
* @details :
* @author : qh.zhang@atisz.ac.cn
* @date : 2019-7-9
*********************************************************/
#include "AboutDlg.h"

AboutDlg::AboutDlg(QWidget *parent)
: QDialog(parent)
{
ui.setupUi(this);
}

AboutDlg::~AboutDlg()
{
}

/********************************************************
* @function : changeLanguage
* @brief : ±ä¸üÓïÑÔ
* @input :
* @output :
* @return :
*********************************************************/
void AboutDlg::changeLanguage()
{
ui.retranslateUi(this);
}

+ 24
- 0
UltimageTK/AboutDlg.h View File

@@ -0,0 +1,24 @@
/********************************************************
* @file : AboutDlg.h
* @brief : brief
* @details :
* @author : qh.zhang@atisz.ac.cn
* @date : 2019-7-9
*********************************************************/
#pragma once

#include <QDialog>
#include "ui_AboutDlg.h"

class AboutDlg : public QDialog
{
Q_OBJECT

public:
AboutDlg(QWidget *parent = Q_NULLPTR);
~AboutDlg();
//±ä¸üÓïÑÔ
void changeLanguage();
private:
Ui::AboutDlg ui;
};

+ 84
- 0
UltimageTK/AboutDlg.ui View File

@@ -0,0 +1,84 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>AboutDlg</class>
<widget class="QDialog" name="AboutDlg">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>255</width>
<height>107</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>255</width>
<height>107</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>255</width>
<height>107</height>
</size>
</property>
<property name="windowTitle">
<string>关于 UltImageTK</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="label">
<property name="styleSheet">
<string notr="true">font: 9pt &quot;微软雅黑&quot;;</string>
</property>
<property name="text">
<string>UltImageTK</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_2">
<property name="styleSheet">
<string notr="true">font: 9pt &quot;微软雅黑&quot;;</string>
</property>
<property name="text">
<string>版本1.0.1</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_5">
<property name="styleSheet">
<string notr="true">font: 9pt &quot;微软雅黑&quot;;</string>
</property>
<property name="text">
<string>©2019 苏州中科华影健康科技有限公司</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_4">
<property name="styleSheet">
<string notr="true">font: 9pt &quot;微软雅黑&quot;;</string>
</property>
<property name="text">
<string>保留所有权利</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<layoutdefault spacing="6" margin="11"/>
<resources/>
<connections/>
</ui>

+ 105
- 0
UltimageTK/AllSettings.cpp View File

@@ -0,0 +1,105 @@
/********************************************************
* @file : AllSettings.cpp
* @brief :
* @details :
* @author : qh.zhang@atisz.ac.cn
* @date : 2019-5-20
*********************************************************/
#include "AllSettings.h"

AllSettings* AllSettings::m_pInstance = nullptr;

/********************************************************
* @function : 构造函数
* @brief : brief
* @input :
* @output :
* @return :
*********************************************************/
AllSettings::AllSettings()
{
}

AllSettings::~AllSettings()
{
}

/********************************************************
* @function : UpdateColorMap
* @brief : 更新标签颜色表
* @input :
* @output :
* @return :
*********************************************************/
void AllSettings::UpdateColorMap(const std::map<std::string, int> mapLabelProperty)
{
if (mapLabelProperty.empty())
{
return;
}
m_mapColor.clear();
for each (auto var in mapLabelProperty)
{
m_mapColor[QString::fromLocal8Bit(var.first.c_str())] = var.second;
}
m_strCurLabel = m_mapColor.firstKey();
}


/********************************************************
* @function : GetColorByLabel
* @brief : 根据标签名获取颜色
* @input :
* @output :
* @return :
*********************************************************/
QColor AllSettings::GetColorByLabel(QString str)
{
if (m_mapColor.find(str) != m_mapColor.end())
{
return m_mapColor[str];
}
else
{
return QColor(255, 255, 255);
}
}

/********************************************************
* @function : SetCurLabel
* @brief : 更新当前标签名
* @input :
* @output :
* @return :
*********************************************************/
void AllSettings::SetCurLabel(QString strLabel)
{
m_strCurLabel = strLabel;
}

/********************************************************
* @function : GetCurLabelAndColor
* @brief : 获取当前标签和颜色
* @input :
* @output :
* @return :
*********************************************************/
void AllSettings::GetCurLabelAndColor(QString &str, QColor &color)
{
str = m_strCurLabel;
color = m_mapColor[m_strCurLabel];
}

/********************************************************
* @function : GetCurColor
* @brief : 获取当前颜色
* @input :
* @output :
* @return :
*********************************************************/
QColor AllSettings::GetCurColor()
{
return m_mapColor[m_strCurLabel];
}


+ 60
- 0
UltimageTK/AllSettings.h View File

@@ -0,0 +1,60 @@
/********************************************************
* @file : AllSettings.h
* @brief :
* @details :
* @author : qh.zhang@atisz.ac.cn
* @date : 2019-5-20
*********************************************************/
#pragma once
#include <QColor>
#include <QMap>

/********************************************************
* @class : AllSettings
* @brief : brief
* @details :
*********************************************************/
class AllSettings
{
public:

static AllSettings* getInstance()
{
if (nullptr == m_pInstance)
{
m_pInstance = new AllSettings;
}
return m_pInstance;
}

static void releaseInstance()
{
if (nullptr != m_pInstance)
{
delete m_pInstance;
m_pInstance = nullptr;
}
}

//更新颜色表
void UpdateColorMap(const std::map<std::string, int> mapLabelProperty);
//根据标签获取颜色
QColor GetColorByLabel(QString str);
//设置当前标签名
void SetCurLabel(QString strLabel);
//获取当前标签和颜色
void GetCurLabelAndColor(QString &str, QColor &color);
//获取当前颜色
QColor GetCurColor();

private:
AllSettings();
~AllSettings();

private:
static AllSettings* m_pInstance;

QMap<QString, QColor> m_mapColor; //颜色标签表
QString m_strCurLabel; //当前标签名

};

+ 70
- 0
UltimageTK/ConfigHelper.cpp View File

@@ -0,0 +1,70 @@
/********************************************************
* @file : ConfigHelper.cpp
* @brief : brief
* @details :
* @author : qh.zhang@atisz.ac.cn
* @date : 2019-8-6
*********************************************************/
#include <QSettings>
#include "ConfigHelper.h"

#define CONFIG_FILE "./config.ini"

ConfigHelper * ConfigHelper::m_pInstance = nullptr;
ConfigHelper::ConfigHelper()
{
}


ConfigHelper::~ConfigHelper()
{
}

/********************************************************
* @function : readConfig
* @brief : 读配置
* @input :
* @output :
* @return :
*********************************************************/
bool ConfigHelper::readConfig(AllConfig &stAllConfig)
{
//设置好格式和编码
QSettings stSetting(CONFIG_FILE, QSettings::IniFormat);
stSetting.setIniCodec("GBK");

//在AllConfig组中读取各个项
stSetting.beginGroup("AllConfig");
m_stAllConfig.emLanguage = (QLocale::Language)stSetting.value("Language").toInt();
stSetting.endGroup();

//简单校验一下,如果用户名是空的,返回错误
if (m_stAllConfig.emLanguage<=0)
{
return false;
}
stAllConfig = m_stAllConfig;
return true;
}

/********************************************************
* @function : writeConfig
* @brief : 写配置
* @input :
* @output :
* @return :
*********************************************************/
bool ConfigHelper::writeConfig(const AllConfig &stAllConfig)
{
//设置好格式和编码
QSettings stSetting(CONFIG_FILE, QSettings::IniFormat);
stSetting.setIniCodec("GBK");

m_stAllConfig = stAllConfig;
//写AllConfig组中各个项
stSetting.beginGroup("AllConfig");
stSetting.setValue("Language", m_stAllConfig.emLanguage);
stSetting.endGroup();

return true;
}

+ 66
- 0
UltimageTK/ConfigHelper.h View File

@@ -0,0 +1,66 @@
/********************************************************
* @file : ConfigHelper.h
* @brief : brief
* @details :
* @author : qh.zhang@atisz.ac.cn
* @date : 2019-8-6
*********************************************************/
#pragma once

#include <QLocale>

/********************************************************
* @struct : AllConfig
* @brief : 配置文件中的各项
* @details :
*********************************************************/
struct AllConfig
{
QLocale::Language emLanguage; //用户名
AllConfig()
{
emLanguage = QLocale::Language::Chinese;
}
};

/********************************************************
* @class : ConfigHelper
* @brief : 配置文件帮助单例
* @details :
*********************************************************/
class ConfigHelper
{
ConfigHelper();
~ConfigHelper();

public:
static ConfigHelper* getInstance()
{
if (nullptr == m_pInstance)
{
m_pInstance = new ConfigHelper;
}
return m_pInstance;
}

static void releaseInstance()
{
if (nullptr != m_pInstance)
{
delete m_pInstance;
m_pInstance = nullptr;
}
}


public:
//读配置文件
bool readConfig(AllConfig &stAllConfig);
//写配置文件
bool writeConfig(const AllConfig &stAllConfig);

private:
static ConfigHelper* m_pInstance; //单例句柄
AllConfig m_stAllConfig; //所有配置信息
};


+ 612
- 0
UltimageTK/DataLoad.cpp View File

@@ -0,0 +1,612 @@
/********************************************************
* @file : DataLoad.cpp
* @brief :
* @details :
* @author : qh.zhang@atisz.ac.cn
* @date : 2019-4-25
*********************************************************/
#include <QFileInfo>
#include <QDir>
#include "DataLoad.h"
#include "dcmtk/config/osconfig.h"
#include "dcmtk/dcmdata/dctk.h"
#include "opencv2/opencv.hpp"
#include "ImageDataManager.h"

#include "itkImageFileReader.h"
#include "itkImageFileWriter.h"
#include "itkRescaleIntensityImageFilter.h"

#include "itkImageSeriesReader.h"
#include "itkImageFileWriter.h"

#include "itkGDCMImageIOFactory.h"
#include "itkNiftiImageIOFactory.h"
#include "itkJPEGImageIOFactory.h"
#include "itkPNGImageIOFactory.h"
#include "itkBMPImageIOFactory.h"
#include "itkNRRDImageIOFactory.h"

#include "./BridgeOpenCV/include/itkOpenCVImageBridge.h"
/********************************************************
* @class : DataLoad
* @brief : 构造函数
* @details :
*********************************************************/
DataLoad::DataLoad()
{
}

DataLoad::~DataLoad()
{
}

/********************************************************
* @function : Init
* @brief : 初始化
* @input :
* @output :
* @return :
*********************************************************/
bool DataLoad::Init()
{
//初始化的时候注册一些要用到的图像类别工厂,否则后面的读写操作无法成功
itk::GDCMImageIOFactory::RegisterOneFactory();
itk::NiftiImageIOFactory::RegisterOneFactory();
itk::PNGImageIOFactory::RegisterOneFactory();
itk::JPEGImageIOFactory::RegisterOneFactory();
itk::BMPImageIOFactory::RegisterOneFactory();
itk::NrrdImageIOFactory::RegisterOneFactory();

if (m_itkReader == nullptr)
{
m_itkReader = SeriesReaderType::New();
}

return true;
}

/********************************************************
* @function : GetHUValueImg
* @brief : //获取原始图像的CT值图像
* @input :
* @output :
* @return :
*********************************************************/
bool GetHUValueImg(cv::Mat& img, int nIntercept, int nSlope)
{
if (!img.data)
{
return false;
}

int rows(img.rows);
int cols(img.cols);
unsigned short* data = nullptr;

/*
首先,需要读取两个DICOM Tag信息,(0028|1052):rescale intercept和(0028|1053):rescale slope.
然后通过公式:
Hu = pixel * slope(1) + intercept(-1024)
计算得到CT值。
*/
for (int i = 0; i < rows; ++i)
{
data = img.ptr<unsigned short>(i);
for (int j = 0; j < cols; ++j)
{
int temp = data[j] * nSlope + nIntercept;
if (temp < 0)
{
temp = 0;
}
data[j] = temp;
}
}

return true;
}
/*
bool DataLoad::loadDcmImage(const QString &strImgPath, QImage &img, ImageHeaderInfo *pImageInfoStru)
{
DcmFileFormat fileformat;
OFCondition oc = fileformat.loadFile(strImgPath.toLocal8Bit().data()); //读取Dicom图像
if (!oc.good()) //判断Dicom文件是否读取成功
{
std::cout << "file Load error" << std::endl;
return false;
}
DcmDataset *dataset = fileformat.getDataset(); //得到Dicom的数据集,所有的数据都存储在数据集当中
E_TransferSyntax xfer = dataset->getOriginalXfer(); //得到传输语法

unsigned short sImgWidth; //获取图像的窗宽高
unsigned short sImgHeight;
dataset->findAndGetUint16(DCM_Rows, sImgHeight);
dataset->findAndGetUint16(DCM_Columns, sImgWidth);

int nIntercept(-1), nSlope(-1);
OFString strIntercept, strSlope;
dataset->findAndGetOFString(DCM_RescaleIntercept, strIntercept);//Intercept
dataset->findAndGetOFString(DCM_RescaleSlope, strSlope);//Slope
nIntercept = QString::fromLatin1(strIntercept.c_str()).toInt();
nSlope = QString::fromLatin1(strSlope.c_str()).toInt();

if (nullptr != pImageInfoStru)
{
OFString stPatientName;
dataset->findAndGetOFString(DCM_PatientName, stPatientName); //获取病人姓名

OFString strPatientAge;
dataset->findAndGetOFString(DCM_PatientAge, strPatientAge); //获取病人年龄

OFString strPatientSex;
dataset->findAndGetOFString(DCM_PatientSex, strPatientSex); //获取病人性别

unsigned short bit_count(0);
dataset->findAndGetUint16(DCM_BitsStored, bit_count); //获取像素的位数 bit

OFString isRGB;
dataset->findAndGetOFString(DCM_PhotometricInterpretation, isRGB);//DCM图片的图像模式

unsigned short img_bits(0);
dataset->findAndGetUint16(DCM_SamplesPerPixel, img_bits); //单个像素占用多少byte

unsigned short sWinCenter, sWinWidth; //获取源图像中的窗位和窗宽
dataset->findAndGetUint16(DCM_WindowCenter, sWinCenter);
dataset->findAndGetUint16(DCM_WindowWidth, sWinWidth);

float fPixelSpacing;
OFString strPixelSpacing;
dataset->findAndGetOFString(DCM_PixelSpacing, strPixelSpacing);//DCM图片的图像模式
fPixelSpacing = QString::fromLatin1(strPixelSpacing.c_str()).toFloat();

pImageInfoStru->strPatientName = QString::fromLocal8Bit(stPatientName.c_str());
pImageInfoStru->strPatientAge = QString::fromLocal8Bit(strPatientAge.c_str());
pImageInfoStru->strPatientSex = QString::fromLocal8Bit(strPatientSex.c_str());
pImageInfoStru->nWidth = sImgWidth;
pImageInfoStru->nHeight = sImgHeight;
pImageInfoStru->fPixelSpacing = fPixelSpacing;
pImageInfoStru->nIntercept = nIntercept;
pImageInfoStru->nSlope = nSlope;

}

DcmElement* element = NULL; //读取dcm中的像素值
OFCondition result = dataset->findAndGetElement(DCM_PixelData, element);
if (result.bad() || element == NULL)
{
return false;
}
Uint16* pixData16;
result = element->getUint16Array(pixData16);
if (result.bad())
{
return false;
}

cv::Mat matImg(sImgWidth, sImgHeight, CV_16UC1, cv::Scalar::all(0));
unsigned short* data = nullptr;
for (int i = 0; i < sImgHeight; i++)
{
data = matImg.ptr<unsigned short>(i); //取得每一行的头指针 也可使用dst2.at<unsigned short>(i, j) = ?
for (int j = 0; j < sImgWidth; j++)
{
unsigned short temp = pixData16[i * 512 + j];
temp = temp == 63536 ? 0 : temp;
*data++ = temp;
}
}
GetHUValueImg(matImg, nIntercept, nSlope);
matImg.convertTo(matImg, CV_8U);
QImage img2Copy(matImg.data, sImgWidth, sImgHeight, matImg.step, QImage::Format_Grayscale8);
img = img2Copy.copy();
if (img.isNull())
{
return false;
}
return true;
}

bool DataLoad::loadNoSuffixImage(const QString &strImgPath, QImage &img, ImageHeaderInfo *pImageInfoStru)
{
//加载无后缀名的图片
//目前先认为是dcm
return loadDcmImage(strImgPath, img, pImageInfoStru);
}

bool DataLoad::loadImage(const QString &strImgPath, QImage &img, ImageHeaderInfo *pImageInfoStru)
{
//格式判断
QFileInfo fileInfo(strImgPath);
if (fileInfo.suffix() == "dcm")
{
return loadDcmImage(strImgPath, img, pImageInfoStru);
}
else if (fileInfo.suffix() == "")
{
return loadNoSuffixImage(strImgPath, img, pImageInfoStru);
}

return true;
}

bool DataLoad::loadSingleImage(const QString &strFilePath, ImageInfoStru &stImageInfoStru)
{
ImageHeaderInfo stImageHeaderInfo;
QImage imgLoadTemp;
bool bRet = loadImage(m_strCurFileOrDirPath, imgLoadTemp, &stImageHeaderInfo);
stImageInfoStru.stImageHeaderInfo = stImageHeaderInfo;
stImageInfoStru.vecImg.push_back(imgLoadTemp);
return bRet;
}

bool DataLoad::loadImageFromDir(const QString &strDirPath, ImageInfoStru &stImageInfoStru)
{
//判断路径是否存在
QDir dir(strDirPath);
if (!dir.exists())
{
return false;
}

QStringList filters;
//filters << QString("*.dcm");
dir.setFilter(QDir::Files | QDir::NoSymLinks); //设置类型过滤器,只为文件格式
dir.setNameFilters(filters); //设置文件名称过滤器,只为filters格式(后缀为.jpeg等图片格式)

//获取分隔符
QChar separator = QChar('/');
if (!strDirPath.contains(separator))
{
separator = QChar('\\');
}
QChar last_char = strDirPath.at(strDirPath.length() - 1);
if (last_char == separator)
{
separator = QChar();
}

QStringList lstFilePath;
foreach(QFileInfo mfi, dir.entryInfoList())
{
if (mfi.isFile() && (mfi.suffix() == "dcm" || mfi.suffix() == ""))
{
QString file_path = strDirPath + separator + mfi.fileName();
lstFilePath.append(file_path);
}
}
qSort(lstFilePath.begin(), lstFilePath.end(), [](const QString &s1, const QString &s2) {
return s1.size() < s2.size() || (s1.size() == s2.size() && s1 < s2);
});

bool bRet = false;
for (int i = 0 ; i< lstFilePath.size();i++)
{
QImage imgLoadTemp;
if (i == 0)
{
ImageHeaderInfo stImageHeaderInfo;
bRet = loadImage(lstFilePath[i], imgLoadTemp, &stImageHeaderInfo);
stImageInfoStru.stImageHeaderInfo = stImageHeaderInfo;
}
else
{
bRet = loadImage(lstFilePath[i], imgLoadTemp);
}
if (!bRet)
{
return false;
}
stImageInfoStru.vecImg.push_back(imgLoadTemp);
}

return true;
}
*/

/********************************************************
* @function : loadImageSeries
* @brief : 加载图片序列
* @input :
* @output :
* @return :
*********************************************************/
bool DataLoad::loadImageSeries(const QString &strDirPath, ImageHeaderInfo &stImageHeaderInfo)
{
bool bRet = true;
if (m_pItkDicomIO == nullptr)
{
m_pItkDicomIO = itk::GDCMImageIO::New();
}

// Get the DICOM filenames from the directory
if (m_nameGenerator == nullptr)
{
m_nameGenerator = itk::GDCMSeriesFileNames::New();
}
m_nameGenerator->SetDirectory(strDirPath.toLocal8Bit().data());

try
{
typedef std::vector<std::string> seriesIdContainer;
const seriesIdContainer & seriesUID = m_nameGenerator->GetSeriesUIDs();

seriesIdContainer::const_iterator seriesItr = seriesUID.begin();
seriesIdContainer::const_iterator seriesEnd = seriesUID.end();

while (seriesItr != seriesEnd)
{
seriesItr++;
}

typedef std::vector<std::string> fileNamesContainer;
fileNamesContainer fileNames;

fileNames = m_nameGenerator->GetFileNames("");

m_itkReader->SetFileNames(fileNames);
m_itkReader->SetImageIO(m_pItkDicomIO);
m_itkReader->UpdateLargestPossibleRegion();

try
{
m_itkReader->Update();
}
catch (itk::ExceptionObject &ex)
{
std::string str(ex.what());
std::cout << ex;
return false;
}

char* pParams = new char[256];
m_pItkDicomIO->GetPatientName(pParams);
stImageHeaderInfo.strPatientName = pParams;
memset(pParams, 0, 256);
m_pItkDicomIO->GetPatientAge(pParams);
stImageHeaderInfo.strPatientAge = pParams;
memset(pParams, 0, 256);
m_pItkDicomIO->GetPatientSex(pParams);
stImageHeaderInfo.strPatientSex = pParams;
memset(pParams, 0, 256);
m_pItkDicomIO->GetModality(pParams);
stImageHeaderInfo.strFileType = pParams;
delete[] pParams;
pParams = nullptr;

std::string strWindowCenter;
getValueFromTag(DCM_WindowCenter, strWindowCenter);
std::string strWindowWidth;
getValueFromTag(DCM_WindowWidth, strWindowWidth);
std::string strSlope;
getValueFromTag(DCM_RescaleSlope, strSlope);
std::string strIntercept;
getValueFromTag(DCM_RescaleIntercept, strIntercept);
std::string strALinePixelSpacing;
getValueFromTag(DCM_ALinePixelSpacing, strALinePixelSpacing);

ImageType3D::PixelType maxValue, minValue;
std::string strSmallestImagePixelValue;
//getValueFromTag(DCM_SmallestImagePixelValue, strSmallestImagePixelValue);
//minValue = std::atoi(strSmallestImagePixelValue.c_str());
std::string strLargestImagePixelValue;
//getValueFromTag(DCM_LargestImagePixelValue, strLargestImagePixelValue);
//maxValue = std::atoi(strLargestImagePixelValue.c_str());

double dS1 = m_pItkDicomIO->GetSpacing(SagittalPlane);
double dS2 = m_pItkDicomIO->GetSpacing(CoronalPlane);
double dS3 = m_pItkDicomIO->GetSpacing(TransversePlane);
ImageType3D *img = m_itkReader->GetOutput();
itk::Size<3> sliceSize = img->GetLargestPossibleRegion().GetSize();

if (strSmallestImagePixelValue.empty() || strLargestImagePixelValue.empty())
{
typedef itk::MinimumMaximumImageCalculator <ImageType3D> IMinimumMaximumImageCalculatorType;
IMinimumMaximumImageCalculatorType::Pointer imageCalculatorFilter = IMinimumMaximumImageCalculatorType::New();
imageCalculatorFilter->SetImage(img);
imageCalculatorFilter->Compute();
maxValue = imageCalculatorFilter->GetMaximum(); //最大值
minValue = imageCalculatorFilter->GetMinimum(); //最小值
}


stImageHeaderInfo.nWidth = sliceSize[SagittalPlane];
stImageHeaderInfo.nHeight = sliceSize[CoronalPlane];
stImageHeaderInfo.nThickNess = sliceSize[TransversePlane];
stImageHeaderInfo.strFilePath = strDirPath;
stImageHeaderInfo.fPixelSpacingW = dS1;
stImageHeaderInfo.fPixelSpacingH = dS2;
stImageHeaderInfo.fPixelSpacingT = dS3;
//stImageHeaderInfo.nWinCenter = std::atoi(strWindowCenter.c_str());
//stImageHeaderInfo.nWinWidth = std::atoi(strWindowWidth.c_str());
stImageHeaderInfo.nWinCenter = (maxValue + minValue) / 2;
stImageHeaderInfo.nWinWidth = maxValue - minValue;
stImageHeaderInfo.nSlope = std::atoi(strSlope.c_str());
stImageHeaderInfo.nIntercept = std::atoi(strIntercept.c_str());

ImageDataManager::getInstance()->setHeaderInfo(stImageHeaderInfo);
ImageDataManager::getInstance()->setImageSerial(img);
bRet = ImageDataManager::getInstance()->loadLabelInfo(strDirPath,IMAGE_TYPE::IMG_DCM);
}
catch (itk::ExceptionObject &ex)
{
std::string str(ex.what());
std::cout << ex;
return 0;
}


return bRet;
}

/********************************************************
* @function : loadNiiImage
* @brief : 加载Nii图片
* @input :
* @output :
* @return :
*********************************************************/
bool DataLoad::loadNiiImage(const QString &strDirPath, ImageHeaderInfo &stImageHeaderInfo)
{
bool bRet = true;
if (m_pItkNiiIO== nullptr)
{
m_pItkNiiIO = itk::NiftiImageIO::New();
}

try
{
if (m_itkNiiReader == nullptr)
{
m_itkNiiReader = itk::ImageFileReader<ImageType3D>::New();
}

m_itkNiiReader->SetFileName(strDirPath.toLocal8Bit().data());
m_itkNiiReader->SetImageIO(m_pItkNiiIO);
m_itkNiiReader->UpdateLargestPossibleRegion();

try
{
m_itkNiiReader->Update();
}
catch (itk::ExceptionObject &ex)
{
std::string str(ex.what());
std::cout << ex;
return false;
}

/*char* pParams = new char[256];
m_pItkNiiIO->GetPatientName(pParams);
stImageHeaderInfo.strPatientName = pParams;
memset(pParams, 0, 256);
m_pItkNiiIO->GetPatientAge(pParams);
stImageHeaderInfo.strPatientAge = pParams;
memset(pParams, 0, 256);
m_pItkNiiIO->GetPatientSex(pParams);
stImageHeaderInfo.strPatientSex = pParams;
delete[] pParams;
pParams = nullptr;

std::string strWindowCenter;
getValueFromTag(DCM_WindowCenter, strWindowCenter);
std::string strWindowWidth;
getValueFromTag(DCM_WindowWidth, strWindowWidth);
std::string strSlope;
getValueFromTag(DCM_RescaleSlope, strSlope);
std::string strIntercept;
getValueFromTag(DCM_RescaleIntercept, strIntercept);
std::string strALinePixelSpacing;
getValueFromTag(DCM_ALinePixelSpacing, strALinePixelSpacing);*/

double dS1 = m_pItkNiiIO->GetSpacing(SagittalPlane);
double dS2 = m_pItkNiiIO->GetSpacing(CoronalPlane);
double dS3 = m_pItkNiiIO->GetSpacing(TransversePlane);

ImageType3D *img = m_itkNiiReader->GetOutput();
itk::Size<3> sliceSize = img->GetLargestPossibleRegion().GetSize();
ImageType3D::PixelType pixelValue, maxValue, minValue;
typedef itk::MinimumMaximumImageCalculator <ImageType3D> IMinimumMaximumImageCalculatorType;
IMinimumMaximumImageCalculatorType::Pointer imageCalculatorFilter = IMinimumMaximumImageCalculatorType::New();
imageCalculatorFilter->SetImage(img);
imageCalculatorFilter->Compute();
maxValue = imageCalculatorFilter->GetMaximum(); //最大值
minValue = imageCalculatorFilter->GetMinimum(); //最小值

stImageHeaderInfo.nWidth = sliceSize[SagittalPlane];
stImageHeaderInfo.nHeight = sliceSize[CoronalPlane];
stImageHeaderInfo.nThickNess = sliceSize[TransversePlane];
stImageHeaderInfo.strFilePath = strDirPath;
stImageHeaderInfo.fPixelSpacingW = dS1;
stImageHeaderInfo.fPixelSpacingH = dS2;
stImageHeaderInfo.fPixelSpacingT = dS3;
//stImageHeaderInfo.nWinCenter = std::atoi(strWindowCenter.c_str());
//stImageHeaderInfo.nWinWidth = std::atoi(strWindowWidth.c_str());
stImageHeaderInfo.nWinCenter = (maxValue + minValue) / 2;
stImageHeaderInfo.nWinWidth = maxValue - minValue;
/*stImageHeaderInfo.nSlope = std::atoi(strSlope.c_str());
stImageHeaderInfo.nIntercept = std::atoi(strIntercept.c_str());*/

ImageDataManager::getInstance()->setImageSerial(img);
ImageDataManager::getInstance()->setHeaderInfo(stImageHeaderInfo);
bRet = ImageDataManager::getInstance()->loadLabelInfo(strDirPath, IMAGE_TYPE::IMG_NII);
}
catch (itk::ExceptionObject &ex)
{
std::string str(ex.what());
std::cout << ex;
return 0;
}


return bRet;
}

/********************************************************
* @function : getValueFromTag
* @brief : 根据tag获取值
* @input :
* @output :
* @return :
*********************************************************/
void DataLoad::getValueFromTag(DcmTagKey keyTag, std::string& strValue)
{
QString strTagSpacing(keyTag.toString().c_str());
strTagSpacing = strTagSpacing.mid(1, strTagSpacing.size() - 2);
strTagSpacing.replace(',', '|');
m_pItkDicomIO->GetValueFromTag(strTagSpacing.toLocal8Bit().data(), strValue);
}

/********************************************************
* @function : startLoadData
* @brief : 开启线程加载数据
* @input :
* @output :
* @return :
*********************************************************/
void DataLoad::startLoadData(const QString &strPath)
{
m_strCurFileOrDirPath = strPath;

std::thread thrAnalysis(&DataLoad::run, this);
if (thrAnalysis.joinable())
{
thrAnalysis.detach();
}

}

/********************************************************
* @function : run
* @brief : 加载数据线程函数
* @input :
* @output :
* @return :
*********************************************************/
void DataLoad::run()
{
QFileInfo fileInfo(m_strCurFileOrDirPath);

bool bRet = false;
ImageHeaderInfo stImageHeaderInfo;
//如果是文件
if (fileInfo.isFile())
{
if (fileInfo.completeSuffix() == "nii"
||fileInfo.completeSuffix() == "nii.gz")
{
bRet = loadNiiImage(m_strCurFileOrDirPath, stImageHeaderInfo);
}
//bRet = loadSingleImage(m_strCurFileOrDirPath, stImageHeaderInfo);
}
else if (fileInfo.isDir())
{
//bRet = loadImageFromDir(m_strCurFileOrDirPath,stImageInfoStru);
bRet = loadImageSeries(m_strCurFileOrDirPath, stImageHeaderInfo);
}

emit sigDataLoadFinish(bRet);
}

+ 61
- 0
UltimageTK/DataLoad.h View File

@@ -0,0 +1,61 @@
/********************************************************
* @file : DataLoad.h
* @brief : brief
* @details :
* @author : qh.zhang@atisz.ac.cn
* @date : 2019-7-9
*********************************************************/
#pragma once

#include <QObject>
#include <thread>
#include <QImage>

#include "GlobalDef.h"
#include "ImageDataManager.h"
#include "dcmtk/config/osconfig.h"
#include "dcmtk/dcmdata/dctk.h"
#include "itkGDCMSeriesFileNames.h"
#include "itkGDCMImageIO.h"
#include "itkImageFileReader.h"
#include "itkNiftiImageIO.h"
class DataLoad : public QObject
{
Q_OBJECT

public:
DataLoad();
~DataLoad();
bool Init();

protected:
//bool loadDcmImage(const QString &strImgPath, QImage &img, ImageHeaderInfo *pImageInfoStru = nullptr);
//bool loadNoSuffixImage(const QString &strImgPath, QImage &img, ImageHeaderInfo *pImageInfoStru = nullptr);
//bool loadImage(const QString &strImgPath, QImage &img, ImageHeaderInfo *pImageInfoStru = nullptr);
//bool loadSingleImage(const QString &strFilePath, ImageInfoStru &stImageInfoStru);
//bool loadImageFromDir(const QString &strDirPath, ImageInfoStru &stImageInfoStru);
//加载图片序列
bool loadImageSeries(const QString &strDirPath, ImageHeaderInfo &stImageHeaderInfo);
//加载Nii文件
bool loadNiiImage(const QString &strDirPath, ImageHeaderInfo &stImageHeaderInfo);
//根据tag获取值
void getValueFromTag(DcmTagKey keyTag, std::string& strValue);
public:
//开始加载数据
void startLoadData(const QString &strPath);
//线程处理函数
void run();

signals:
void sigDataLoadFinish(bool bSuccess = true);

private:
QString m_strCurFileOrDirPath; //当前加载的文件或文件夹路径
SeriesReaderType::Pointer m_itkReader = nullptr; // 读文件句柄
itk::GDCMSeriesFileNames::Pointer m_nameGenerator =nullptr; //文件生成器
itk::GDCMImageIO::Pointer m_pItkDicomIO = nullptr; //文件io句柄

itk::ImageFileReader<ImageType3D>::Pointer m_itkNiiReader = nullptr; //nii文件读取句柄
itk::NiftiImageIO::Pointer m_pItkNiiIO = nullptr; //nii文件io句柄
};

+ 38
- 0
UltimageTK/DrawBase.cpp View File

@@ -0,0 +1,38 @@
/********************************************************
* @file : DrawBase.cpp
* @brief :
* @details :
* @author : qh.zhang@atisz.ac.cn
* @date : 2019-5-8
*********************************************************/

#include "DrawBase.h"

PEN_TYPE DrawBase::m_emPenType = PenNone;
int DrawBase::m_nAlphaValue = 100;

DrawBase::DrawBase()
{

}

DrawBase::~DrawBase()
{

}

PEN_TYPE DrawBase::getCurPenType()
{
return m_emPenType;
}

void DrawBase::setCurPenType(PEN_TYPE emPenType)
{
m_emPenType = emPenType;
}

void DrawBase::setAlphaValue(int nAlphaValue)
{
m_nAlphaValue = nAlphaValue;
}


+ 89
- 0
UltimageTK/DrawBase.h View File

@@ -0,0 +1,89 @@
/********************************************************
* @file : DrawBase.h
* @brief :
* @details :
* @author : qh.zhang@atisz.ac.cn
* @date : 2019-5-8
*********************************************************/
#pragma once
#include <QObject>
#include <QMouseEvent>
#include <QPolygon>
#include <QColor>
#include <QLineF>
#include "GlobalDef.h"

#define SELECT_LINE_COLOR QColor(0,255,0)
#define DRWAING_POINT_COLOR QColor(255,255,0)
#define COMMON_POINT_LINE_COLOR QColor(255,255,0)

//画笔类型
enum PEN_TYPE
{
PenNone,
PenPoint,
PenPolygon, //多边形
PenDrawPen, //画笔
MeasureRuler,
MeasurePolygon,
MeasureCircle,
MeasureOval,
};

class ExLineF :public QLineF
{
public:
std::string strTargetName;
};

class ExPolygonF :public QPolygonF
{
public:
std::string strTargetName;
std::string strTargetDisc;
int nTargetID;
int nTargetType = PenPolygon;
bool bClosed = false; //是否已封闭
bool bIsSelected = false; //是否被选中
int nSelectPoint = -1; //当前被选中的点
QVector<int> lstValue; //存所有valve值
void reset()
{
strTargetName = "";
strTargetDisc = "";
nTargetID = -1;
bClosed = false;
bIsSelected = false;
nSelectPoint = -1;
this->clear();
lstValue.clear();
}
};

class DrawBase : public QObject
{
Q_OBJECT
public:
DrawBase();
~DrawBase();

//virtual bool initDraw();

PEN_TYPE getCurPenType();
static void setCurPenType(PEN_TYPE emPenType);
static void setAlphaValue(int nAlphaValue);
//virtual void mousePressEvent(QMouseEvent *event);
//virtual void mouseMoveEvent(QMouseEvent *event);
//virtual void mouseReleaseEvent(QMouseEvent *event);
//virtual void resizeEvent(QResizeEvent *event);
protected:
virtual void DrawGraphics() = 0;
protected:
static PEN_TYPE m_emPenType; //画笔类型
static int m_nAlphaValue; //透明度
QColor m_Color; //画笔颜色
int m_nSize; //画笔宽度
ImageHeaderInfo m_stImageHeaderInfo; //图片序列头信息
bool m_bIsDrawing = false;
bool m_bIsMoving = false;
};

+ 817
- 0
UltimageTK/DrawPen.cpp View File

@@ -0,0 +1,817 @@
/********************************************************
* @file : DrawPen.cpp
* @brief :
* @details :
* @author : qh.zhang@atisz.ac.cn
* @date : 2019-7-3
*********************************************************/
#include <QPainter>
#include <QDebug>
#include <QTime>
#include "DrawPen.h"
#include "ImageDataManager.h"
#include "AllSettings.h"
class ImageWidget;

#define POINT_RADIUS 2
#define LINE_WIDTH 2
#define POINT_LINE_WIDTH 1
#define POINT_SELECT_RADIUS 3
#define DRAWPEN_POINT_COUNTS_LIMIT 20

INTERP_METHOD DrawPen::m_emInterpMethod = IM_None;
QPair<VIEW_PLANE, int> DrawPen::m_pairSelectTarget = qMakePair(VIEW_PLANE::CoronalPlane,-1);

/********************************************************
* @function : DrawPen
* @brief : 构造函数
* @input :
* @output :
* @return :
*********************************************************/
DrawPen::DrawPen(QWidget *parent)
:m_pParent(parent)
{

}

DrawPen::~DrawPen()
{
}

/********************************************************
* @function : initDraw
* @brief : 初始化
* @input :
* @output :
* @return :
*********************************************************/
bool DrawPen::initDraw(VIEW_PLANE emPlane)
{
m_emPlane = emPlane;
return true;
}

/********************************************************
* @function : setImgPointer
* @brief : 设置传入的图片指针
* @input :
* @output :
* @return :
*********************************************************/
void DrawPen::setImgPointer(QImage *pImgSrc, QImage *pImgDrawing)
{
m_pImgSrc = pImgSrc;
m_pImgDrawing = pImgDrawing;
}

/********************************************************
* @function : setCross
* @brief : 设置十字光标位置
* @input :
* @output :
* @return :
*********************************************************/
void DrawPen::setCross(QPointF ptCross)
{
m_ptCross = ptCross;
//DrawGraphics();
}

/********************************************************
* @function : setZoom
* @brief : 设置缩放值
* @input :
* @output :
* @return :
*********************************************************/
void DrawPen::setZoom(float fZoom)
{
m_fZoom = fZoom;
//DrawGraphics();
}

/********************************************************
* @function : reload
* @brief : 重新加载图片
* @input :
* @output :
* @return :
*********************************************************/
void DrawPen::reload(int nIndex)
{
m_nCurFrameIndex = nIndex;
std::map<int, LabelAnalysis::Target> mapTargetsTmp;
ImageDataManager::getInstance()->getLabelInfo(m_emPlane, m_nCurFrameIndex, mapTargetsTmp);
m_stImageHeaderInfo = ImageDataManager::getInstance()->getImageHeaderInfo();

m_mapTargets.clear();
for each (auto var in mapTargetsTmp)
{
ExPolygonF poly;
for each (auto ptF in var.second.lstVertex)
{
poly.append(QPointF(ptF.fX,ptF.fY));
}
poly.bClosed = true;
poly.strTargetName = var.second.strTargetName;
m_mapTargets[var.first] = poly;
}
m_pairCurTarget.second.reset();
m_bIsDrawing = false;
m_bIsMoving = false;
emit sigSelectPolygon(QSize(0, 0));

//m_pairSelectTarget = qMakePair(VIEW_PLANE::CoronalPlane, -1);
//DrawGraphics();
}

/********************************************************
* @function : delTarget
* @brief : 删除选中目标
* @input :
* @output :
* @return :
*********************************************************/
void DrawPen::delTarget(int nIndex)
{
if (m_mapTargets.find(nIndex)!=m_mapTargets.end())
{
m_mapTargets.erase(nIndex);
}
ImageDataManager::getInstance()->updateLabelInfo(m_emPlane, m_nCurFrameIndex, m_mapTargets);
m_pairSelectTarget = qMakePair(VIEW_PLANE::CoronalPlane, -1);
emit sigSelectPolygon(QSize(0, 0));
}

/********************************************************
* @function : clearTargets
* @brief : 删除当前页面的目标
* @input :
* @output :
* @return :
*********************************************************/
void DrawPen::clearTargets()
{
m_mapTargets.clear();
m_pairSelectTarget = qMakePair(VIEW_PLANE::CoronalPlane, -1);
emit sigSelectPolygon(QSize(0,0));
ImageDataManager::getInstance()->updateLabelInfo(m_emPlane, m_nCurFrameIndex, m_mapTargets);
}

/********************************************************
* @function : mousePressEvent
* @brief : 鼠标按下事件
* @input :
* @output :
* @return :
*********************************************************/
void DrawPen::mousePressEvent(QMouseEvent *event, QPointF ptF, int nValue)
{
//如果没有在绘图,判断是否选择了点或者目标
if ((event->buttons() & Qt::LeftButton) == Qt::LeftButton)
{
if (!m_bIsDrawing)
{
checkSelect(ptF);
}
//判断是否选中了某个目标
if (m_pairSelectTarget.first == m_emPlane
&& m_pairSelectTarget.second != -1)
{
//选中点,进入点移动模式
m_bIsMoving = true;
m_ptfMoveLastPos = ptF;
m_pParent->setCursor(Qt::ClosedHandCursor); //范围之外变回原来形状
}
else
{
m_bIsMoving = false;
}
}

m_ptCross = ptF;

switch (m_emPenType)
{
case PenNone:
//DrawGraphics();
break;
case PenPoint:
break;
case PenPolygon:
case PenDrawPen:
if ((event->buttons() & Qt::LeftButton) == Qt::LeftButton)
{
//判断是否选中了某个目标
if (m_bIsMoving)
{
//do nothing
}
else
{
while (1)
{
if (m_pairCurTarget.second.size() == 0
&& m_mapTargets.find(m_pairCurTarget.first) != m_mapTargets.end())
{
++m_pairCurTarget.first;
}
else
{
break;
}
}
if (m_pairCurTarget.second.size()==0)
{
QString strCurLabel;
AllSettings::getInstance()->GetCurLabelAndColor(strCurLabel, m_Color);
m_pairCurTarget.second.strTargetName = strCurLabel.toLocal8Bit().data();
}
m_pairCurTarget.second.append(ptF);
m_pairCurTarget.second.lstValue.append(nValue);
m_bIsDrawing = true;
}

}
else if ((event->buttons() & Qt::RightButton) == Qt::RightButton)
{
m_bIsDrawing = false;
if (m_pairCurTarget.second.size() <= 2)
{
m_pairCurTarget.second.reset();
return;
}

//插值一下
generateInterpolationPoints(m_pairCurTarget.second);
m_pairCurTarget.second.bClosed = true;
m_pairCurTarget.second.nTargetID = m_pairCurTarget.first;
m_mapTargets.insert(m_pairCurTarget);
ImageDataManager::getInstance()->updateLabelInfo(m_emPlane, m_nCurFrameIndex, m_mapTargets);
m_pairCurTarget.second.reset();
}
//DrawGraphics();
break;
case MeasureRuler:
break;
case MeasurePolygon:
break;
case MeasureCircle:
break;
case MeasureOval:
break;
default:
break;
}
}

/********************************************************
* @function : mouseMoveEvent
* @brief : 鼠标移动事件
* @input :
* @output :
* @return :
*********************************************************/
void DrawPen::mouseMoveEvent(QMouseEvent *event, QPointF ptF, int nValue)
{
if (m_bIsDrawing)
{
switch (m_emPenType)
{
case PenNone:
break;
case PenPoint:
break;
case PenPolygon:
break;
case PenDrawPen:
if ((event->buttons() & Qt::LeftButton) == Qt::LeftButton)
{
if (m_bIsDrawing)
{
m_pairCurTarget.second.append(ptF);
m_pairCurTarget.second.lstValue.append(nValue);
//qDebug() << ptF;
//DrawGraphics();
}
}
break;
case MeasureRuler:
break;
case MeasurePolygon:
break;
case MeasureCircle:
break;
case MeasureOval:
break;
default:
break;
}
}
else if (m_bIsMoving)
{
if ((event->buttons() & Qt::LeftButton) == Qt::LeftButton
&& m_pairSelectTarget.first == m_emPlane
&& m_pairSelectTarget.second != -1
)
{
QPointF ptfDist = ptF - m_ptfMoveLastPos;//位移
m_ptfMoveLastPos = ptF;
ExPolygonF& poly = m_mapTargets[m_pairSelectTarget.second];
if (poly.nSelectPoint!=-1)
{
poly[poly.nSelectPoint] = poly[poly.nSelectPoint] + ptfDist;

}
else
{
ExPolygonF polyTemp = poly;
for (int i = 0; i < poly.size(); i++)
{
polyTemp[i] = poly[i] + ptfDist;
if (polyTemp[i].rx() > 1
|| polyTemp[i].ry() > 1
|| polyTemp[i].rx() < 0
|| polyTemp[i].ry() < 0
)
{
return;
}
}
poly = polyTemp;
}
}
ImageDataManager::getInstance()->updateLabelInfo(m_emPlane, m_nCurFrameIndex, m_mapTargets);

}
else
{
m_ptCross = ptF;
}
}

/********************************************************
* @function : mouseReleaseEvent
* @brief : 鼠标释放事件
* @input :
* @output :
* @return :
*********************************************************/
void DrawPen::mouseReleaseEvent(QMouseEvent *event, QPointF ptF, int nValue)
{
m_bIsMoving = false;
switch (m_emPenType)
{
case PenNone:
break;
case PenPoint:
break;
case PenPolygon:
break;
case PenDrawPen:
break;
case MeasureRuler:
break;
case MeasurePolygon:
break;
case MeasureCircle:
break;
case MeasureOval:
break;
default:
break;
}
}

/********************************************************
* @function : resizeEvent
* @brief :
* @input :
* @output :
* @return :
*********************************************************/
void DrawPen::resizeEvent(QResizeEvent *event)
{
//需要调整尺寸, 并且重新绘制
//DrawGraphics();
}

/********************************************************
* @function : wheelEvent
* @brief : brief
* @input :
* @output :
* @return :
*********************************************************/
void DrawPen::wheelEvent(QWheelEvent *event)
{
//需要调整尺寸, 并且重新绘制
//DrawGraphics();
}

/********************************************************
* @function : DrawGraphics
* @brief : 整个画面绘制
* @input :
* @output :
* @return :
*********************************************************/
void DrawPen::DrawGraphics()
{
QList<ExLineF> lstWTargets;
QList<ExLineF> lstHTargets;
ImageDataManager::getInstance()->getOtherPlaneTargetWH(m_emPlane, lstWTargets, lstHTargets, m_nCurFrameIndex);

QPainter painter;
QTime atime;

*m_pImgDrawing = m_pImgSrc->convertToFormat(QImage::Format_RGB32);

*m_pImgDrawing = m_pImgDrawing->scaled(m_pImgSrc->size()*m_fZoom, Qt::KeepAspectRatio);

//设置画笔画刷
QPen penCommonLine;
QBrush brushNone;
penCommonLine.setColor(Qt::red); //改变颜色
penCommonLine.setWidth(LINE_WIDTH);

QPen penCommenPoint;
penCommenPoint.setColor(COMMON_POINT_LINE_COLOR); //改变颜色
penCommenPoint.setWidth(POINT_LINE_WIDTH);

//正在绘制的画笔
QPen penDrawingLine;
penDrawingLine.setColor(m_Color);
penDrawingLine.setWidth(LINE_WIDTH);

QPen penDrawingPoint;
penDrawingPoint.setColor(DRWAING_POINT_COLOR);
penDrawingPoint.setWidth(POINT_RADIUS);
QBrush brushDrawingPoint(Qt::red);

QPen penSelectLine;
penSelectLine.setColor(SELECT_LINE_COLOR);
penSelectLine.setWidth(LINE_WIDTH);

painter.begin(m_pImgDrawing);

for each (auto var in lstWTargets)
{
QColor clrCurTgt = AllSettings::getInstance()->GetColorByLabel(QString::fromLocal8Bit(var.strTargetName.c_str()));
clrCurTgt.setAlpha(m_nAlphaValue);
painter.setPen(QPen(clrCurTgt));
QColor clrCurTgtBrush = clrCurTgt;
clrCurTgtBrush.setAlpha(m_nAlphaValue);
painter.setBrush(clrCurTgtBrush);
drawLayer(&painter, var.p1(), var.p2());
}
for each (auto var in lstHTargets)
{
QColor clrCurTgt = AllSettings::getInstance()->GetColorByLabel(QString::fromLocal8Bit(var.strTargetName.c_str()));
clrCurTgt.setAlpha(m_nAlphaValue);
painter.setPen(QPen(clrCurTgt));
QColor clrCurTgtBrush = clrCurTgt;
clrCurTgtBrush.setAlpha(m_nAlphaValue);
painter.setBrush(clrCurTgtBrush);
drawLayer(&painter, var.p1(), var.p2());
}

//绘制已完成的图形
for each (auto varShape in m_mapTargets)
{
QColor clrCurTgt = AllSettings::getInstance()->GetColorByLabel(QString::fromLocal8Bit(varShape.second.strTargetName.c_str()));
QColor clrCurTgtBrush = clrCurTgt;
clrCurTgtBrush.setAlpha(m_nAlphaValue);
penCommonLine.setColor(clrCurTgt);
//绘图
painter.setPen(penCommonLine);
painter.setBrush(clrCurTgtBrush);
//当前选中框
if (m_pairSelectTarget.first == m_emPlane
&&m_pairSelectTarget.second == varShape.first)
{
painter.setPen(penSelectLine);
painter.setBrush(brushNone);
drawPolygon(&painter, varShape.second,true);
}
else
{
drawPolygon(&painter, varShape.second);
}
//绘点
if (varShape.second.size() < DRAWPEN_POINT_COUNTS_LIMIT)
{
for (int i = 0; i < varShape.second.size(); i++)
{
if (varShape.second.nSelectPoint == i)
{
painter.setPen(penDrawingPoint);
painter.setBrush(brushDrawingPoint);
}
else
{
painter.setPen(penCommenPoint);
}
//painter.drawEllipse(QPoint(varShape.second[i].rx()*m_pImgDrawing->width(), varShape.second[i].ry()*m_pImgDrawing->height()), POINT_RADIUS, POINT_RADIUS);
drawPoint(&painter, varShape.second[i]);
}
}
}

//绘制正在绘制的图形
//绘图
painter.setPen(penDrawingLine);
drawPolygon(&painter, m_pairCurTarget.second);

painter.setPen(penDrawingPoint);
painter.setBrush(brushDrawingPoint);

if (m_pairCurTarget.second.size()< DRAWPEN_POINT_COUNTS_LIMIT)
{
//绘点
for (int i = 0; i < m_pairCurTarget.second.size(); i++)
{
drawPoint(&painter, m_pairCurTarget.second[i]);
}
}

//画十字
{
painter.setPen(QPen(QBrush(WIDGET_CROSS_COLOR), 1, Qt::DashDotLine));//设置画刷形式,背景色

drawLine(&painter, QPointF(0.0f, m_ptCross.ry()), QPointF(1.0f, m_ptCross.ry()));
drawLine(&painter, QPointF(m_ptCross.rx(), 0), QPointF(m_ptCross.rx(), 1.0f));
}

painter.end();
}

/********************************************************
* @function : drawPoint
* @brief : 画点
* @input :
* @output :
* @return :
*********************************************************/
void DrawPen::drawPoint(QPainter *p, QPointF pt)
{
if (m_emPlane != TransversePlane)
{
pt = QPointF(pt.rx(), 1.0f - pt.ry());
}
p->drawEllipse(QPoint(pt.rx()*m_pImgDrawing->width(), pt.ry()*m_pImgDrawing->height()), POINT_RADIUS, POINT_RADIUS);
}

/********************************************************
* @function : drawLine
* @brief : 画线
* @input :
* @output :
* @return :
*********************************************************/
void DrawPen::drawLine(QPainter *p, QPointF pt1, QPointF pt2)
{
if (m_emPlane != TransversePlane)
{
pt1 = QPointF(pt1.rx(), 1.0f - pt1.ry());
pt2 = QPointF(pt2.rx(), 1.0f - pt2.ry());
}
p->drawLine(QPoint(round(pt1.rx()*m_pImgDrawing->width()), round(pt1.ry()*m_pImgDrawing->height())),
QPoint(round(pt2.rx()*m_pImgDrawing->width()), round(pt2.ry()*m_pImgDrawing->height())));
}

/********************************************************
* @function : drawLayer
* @brief : 绘制透视出来的图层
* @input :
* @output :
* @return :
*********************************************************/
void DrawPen::drawLayer(QPainter *p, QPointF pt1, QPointF pt2)
{
if (m_emPlane != TransversePlane)
{
pt1 = QPointF(pt1.rx(), 1.0f - pt1.ry());
pt2 = QPointF(pt2.rx(), 1.0f - pt2.ry());
}
QRect rect;
float fSpacing = m_stImageHeaderInfo.fPixelSpacingT/ m_stImageHeaderInfo.fPixelSpacingW;

//横线
if (abs(pt1.x()-pt2.x())<0.000001)
{
QPoint ptTopLeft = QPoint(round(pt1.rx()*m_pImgDrawing->width()), round(pt1.ry()*m_pImgDrawing->height()));
QPoint ptBottomRight = QPoint(round(pt2.rx()*m_pImgDrawing->width() + m_fZoom), round(pt2.ry()*m_pImgDrawing->height()));
switch (m_emPlane)
{
case SagittalPlane:
case CoronalPlane:
ptTopLeft = QPoint(round(pt1.rx()*m_pImgDrawing->width()), round(pt1.ry()*m_pImgDrawing->height()));
ptBottomRight = QPoint(round(pt2.rx()*m_pImgDrawing->width() + m_fZoom* fSpacing), round(pt2.ry()*m_pImgDrawing->height()));
break;
case TransversePlane:
break;
default:
break;
}
rect.setTopLeft(ptTopLeft);
rect.setBottomRight(ptBottomRight);
}
//竖线
else if (abs(pt1.y() - pt2.y()) < 0.000001)
{
QPoint ptTopLeft = QPoint(round(pt1.rx()*m_pImgDrawing->width()), round(pt1.ry()*m_pImgDrawing->height()));
QPoint ptBottomRight = QPoint(round(pt2.rx()*m_pImgDrawing->width()), round(pt2.ry()*m_pImgDrawing->height() + m_fZoom));
switch (m_emPlane)
{