Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

in JPEG2000, CompressionRatioFactor is different in the image #29

Open
sylvain-rouquette opened this issue Jun 6, 2024 · 4 comments
Open

Comments

@sylvain-rouquette
Copy link

problem:

  • compress a dicom image with org.dcm4che3.img.Transcoder.dcm2dcm in JPEG2000 (1.2.840.10008.1.2.4.91)
  • check dicom tag LossyImageCompressionRatio

result:
default value for CompressionRatioFactor is 10
LossyImageCompressionRatio is 13.332180546726

expected:
LossyImageCompressionRatio should be 10

@nroduit
Copy link
Owner

nroduit commented Jun 9, 2024

The ratio is recalculated from the result, see this code.

I guess the result differs in openJPEG due to the wavelet decomposition and an approximation to obtain a certain number of bytes, see the documentation https://github.com/uclouvain/openjpeg/wiki/DocJ2KCodec

@sylvain-rouquette
Copy link
Author

The problem lies in how you compute the compressed length:

int compressedLength = buf.width() * buf.height() * (int) buf.elemSize();

You seem to assume that the size of the buffer didn't change, and that the size of each element changed, when in fact it's both. You should compute the ratio based on the size of the buffer in bytes.

def in_place_compress_file_data_set(ds: FileDataset,
                                    c_ratio: float = 14,
                                    buff_size: int = int(3e5)) -> None:
    pixel_array = ds.pixel_array
    # Compression
    mem_buffer = np.zeros(buff_size).astype(np.uint8)
    comp = MemJ2K(mem_buffer, data=pixel_array, c_ratio=c_ratio)
    compressed_pixel_data = mem_buffer[:comp.mem_pos].tobytes()
    original_size = pixel_array.nbytes
    compressed_size = len(compressed_pixel_data)
    compression_ratio = original_size / compressed_size
    # -------------------------
    ds.PixelData = pydicom.encaps.encapsulate([compressed_pixel_data])
    ds.file_meta.TransferSyntaxUID = pydicom.uid.JPEG2000
    ds.add_new((0x0028, 0x2110), 'CS', '01')
    ds.add_new((0x0028, 0x2112), 'DS', str(np.float16(compression_ratio)))
    ds.add_new((0x0028, 0x2114), 'CS', "ISO_15444_1")
    ds.SOPInstanceUID = pydicom.uid.generate_uid()
    ds.is_implicit_VR = False
    ds.is_little_endian = True

comp.mem_pos is the size of the compressed buffer.

@nroduit
Copy link
Owner

nroduit commented Jun 13, 2024

I'm not sure I follow the reasoning.
The compression ratio is defined by compressedLength, which are the compressed bytes, and uncompressed, which are the bytes of the raw image.

int compressedLength = buf.width() * buf.height() * (int) buf.elemSize();
pixel numbers => buf.width() * buf.height()
buf.elemSize() => number channels multiply by the size of pixel (1 for 8 bits, 2 for 16 bits)

For compressedLength, the Mat object is used only to store the compressed bytes. It is not an image, buf.elemSize() is always egual to 1.

@nroduit
Copy link
Owner

nroduit commented Jun 15, 2024

If you think something is wrong with the current code, please publish a PR.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants