-
Notifications
You must be signed in to change notification settings - Fork 290
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
fix(color): Fix RGB inconsistencies #829
fix(color): Fix RGB inconsistencies #829
Conversation
✅ Deploy Preview for cornerstone-3d-docs ready!
To edit notification comments on pull requests, go to your Netlify site configuration. |
This part of the code is very tricky to touch. Please explain the logic behind your addition |
As stated in the lines 21-28 of the same file, the purpose of the code is to transform from RGBA -> RGB, as the main job was already been done by the decompressors. So if the data is already in RGB (3 bytes per voxel), then there is no need to go further in the code. Also the code in here Lines 8 to 14 in b33eac9
|
I'm pretty certain this breaks a lot of other color images. It is not just RGBA -> RGB, it also handles color space conversions if (TRANSFER_SYNTAX_USING_PHOTOMETRIC_COLOR[transferSyntax]) {
canvas.height = imageFrame.rows;
canvas.width = imageFrame.columns;
const context = canvas.getContext('2d');
const imageData = context.createImageData(
imageFrame.columns,
imageFrame.rows
);
convertColorSpace(imageFrame, imageData.data, useRGBA);
imageFrame.imageData = imageData;
imageFrame.pixelData = imageData.data;
imageFrame.pixelDataLength = imageData.data.length;
} |
I see. But we need to make sure which conversions were completely handled by decompression. This particular color space, ybr full 422 was completely handled by the decompression and doesn't need any further processing |
Yeah i know exactly what you are talking about. I tried to find the correct condition, but everytime I tried something it failed something else. @wayfarer3130 maybe he has an idea of what is the best way to identify if something comes out of the decompresser as RGB |
What if for the moment we get the requirements for each conversion and only allow if the input is correct: for example ybr_full_422 expects two bytes data. What if we check that and if the requirements does match we allow proceed and if not we return the data as is? The only extepcting two byte data is the YBR_FULL_422, all others expects three byte data |
…one3D-beta into fix/RGBinconsistencies
Adde a new function that checks the requirements for color space convertions. It convers only one color space conversion for now, the YBR_FULL_422, as it tests if the voxel byte size is equal to 2. |
packages/dicomImageLoader/src/imageLoader/colorSpaceConverters/convertYBRFullByPixel.ts
Outdated
Show resolved
Hide resolved
packages/dicomImageLoader/src/imageLoader/colorSpaceConverters/convertYBRFullByPixel.ts
Outdated
Show resolved
Hide resolved
Changed as suggested |
Testing on https://www.cornerstonejs.org/live-examples/dicomimageloaderwadouri These two are broken which is not in main right now |
…one3D-beta into fix/RGBinconsistencies
Fixed the issue!!! |
packages/dicomImageLoader/src/imageLoader/isColorConversionRequirementsFulfilled.ts
Outdated
Show resolved
Hide resolved
* @param RGBA | ||
* @returns | ||
*/ | ||
export default function isColorConversionRequirementsFulfilled( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@wayfarer3130 Can you please look at this function? Thanks
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I will limit the checks to only YBR_FULL_422
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Limitting the checks to the types ending in _420 or _422 is sufficient, but I would also add an override to allow forcing the issue to return RGB if a flag is set and it starts with YBR - that will then automagically include things like:
YBR_ICT, YBR_RCT, YBR_PARTIAL_422
etc.
There are a few other retired PMI values such as HSV and CMYK, but I doubt you will ever see those.
Changed as suggested |
I don't think we need this after this PR can someone confirm? OHIF/Viewers#3883 |
@@ -6,10 +6,12 @@ export default function ( | |||
useRGBA: boolean | |||
): void { | |||
if (imageFrame === undefined) { | |||
throw new Error('decodeRGB: ybrBuffer must not be undefined'); | |||
throw new Error('convertYBRFull422ByPixel: ybrBuffer must be defined'); | |||
} | |||
if (imageFrame.length % 2 !== 0) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please add a comment here that the length is divisible by 2 because for 4 pixels (a 2x2 array), there are 4 y and 2 b and 2 r values, so 4+2+2 / 4 = 2 bytes per pixel
packages/dicomImageLoader/src/imageLoader/isColorConversionRequirementsFulfilled.ts
Outdated
Show resolved
Hide resolved
packages/dicomImageLoader/src/imageLoader/isColorConversionRequirementsFulfilled.ts
Outdated
Show resolved
Hide resolved
return false; | ||
} | ||
const { rows, columns } = imageFrame; | ||
if (imageFrame.photometricInterpretation === 'YBR_FULL_422') { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can check endsWith 422 or endsWIth 420 to support all the test values we can check.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
as we talked to Bill
the formula should be edited to
for ybr_Full_422
ybr y ybr y ybr .... (ybr or y)
y y y y y ........ y
..
..
..
(last row)
ybr y ybr y ybr y ... (ybr or y)
OR
y y y y y y
-> 3 * ceil(cols/2) + florr(cols/2) * ceil(rows/2) + cols * floor(rows/2)
number of ybr y ybr y ybr y elements
ybr full 420
ybr y ybr y ybr y
ybr y ybr y ybr y
for ybr_420
(3 * ceilt(cols/2) + floor (col/2)) * rows
if it is /3 -> convert as RGB
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
as we talked to Bill the formula should be edited to
for ybr_Full_422
ybr y ybr y ybr .... (ybr or y) y y y y y ........ y .. .. .. (last row) ybr y ybr y ybr y ... (ybr or y) OR y y y y y y
-> 3 * ceil(cols/2) + florr(cols/2) * ceil(rows/2) + cols * floor(rows/2)
That is:
(3 * ceil(cols/2) + floor(cols/2)) * ceil(rows/2) + cols * floor(rows/2)
Note the parenthesis around the first expression. That breaks down into:
There are ceil(rows/2) YBR containing rows (think about 1,2,3 rows, having 1 1 and 2 YBR containing rows)
There are floor(rows/2) NON YBR containing rows. Those have length cols as they are just y containing.
For each YBR row, there are ceil(cols/2) YBR elements - think about hte same thing as the rows, but for columns - the first, third, fifth etc column has YBR. Each YBR has 3 elements (Y b and r)
Then, for each YBR row, there are floor(cols/2) Y element only.
const { rows, columns } = imageFrame; | ||
if (imageFrame.photometricInterpretation === 'YBR_FULL_422') { | ||
return imageFrame.pixelDataLength === 2 * rows * columns && rows % 2 === 0; | ||
} else { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add a settable flag allowing coercion to RGB if the response is uncompressed, and the original is YBR of some flavour. That flag could go in cornerstone settings, and maybe be triggered based on the URL prefix (eg url.startsWith), or a regular expression match.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you please add more to this? I don't understand
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the photometric interpretation is YBR_FULL_422, BUT the length is 3rowscolumns, then assume it is the configurable value of either RGB or YBR_FULL. That is invalid data, but is common enough that we see it regularly as a "bug" fix request.
To decide whether RGB or YBR_FULL, check the datasource configuration - something like:
UncompressedColorEncoding=YBR_FULL | RGB (default is RGB)
and perhaps
ForceColorEncoding=AllUncompressed | All | WrongLength (default is WrongLength)
Then, have the above function return the PMI to use for the given image.
const {ForceColorEncoding = WrongLength, UncompressedColorEncoding = RGB} = dataSourceConfiguration;
if( pixel length === 3 * rows * columns && pmi ==='YBR_FULL_422 && forceColorEncoding===WrongLength ) {
return UncompressedColorEncoding
indicating that this image needs decompression handling, in the specified color encoding.
Unfortunately, the back end can still decide to reply in uncompressed, and they can still be invalid, so yes, I think this is a good idea still. It is technically wrong, but practically is the right thing to do. |
…/rodrigobasilio2022/fix/RGBinconsistencies
Context
This PR aims to fix the problem with RGB inconsistencies in loading specific color dicom images
Changes & Results
Testing
Checklist
PR
semantic-release format and guidelines.
Code
[] My code has been well-documented (function documentation, inline comments,
etc.)
[] I have run the
yarn build:update-api
to update the API documentation, and havecommitted the changes to this PR. (Read more here https://www.cornerstonejs.org/docs/contribute/update-api)
Public Documentation Updates
additions or removals.
Tested Environment