Skip to content

Commit

Permalink
ENH: update dicom db schema and api for files and urls
Browse files Browse the repository at this point in the history
Schema now has URL column of size 2048 (which based on research
is suggested as the max size for URLs).  The schema now
allows either field to be NULL and does not require the
Filename column to be unique.

The api now icnludes a urlsForSeries option to get the list
of URLs for a given series instance uid.

The fileValue method now accepts either filePaths or URLs with
filePaths being given priority if both exist.
  • Loading branch information
pieper committed Nov 14, 2023
1 parent 0c47c6a commit d6be887
Show file tree
Hide file tree
Showing 3 changed files with 47 additions and 7 deletions.
6 changes: 3 additions & 3 deletions Libs/DICOM/Core/Resources/dicom-schema.sql
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ INSERT INTO 'SchemaInfo' VALUES('0.8.0');

CREATE TABLE 'Images' (
'SOPInstanceUID' VARCHAR(64) NOT NULL,
'Filename' VARCHAR(1024),
'URL' VARCHAR(2048),
'Filename' VARCHAR(1024) NULL,
'URL' VARCHAR(2048) NULL,
'SeriesInstanceUID' VARCHAR(64) NOT NULL ,
'InsertTimestamp' VARCHAR(20) NOT NULL ,
'DisplayedFieldsUpdatedTimestamp' DATETIME NULL ,
Expand Down Expand Up @@ -85,7 +85,7 @@ CREATE TABLE 'Series' (
'DisplayedFieldsUpdatedTimestamp' DATETIME NULL ,
PRIMARY KEY ('SeriesInstanceUID') );

CREATE UNIQUE INDEX IF NOT EXISTS 'ImagesFilenameIndex' ON 'Images' ('Filename');
CREATE INDEX IF NOT EXISTS 'ImagesFilenameIndex' ON 'Images' ('Filename');
CREATE INDEX IF NOT EXISTS 'ImagesSeriesIndex' ON 'Images' ('SeriesInstanceUID');
CREATE INDEX IF NOT EXISTS 'SeriesStudyIndex' ON 'Series' ('StudyInstanceUID');
CREATE INDEX IF NOT EXISTS 'StudiesPatientIndex' ON 'Studies' ('PatientsUID');
Expand Down
36 changes: 34 additions & 2 deletions Libs/DICOM/Core/ctkDICOMDatabase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2111,6 +2111,28 @@ QStringList ctkDICOMDatabase::filesForSeries(QString seriesUID, int hits/*=-1*/)
return allFileNames;
}

//------------------------------------------------------------------------------
QStringList ctkDICOMDatabase::urlsForSeries(QString seriesUID, int hits/*=-1*/)
{
Q_D(ctkDICOMDatabase);
QSqlQuery query(d->Database);
query.prepare("SELECT URL FROM Images WHERE SeriesInstanceUID=?");
query.addBindValue(seriesUID);
query.exec();
QStringList allURLs;
while (query.next())
{
QString url = query.value(0).toString();
allURLs << url;
if (hits > 0 && allURLs.size() >= hits)
{
// reached the number of requested files
break;
}
}
return allURLs;
}

//------------------------------------------------------------------------------
QString ctkDICOMDatabase::fileForInstance(QString sopInstanceUID)
{
Expand Down Expand Up @@ -2345,8 +2367,18 @@ QString ctkDICOMDatabase::fileValue(const QString fileName, QString tag)
Q_D(ctkDICOMDatabase);

// Read from cache, if available
tag = tag.toUpper();

// first, try treating argument as filePath
QString sopInstanceUID = this->instanceForFile(fileName);

// second, try treating argument as a url
if (sopInstanceUID.isEmpty())
{
sopInstanceUID = this->instanceForURL(fileName);
}

// third, look for the value
tag = tag.toUpper();
QString value = this->cachedTag(sopInstanceUID, tag);
if (value == TagNotInInstance || value == ValueIsEmptyString || value == ValueIsNotStored)
{
Expand All @@ -2357,7 +2389,7 @@ QString ctkDICOMDatabase::fileValue(const QString fileName, QString tag)
return value;
}

// Read value from file
// Read value from file as a fallback (won't work if fileName is a URL)
value = d->readValueFromFile(fileName, sopInstanceUID, tag);
return value;
}
Expand Down
12 changes: 10 additions & 2 deletions Libs/DICOM/Core/ctkDICOMDatabase.h
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,7 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMDatabase : public QObject
/// This is useful for retrieving first file, for example for getting access to fields within that file
/// using fileValue() method.
Q_INVOKABLE QStringList filesForSeries(const QString seriesUID, int hits=-1);
Q_INVOKABLE QStringList urlsForSeries(const QString seriesUID, int hits=-1);

Q_INVOKABLE QHash<QString,QString> descriptionsForFile(QString fileName);
Q_INVOKABLE QString descriptionForSeries(const QString seriesUID);
Expand Down Expand Up @@ -267,7 +268,7 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMDatabase : public QObject
/// When a DICOM file is stored in the database (insert is called with storeFile=true) then
/// path is constructed from study, series, and SOP instance UID.
/// If useShortStoragePath is false then the full UIDs are used as subfolder and file name.
/// If useShortStoragePath is true (this is the default) then the path is shortened
/// If useShortStoragePath is true (this is the default) then the path is shortened
/// to approximately 40 characters, by replacing UIDs with hashes generated from them.
/// UIDs can be 40-60 characters long each, therefore the the total path (including database folder base path)
/// can exceed maximum path length on some file systems. It is recommended to enable useShortStoragePath
Expand Down Expand Up @@ -320,7 +321,14 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMDatabase : public QObject
/// \brief Access element values for given instance
/// @param sopInstanceUID A string with the uid for a given instance
/// (corresponding file will be found via the database)
/// @param fileName Full path to a dicom file to load.
/// @param fileName Full path to a dicom file to load. Note that this string
/// can either be a file path or a URL since the database can have one or
/// both of these fields as of schema version 0.8.0. Since the fileValue
/// api is widely used, this overload allows either value to be used to retrieve
/// values from the tag cache. If a value is found for the fileName the
/// fileName it is returned; if not, the fileName is used as a URL. If no
/// value is found for the URL, the fileName is used as a path to a dicom file
/// where the value is looked up.
/// @param group The group portion of the tag as an integer
/// @param element The element portion of the tag as an integer
/// @Returns empty string if element is missing or excluded from storage.
Expand Down

0 comments on commit d6be887

Please sign in to comment.