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

Code in fo-dicom to apply window width and center on an image #1765

Open
soothingmusic opened this issue Mar 20, 2024 · 1 comment
Open

Code in fo-dicom to apply window width and center on an image #1765

soothingmusic opened this issue Mar 20, 2024 · 1 comment
Labels

Comments

@soothingmusic
Copy link

soothingmusic commented Mar 20, 2024

I am using fo-dicom to render images and change the window width and center of the images. All works great.

I have a scenario where i dont have the dicom file and yet, and i need to apply the window width and center.
I have looked at the fo-dicom code and I see that we have Modality LUT class that overrides indexer that transforms the pixel value by applying rescale slope and intercept..

This is then used while rendering the image.

public void Render(ILUT lut, int[] output)
       {
           var data = Data;
           if (lut == null)
           {
               //.........
           }
           else
           {
               Parallel.For(0, Height, y =>
               {
                   for (int i = Width * y, e = i + Width; i < e; i++)
                   {
                       output[i] = (int)lut[data[i]];
                   }
               }
               );
           }
       }

I would like to know where exactly the window width and center are applied?

I would have expected following code somewhere to apply ww/wl..

public byte[] AdjustWindowWidthAndLevel(byte[] image, int width, int height, ushort newWindowWidth, ushort newWindowLevel, ushort maxPixelValue)
 {
     byte[] adjustedImage = new byte[image.Length];

     // Calculate the scaling factor for adjusting pixel values
     double scalingFactor = (double)newWindowWidth / (double)maxPixelValue;

     // Adjust the pixel values based on the new window width and level
     for (int i = 0; i < image.Length; i += 2)
     {
         ushort pixelValue = BitConverter.ToUInt16(image, i);

         // Apply window level adjustment
         int adjustedPixelValue = pixelValue - newWindowLevel;

         // Apply window width adjustment
         adjustedPixelValue = (int)Math.Round(adjustedPixelValue * scalingFactor);

         // Clamp the adjusted pixel value within the valid range
         adjustedPixelValue = Math.Max(0, Math.Min(adjustedPixelValue, maxPixelValue));

         byte[] adjustedPixelBytes = BitConverter.GetBytes((ushort)adjustedPixelValue);
         adjustedImage[i] = adjustedPixelBytes[0];
         adjustedImage[i + 1] = adjustedPixelBytes[1];
     }

     return adjustedImage;
 }

Where can i find this in fo-dicom code??

@gofal
Copy link
Contributor

gofal commented Mar 24, 2024

fo-dicom uses a more generic approach than your code. The calculation of the pixel data depending on WindowLevel and WindowWith does not apply to the definition in DICOM-Standard. WindowWidth and WindowCenter are not always applied in a linear way, sometimes you have to use SIGMOID, a exponential function. This depends on the content of VOILUTFunction (https://dicom.innolitics.com/ciods/breast-projection-x-ray-image/breast-projection-x-ray-image-multi-frame-functional-groups/52009229/00289132/00281056)

Then there are other properties like rescale slope and rescale intercept.

Futhermore some images contain Lookup-Tables, that have to be applied.

Your code assumes that the pixeldata always has 16 bits, but it also may be 8 bit, or 24 bit color.

etc etc etc

In fo-dicom the ILUT interface is the base class for all of these transformations, and the IPipeline is the base interface for the various factory implementations: GenericGrayscale, PaletteColor and RGBColor.
Each of these pipelines reads all the various dicomtags and builds up the renderpipeline by applying a chain of ILUT-Transformations. (https://dicom.nema.org/medical/dicom/current/output/chtml/part04/sect_n.2.html)

The DicomImage class handles this all for you. You have to create an instance of DicomImage. If you call .RenderImage() then all those pipelines are built up and the image is rendered correctly. You can adjust the rendering by changing the properties WindowCenter, WindowWidth, UseVOILUT, ShowOverlays etc.

@gofal gofal added the question label Mar 24, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants