Problems creating QT widgets together with DCMTK

All other questions regarding DCMTK

Moderator: Moderator Team

Message
Author
Spirit
Posts: 11
Joined: Mon, 2006-05-01, 18:13

Problems creating QT widgets together with DCMTK

#1 Post by Spirit »

Hello,

I am new here on the Forum hope to find some answers. First of all, I like the dcmtk toolkit very much!

My plan is to write some qt (see www.trolltech.com) widgets to embed them in there GUI designer. The idea is to make a environment for click and go medical viewers. This video demonstrates the environment of QT creating some HTML viewer: http://www.trolltech.com/video/index.html
Exchange in this video the HTML widget for a set of dcmtk-widgets for different images, slices, run's etc. and you get a nice environment to develop medical related software.

Some weeks ago I started coding and the first widget is almost ready. It can handle monochrome, color, frames.

To test the widget, I downloaded some dicom images from several sites.

I noticed however that not all images can be opened. Sometimes I got EIR_MissingAttribute and the image could not be opened.

To find the problem the library is built in debug mode. Next SetDebugLevel is used to see the library messages.

Currently I do not see any library messages occur. I do not understand that the messages do not occur. Is building in debug mode and setting the SetDebugLevel not enough?

Secondly I like to know what I should do if an image can not be opened. Is it a problem of the image or can dcmtk not handle it or is it a configuration problem etc.

Kind regards,


Spirit

Michael Onken
DCMTK Developer
Posts: 2048
Joined: Fri, 2004-11-05, 13:47
Location: Oldenburg, Germany
Contact:

#2 Post by Michael Onken »

Hi Spirit,

first of all: Using DCMTK and QT together should work well, we did some projects with this. In the current version of QT, some adaptions may be necessary, but it seems you managed all that, fine :)

As I think you should get some debug messages when setting the debug level. But this is not really your problem, if you encounter messages like "missing tag" or something similar. DICOM stores all information in attributes identified by tags. The pixel data is stored in such an attribute too. To interpret this pixel data correctly, DICOM specifies some additional attributes, like number of rows and columns, colour model, compression details etc. If some of this (mandatory) tags are missing, it's not possible to interpret the pixel data, i. e. the DCMTK fails, because it could only guess how the pixel data is encoded. So, this is not a problem in DCMTK but the files are not valid DICOM files.

Regards,
Michael

Spirit
Posts: 11
Joined: Mon, 2006-05-01, 18:13

#3 Post by Spirit »

Hi Michael,

Thanks for the fast response! Nice to hear other people using qt and dcmtk together too.

I am working to integrate dicom in the KDE environment. I like to get the konqueror browser of kde showing dicom thumbnails, showing meta data like patient name and other related data. Next a simple viewer should be available when clicking on a image. For this reason I wrote a qDicomImage class that reads dicom files using dcmtk and delivers qimages.

Kind regards,

Spirit

Spirit
Posts: 11
Joined: Mon, 2006-05-01, 18:13

progress and new questions

#4 Post by Spirit »

Hello,

I am happy to say things are starting to work! First I created a class to convert a dcmtk image buffer to a QT QImage. This works for some images (not all!) and I wrote for the KDE environment an thumbnail plug-in. The result can be seen via next link.
http://home.kabelfoon.nl/~jwaal/

Next, I like to make some widgets to add to the designer of qt and create viewers for different dicom studies.

The question I have, is that only some images are correctly converted by my class. I saw e.g. that compressed Images (JPG) are not converted. If I use dcmj2pnm I am able to convert the image. So that should be possible I guess. Maybe it's the code I wrote, who can have a look at it?

Header of conversion class dcmtk -> QT

Code: Select all

#ifndef QDICOMIMAGE_H
#define QDICOMIMAGE_H

/**
	@author Jeroen van der Waal <jwaal@kabelfoon.nl>
*/

#include "qimage.h"

#include <dcmtk/config/cfunix.h>
#include <dcmtk/config/osconfig.h>
#include <dcmtk/ofstd/ofstring.h>
#include <dcmtk/ofstd/ofconsol.h>
#include <dcmtk/dcmdata/dcdeftag.h>
#include <dcmtk/dcmdata/dcfilefo.h>
#include <dcmtk/dcmimgle/dcmimage.h>
#include "dcmtk/dcmimage/diregist.h"      /* include to support color images */

