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

It prompts Failed to read frame #18, when using highdicom to read the specified dicom file. #235

Open
Marsxpc opened this issue Jun 15, 2023 · 17 comments

Comments

@Marsxpc
Copy link

Marsxpc commented Jun 15, 2023

hello, you can reproduce this issue by using the script below:
(environment: windows system and highdicom 0.21.1)

from highdicom.io import ImageFileReader
from pydicom import dcmread, pixel_data_handlers
import numpy as np
import matplotlib.pyplot as plt
path = r'C:\Users\Desktop\XX000090'
frame_idx = 18
with ImageFileReader(path) as image_obj:
arr = image_obj.read_frame(frame_idx, correct_color=False)
plt.imshow(arr)
plt.show()

error message:
Failed to read frame #18. File "C:\Users\test.py", line 12, in
arr = image_obj.read_frame(frame_idx, correct_color=False)
File "C:\Users\AppData\Local\Programs\Python\Python39\lib\site-packages\highdicom\io.py", line 574, in read_frame
frame_data = self.read_frame_raw(index)
File "C:\Users\AppData\Local\Programs\Python\Python39\lib\site-packages\highdicom\io.py", line 545, in read_frame_raw
raise IOError(f'Failed to read frame #{index}.')

Note: Using pydicom to read this dicom file is OK

@Marsxpc
Copy link
Author

Marsxpc commented Jun 15, 2023

XX000090.zip

@CPBridge
Copy link
Collaborator

Hi @Marsxpc thanks for reporting this issue. I took a quick look and I while I can read the file ok on my linux machine, there is definitely something strange going on: the colours appear to be incorrect and there is some distortion of the frames. I'm not sure whether this is a problem with the library or whether the file is somehow corrupted. I will have to dig in further later.

Can you load the entire file correctly with pydicom (just access the pixel_array)? That would be a useful point of reference to figure out what's going on. Thanks

@CPBridge
Copy link
Collaborator

Ah sorry, I just noticed your comment about pydicom

@Marsxpc
Copy link
Author

Marsxpc commented Jun 17, 2023

Hi @CPBridge
First,
Let me talk about my test results, the following two pictures are the results when I set the frame_idx to 0 and 1
Second,
frame0 looks ok,after frame0, it looks abnormal. According to code "self._fp.seek(self._first_frame_offset + frame_offset, 0)".So, there may be a problem with the calculation of the frame_offset.
Third,
Based on what to set the colour of the image correctly?I think it's related to the value of tag(0028,0008), but this dicom file i provided contains tag(0088,0200), and also there is another tag(0028,0008) in it.
What are your thoughts?
frame_0
frame_1

@CPBridge
Copy link
Collaborator

CPBridge commented Jun 20, 2023

Hi @Marsxpc thanks for the further information, it will help me investigate further. I suspect that you are correct that this is related to an incorrect frame offset calculation. My results are similar to yours.

I think it is unlikely that this is related to the IconImageSequence, since the pixel data for the icon image is stored at a different nested level of the data and so shouldn't interfere with the "main" pixel data. But I will bear it in mind in case it is relevant.

I notice that the image is stored with photometric interpretation YBR_FULL_422 i.e. in YBR colorspace (see here), which explains the funky colors. Unfortunately, highdicom has no tools to convert this to RGB, so you will have to handle this conversion yourself some other way.

I will continue to look into the frame offset problem

@CPBridge
Copy link
Collaborator

P.s. what is the difference between your code that gets the IOError and the code that is able to read the images (with the strange frame offset)?

@Marsxpc
Copy link
Author

Marsxpc commented Jun 21, 2023

P.s. what is the difference between your code that gets the IOError and the code that is able to read the images (with the strange frame offset)?

like this way:Read all at once, then extract a certain frame
import pydicom
ds = pydicom.dcmread('multi_frame_dicom.dcm')
frame = ds.pixel_array[1]

@Marsxpc
Copy link
Author

Marsxpc commented Jun 21, 2023

