ctkDICOMDatabase 源码解析

ctkDICOMDatabase是一个用于处理DICOM对象的类,它基于SQLite数据库,支持在内存中或磁盘上的持久化存储。类提供了添加、查询和管理DICOM数据的功能,包括缩略图生成、模式更新以及缓存策略。此外,它还支持数据库模式的更新和维护,如初始化、更新和清理。
摘要由CSDN通过智能技术生成

#ifndef __ctkDICOMDatabase_h
#define __ctkDICOMDatabase_h

// Qt includes
#include <QObject>
#include <QStringList>
#include <QSqlDatabase>

#include "ctkDICOMItem.h"
#include "ctkDICOMCoreExport.h"

class QDateTime;
class ctkDICOMDatabasePrivate;
class DcmDataset;
class ctkDICOMAbstractThumbnailGenerator;
class ctkDICOMDisplayedFieldGenerator;

// / \ingroup DICOM_Core
// /
// / 处理 DICOM 对象数据库的类。到目前为止,底层是SQLITE 数据库。
// / 通常,添加的 DICOM 对象也是存储在这个数据库文件中。
// / SQLITE数据库文件可以由用户指定。
// / SQLITE(和当前类) 还支持特殊的内存模式,即不创建数据库文件
// / 但数据库完全保存在内存中(并且在退出程序后,数据库文件消失)。
// / 如果在“memory mode”下,DICOM对象不会写入磁盘,
// / 否则它们存储在 SQLITE 数据库文件的子目录中名为“dicom”的目录。
// / 在内部,创建了一个文件夹结构,其中包含
// / 每个研究的目录,包含每个系列的目录,包含
// / 每个对象的文件。相应的 UID 用作文件名。
// / 可以创建每个图像的缩略图;它们存储在和“dicom”一个目录中
// / 名为“thumbs”的目录。
class CTK_DICOM_CORE_EXPORT ctkDICOMDatabase : public QObject
{

  Q_OBJECT
  Q_PROPERTY(bool isOpen READ isOpen)
  Q_PROPERTY(bool isInMemory READ isInMemory)
  Q_PROPERTY(QString lastError READ lastError)
  Q_PROPERTY(QString databaseFilename READ databaseFilename)
  Q_PROPERTY(QString databaseDirectory READ databaseDirectory)
  Q_PROPERTY(QStringList tagsToPrecache READ tagsToPrecache WRITE setTagsToPrecache)
  Q_PROPERTY(QStringList tagsToExcludeFromStorage READ tagsToExcludeFromStorage WRITE setTagsToExcludeFromStorage)
  Q_PROPERTY(QStringList patientFieldNames READ patientFieldNames)
  Q_PROPERTY(QStringList studyFieldNames READ studyFieldNames)
  Q_PROPERTY(QStringList seriesFieldNames READ seriesFieldNames)
  Q_PROPERTY(bool useShortStoragePath READ useShortStoragePath WRITE setUseShortStoragePath)

public:
  struct IndexingResult
  {
    QString filePath;
    QSharedPointer<ctkDICOMItem> dataset;
    bool copyFile;
    bool overwriteExistingDataset;
  };

  explicit ctkDICOMDatabase(QObject *parent = 0);
  explicit ctkDICOMDatabase(QString databaseFile);
  virtual ~ctkDICOMDatabase();

  const QSqlDatabase& database() const;
  const QString lastError() const;
  const QString databaseFilename() const;

  static const char* defaultSchemaFile() { return ":/dicom/dicom-schema.sql"; };

  ///返回数据库目录的绝对路径
  // /(数据库文件所在的位置)采用操作系统首选路径格式。
  // / @return 数据库目录的绝对路径
  const QString databaseDirectory() const;

  ///尝试打开数据库后应该检查
  // / @Returns true 如果数据库是打开的
  bool  isOpen () const ;

  ///返回数据库是否只驻留在内存中,即
  // / SQLITE DB 不写入存储到磁盘和 DICOM 对象不
  // / 存储到文件系统。
  // / @return 如果处于内存模式则为真,否则为假。
  bool  isInMemory () const ;