class QGrayColorTable
{
	public:
		static const unsigned int GRAY_LEVELS = 256;
		QGrayColorTable();
		virtual ~QGrayColorTable() {}
		
		QRgb* getGrayColorTable() { return m_colortable; }
	private:
		static QRgb m_colortable[GRAY_LEVELS];
};


class QDicomImage
{
	public:
		QDicomImage(const char* filename, const unsigned long flags=0,
					const unsigned long fstart=0, const unsigned long fcount=0);
		virtual ~QDicomImage();
		QImage getOutputData(const unsigned long p_frame=0, const int p_planar=0);
		unsigned long getFrameCount() const;
		
	private:
		///Dcmtk attributes
		DicomImage* m_dicomImage;
		EI_Status m_status;

		///Qt related attributes
		static QGrayColorTable m_grayColorTable;
		static const int IMG_DEPTH = 8;
		unsigned long m_qimageSize;
		///Image buffer is maintained by this class instead of dcmtk or qt.
		///It is to prevent create/delete of buffer for every frame in case
		///of a multi frame dicom file.
		uchar* m_qimageBuffer;
};

#endif
Source code of conversion class

Code: Select all

#include "qdicomimage.h"

QGrayColorTable::QGrayColorTable()
{
        //color table for monochroom images.
        for(unsigned int i=0; i < GRAY_LEVELS; i++) {
                m_colortable[i] = qRgb(i,i,i);
        }
}

QRgb QGrayColorTable::m_colortable[GRAY_LEVELS];



QDicomImage::QDicomImage(const char* filename, const unsigned long flags,
		const unsigned long fstart, const unsigned long fcount)
                :m_dicomImage(NULL)
                ,m_status(EIS_Normal)
                ,m_qimageSize(0)
                ,m_qimageBuffer(NULL)
{
        m_dicomImage = new DicomImage(filename, flags, fstart, fcount);
        m_status = m_dicomImage->getStatus();
        if(EIS_Normal == m_status) {
                m_qimageSize = m_dicomImage->getWidth() * m_dicomImage->getHeight();
                if(!m_dicomImage->isMonochrome()) {
                        /// color images needs 4 times the amount of memory (RGBA)
                        m_qimageSize = m_qimageSize * 4;
                }
                m_qimageBuffer = new uchar[m_qimageSize];
        }
}

QDicomImage::~QDicomImage()
{
        if(m_dicomImage) {
                delete m_dicomImage;
        }

        if(m_qimageBuffer) {
                delete[] m_qimageBuffer;
        }
}

QImage QDicomImage::getOutputData(const unsigned long p_frame, const int p_planar)
{
        QImage image;
        if(EIS_Normal == m_status) {
                if (m_dicomImage->isMonochrome()) {
                        ///Monochroom images
                        m_dicomImage->setMinMaxWindow(); //??
                        m_dicomImage->getOutputData(m_qimageBuffer, m_qimageSize, IMG_DEPTH, p_frame, p_planar);
                        image = QImage(m_qimageBuffer, m_dicomImage->getWidth(), m_dicomImage->getHeight(),
                                       IMG_DEPTH, m_grayColorTable.getGrayColorTable(), QGrayColorTable::GRAY_LEVELS,
                                       QImage::BigEndian);
                } else {
                        ///color images
                        uchar* pixelData = (uchar *)(m_dicomImage->getOutputData(IMG_DEPTH, p_frame, p_planar));

                        ///transform representation from dcmtk(RGB) to qimage (RGBA)
                        unsigned long y = 0;
                        for(unsigned long x = 0; x < m_qimageSize; x+=4) {
                                m_qimageBuffer[x] = pixelData[y];     //R
                                m_qimageBuffer[x+1] = pixelData[y+1]; //G
                                m_qimageBuffer[x+2] = pixelData[y+2]; //B
                                m_qimageBuffer[x+3] = 0xFF; //Alpha
                                y += 3;
                        }
                        image = QImage(m_qimageBuffer, m_dicomImage->getWidth(), m_dicomImage->getHeight(),
                                       32, 0, 0, QImage::BigEndian);
                }
        }
        return image; ///< is ok to return, qimage supports shallow copy
}

