0

I am new at DICOM and DCMTK. I have a set of BITMAP Ultrasound data that I would like to save as a lossless DICOM file.

After loadding the data in a queue frame_queue and setting all the mandatory DICOM necessary parameters for Ultrasound Multiframe images I realise a loop to add the images in the DcmDataset as follows:

std::unique_ptr<DcmDataset> dataset;

/*** 
DICOM parameters A setted not added here as many parameters are set
***/


std::shared_ptr<unsigned char[]> frame;
std::shared_ptr<RGBQUAD[]> colorTable;

DJEncoderRegistration::registerCodecs();
DJ_RPLossless params; // codec parameters, we use the defaults
// this causes the lossless JPEG version of the m_dataset to be created
    
/**/
E_TransferSyntax xfer = DcmXfer(EXS_JPEGProcess14).getXfer();
DcmPixelSequence* sequence = new DcmPixelSequence(DcmTag(DCM_PixelData, EVR_OB));
DcmPixelData* newPixelData = new DcmPixelData(DCM_PixelData);
dataset->insert(sequence, OFTrue);

OFstatic_cast(DcmPixelData*, newPixelData)->putOriginalRepresentation(xfer, NULL, sequence);

while (frame_queue.popFrame(frame, colorTable)) // we extract a new frame from the queue
{
    DcmPixelItem* newItem = new DcmPixelItem(DcmTag(DCM_Item, EVR_OB));
    if (newItem != NULL)
    {
        sequence->insert(newItem);
        /* put pixel data into the item */
        std::shared_ptr<Uint8[]> newDCMFrame;
        
        colorTableToRGB(frame, colorTable, newDCMFrame, m_frameWidth, m_frameHeight);
        result = newItem->putUint8Array((Uint8*)newDCMFrame.get(), m_frameHeight * m_frameWidth * 3).good(); // this returns true in my test
        frame.reset();
        colorTable.reset();
    }
}

dataset->chooseRepresentation(EXS_JPEGProcess14, &params); 
dataset->saveFile(path.c_str());

The call of dataset->chooseRepresentation(EXS_JPEGProcess14, &params); raises the error:

DcmDataset: Wrong class for pixel data element, cannot change representation

A DICOM File is saved but it is empty.

Does anyone know what's causing this error?

2 Answers 2

1

Just a guess but you are creating the dataset in uncompressed representation. In this case, the pixel data must NOT be represented as a sequence of frames. You should try calling putUint8Array() directly on the DcmPixelData element instead of inserting sequences to it.

DCMTK will change that way of encoding to the one you want to achieve directly, when you call DcmDataset::chooseRepresentation().

For uncompressed pixel data, it is not allowed to used the "encapsulated" pixel data encoding with sequences, and I suspect that this is why DcmDataset::chooseRepresentation() complains about "not finding what was expected".

12
  • Thanks kritzel_sw, I will look into that and be back but one first remarque, It was under my understanding the putUint8Array() was used only for a single frame as calling it multiple times would erase the previous image. Or I should give the buffer containing all frames once? link
    – firehelm
    Commented Jul 19, 2021 at 13:41
  • 1
    @firehelm: True. One buffer for all frames would be one possible solution. If you are worried about memory consumption, there is also a possibility to assign and attribute's value chunk by chunk (in case of pixel data: frame by frame) from a DcmInputStream. Commented Jul 19, 2021 at 14:18
  • Memory should not be a problem as I devide a long stream into subfiles so I control the number of images present in the buffer. Now if I create a single DICOM buffer, I am guessing that I should copy the images in an object as Uint8[imageSize * nbFrames] imagesBuffer and then use an object DcmPixelData* dcmData and add the frames as result = dcmData->putUint8Array(imagesBuffer, imageSize * nbFrames).good();? But in this case how can dcmtk define a separation between the frames?
    – firehelm
    Commented Jul 19, 2021 at 14:37
  • yes, exactly. a separation between frames is only needed in an encapsulated representation. For uncompressed multi-frame data, the size of a frame is defined by the corresponding header elements: Rows, Columns, BitsAllocated and (for color images) SamplesPerPixel Commented Jul 19, 2021 at 14:45
  • So I think this is going on a good path for now as the new error is True lossless encoder: Unable to get relevant attributes from dataset I am guessing that some DICOM Tags are missing, I am scanning through tags from other Multiframe US that I have to see which ones are missing and found (0028,0004) CS RGB # 1, 4 Photometric Interpretation looking into this I saw that RGB can be replaced by PALETTE COLOR. I am guessing then that I can avoid converting my palette color images to RGB images.
    – firehelm
    Commented Jul 20, 2021 at 9:12
1

So I finaly got it to work:

std::unique_ptr<DcmDataset> dataset;

/*** 
DICOM parameters A setted not added here as many parameters are set
***/


std::shared_ptr<unsigned char[]> frame;
std::shared_ptr<RGBQUAD[]> colorTable;

DJEncoderRegistration::registerCodecs();
DJ_RPLossless params; // codec parameters, we use the defaults
// this causes the lossless JPEG version of the m_dataset to be created
    
/**/
E_TransferSyntax xfer = DcmXfer(EXS_JPEGProcess14).getXfer();

DcmPixelData* newPixelData = new DcmPixelData(DCM_PixelData);
dataset->insert(newPixelData , OFTrue);


bool result = true;
static const int imageSize = m_frameWidth * m_frameHeight * 3;
std::shared_ptr<Uint8[]> imagesBufferPtr(new Uint8[imageSize * m_nbFrames], array_deleter<Uint8>());

std::vector<std::vector<Uint8>> dataImages(m_nbFrames, std::vector<Uint8>(imageSize));

int frameId = 0;

while (this->popFrame(frame, colorTable) == cwlibdicom::frameBufferStatus::BUFFER_SUCCESS && result)
{
    Uint8 *img = imagesBufferPtr.get();
    img = &img[frameId];
    colorTableToRGB(frame, colorTable, img, m_frameWidth, m_frameHeight);

    std::memcpy(&imagesBufferPtr.get()[frameId * imageSize], img.data(), imageSize);

    frameId++;
    frame.reset();
    colorTable.reset();
}
result = newPixelData->putUint8Array(imagesBufferPtr.get(), imageSize * m_nbFrames).good();
header->setProperty(DCM_NumberOfFrames, nb_frame);
header->setProperty(DCM_SamplesPerPixel, "3");
header->setProperty(DCM_PhotometricInterpretation, "RGB");
header->setProperty(DCM_Rows, m_imageHeight);
header->setProperty(DCM_Cols, m_imageWidth);
header->setProperty(DCM_BitsAllocated, "8");
header->setProperty(DCM_BitsStored, "8");
header->setProperty(DCM_HighBit, "7");
header->setProperty(DCM_PixelRepresentation, "0");
header->setProperty(DCM_FrameIncrementPointer, "(0018, 1065)\\(F9E4, 3317)"); // Found this in a multiframe image that already existed.

dataset->chooseRepresentation(EXS_JPEGProcess14, &params); 
dataset->saveFile(path.c_str(), EXS_JPEGProcess14); 

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.