png to dicom

All other questions regarding DCMTK

Moderator: Moderator Team

Post Reply
Message
Author
w.bahadoer
Posts: 27
Joined: Mon, 2020-01-20, 11:36

png to dicom

#1 Post by w.bahadoer »

Hi,

I'm trying to read a png encoded image in memory and create a dicom file with it. I have scoured the internet and this board for a proper example but no success so I tried to break down the problem and to see if I can get it to work from a file on disk....also not so successful. The DICOM file gets created and I can upload it to the PACS/DICOM server but when I view the image in the Osimis Webviewer I see garbage. In the code below I have left out the irrelevant code lines.

Code: Select all

    std::ifstream file;
    file.open("c:\\temp\\image.png", std::ios::in | std::ios::binary);
    char* data = 0;

    file.seekg(0, std::ios::end);
    size_t size = file.tellg();
    file.seekg(0, std::ios::beg);

    data = new char[size - 8 + 1];
    file.seekg(8); 
    file.read(data, size - 8);
    file.close();
    data[size] = '\0';
    
    char uid[100];

    DcmFileFormat fileformat;

    DcmDataset *dataset = fileformat.getDataset();
        
    dataset->putAndInsertString(DCM_SOPClassUID, UID_OphthalmicPhotography8BitImageStorage);
    dataset->putAndInsertUint16(DCM_SamplesPerPixel, 1);
    dataset->putAndInsertString(DCM_PhotometricInterpretation, "MONOCHROME1");
    dataset->putAndInsertUint16(DCM_Rows, 1754 );
    dataset->putAndInsertUint16(DCM_Columns, 2152 );
    dataset->putAndInsertUint16(DCM_BitsAllocated, 8);
    dataset->putAndInsertUint16(DCM_BitsStored, 8);
    dataset->putAndInsertUint16(DCM_HighBit, 7);
    dataset->putAndInsertUint16(DCM_PixelRepresentation, 0);
    dataset->putAndInsertUint8Array(DCM_PixelData, (Uint8*)data, (ulong)(size));
    
    OFCondition status = fileformat.saveFile("c:\\temp\\test.dcm", EXS_LittleEndianExplicit);

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

Re: png to dicom

#2 Post by Michael Onken »

Hi,

I dont know the PNG format in detail, but it's not possible to simply copy a PNG bitstream to a DICOM dataset's Pixel Data element and create a valie DICOM image from it.

DCMTK (if this is part of your question?) has a tool called img2dcm that creates DICOM files from JPEG and BMP. Probably you can decompress your PNG and bring it into BMP format, for example, and then use the img2dcm code to make valid DICOM image out of it. JPEG and BMP handling is handled in plugins, so you could also write a plugin that handles PNG as input format directly.

Best regards,
Michael

w.bahadoer
Posts: 27
Joined: Mon, 2020-01-20, 11:36

Re: png to dicom

#3 Post by w.bahadoer »

Michael, thanks for your response.

what if I convert my png to jpg ( in memory ) will that work with the current state of dcmtk and the jpeg plugin? The tool img2dcm is an application and I want everything done in my C++ code without invoking external processes. Once I have a proof of concept that it works, I might look in to building a png plugin.

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

Re: png to dicom

#4 Post by Michael Onken »

Hi,

yes, I think it should work in memory, though image2dcm and the underlying classes (as the input plugins) usually work on files and therefore the I2DJpegSource plugin might need adaptations (i.e. derive from it or in the worst case copy it and modify methods as necessary).

The general flow is that you instantiate class Image2Dcm and call convert() on it. convert() requires you to hand in input and output plugin. Hand in your own derived JPEG plugin (see above) that uses pixel data from memory instead of from file and you should be fine.

Best regards,
Michael

w.bahadoer
Posts: 27
Joined: Mon, 2020-01-20, 11:36

Re: png to dicom

#5 Post by w.bahadoer »

Michael,

I have followed your advise but I think I'm doing something wrong

Code: Select all

       
        Image2Dcm i2d;
        I2DOutputPlug *outPlug = new I2DOutputPlugSC();
        I2DImgSource *inputPlug = new I2DJpegSource();
        E_TransferSyntax writeXfer;
        inputPlug->setImageFile( std::string( "c:\\temp\\imagejpg.jpg" ));
        OFCondition result = i2d.convert(inputPlug, outPlug, dataset, writeXfer);

        if( !result.good() )
               return false;
               
        OFCondition status = fileformat.saveFile("c:\\temp\\test.dcm", EXS_LittleEndianExplicit);

    	if (status.bad())
      		std::cout << "Error: cannot write DICOM file (" << status.text() << ")" << std::endl;
OFCondition result is OK but my dicom file is 1kb, so no image data. If I use the command line tool img2dcm.exe with this image I get a correct dicom file. Can you tell me what I'm doing wrong?

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

