img2dcm fail with converted images

All other questions regarding DCMTK

Moderator: Moderator Team

Post Reply
Message
Author
George
Posts: 50
Joined: Sun, 2023-11-12, 16:50

img2dcm fail with converted images

#1 Post by George »

Hello,

I have used convert (image magic library) to convert between a 24 bit depth bmp to 16 bit depth bmp.
but when I use img2dcm it complains that this newly converted file is not recognized, I faced this with freeimage too.
Is there any solution for this because I have a need to be able to be very accurate on extracting and restoring the image.

Best Regards
George

George
Posts: 50
Joined: Sun, 2023-11-12, 16:50

Re: img2dcm fail with converted images

#2 Post by George »

Hello,

I went over and taught about this problem, I think the only way to solve it is to manually create this code by using the function writeFrameToDataset
it would be nice to give me an example to make it work, because I am new to this

Best Regards
George

George
Posts: 50
Joined: Sun, 2023-11-12, 16:50

Re: img2dcm fail with converted images

#3 Post by George »

Hello,

I am very intrested in a hand, I am trying to use this code as a png plugin to support 16 bit but it seems that the generated image size is 3 times larger than the image used with img2dcm

I am sharing the plug in code

Code: Select all

#include "dcmtk/config/osconfig.h"
#include "dcmtk/dcmdata/libi2d/i2dpngs.h"
#include "dcmtk/ofstd/ofstd.h"
#include "png.h"

I2DPngSource::I2DPngSource() {
}

I2DPngSource::~I2DPngSource() {
}

OFString I2DPngSource::inputFormat() const {
    return "PNG";
}

OFCondition I2DPngSource::readPixelData(Uint16& rows, Uint16& cols, Uint16& samplesPerPixel, OFString& photoMetrInt, Uint16& bitsAlloc, Uint16& bitsStored, Uint16& highBit, Uint16& pixelRepr, Uint16& planConf, Uint16& pixAspectH, Uint16& pixAspectV, char*& pixData, Uint32& length, E_TransferSyntax& ts) 
{
     DCMDATA_LIBI2D_DEBUG("I2DJpegSource: Importing JPEG pixel data, m_imageFile= ", this->getImageFile());

    FILE* fp = fopen(m_imageFile.c_str(), "rb");
    if (!fp) return EC_FileReadError;

    png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
    if (!png_ptr) {
        fclose(fp);
        return EC_MemoryExhausted;
    }

    png_infop info_ptr = png_create_info_struct(png_ptr);
    if (!info_ptr) {
        png_destroy_read_struct(&png_ptr, NULL, NULL);
        fclose(fp);
        return EC_MemoryExhausted;
    }

    if (setjmp(png_jmpbuf(png_ptr))) {
        png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
        fclose(fp);
        return EC_InvalidStream;
    }

    png_init_io(png_ptr, fp);
    png_read_info(png_ptr, info_ptr);

    cols = png_get_image_width(png_ptr, info_ptr);
    rows = png_get_image_height(png_ptr, info_ptr);
    bitsAlloc = png_get_bit_depth(png_ptr, info_ptr) * png_get_channels(png_ptr, info_ptr);
    bitsStored = bitsAlloc;
    highBit = bitsStored - 1;
    pixelRepr = 0;
    samplesPerPixel = png_get_channels(png_ptr, info_ptr);
    photoMetrInt = (samplesPerPixel == 1) ? "MONOCHROME1" : "RGB";
    planConf = (samplesPerPixel > 1) ? 0 : 1;
    pixAspectH = pixAspectV = 1; // PNG does not inherently contain pixel aspect ratio
    ts = EXS_LittleEndianExplicit; // Default transfer syntax

    length = rows * cols * samplesPerPixel * (bitsAlloc / 8);
    png_bytep* row_pointers = new png_bytep[rows];
    pixData = new char[length];
    for (size_t i = 0; i < rows; i++) {
        row_pointers[i] = reinterpret_cast<png_bytep>(pixData + i * cols * samplesPerPixel * (bitsAlloc / 8));
    }

    png_read_image(png_ptr, row_pointers);
    png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
    fclose(fp);
    delete[] row_pointers;

    return EC_Normal;
}

OFCondition I2DPngSource::getLossyComprInfo(OFBool& srcEncodingLossy, OFString& srcLossyComprMethod) const {
    srcEncodingLossy = OFFalse; // PNG compression is lossless
    srcLossyComprMethod.clear();
    return EC_Normal;
}

void I2DPngSource::setImageFile(const OFString& filename) {
    m_imageFile = filename;
}

OFString I2DPngSource::getImageFile() {
    return m_imageFile;
}



Take a look at the difference:

$ ll out.dcm
-rw-rw-r-- 1 george_z george_z 30131988 Mar 24 13:19 out.dcm
$ ll frame_0.png
-rw-rw-r-- 1 george_z george_z 10070288 Mar 21 17:39 frame_0.png

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

Re: img2dcm fail with converted images

#4 Post by Marco Eichelberg »