  ///设置缩略图生成器对象
  Q_INVOKABLE void  setThumbnailGenerator (ctkDICOMAbstractThumbnailGenerator* generator);
  ///获取缩略图生成器对象
  Q_INVOKABLE ctkDICOMAbstractThumbnailGenerator* thumbnailGenerator ();

  // / 在@param databaseFile 中打开 SQLite 数据库。如果文件不存在,默认创建并初始化一个新的数据库
  // /
  // / @param databaseFile 存储SQLITE数据库的文件。
  // / 如果用“:memory:”指定,则数据库完全不写入磁盘,只保存在内存中(因此在此对象之后会被销毁)。
  // / @param connectionName 数据库连接名称。如果没有指定,然后生成一个随机名称(重复使用连接名称
  // / 必须避免,因为使用相同的连接名称会破坏之前创建的数据库对象)。
  // / @param 如果发现模式过期则更新模式
  Q_INVOKABLE virtual  void  openDatabase ( const QString databaseFile,
                                        const QString& connectionName = " " );

  // / 关闭数据库。之后不得使用。
  Q_INVOKABLE void  closeDatabase ();

  // / 删除所有数据并(重新)初始化数据库。
  Q_INVOKABLE bool  initializeDatabase ( const  char * schemaFile = ctkDICOMDatabase::defaultSchemaFile());

  // /更新数据库模式并重新插入所有现有文件
  // / \param schemaFile SQL 文件包含模式定义的 
  // / \param newDatabaseDir 更新数据库的新数据库目录路径。
  // / 默认值为空,意味着目录将保持不变;
  // / 注意:需要“即时”切换数据库文件夹,以便复制数据库可以简单地通过 createBackupFileList 和一系列插入操作
  // / \返回值为真 :如果模式已更新
  Q_INVOKABLE bool updateSchema(
    const char* schemaFile = ctkDICOMDatabase::defaultSchemaFile(),
    const char* newDatabaseDir = nullptr);

  // / 仅当版本不匹配时才更新数据库模式
  // / \param schemaFile 包含模式定义的 SQL 文件
  // / \param newDatabaseDir 更新数据库的新数据库目录路径。
  // / 默认为空,意味着目录将保持不变
  // / 注意:需要“即时”切换数据库文件夹,以便复制
  // / 数据库可以简单地通过 createBackupFileList 和以下插入来完成
  // / \如果架构已更新则返回真
  Q_INVOKABLE bool  updateSchemaIfNeeded (
    const  char * schemaFile = ctkDICOMDatabase::defaultSchemaFile(),
    const char* newDatabaseDir = nullptr);

  ///返回这段代码当前版本需要的schema版本
  Q_INVOKABLE QString schemaVersion ();

  // / 返回当前打开的数据库的模式版本
  // / 为了支持模式更新
  Q_INVOKABLE QString schemaVersionLoaded ();

  // / 在使用非标准模式的情况下在外部设置模式版本
  Q_INVOKABLE void  setSchemaVersion (QString schemaVersion);

  // / \数据库访问
  Q_INVOKABLE QStringList patients();
  Q_INVOKABLE QStringList studiesForPatient ( const QString patientUID);
  Q_INVOKABLE QStringList seriesForStudy ( const QString studyUID);
  // / 由于一个系列可能包含数百个实例,因此此方法可能很慢。
  // / 如果指定了 hits > 0,则返回的实例将被限制为该数量。
  // / 这对于检索第一个文件很有用,例如用于访问该文件中的字段
  // / 使用 instanceValue() 方法。
  Q_INVOKABLE QStringList instancesForSeries ( const QString seriesUID, int hits = - 1 );
  Q_INVOKABLE QString studyForSeries (QString seriesUID);
  Q_INVOKABLE QString patientForStudy (QString studyUID);
  // / 由于系列可能包含数百个文件,因此此方法可能很慢。
  // / 如果指定了 hits > 0,则返回的文件名将被限制为该数字。
  // / 这对于检索第一个文件很有用,例如用于访问该文件中的字段
  // / 使用 fileValue() 方法。
  Q_INVOKABLE QStringList filesForSeries ( const QString seriesUID, int hits=- 1 );