Re: png to dicom

#6 Post by Michael Onken »

Hi,

in saveFile(), use writeXfer (returned from convert()) instead of EXS_LittleEndianExplicit.

Best regards,
Michael

w.bahadoer
Posts: 27
Joined: Mon, 2020-01-20, 11:36

Re: png to dicom

#7 Post by w.bahadoer »

I tried the writeXfer parameter ( value is by the way : EXS_JPEGProcess1 ) in the savefile function instead of EXS_LittleEndianExplicit but my dicom file is still 1 kb. I also tried to compress the dataset AFTER the covert by

Code: Select all

dataset->chooseRepresentation( EXS_JPEGProcess1, &params )  
but that didn't help. This is my output when I run img2dcm on the command line ( hope it helps us ) :

Code: Select all

c:\Development\dcmtk\build\bin>img2dcm.exe -v -d c:\temp\imagejpg.jpg c:\temp\test.dcm
D: I2DJpegSource: Plugin instantiated
I: img2dcm: Instantiated input plugin: JPEG
D: I2DOutputPlugSC: Output plugin for Secondary Capture initialized
I: img2dcm: Instantiated output plugin: Secondary Capture Image SOP Class
I: img2dcm: Starting image conversion
D: Image2Dcm: Starting conversion of file: c:\temp\imagejpg.jpg
D: Image2Dcm: Generate and insert new UIDs if necessary
D: I2DJpegSource: Importing JPEG pixel data
D: I2DJpegSource: Opening JPEG file: c:\temp\imagejpg.jpg
D: I2DJpegSource: Examing JPEG file and creating map of JPEG markers
D: I2DJpegSource: Dumping JPEG marker file map:
D: I2DJpegSource:   Byte Position: 0x00000002 | Marker: SOI: Start of image
D: I2DJpegSource:   Byte Position: 0x00000004 | Marker: APPn: Application segment
D: I2DJpegSource:   Byte Position: 0x00000016 | Marker: DQT: Quantization table(s)
D: I2DJpegSource:   Byte Position: 0x0000005b | Marker: SOF0: Baseline DCT
D: I2DJpegSource:   Byte Position: 0x00000068 | Marker: DHT: Huffman table(s)
D: I2DJpegSource:   Byte Position: 0x00000086 | Marker: DHT: Huffman table(s)
D: I2DJpegSource:   Byte Position: 0x000000dc | Marker: SOS: Start of scan
D: I2DJpegSource:   Byte Position: 0x000366ad | Marker: EOI: End of image
D: I2DJpegSource: Checking whether JPEG encoding is supported
D: I2DJpegSource:   Encoding: SOF0: Baseline DCT
D: I2DJpegSource: Examining JPEG SOF image parameters
D: I2DJpegSource: Dumping JPEG SOF image parameters:
D: I2DJpegSource:   Image Width: 2152
D: I2DJpegSource:   Image Height: 1754
D: I2DJpegSource:   Number of Components: 1
D: I2DJpegSource:   Data Precision: 8
D: I2DJpegSource: Examining JFIF information
D: I2DJpegSource: Dumping some JFIF image parameters:
D: I2DJpegSource:   JFIF version: 257
D: I2DJpegSource:   Horizontal Pixel Aspect Ratio 96
D: I2DJpegSource:   Vertical Pixel Aspect Ratio: 96
D: I2DJpegSource:   Units: 1
D: I2DJpegSource: Extracting JPEG data from JPEG file
D: I2DJpegSource: Skipping application segment APP0
D: Image2Dcm: Storing imported pixel data to DICOM file
D: Image2Dcm: Inserting Image Pixel module information
D: I2DOutputPlugSC: Inserting SC specific attributes
D: Image2Dcm: Checking validity of DICOM output dataset
D: Image2Dcm: Inserting missing type 2 attribute: PatientName
D: Image2Dcm: Inserting missing type 2 attribute: PatientSex
D: Image2Dcm: Inserting missing type 2 attribute: PatientBirthDate
D: Image2Dcm: Inserting missing type 2 attribute: PatientID
D: Image2Dcm: Inserting missing type 2 attribute: StudyDate
D: Image2Dcm: Inserting missing type 2 attribute: StudyTime
D: Image2Dcm: Inserting missing type 2 attribute: ReferringPhysicianName
D: Image2Dcm: Inserting missing type 2 attribute: StudyID
D: Image2Dcm: Inserting missing type 2 attribute: AccessionNumber
D: Image2Dcm: Inserting missing type 2 attribute: SeriesNumber
D: Image2Dcm: Inserting missing type 2 attribute: InstanceNumber
D: Image2Dcm: Inserting missing type 2 attribute: PatientOrientation
D: I2DOutputPlugSC: Checking SC specific attributes
D: I2DOutputPlug: Inserting missing type 1 attribute: ConversionType with value WSD
I: img2dcm: Saving output DICOM to file c:\temp\test.dcm
D: DcmFileFormat::checkMetaHeaderValue() Version of MetaHeader is ok: 0x0001
D: DcmFileFormat::checkMetaHeaderValue() use SOPClassUID [1.2.840.10008.5.1.4.1.1.7] from Dataset
D: DcmFileFormat::checkMetaHeaderValue() use SOPInstanceUID [1.2.276.0.7230010.3.1.4.3741632033.4616.1579863635.278] from Dataset
D: DcmFileFormat::checkMetaHeaderValue() use new TransferSyntaxUID [JPEG Baseline] on writing following Dataset
D: DcmFileFormat::validateMetaInfo() found 7 Elements in DcmMetaInfo 'metinf'
D: I2DJpegSource: Closing JPEG file and cleaning up memory
D: Freeing memory

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