That is probably correct. PNG is a compressed image format, DICOM is uncompressed. So the DICOM image file will necessarily be larger than the PNG file. A compression ratio of 3:1 sounds plausible.

George
Posts: 50
Joined: Sun, 2023-11-12, 16:50

Re: img2dcm fail with converted images

#5 Post by George »

Hello,

I cannot afford this ratio, but I was wondering if this can happen with ppm or pgm or jpeg2000 lossless.
Is there any way to control the ratio?

Best Regards
George

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

Re: img2dcm fail with converted images

#6 Post by Marco Eichelberg »

Once you have converted the image to uncompressed DICOM, you can use one of the lossless compression schemes available in DCMTK to apply an image compression, e.g. check the "dcmcjpeg" and "dcmcjpls" tools.
Note that it is NOT possible to convert a PNG image to DICOM without decompression/recompression since the compressed PNG bitstream is not DICOM compliant.

George
Posts: 50
Joined: Sun, 2023-11-12, 16:50

Re: img2dcm fail with converted images

#7 Post by George »

Hello,

I have added implementation for pgm images as a way to add support for 16bit images

but I get a pale black image instead, I am currently trying to investage the issue but it would be nice to help me find the issue.

These are the two documents in which I extract the image from and create it back to out.dcm.

https://drive.google.com/file/d/1ot_mIU ... sp=sharing --> out.dcm
https://drive.google.com/file/d/1qNfDYk ... drive_link --> extracted image that was used to create out.dcm and extracted from original dicom
https://drive.google.com/file/d/19D6sH3 ... sp=sharing --> original dicom

The used code:

Code: Select all

#include <fstream>
#include <vector>
#include <cmath>
#include <string>

#include "i2dpgm.h"

    I2DPGMSource::I2DPGMSource() : pixData(nullptr) {}
    I2DPGMSource::~I2DPGMSource() { delete[] pixData; }

    OFCondition I2DPGMSource::openFile(const OFString& filename) {
        this->filename = filename;
        return EC_Normal;
    }

    void I2DPGMSource::closeFile() {}

    OFString I2DPGMSource::inputFormat() const {
        return "PGM";
    }

    OFCondition I2DPGMSource::readPixelData(Uint16& rows, Uint16& cols, Uint16& samplesPerPixel, OFString& photoMetrInt,
                              Uint16& bitsAlloc, Uint16& bitsStored, Uint16& highBit, Uint16& pixelRepr,
                              Uint16& planConf, Uint16& pixAspectH, Uint16& pixAspectV, char*& pixData,
                              Uint32& length, E_TransferSyntax& ts) {
        std::ifstream pgmFile(this->filename, std::ios::binary);
        if (!pgmFile.is_open()) {
            return EC_InvalidStream;
        }

        std::string header;
        std::getline(pgmFile, header);
        if (header != "P2" && header != "P5") {
            return EC_IllegalParameter;
        }

        while (pgmFile.peek() == '#') {
            std::getline(pgmFile, header);
        }

        pgmFile >> cols >> rows;
        int maxVal;
        pgmFile >> maxVal;
        pgmFile.ignore();

        bitsStored = static_cast<Uint16>(ceil(log2(maxVal + 1)));
        highBit = bitsStored - 1;
        bitsAlloc = bitsStored <= 8 ? 8 : 16;

        samplesPerPixel = 1;
        photoMetrInt = "MONOCHROME2";
        pixelRepr = 0;
        planConf = 0;
        pixAspectH = pixAspectV = 1;
        ts = EXS_LittleEndianExplicit;

        length = rows * cols * samplesPerPixel * (bitsAlloc / 8);
        this->pixData = new char[length];

        if (header == "P2") {
            std::vector<Uint16> pixels;
            Uint16 pixelValue;
            while (pixels.size() < rows * cols * samplesPerPixel && pgmFile >> pixelValue) {
                pixels.push_back(pixelValue);
            }

            if (bitsAlloc == 8) {
                for (size_t i = 0; i < pixels.size(); ++i) {
                    this->pixData[i] = static_cast<char>(pixels[i]);
                }
            } else {
                auto *pixData16 = reinterpret_cast<Uint16*>(this->pixData);
                for (size_t i = 0; i < pixels.size(); ++i) {
                    pixData16[i] = pixels[i];
                }
            }
        } else {
            pgmFile.read(this->pixData, length);
            if (pgmFile.gcount() != static_cast<std::streamsize>(length)) {
                delete[] this->pixData;
                this->pixData = nullptr;
                return EC_CorruptedData;
            }
        }

        pixData = this->pixData;
        return EC_Normal;
    }

    // Implement the pure virtual function from I2DImgSource
    OFCondition I2DPGMSource::getLossyComprInfo(OFBool& isLossy, OFString& method) const override {
        // Assuming PGM format does not support lossy compression
        isLossy = OFFalse;
        method.clear();  // No compression method used
        return EC_Normal;
    }

If I try to decompress the image of PNG will this assist anything? will this solve the problem

I would humbly also add that I tried to use different images that have a better SOP but still the image is always printed as pale

Post Reply

Who is online

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