  Q_INVOKABLE QHash<QString,QString> descriptionsForFile (QString fileName);
  Q_INVOKABLE QString descriptionForSeries ( const QString seriesUID);
  Q_INVOKABLE QString descriptionForStudy ( const QString studyUID);
  Q_INVOKABLE QString nameForPatient ( const QString patientUID);
  Q_INVOKABLE QString displayedNameForPatient ( const QString patientUID);
  Q_INVOKABLE QString fieldForPatient ( const QString field, const QString patientUID);
  Q_INVOKABLE QString fieldForStudy ( const QString field, const QString studyInstanceUID);
  Q_INVOKABLE QString fieldForSeries ( const QString field, const QString seriesInstanceUID);

  QStringList patientFieldNames () const ;
  QStringList studyFieldNames () const ;
  QStringList seriesFieldNames () const ;

  Q_INVOKABLE QString fileForInstance ( const QString sopInstanceUID);
  Q_INVOKABLE QString seriesForFile (QString fileName);
  Q_INVOKABLE QString instanceForFile ( const QString fileName);
  Q_INVOKABLE QDateTime insertDateTimeForInstance ( const QString fileName);

  Q_INVOKABLE int  patientsCount ();
  Q_INVOKABLE int  studiesCount ();
  Q_INVOKABLE int  seriesCount ();
  Q_INVOKABLE int  imagesCount ();

  Q_INVOKABLE QStringList allFiles ();

  bool  allFilesModifiedTimes (QMap<QString, QDateTime>& modifiedTimeForFilepath);

  // / \brief 从文件加载标题并允许访问元素
  // / @param sopInstanceUID 具有给定实例的 uid 的字符串
  // /(会通过数据库找到对应的文件)
  // / @param fileName 要加载的 dicom 文件的完整路径。
  // / @param key A group, element tag in zero-filled hex
  Q_INVOKABLE void  loadInstanceHeader ( const QString sopInstanceUID);
  Q_INVOKABLE void  loadFileHeader ( const QString fileName);
  Q_INVOKABLE QStringList headerKeys ();
  Q_INVOKABLE QString headerValue ( const QString key);

  // / \brief 应用程序定义的感兴趣的标签
  // / 这个标签列表在导入时被添加到内部标签缓存中
  // / 操作。该列表应由应用程序准备为
  // / 向数据库提示这些标签很可能被访问
  // / 稍后。在内部,数据库会缓存这些值
  // / 标记,以便后续调用 fileValue 或 instanceValue
  // / 能够使用缓存而不是重新读取文件。
  // / @param 标签应该是 ascii 十六进制组/元素标签的列表
  // / 类似于 instanceValue 和 fileValue 调用中的“0008,0008”
  void  setTagsToPrecache ( const QStringList tags);
  const QStringList tagsToPrecache ();

  // / \brief 不得存储在标签缓存中的标签。
  ///如果标签不适合存储在数据库中(例如,二进制数据),则可能会从存储中排除
  // / 或者如果该字段的内容通常非常大。
  // / 仍然可以使用 instanceValueExists 或 fileValueExists 检查是否存在非空标签。
  // / 默认情况下,只有 PixelData 标签被排除在存储之外。
  void  setTagsToExcludeFromStorage ( const QStringList tags);
  const QStringList tagsToExcludeFromStorage ();

  // / 如果不存在则插入到数据库中。
  // / @param dataset 要存储到数据库中的数据集。通常,这是
  // / 是一个完整的 DICOM 对象,就像一个完整的图像。然而
  // / 数据库也插入部分对象,比如study
  ///信息到数据库,即使没有图像数据
  // / 包含。这有助于存储来自
  // / 查询 PACS 中的患者/研究/系列或图像
  ///信息,这里只构造了一个完整的层次结构
  // / 经过一些查询。
  // / @param storeFile 如果设置了存储文件(默认),那么数据集将
  // / 被存储到磁盘。请注意,在仅内存的情况下
  // / 数据库,忽略此标志。通常,这个标志
  // / 只有在接收到完整对象时才有意义。
  // / @param @generateThumbnail 如果为真,则生成缩略图。
  // /
  Q_INVOKABLE void  insert ( const ctkDICOMItem& ctkDataset,
                              bool storeFile, bool generateThumbnail);
  void  insert ( DcmItem *item,
                              bool storeFile = true , bool generateThumbnail = true );
  Q_INVOKABLE void  insert ( const QString& filePath,
                            bool storeFile = true , bool generateThumbnail = true ,
                            bool createHierarchy = true ,
                            const QString& destinationDirectoryName = QString() );