Re: png to dicom

#8 Post by Michael Onken »

Hi,

do you actually feed your "dataset" into the "fileformat"? Otherwise you will write a file without dataset.
See how it's done in img2dcm. Applied to your code this would be something like:

Code: Select all

Image2Dcm i2d;
I2DOutputPlug *outPlug = new I2DOutputPlugSC();
I2DImgSource *inputPlug = new I2DJpegSource();
E_TransferSyntax writeXfer;
inputPlug->setImageFile( std::string( "c:\\temp\\imagejpg.jpg" ));
DcmDataset* dataset = NULL;
OFCondition result = i2d.convert(inputPlug, outPlug, dataset, writeXfer);

if( !result.good() )
       return false;
       
DcmFileFormat fileformat(dataset); // Feed into fileformat!
OFCondition status = fileformat.saveFile("c:\\temp\\test.dcm", writeXfer);  // Use JPEG transfersyntax

if (status.bad())
	std::cout << "Error: cannot write DICOM file (" << status.text() << ")" << std::endl:

w.bahadoer
Posts: 27
Joined: Mon, 2020-01-20, 11:36

Re: png to dicom

#9 Post by w.bahadoer »

Hi, yes I do with this line

Code: Select all

    DcmFileFormat fileformat;
    DcmDataset *dataset = fileformat.getDataset();
but what I also found out is that "size" is just 4 in the code below, that might be the cause but I don't understand why ( code is not nicely written but bare with me please )

Code: Select all

    Image2Dcm i2d;
    I2DImgSource *inputPlug = new I2DJpegSource();
    inputPlug->setImageFile( std::string( "c:\\temp\\imagejpg.jpg" ));

    OFCondition result;
    Uint16 rows,cols,sampPPix,bitsAll, bitsStored,highbit, pixrepr, planconf, pixasph, pixaspv;
    Uint32 length;
    std::string photoMetrInt;
    char* pixdata = new char();
    E_TransferSyntax transyntax;
    result = inputPlug->readPixelData(rows, cols, sampPPix,photoMetrInt, bitsAll, bitsStored, highbit, pixrepr, planconf, pixasph, pixaspv, pixdata, length, transyntax);

    size_t size = std::strlen( pixdata );

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

Re: png to dicom

#10 Post by Michael Onken »

Please use the dataset/file format variant that I posted instead of yours,i.e. in principle

Code: Select all

DcmDataset *dataset = NULL;
convert(...);
DcmFileFormt fileformat(dataset);

The convert() call returns a newly allocated dataset, and does not use the empty one (created by DcmFileFormat internally) that you provide in your version of the code.

Also, pixel data can contain \0 bytes, so using strlen doesnt seem to be a good idea to me. The value you look for (number of bytes in "pixdata") should be in "length" aftr calling readPixelData().

HTH,
Michael

w.bahadoer
Posts: 27
Joined: Mon, 2020-01-20, 11:36

Re: png to dicom

#11 Post by w.bahadoer »

Michael, thanks that did the trick. Is this designed behavior or a bug ? I would expect that if you have a pointer to a DcmDataset object and pass it to convert() it would modify the object pointed to not completely reset it.... right??

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

Re: png to dicom

#12 Post by Michael Onken »

Great :)

I consider the allocation in convert() a feature :) Since the parameter is a reference to a pointer, this is usually a sufficient note to the user that the method returns a new pointer for newly allocated memory. There is no reason to make it a reference if one does not return a new pointer.

I added a short note to the existing API documentation of convert() that hopefully makes it more obvious. The related commit will be online within the next days.

Best regards,
Michael

w.bahadoer
Posts: 27
Joined: Mon, 2020-01-20, 11:36

Re: png to dicom

#13 Post by w.bahadoer »

sorry I meant in my previous post a reference to a pointer ( instead of just pointer ) as the function signature illustrates. But I agree with you :D

Post Reply

Who is online

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