Search code examples
c#dicomgdcm

GDCM incorrect image data


I'am using gdcm (nuget gdcm-sharp 2.4.4) library to get image and patient data from *.dcm files. All was fine on files with photometric interpretation MONOCHROME2, but then i got some files with MONOCHROME1 and about 30% of them were with a weird bug: images where "sliced" diagonally and i can't find why. Links to samples:incorrect image, correct image

Every row of pixels has offset of row number... easy to correct, but i don't know which file needs correction.

Question is: how to tell wich file needs to be corrected, dcm tags are same, the only thing that is different in .dcm files are image dimensions and pixel data ofc...

One more thing... any other soft i tried to view those images render them correctly.

Some code to show what is done

// pixel data copied to WriteableBitmap then saved by JpegBitmapEncoder 
var reader = new gdcm.ImageReader();
var gimg = reader.GetImage();
WriteableBitmap wb = new WriteableBitmap((int)cols, (int)rows, 120, 120, PixelFormats.Gray16, null);
wb.Lock();
Marshal.Copy(buff, 0, wb.BackBuffer, buff.Length);
wb.Unlock();

using (var ms2 = new MemoryStream())
{
    JpegBitmapEncoder enc = new JpegBitmapEncoder();
    enc.QualityLevel = 95;
    enc.Frames.Add(BitmapFrame.Create(wb));
    enc.Save(ms2);
    jpegfile = ms2.GetBuffer();
}

Solution

  • Just looking at the good vs bad image you provided: The good one is 1290 x 1249 and the bad one is 1195 x 1193

    It has been awhile since I have used windows bitmaps, but I am almost certain that they are padded row by row (not sure if it is to 2 byte, 4 byte (most likely) or whatever alignment), but given your examples, this is 99% sure what has happened.

    Rather than doing a single Marshal.Copy of the whole buffer, you will need to do it row by row (just my pseudo code):

    Foreach row:
       Marshal.Copy(buff + row_number * DicomImageRowLength, 0, wb.BackBuffer + row_number * wb.BackBufferStride, DicomImageRowLength);
    

    In case Marshal.Copy needs to have buff as the first argument:

    Foreach row:
       Marshal.Copy(buff, row_number * DicomImageRowLength, wb.BackBuffer + row_number * wb.BackBufferStride, DicomImageRowLength);
    

    If you copy row by row for every case, hopefully this will work for all images...

    I think you can validate that this is happening by looking at wb.BackBuffer.Size and comparing it to wb.PixelWidth, wb.PixelHeight, and wb.BackBufferStride for both cases.