Some dicom files that contain tag photometric interpretation 'YBR_FULL_422' have abnormal colors after conversion using this code:
if self.color_space == 'YBR_FULL' or 'YBR_FULL_422':
arr = pixel_data_handlers.util.convert_color_space(arr, current=self.color_space, desired='RGB')
For example this file attached
My project is to implement a dicom view tool, so, I want to know based on what to set the colour of the image correctly.

@Marsxpc
Copy link
Author

Marsxpc commented Jun 21, 2023

N3HAT0O2.zip

@Marsxpc
Copy link
Author

Marsxpc commented Jul 6, 2023

@CPBridge any progress on the investigation?

@CPBridge
Copy link
Collaborator

@Marsxpc after a little digging this turns out to be fairly simple, I think.

Your image uses YBR_FULL_422 photometric interpretation with native (uncompressed) pixels. This means that the two chrominance channels are subsampled by a factor of two relative to the overall image resolution. This is fairly unsual for native pixels as it is typically used with JPEG compression, in which case the JPEG decoder handles this for us. The bug is due to the fact that highdicom does not correctly account for the subsampling when calculating the number of bytes per pixel used to get the offset of a given frame in the PixelData array.

Would you please be able to test the fix on #242 for a few of your images and let us know this solves the issue?

@CPBridge
Copy link
Collaborator

Any news on whether #242 fixes this @Marsxpc ?

@Marsxpc
Copy link
Author

Marsxpc commented Jul 25, 2023

@CPBridge the offset is correct, the colour of image is wrong. I want to know based on which information to set the colour correctly. Do you know this?

@CPBridge
Copy link
Collaborator

@Marsxpc yes, as the photometric interpretation is YBR_FULL_422 the image is stored in YBR format and this is what is returned to you. If you want to convert to RGB, you can use Pillow to do that quite easily:

from PIL import Image
import numpy as np

# Assuming fm is a numpy array for a single frame, of shape (rows, columns, 3) 
im = Image.fromarray(fm, mode="YCbCr")
rgb = np.array(im.convert(mode="RGB"))

Note that I am not 100% sure that the YCbCr mode used by pillow exactly matches the definition of the YBR_FULL_422 photometric intperpretation used in DICOM. If you want to be completely accurate you may want to check this and/or implement the conversion yourself based on the definition used in the DICOM standard:

In the case where Bits Allocated (0028,0100) has a value of 8 then the following equations convert between RGB and YCBCR Photometric Interpretation.

Y = + .2990R + .5870G + .1140B

CB= - .1687R - .3313G + .5000B + 128

CR= + .5000R - .4187G - .0813B + 128

I will think about whether highdicom should maybe do this colour conversion for you in read_frame(), it is a somewhat tricky topic though. @hackermd I'm sure has a strong opinion on the matter

@hackermd
Copy link
Collaborator

It's a tricky one and I am not sure what the optimal behavior should be in this case.

I think we should differentiate between the photometric interpretation of encapsulated encoding and native encoding. In case of encapsulated encoding, the photometric interpretation refers to the compressed representation and the pixels should be converted to RGB upon decoding in my opinion (see pydicom/pydicom#1570 (comment)). I am not sure what to do in case of native encoding.

@dclunie what are your thoughts on this?

@dclunie
Copy link

dclunie commented Jul 26, 2023

Since this is a matter of API design and consistency, and there is no simple answer, I don't really have anything to add beyond what was already discussed in "pydicom/pydicom#1570". Certainly the user needs a means of having the pixels converted to the form they need for rendering (RGB +/- ICC profile application) but they may also need a means of getting the unconverted pixels as discussed in 1570, so whatever is the "default" it needs to be parameterized to allow the other.

@hackermd
Copy link
Collaborator

hackermd commented Aug 5, 2023

Asked differently, why are the US data natively encoded in YCbCr color space? Are the data supposed to be processed or displayed in that color space? What does the embedded input ICC profile say? My assumption was that the device-specific color space is per definition an RGB color space.

My personal preference for highdicom would be to convert color images into RGB color space. However, it raises questions about the appropriate input and output ICC profiles (we currently default to an output ICC profile that converts to sRGB).

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

No branches or pull requests

4 participants