  Q_INVOKABLE void  insert ( const QString& filePath, const ctkDICOMItem& ctkDataset,
    bool storeFile = true , bool generateThumbnail = true );

  Q_INVOKABLE void  insert ( const QList<ctkDICOMDatabase::IndexingResult>& indexingResults);

  // / 当一个 DICOM 文件存储在数据库中时(使用 storeFile=true 调用插入)然后
  // / 路径由研究、系列和 SOP 实例 UID 构成。
  // / 如果 useShortStoragePath 为 false,则完整的 UID 将用作子文件夹和文件名。
  // / 如果 useShortStoragePath 为 true(这是默认值)则路径被缩短到大约 40 个字符
  // / ,通过将 UID 替换为从它们生成的哈希值。
  // / 每个 UID 的长度可以是 40-60 个字符,因此是总路径(包括数据库文件夹基本路径)
  // / 在某些文件系统上可以超过最大路径长度。建议启用 useShortStoragePath
  // / 为了更好的兼容性,除非可以保证文件系统可以存储完整的 UID。
  void  setUseShortStoragePath ( bool useShort);
  bool  useShortStoragePath () const ;

  ///更新数据库中用于显示信息的字段
  // / 来自存储在标签缓存中的信息。
  // / 如果原始 DICOM 标签不是人类可读的,则显示的字段很有用,或者
  // / 当我们想要显示派生的信息时(例如图像大小或
  // / 患者的研究数量)。
  Q_INVOKABLE virtual  void  updateDisplayedFields ();

  // / 如果显示的字段已定义,则获取。对于使用旧模式创建的数据库,它返回 false
  // / 不包含 ColumnDisplayProperties 表。
  Q_INVOKABLE bool  isDisplayedFieldsTableAvailable () const ;
  // / 获取显示的字段生成器,以便能够在外部设置新规则
  ctkDICOMDisplayedFieldGenerator* displayedFieldGenerator () const ;

  // / 重置缓存的项目 ID 以确保以前
  // / 插入不会干扰即将到来的插入操作。
  // / 通常,它应该在一批文件之前调用
  // / 插入开始。
  // /
  // / 如果有机会,这必须在 insert() 调用之前调用
  // / 该项目已从数据库中删除,因为
  // / 最后一次 insert() 调用。如果自那以后没有任何 insert() 调用
  ///连接到数据库,那么应该先调用它
  // / 插入()。
  Q_INVOKABLE void  prepareInsert ();

  // / 检查文件是否已经在数据库中并且是最新的
  Q_INVOKABLE bool  fileExistsAndUpToDate ( const QString& filePath);

  ///从数据库中取出系列,包括图片和缩略图
  // / 如果 clearCachedTags 设置为 true 则删除与该系列关联的缓存标签,
  // / 如果设置为 False,它们将保留在数据库中不变。
  // / 默认情况下禁用 clearCachedTags,因为它会显着增加删除时间
  // / 在大型数据库上。
  Q_INVOKABLE bool  removeSeries ( const QString& seriesInstanceUID, bool clearCachedTags= false );
  Q_INVOKABLE bool  removeStudy ( const QString& studyInstanceUID);
  Q_INVOKABLE bool  removePatient ( const QString& patientID);
  // / 删除所有没有关联图像的患者、研究、系列。
  // / 如果 vacuum 设置为 true 那么整个数据库内容都会被尝试
  // / 从文件中清除所有先前删除的数据的残余。
  // / 如果有多个数据库连接,Vacuuming 可能会失败。
  Q_INVOKABLE bool  cleanup ( bool vacuum= false );

  // / \brief 访问给定实例的元素值
  // / @param sopInstanceUID 具有给定实例的 uid 的字符串
  // /(会通过数据库找到对应的文件)
  // / @param fileName 要加载的 dicom 文件的完整路径。
  // / @param group 标签的组部分作为一个整数
  // / @param element 标记的元素部分为整数
  // / @如果元素丢失或从存储中排除,则返回空字符串。
  Q_INVOKABLE QString instanceValue ( const QString sopInstanceUID, const QString tag);
  Q_INVOKABLE QString instanceValue ( const QString sopInstanceUID, const  unsigned  short group, const  unsigned  short element);
  Q_INVOKABLE QString fileValue ( const QString fileName, const QString tag);
  Q_INVOKABLE QString fileValue ( const QString fileName, const  unsigned  short group, const  unsigned  short element);
  Q_INVOKABLE bool  tagToGroupElement ( const QString tag, unsigned  short & group, unsigned  short & element);
  Q_INVOKABLE QString groupElementToTag ( const  unsigned  short & group, const  unsigned  short & element);

  // / \brief 检查具有给定属性标签的元素是否存在于数据集中并且具有非空值。
  // / @param sopInstanceUID 具有给定实例的 uid 的字符串
  // /(会通过数据库找到对应的文件)
  // / @param fileName 要加载的 dicom 文件的完整路径。
  // / @param group 标签的组部分作为一个整数
  // / @param element 标记的元素部分为整数
  // / @Returns true 如果标签存在并且具有非空值。即使该值已从数据库中的存储中排除,也返回 true。
  Q_INVOKABLE bool  instanceValueExists ( const QString sopInstanceUID, const QString tag);
  Q_INVOKABLE bool  instanceValueExists ( const QString sopInstanceUID, const  unsigned  short group, const  unsigned  short element);
  Q_INVOKABLE bool  fileValueExists ( const QString fileName, const QString tag);
  Q_INVOKABLE bool  fileValueExists ( const QString fileName, const  unsigned  short group, const  unsigned  short element);

  // / \brief 存储先前请求的实例元素的值
  // / 这些是 instanceValue 和 fileValue 使用的内部方法
  // / 方法,但它们可以通过调用类来填充或访问
  // / 根据需要实例标记值。
  // / @param sopInstanceUID 具有给定实例的 uid 的字符串
  // /(会通过数据库找到对应的文件)
  // / @param key A group, element tag in zero-filled hex
  // / @如果 uid 的元素从缓存中丢失,则返回空字符串
  // /
  // / 标签缓存存在​​的轻量级检查(每个运行时检查一次数据库)
  Q_INVOKABLE bool  tagCacheExists ();
  ///在当前数据库中创建一个tagCache。如果存在,请删除现有的。
  Q_INVOKABLE bool  initializeTagCache ();
  ///返回缓存标签的值
  Q_INVOKABLE QString cachedTag ( const QString sopInstanceUID, const QString tag);
  // / 返回指定 sopInstanceUID 的所有缓存标签和值的列表。如果标签不在缓存中,则返回空字符串。
  Q_INVOKABLE void  getCachedTags ( const QString sopInstanceUID, QMap<QString, QString> &cachedTags);
  // / 将实例标签的值插入到缓存中
  Q_INVOKABLE bool  cacheTag ( const QString sopInstanceUID, const QString tag, const QString value);
  ///将标签列表作为批量查询操作插入缓存
  Q_INVOKABLE bool  cacheTags ( const QStringList sopInstanceUIDs, const QStringList tags, const QStringList values);
  ///移除一个SOP实例UID对应的所有标签
  Q_INVOKABLE void  removeCachedTags ( const QString sopInstanceUID);

