Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / desktop / QT

Link third party libraries (DCMTK) in Qt projects

0.00/5 (No votes)
6 Jun 2016CPOL4 min read 23.8K   488  
This article demonstrates how to link a third party libraries (DCMTK) to Qt based project.

Introduction

This article demonstrates how to correctly link third party libraries to Qt based project. Qt is a widely used cross platform C++ framework. We will develop a simple console based application using Qt Creator and link a DCMTK library to read a DICOM file and display its contents. DCMTK is a widely used library to work with DICOM images.

Background

This article assumes that you are already familiar with DICOM (Digital Imaging and Communications in Medicine) protocol, DCMTK library and Qt. For more information on these use the links below:

http://dicom.offis.de/dcmtk.php.en
http://dicom.nema.org/
https://www.qt.io/developers/
 

Getting Started

Make sure that you have the following prerequisites:

  • DCMTK library built following the instructions as mentioned in DCMTK homepage.
  • Qt Creator (I've used version 3.6)

All the source code used in this project can be downloaded using the download link above. 

Project Structure

Under the root folder named DcmtkQtLink, there are following sub-directories and files.

  1. main.cpp
  2. MyQtApp.pro
  3. thirdparty/dcmtk
  4. resources

"main.cpp" is the main program entry file. "MyQtapp.pro" is  a project file created in Qt Creator which contains scripts that will be used by 'qmake' to build our project. The subdirectory "thirdparty/dcmtk" contains pre-compiled lib, dll and include headers. Only the necessary lib, dll and include header files are included that are relevant to run the application in this article. The "resources" folder contains a sample DICOM file that will be used in the application.

Building the project

After you have downloaded the source code, follow the following instructions to build the project:

  1. Simply open MyQtApp.pro file in the Qt Creator IDE.
  2. In the mode selector, select "Projects". In "Edit build configuration", select the build configuration mode. In the "Build directory" select "Browse" to navigate to the application code folder. For eg: C:\qt\DcmtkQtLink
  3. In the "Build Steps", click "Add Build Step". This will bring a form with default values. Leave the default values and enter "install" in the "Make arguments:". This step will make sure that the necessary lib and Dll files are copied to the folder where .exe file is generated.
  4. Select "Build" and "Run qmake".
  5. Click "Build" button to build the project.
  6. Click "Run" button to run the project.

Using the code

This section describes how the code works:

First, let's look in the project file "MyQtApp.pro" as this is the main file used by Qt Creator to build the application successfully. If you need a basic tutorial on qmake project file, see http://doc.qt.io/qt-4.8/qmake-project-files.html

C++
// MyQtApp.pro

QT += core
QT -= gui

CONFIG += c++11

TARGET = MyQtApp
CONFIG += console
CONFIG -= app_bundle

TEMPLATE = app

SOURCES += main.cpp

INCLUDEPATH +=  $$PWD/thirdparty/dcmtk/include
DEPENDPATH += $$PWD/thirdparty/dcmtk/include

win32:CONFIG(debug,debug|release): LIBS += -L$$PWD/thirdparty/dcmtk/lib/ -ldcmdatad -ldcmimgled -ldcmimaged -ldcmjplsd -loflogd -lofstdd -ldcmjpegd
win32:CONFIG(release,debug|release): LIBS += -L$$PWD/thirdparty/dcmtk/lib/ -ldcmdata -ldcmimgle -ldcmimage -ldcmjpls -loflog -lofstd -ldcmjpeg

win32:CONFIG(debug,debug|release): DESTDIR = bin/debug
else:win32:CONFIG(release,release|debug) : DESTDIR = bin/release

win32{
    CONFIG(debug,debug|release) {
        dlls_to_move.path = $$DESTDIR
        dlls_to_move.files += $$PWD/thirdparty/dcmtk/bin/dcmdatad.dll
        dlls_to_move.files += $$PWD/thirdparty/dcmtk/bin/dcmimgled.dll
        dlls_to_move.files += $$PWD/thirdparty/dcmtk/bin/dcmimaged.dll
        dlls_to_move.files += $$PWD/thirdparty/dcmtk/bin/dcmjplsd.dll
        dlls_to_move.files += $$PWD/thirdparty/dcmtk/bin/oflogd.dll
        dlls_to_move.files += $$PWD/thirdparty/dcmtk/bin/ofstdd.dll
        dlls_to_move.files += $$PWD/thirdparty/dcmtk/bin/dcmjpegd.dll
        INSTALLS += dlls_to_move
    }
    else {
        dlls_to_move.path = $$DESTDIR
        dlls_to_move.files += $$PWD/thirdparty/dcmtk/bin/dcmdata.dll
        dlls_to_move.files += $$PWD/thirdparty/dcmtk/bin/dcmimgle.dll
        dlls_to_move.files += $$PWD/thirdparty/dcmtk/bin/dcmimage.dll
        dlls_to_move.files += $$PWD/thirdparty/dcmtk/bin/dcmjpls.dll
        dlls_to_move.files += $$PWD/thirdparty/dcmtk/bin/oflog.dll
        dlls_to_move.files += $$PWD/thirdparty/dcmtk/bin/ofstd.dll
        dlls_to_move.files += $$PWD/thirdparty/dcmtk/bin/dcmjpeg.dll
        INSTALLS += dlls_to_move
    }

    # copy resources
    resources_to_move.path = $$DESTDIR
    resources_to_move.files += $$PWD/resources/dicom_testdata.dcm
    INSTALLS += resources_to_move
}

The line QT += core and QT += gui are QT specific configuration options. CONFIG += c++11 is a general project configuration option that tells qmake that we are using C++11 features. Other CONFIG parameters define that this is a console based application.

We have only one source file that is added to SOURCES variable as SOURCES += main.cpp

To be able to include the header files in our source code, we have to include the paths and dependent paths as follows:

C++
INCLUDEPATH +=  $$PWD/thirdparty/dcmtk/include
DEPENDPATH += $$PWD/thirdparty/dcmtk/include

Next, we add the actual library files (.lib) from DCMTK that we are going to use. Note that there are two versions of library files. One compiled in debug mode (have suffix d at the end of a library name) and the other compiled in release mode.

C++
win32:CONFIG(debug,debug|release): LIBS += -L$$PWD/thirdparty/dcmtk/lib/ -ldcmdatad -ldcmimgled -ldcmimaged -ldcmjplsd -loflogd -lofstdd -ldcmjpegd
win32:CONFIG(release,debug|release): LIBS += -L$$PWD/thirdparty/dcmtk/lib/ -ldcmdata -ldcmimgle -ldcmimage -ldcmjpls -loflog -lofstd -ldcmjpeg

When the project is built, a lot of intermediate files and final executable file is created. If we want to create those files outside our source file directory, we can add the following code to define our destination directory path by using a variable DESTDIR. Again, this is set separately for debug and config configuration. This step is also important to tell the 'qmake' later while we copy necessary DLL files and test data to the folder where the final executable will be created.

C++
win32:CONFIG(debug,debug|release): DESTDIR = bin/debug
else:win32:CONFIG(release,release|debug) : DESTDIR = bin/release

Finally, as we are linking DCMTK statically in this application, we have to have a necessary DLL files in the folder where the executable file gets created. For this, we add a following script that simply copies the debug/release version of DLL files from the thirdparty/dcmtk directory to the DESTDIR path that we assigned earlier. This is done by first assigning relative paths where the DLL file resides and then finally using a variable INSTALL to do a actual copy operation as a post build operation. Here, it should be noted that we have to call qmake install in order for this script to be effective. And, we have done this in the step 3 as described in the section Buidling the project.

Also, we can copy the files located in the 'resource' folder (our sample DICOM test file) using the variable INSTALL.

C++
win32{
    CONFIG(debug,debug|release) {
        dlls_to_move.path = $$DESTDIR
        dlls_to_move.files += $$PWD/thirdparty/dcmtk/bin/dcmdatad.dll
        dlls_to_move.files += $$PWD/thirdparty/dcmtk/bin/dcmimgled.dll
        dlls_to_move.files += $$PWD/thirdparty/dcmtk/bin/dcmimaged.dll
        dlls_to_move.files += $$PWD/thirdparty/dcmtk/bin/dcmjplsd.dll
        dlls_to_move.files += $$PWD/thirdparty/dcmtk/bin/oflogd.dll
        dlls_to_move.files += $$PWD/thirdparty/dcmtk/bin/ofstdd.dll
        dlls_to_move.files += $$PWD/thirdparty/dcmtk/bin/dcmjpegd.dll
        INSTALLS += dlls_to_move
    }
    else {
        dlls_to_move.path = $$DESTDIR
        dlls_to_move.files += $$PWD/thirdparty/dcmtk/bin/dcmdata.dll
        dlls_to_move.files += $$PWD/thirdparty/dcmtk/bin/dcmimgle.dll
        dlls_to_move.files += $$PWD/thirdparty/dcmtk/bin/dcmimage.dll
        dlls_to_move.files += $$PWD/thirdparty/dcmtk/bin/dcmjpls.dll
        dlls_to_move.files += $$PWD/thirdparty/dcmtk/bin/oflog.dll
        dlls_to_move.files += $$PWD/thirdparty/dcmtk/bin/ofstd.dll
        dlls_to_move.files += $$PWD/thirdparty/dcmtk/bin/dcmjpeg.dll
        INSTALLS += dlls_to_move
    }

    # copy resources
    resources_to_move.path = $$DESTDIR
    resources_to_move.files += $$PWD/resources/dicom_testdata.dcm
    INSTALLS += resources_to_move
}

 

Second, we look into the file "main.cpp".

C++
// main.cpp

#include <QCoreApplication>
#include <QDebug>

// DCMTK includes
#include <dcmtk/dcmdata/dcfilefo.h>
#include <dcmtk/dcmdata/dcdeftag.h>

// STL includes
#include <memory>

int main(int argc, char *argv[])
{
    QCoreApplication app(argc, argv);

    qDebug() << "**********************************************\n";
    qDebug() << "Demo - linking DCMTK library to QT project";
    qDebug() << "**********************************************\n";

    // get the test data to load
    QString test_data_file_name = "/dicom_testdata.dcm";
    QString test_data_file_path = QCoreApplication::applicationDirPath() + test_data_file_name;

    qDebug() << "\nTest data file path: \n" << test_data_file_path << "\n";

    DcmFileFormat file_format;
    OFCondition status = file_format.loadFile(test_data_file_path.toStdString().c_str());
    std::shared_ptr<DcmDataset> dataset(file_format.getDataset());

    qDebug() << "\nInformation extracted from DICOM file: \n";

    const char* buffer = nullptr;
    DcmTagKey key = DCM_PatientName;
    dataset->findAndGetString(key,buffer);
    std::string tag_value = buffer;
    qDebug() << "Patient name: " << tag_value.c_str();

    key = DCM_PatientID;
    dataset->findAndGetString(key,buffer);
    tag_value = buffer;
    qDebug() << "Patient id: " << tag_value.c_str();

    key = DCM_TransferSyntaxUID;
    dataset->findAndGetString(key,buffer);
    if(buffer == NULL) {
        qDebug() << "Transfer syntax value is NULL";;
    }
    else {
        tag_value = buffer;
        qDebug() << "Transfer syntax: " << tag_value.c_str();
    }

    E_TransferSyntax transfer_syntax = dataset->getCurrentXfer();
    DcmXfer dcm_ts(transfer_syntax);
    std::string ts_name = dcm_ts.getXferName();
    qDebug() << "Transfer syntax name: " << ts_name.c_str();

    return app.exec();
}

The file includes the necessary header files from DCMTK as follows:

C++
#include <dcmtk/dcmdata/dcfilefo.h>
#include <dcmtk/dcmdata/dcdeftag.h>

We can get the test DICOM file that has been copied into the folder where .exe file is created (as a post build step) by using the following code:

C++
QString test_data_file_name = "/dicom_testdata.dcm";
QString test_data_file_path = QCoreApplication::applicationDirPath() + test_data_file_name;

 

Output

**********************************************

Demo - linking DCMTK library to QT project
**********************************************

Test data file path:
 "C:/p/qt/ext_lib_link_demo/tutorial/MyQtApp/bin/debug/dicom_testdata.dcm"

Information extracted from DICOM file:

Patient name:  PELVIX
Patient id:  Vafm7b0J
Transfer syntax value is NULL
Transfer syntax name:  JPEG 2000 (Lossless or Lossy)

History

  • 2016-06-06: First published

Keep a running update of any changes or improvements you've made here.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)