unsigned long QDicomImage::getFrameCount() const
{
        return m_dicomImage->getFrameCount();
}


QGrayColorTable QDicomImage::m_grayColorTable;

[/b]


Kind regards,


Jeroen van der Waal

Marco Eichelberg
OFFIS DICOM Team
OFFIS DICOM Team
Posts: 1437
Joined: Tue, 2004-11-02, 17:22
Location: Oldenburg, Germany
Contact:

#5 Post by Marco Eichelberg »

In order to work correctly with compressed images, your code should register one or more decompression codecs before the first DicomImage instance is created. For JPEG compression, the code looks like this:

Code: Select all

#include "dcmtk/dcmjpeg/djdecode.h" /* for jpeg decoder */
[...]
// register JPEG decompression codecs
DJDecoderRegistration::registerCodecs();
Note that there is also a corresponding cleanup call that should be issued before the tool terminates. A similar structure exists for the RLE codec and the JPEG 2000 codec, the latter of which is not part of the public toolkit, however.

Spirit
Posts: 11
Joined: Mon, 2006-05-01, 18:13

Thanks!

#6 Post by Spirit »

Hello Marco,

Thanks for your answer Marco. I will try immediately to add this to the class. When the dicom viewer is ready, I will be back to show you the results.

Kind Regards,

Jeroen van der Waal

Spirit
Posts: 11
Joined: Mon, 2006-05-01, 18:13

QT designer is extended with plugin for dicom, and question.

#7 Post by Spirit »

Hi,

I am happy to say that I now have a plugin for QT designer that is able to show (color/monochrome, jpeg/rle) and plays dicom images / loops / runs. Via slots the widget can play, reverse play, frame forward/back, zoom and change the speed of playing. The widget itself is a scrollview, so if the image does not fit it can be scrolled.

It works smooth for about 25 frames / sec for images of size 1024x1024. For this I use a single study of size 100 Mb. The load time for this study takes about 10 seconds. I like to not immediately load all the content in one strike, and my question is, is it possible to load it piece by piece?


Kind regards,


Jeroen van der Waal

Marco Eichelberg
OFFIS DICOM Team
OFFIS DICOM Team
Posts: 1437
Joined: Tue, 2004-11-02, 17:22
Location: Oldenburg, Germany
Contact:

#8 Post by Marco Eichelberg »

I like to not immediately load all the content in one strike, and my question is, is it possible to load it piece by piece?
If you are loading single frame images such as the conventional CT and MR SOP classes, you control the behaviour completely on your own. If you are working with multiframe images, there is unfortunately no way to tell DCMTK to only load a certain number of frames. Opening the file (DcmFileFormat::loadFile) will not load everything - all attributes larger than 4K remain in the file and are loaded on demand. However, once you try to access any piece of the pixel data, all pixel data is loaded and there is no way to prevent this. Changing this part of DCMTK's API to allow partial loading and partial decompression is one of the most favourite feature requests, but is not trivial and has not yet happened.

Spirit
Posts: 11
Joined: Mon, 2006-05-01, 18:13

#9 Post by Spirit »

Hi Marco,

Nice hearing from you so soon. Not that bad that this way of loading is not yet supported. I guess the sign 'please wait...' should be enough for the moment ;-). After all Dcmtk is really great, I enjoy working with it very much.

I am recording some gui building with the dicom qt widget and will post it on this site.

Kind regards,

Jeroen van der Waal

Spirit
Posts: 11
Joined: Mon, 2006-05-01, 18:13

Some results of dicom widget set based on dcmtk / qt

#10 Post by Spirit »

Hi,

I worked the last two months using all my sparetime creating a set of three QT widgets. The widgets extending the qt designer environment and can be used to create dicom viewers by dragging components and connecting them with eachother. In only five minutes without compiling a dicom viewer can play, zoom, change contrast etc.