  ///获取给定字段的显示名称
  Q_INVOKABLE QString displayedNameForField (QString table, QString field) const ;
  // / 设置给定字段的显示名称
  Q_INVOKABLE void  setDisplayedNameForField (QString table, QString field, QString displayedName);
  ///获取给定字段的可见性
  Q_INVOKABLE bool  visibilityForField (QString table, QString field) const ;
  // / 设置给定字段的可见性
  Q_INVOKABLE void  setVisibilityForField (QString table, QString field, bool visibility);
  // / 获取给定字段的权重。
  ///权重指定表中字段列的顺序。较小的值位于左侧(“最重的下沉”)
  Q_INVOKABLE int  weightForField (QString table, QString field) const ;
  ///设置给定字段的权重
  ///权重指定表中字段列的顺序。较小的值位于左侧(“最重的下沉”)
  Q_INVOKABLE void  setWeightForField (QString table, QString field, int weight);
  ///获取给定字段的格式
  // / 它包含一个包含以下字段的 json 文档:
  // / - resizeMode:列调整模式。可接受的值为:“interactive”(默认)、“stretch”或“resizeToContents”。
  // / - 排序:默认排序顺序。可接受的值为:空(默认)、“升序”或“降序”。
  // / 每个表中只有一列(或没有)应具有非空排序顺序。
  Q_INVOKABLE QString formatForField (QString table, QString field) const ;
  ///设置给定字段的格式
  Q_INVOKABLE void setFormatForField(QString table, QString field, QString format);

  // / DICOM中没有患者UID,所以我们需要使用这个复合ID,用一个字符串来唯一标识一个患者。
  // / 在插入患者(InsertedPatientsCompositeIDCache)和显示字段更新过程中使用。
  // / 注意:这不是一个比用于决定是否应该进行研究的标准更严格的问题
  // / 插入同一患者下。
  Q_INVOKABLE static QString compositePatientID ( const QString& patientID, const QString& patientsName, const QString& patientsBirthDate);

Q_SIGNALS:

  /// Things inserted to database.
  /// patientAdded arguments:
  ///  - int: database index of patient (unique) within CTK database
  ///  - QString: patient ID (not unique across institutions)
  ///  - QString: patient Name (not unique)
  ///  - QString: patient Birth Date (not unique)
  void patientAdded(int, QString, QString, QString);
  /// studyAdded arguments:
  ///  - studyUID (unique)
  void studyAdded(QString);
  /// seriesAdded arguments:
  ///  - seriesUID (unique)
  void seriesAdded(QString);
  /// instance UID is provided
  /// instanceAdded arguments:
  ///  - instanceUID (unique)
  void instanceAdded(QString);

  /// This signal is emitted when the database has been opened.
  void opened();

  /// This signal is emitted when the database has been closed.
  void closed();

  /// Indicate that an in-memory database has been updated
  void databaseChanged();

  /// Indicate that tagsToPrecache list changed
  void tagsToPrecacheChanged();

  /// Indicate that tagsToExcludeFromStorage list changed
  void tagsToExcludeFromStorageChanged();

  /// Indicate that the schema is about to be updated and how many files will be processed
  void schemaUpdateStarted(int);
  /// Indicate progress in updating schema (int is file number, string is file name)
  void schemaUpdateProgress(int);
  void schemaUpdateProgress(QString);
  /// Indicate schema update finished
  void schemaUpdated();

  /// Trigger showing progress dialog for displayed fields update
  void displayedFieldsUpdateStarted();
  /// Indicate progress in updating displayed fields (int is step number)
  void displayedFieldsUpdateProgress(int);
  /// Indicate displayed fields update finished
  void displayedFieldsUpdated();

protected:
  QScopedPointer<ctkDICOMDatabasePrivate> d_ptr;

private:
  Q_DECLARE_PRIVATE(ctkDICOMDatabase);
  Q_DISABLE_COPY(ctkDICOMDatabase);
};

#endif

ex:

        DICOMUtils.importDicom(dstDir, db)
        databaseDirectory = db.databaseDirectory
        patientUIDs = db.patients()
        print("*******************1********************")
        print('dicomDataDir :', dicomDataDir)
        print('dstDir :', dstDir)
        print('db.patients() :', db.patients())
        print('db.patientsCount() :', db.patientsCount())
        print('db.studiesCount() :', db.studiesCount())
        print('db.seriesCount() :', db.seriesCount())

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值