This is a link to a presentation (and demo) I gave to an audience of MFC programmers in an medical environment (they had a hard time :-)).
http://home.kabelfoon.nl/~jwaal/present ... idgets.pdf

The following components are available extends the components within the QT designer (please visit trolltech site for more information):
  • qdicomdirwidget, show tree view of dicomdir
  • qdicomdiriconwidget, shows all icons of dicomdir
  • qdicomwidget, show dicom images, zoom, contrast, brightness, play, reverse play, single step (mouse/button), mouse zoom/pan.
Performance is excellent! Treeview 51 patients are shown within a second, icons in the widget 200 icons in less of a second, qdicomwidget shows at rates of 60+ frames a second on 512x512.
All widgets supports dropping of files, just drop a DICOMDIR and it is shown in the qdicomdirwidget. All kinds of dicom image files can be dropped on the QDicomWidget for viewing/playing.

I have plenty of ideas to extend the widget set to make it a nice set of building blocks for medical viewing.

Kind regards,


Jeroen van der Waal

Jörg Riesmeier
ICSMED DICOM Services
ICSMED DICOM Services
Posts: 2217
Joined: Fri, 2004-10-29, 21:38
Location: Oldenburg, Germany

#11 Post by Jörg Riesmeier »

Great work, but is the software also available for download.

Michael Onken
DCMTK Developer
Posts: 2048
Joined: Fri, 2004-11-05, 13:47
Location: Oldenburg, Germany
Contact:

#12 Post by Michael Onken »

Very nice, indeed :) Good idea with the konqueror plugin :P

Regards,
Michael

Spirit
Posts: 11
Joined: Mon, 2006-05-01, 18:13

Jörg Riesmeier please send me an e-mail

#13 Post by Spirit »

Hallo Jörg,

Could you please send me your e-mail address to me? jwaal@kabelfoon.nl.

Thanks.

Tsunamis
Posts: 31
Joined: Wed, 2005-05-04, 17:07

#14 Post by Tsunamis »

Hey Spirit,
When I look at your perf ratings I'm a bit puzzled...
I did my own dicomdir usage object and perf is really bad :
For 20 patients, i get around 20 secs to write a 21th.
I may miss some things...
I use the DicomDirInterface to take advantage of the addFile method.
I also need to specify specific tags so i need to reparse my dicom dir file.
This time i use a DcmDirectoryRecord object and manually look for correct record (parse all patients, then studies then series) to get the serie i need and add required information (by example body part examined)

This takes really a long time and I would be really happy to see it shortened by half ^^
Last edited by Tsunamis on Tue, 2011-02-01, 14:40, edited 2 times in total.

Spirit
Posts: 11
Joined: Mon, 2006-05-01, 18:13

#15 Post by Spirit »

Hello Tsunamis,

Currently I only parse an existing dicomdir. To do that you need to go through the records of the DICOMDIR. The next part are some lines from one of the widgets. I guess further explanation is not needed. I like know what you're findings are concerning the speed. Success!

Kind regards,

Spirit

Code: Select all

bool QDicomDirWidget::loadFile(const QString& filename)
{
 
  DcmDicomDir dicomdir(filename);
  if(dicomdir.error().bad())
  {
    return false;
  }
  DcmDirectoryRecord* root = &(dicomdir.getRootRecord());

  for(DcmDirectoryRecord* patient = root->nextSub(NULL); patient != NULL; patient = root->nextSub(patient))
  {
    const char *value;
    patient->findAndGetString (DCM_PatientsName, value);

    for(DcmDirectoryRecord* study = patient->nextSub(NULL); study != NULL; study = patient->nextSub(study))
    {
      study->findAndGetString (DCM_StudyDate, value);
      for(DcmDirectoryRecord* series = study->nextSub(NULL); series != NULL; series = study->nextSub(series))
      {
        series->findAndGetString (DCM_Modality, value);

        for(DcmDirectoryRecord* images = series->nextSub(NULL); images != NULL; images = series->nextSub(images))
        {
          images->findAndGetString(DCM_ImageType, value);
        }
      }
    }
  }
  return true;
}

Post Reply

Who is online

Users browsing this forum: Ahrefs [Bot], Bing [Bot], Google [Bot] and 1 guest