Save image to DICOM with Fellow Oak DICOM for .NET

11,631 views
Skip to first unread message

Mihnea Sucata

unread,
Nov 5, 2012, 6:58:27 AM11/5/12
to fo-d...@googlegroups.com
Hello,

I am trying to save an image file (bmp, tiff, jpeg etc.) to DICOM, using Fellow Oak DICOM for .NET.

I tried integrating the BitmapUtils class that Lawrence wrote ( https://groups.google.com/forum/?fromgroups=#!topic/fo-dicom/rTTkSEVncEA ), and I've attached my code below, which uses the BitmapUtils class. However, if I open let's say the Chrysanthemum.jpg image from Windows ( C:\Users\Public\Pictures\Sample Pictures ), convert it to black and white etc., after I save the DICOM and open it in a DICOM viewer, the image is scrambled.

Could you please let me know what I am doing wrong? Is there someplace where I can find a sample for saving images to DICOM using Fellow Oak DICOM for .NET?

      public static void MakeDicom(Bitmap slideImage, ushort imgwidth, ushort imgheight, string filePath,string doctorName)
      {
         Dicom.DicomDataset data = new Dicom.DicomDataset() { };

         data.Add(Dicom.DicomTag.SOPClassUID, Dicom.DicomUID.ComputedRadiographyImageStorage);
         data.Add(Dicom.DicomTag.SOPInstanceUID, Dicom.DicomUID.ComputedRadiographyImageStorage);
         data.Add(Dicom.DicomTag.StudyDate, DateTime.Now);
         data.Add(Dicom.DicomTag.StudyTime, DateTime.Now);
         data.Add(Dicom.DicomTag.AccessionNumber, "");
         data.Add(Dicom.DicomTag.ReferringPhysicianName, doctorName);
         data.Add(Dicom.DicomTag.StudyID, "1");
         data.Add(Dicom.DicomTag.SeriesNumber, "1");
         data.Add(Dicom.DicomTag.ModalitiesInStudy, "CR");
         data.Add(Dicom.DicomTag.Modality, "CR");
         data.Add(Dicom.DicomTag.NumberOfStudyRelatedInstances, "1");
         data.Add(Dicom.DicomTag.NumberOfStudyRelatedSeries, "1");
         data.Add(Dicom.DicomTag.NumberOfSeriesRelatedInstances, "1");
         data.Add(Dicom.DicomTag.PatientOrientation, "F/A");
         data.Add(Dicom.DicomTag.ImageLaterality, "U");

         data.Add(new Dicom.DicomOtherWord(Dicom.DicomTag.PixelData, new Dicom.IO.Buffer.CompositeByteBuffer()));
         Dicom.Imaging.DicomPixelData pixelData = Dicom.Imaging.DicomPixelData.Create(data);

         pixelData.Width = imgwidth;
         pixelData.Height = imgheight;
         pixelData.SamplesPerPixel = 1;

         pixelData.HighBit = 9;
         pixelData.BitsStored = 10;
         pixelData.BitsAllocated = 16;

         pixelData.PhotometricInterpretation = Dicom.Imaging.PhotometricInterpretation.Monochrome1;

         byte[] uncompressedBitmapByteArray = BitmapUtils.GetPixelData(slideImage);
         var byteBuffer = new Dicom.IO.Buffer.MemoryByteBuffer(uncompressedBitmapByteArray);
         pixelData.AddFrame(byteBuffer);

         var file = new Dicom.DicomFile(data);
         file.Save(filePath);
      }

Thank you

Colby Dillion

unread,
Nov 5, 2012, 11:07:21 AM11/5/12
to fo-d...@googlegroups.com
pixelData.SamplesPerPixel = 3;

pixelData
.HighBit = 7;
pixelData
.BitsStored = 8;
pixelData
.BitsAllocated = 8;

pixelData
.PhotometricInterpretation = Dicom.Imaging.PhotometricInterpretation.Rgb;

It is likely that your images look grayscale but that they are not true single channel 16-bit grayscale images. They may just be RGB images with all three 8-bit color channels set to the same value.

Hesham Desouky

unread,
Nov 5, 2012, 11:30:45 AM11/5/12
to fo-d...@googlegroups.com
One note, you should also specify new SOP Instance UID in the created DICOM file.
So the line 
data.Add(Dicom.DicomTag.SOPInstanceUID, Dicom.DicomUID.ComputedRadiographyImageStorage);

should be
data.Add(Dicom.DicomTag.SOPInstanceUID, "new Instance UID");

Mihnea Sucata

unread,
Nov 6, 2012, 3:29:57 AM11/6/12
to fo-d...@googlegroups.com
Hi and thank you guys for getting back to me on this, I tried both the changes that Colby and Hesham suggested, and now the process fails with a Stack Overflow, in method Size from SwapByteBuffer (the return Internal.Size seems to enter a loop). This is because for pixelData.BitsAllocated = 8, in DicomPixelData.AddFrame it creates a new SwapByteBuffer instead of adding the actual byte buffer for my image.

Here is the pixel data snippet of my code (I bypassed the whole pre-processing for the image, to ensure that it was not causing the issue):

         slideImage = new Bitmap(@"C:\Users\Public\Pictures\Sample Pictures\Chrysanthemum.jpg");

         data.Add(new Dicom.DicomOtherWord(Dicom.DicomTag.PixelData, new Dicom.IO.Buffer.CompositeByteBuffer()));
         Dicom.Imaging.DicomPixelData pixelData = Dicom.Imaging.DicomPixelData.Create(data);

         pixelData.Width = imgwidth;
         pixelData.Height = imgheight;

         pixelData.SamplesPerPixel = 3;

         pixelData.HighBit = 7;
         pixelData.BitsStored = 8;
         pixelData.BitsAllocated = 8;

         pixelData.PhotometricInterpretation = Dicom.Imaging.PhotometricInterpretation.Rgb;

         byte[] uncompressedBitmapByteArray = BitmapUtils.GetPixelData(slideImage);
         var byteBuffer = new Dicom.IO.Buffer.MemoryByteBuffer(uncompressedBitmapByteArray);
         pixelData.AddFrame(byteBuffer);

         var file = new Dicom.DicomFile(data);
         file.Save(filePath);

Thank you in advance


On Monday, November 5, 2012 1:58:27 PM UTC+2, Mihnea Sucata wrote:

Mihnea Sucata

unread,
Nov 7, 2012, 3:08:34 AM11/7/12
to fo-d...@googlegroups.com
Hi guys, any thoughts on this?

Thank you


On Monday, November 5, 2012 1:58:27 PM UTC+2, Mihnea Sucata wrote:

Mahesh Dubey

unread,
Nov 7, 2012, 3:53:30 AM11/7/12
to fo-d...@googlegroups.com

Hello 
       I had created gist for creating dicom dataset from image file, Check it out if any problem please revert  back.

Regards
Mahesh

--
You received this message because you are subscribed to the Google Groups "Fellow Oak DICOM for .NET" group.
To post to this group, send email to fo-d...@googlegroups.com.
To unsubscribe from this group, send email to fo-dicom+u...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msg/fo-dicom/-/Nk_4Zz-ywLcJ.

For more options, visit https://groups.google.com/groups/opt_out.
 
 



--
Thanks And Regards
Mahesh Dubey

Mihnea Sucata

unread,
Nov 7, 2012, 5:03:53 AM11/7/12
to fo-d...@googlegroups.com
Thank you very much, the code that you committed worked very well for me.

Btw, if I would need to add different size images to a DICOM, would I need to create new PixelData (since rows/columns would be different), or is this not feasible in a DICOM?

Mahesh Dubey

unread,
Nov 7, 2012, 5:54:45 AM11/7/12
to fo-d...@googlegroups.com
Are you talking about adding multiple frames in single dataset. If yes then all the frames must have the same properties like height,width, bits store etc.

The simple answer to question is yes you have to create the different dataset for each image as their dimensions are not equal. 

If you want to add multiple images with different size in the one dataset i.e. creating multiframe dicom image then you have to resize all bitmap to common size.

To view this discussion on the web visit https://groups.google.com/d/msg/fo-dicom/-/CcBVuUbjql4J.

For more options, visit https://groups.google.com/groups/opt_out.
 
 

Mihnea Sucata

unread,
Nov 14, 2012, 5:18:03 AM11/14/12
to fo-d...@googlegroups.com
Hi Mahesh,

Thank you for your reply and sorry for not getting back sooner.

Yes, I was referring to adding multiple frames in a single dataset (in which case I will perform resizing of the images before adding them), but can you have multiple datasets in a single DICOM in general? (I know that fo-dicom only supports a single Dataset)

Or should this be done with DICOMDIR (and have multiple DICOM files)?

Thanks

Mahesh Dubey

unread,
Nov 20, 2012, 1:42:13 AM11/20/12
to fo-d...@googlegroups.com
Multiple dataset in single file ?? Well it is not possible. In general a dicom file contains  single dataset only.
 
Papyrus is an image format which extend ACR/NEMA 2.0 to allow multiple dataset in single file, But this format was not popular and most of the dicom implementation doesn't have the support for this format. 

Why you want to club multiple dataset in a single unit ?


Regards
Mahesh

Mihnea Sucata

unread,
Nov 20, 2012, 4:23:12 AM11/20/12
to fo-d...@googlegroups.com
I didn't think so either, but since I'm not too proficient with DICOM, I just wanted to make sure that I'm not missing out on something.


>>Why you want to club multiple dataset in a single unit ? <<
I'm writing a medical application where the viewer shows all of the scans for a patient, so for a patient you can see and import multiple scans (from different scanners, with different resolutions). My aim was to create a single DICOM per patient, and somehow keep the resolution for each image, but as per our previous discussion, this is not feasible in a single dataset, so I thought that maybe saving multiple dataset (each dataset with a specific resolution) would do the trick.

Anyway, thank you for your reply, you've been really helpful.

Yassin Jdaoudi

unread,
Aug 29, 2013, 11:10:48 AM8/29/13
to fo-d...@googlegroups.com
Hi Mahesh, 

Thank you for sharing the gist.

The DICOM file saved with the following lines:
 DicomFile dicomfile = new DicomFile(dataset);
 dicomfile
.Save("dicomfile.dcm");

is in implicit right? even if changing the transfer syntax?

I would like to do the same but for a jpeg file. The DICOM file will then much lighter even with Hi-res images. Note your code is also functional with JPEG images but then it is saved as an implicit file (huge DICOM file size)? Is it possible to compress afterwards ? Should images be opened direclty in JPEG?

Do you have some hints about this?

Sorry if it's a stupid question, I'm just starting with fo-dicom library.

Thank you very much in advance for your help.

Best,
Yassin Jd.

Mahesh Dubey

unread,
Aug 30, 2013, 2:19:22 AM8/30/13
to fo-d...@googlegroups.com
Hello;
        I haven't try with the jpeg compress file; I don't think it will work, If you create the dataset with the Jpeg compressed pixel data using the gist's code  the dicom image viewer cant handle the resultant dataset, 
The easiest way is to 
1) Uncompress jpeg image to bitmap. 
2) Create the dataset with the bitmap data using gist's code.
3) Compress the dataset, using change transfer syntax method of the toolkit.

Regards
Mahesh

Yassin Jd

unread,
Aug 30, 2013, 10:29:12 AM8/30/13
to fo-d...@googlegroups.com
Hi Mahesh, 

Thank you for your response. 

It is actually working with JPEG files since the Bitmap class handles jpg, bmp, tiff,... 

I tried the ChangeTransferSyntax method but the file size is unchanged, am I doing something wrong?
 DicomFile dicomfile = new DicomFile(dataset);
 dicomfile.ChangeTransferSyntax(DicomTransferSyntax.JPEGLSNearLossless);
 dicomfile.Save("dicomfile.dcm");

I tried with several transfer syntax but nothing changes... 

Thanks again for your help! I appreciate. 

Best, 
Yassin Jd. 

Colby Dillion

unread,
Aug 30, 2013, 1:39:35 PM8/30/13
to fo-d...@googlegroups.com
The ChangeTransferSyntax method generates a new instance of DicomFile. Take a look at the example in the README on GitHub.

Mahesh Dubey

unread,
Aug 31, 2013, 2:19:12 AM8/31/13
to fo-d...@googlegroups.com

Hi;
     Yes you are right, I rechecked the gist's code and it is working with any type of image file which is supported by .Net Bitmap object, actually the bitmapdata of bitmap object is always in the uncompress   i.e. in bmp form,  regardless of the original importing file encoding.

Check the below code which may be a solution for your problem

 DicomFile dicomfile = new DicomFile(dataset);
 var compressFile=dicomfile.ChangeTransferSyntax(DicomTransferSyntax.JPEGLSNearLossless);
 compressFile.Save("dicomfile.dcm");

Mahesh


Yassin Jd

unread,
Sep 1, 2013, 1:19:30 PM9/1/13
to fo-d...@googlegroups.com
Hi, 

Okay. I feel stupid about the ChangeTransferSyntax method... :-p I'll give it a try! It should be usable/declared in static only, don't you think? 

Thanks a lot to both of you for you help! 

Yassin Jd. 

Fatih O.

unread,
May 1, 2014, 5:56:19 PM5/1/14
to fo-d...@googlegroups.com
Hi,
how can use your sample to create DICOM files from multiple BMP frames?

Best,

rajk...@pellucidinc.com

unread,
Feb 28, 2019, 2:28:41 AM2/28/19
to Fellow Oak DICOM
I have to the same thing in Clearcanvas I did but not show in viewer

 public bool SetPixelData(Image currimage)
        {
            this._modulestring = "SetPixelData";
            Bitmap bmp = (Bitmap)currimage;
            try
            {
                if (bmp == null)
                {
                    this.LogErrorString("invalid image sent to the function");
                    return false;
                }
                byte[] rawbytesfrombmp = GetRawBytesFromBmp(bmp);//ImageToByteArray(currimage);//GetRawBytesFromBmp(bmp); 
                if (rawbytesfrombmp == null)
                {
                    this.LogErrorString("unable to get raw bytes from BMP image");
                    return false;
                }               
                if (((bmp.PixelFormat == PixelFormat.Format24bppRgb) || (bmp.PixelFormat == PixelFormat.Format32bppArgb)) || (bmp.PixelFormat == PixelFormat.Format32bppRgb))
                {
                    ushort height = (ushort)bmp.Height;
                    ushort width = (ushort)bmp.Width;
                    if (((height % 2) != 0) && ((width % 2) != 0))
                    {
                        width = (ushort)(width - 1);
                    }                   
                    ////---------------------------------
                    if ((bmp.PixelFormat == PixelFormat.Format24bppRgb) || (bmp.PixelFormat == PixelFormat.Format32bppArgb) || (bmp.PixelFormat == PixelFormat.Format32bppRgb))
                    {
                        
                        //int frameNum = 0;
                        int frameNum = 0;
                        //DicomCompressedPixelData fragments = new DicomCompressedPixelData(_dcmfile.DataSet, rawbytesfrombmp);
                        //DicomCompressedPixelData fragments = new DicomCompressedPixelData(_dcmfile);
                        
                        DicomCompressedPixelData fragments = new DicomCompressedPixelData(_dcmfile, rawbytesfrombmp);
                        fragments.ImageWidth = width;//(ushort)height;
                        fragments.ImageHeight = height; //(ushort)image.Height;
                        fragments.BitsStored = 8;//(ushort)bitsPerPixel;
                        fragments.BitsAllocated = 8;//(ushort)bitsPerPixel;
                        fragments.HighBit = 7;
                        fragments.SamplesPerPixel = 3;
                        fragments.PlanarConfiguration = 0;
                        fragments.PhotometricInterpretation = "YBR_FULL_422";
                        fragments.AddFrameFragment(rawbytesfrombmp);///addtional added 
                        fragments.UpdateMessage(_dcmfile);                                    ///
                        //byte[] frame = fragments.GetFrameFragmentData(frameNum);                        
                        //-----------------------------------------------
                        //DicomCompressedPixelData fragments = new DicomCompressedPixelData(file.DataSet);
                        //byte[] frame = fragments.GetFrameFragmentData(frameNum); 
                        //--------------------------------End
                        //if (!this.SetPixelData8bit(frame))///(!this.SetPixelData8bit(rawbytesfrombmp))       //if (!this.SetPixelData8bitDicomSequence(ref seq))                        
                        //{
                        //    return false;
                        //}
                    }

                }
}
catch (Exception exception)
            {
                this.LogErrorString(exception.Message);
                return false;
            }
            return true;
}
public static byte[] ImageToByteArray(Image imageIn)
        {
            MemoryStream ms = new MemoryStream();
            imageIn.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg);
            return ms.ToArray();
        }
private byte[] GetRawBytesFromBmp(Bitmap bmp)
        {
            byte[] raw;
            int row, column;
            int width = bmp.Width;
            int columnWidth = width / 4;
            int height = bmp.Height;
            if (height % 2 != 0 && width % 2 != 0)
            {
                width--;
            }
            int bmpStride = 0;
            int rawStride = 0;
            int BytesPerPixel = 0;
            BitmapData data = null;
            try
            {
                //Lock the bitmap so we can access the raw pixel data
                // Lock the bitmap's bits.  
                data = bmp.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadOnly, bmp.PixelFormat);
                bmpStride = data.Stride;
                if (bmp.PixelFormat == PixelFormat.Format24bppRgb || bmp.PixelFormat == PixelFormat.Format8bppIndexed || bmp.PixelFormat == PixelFormat.Format32bppRgb || bmp.PixelFormat == PixelFormat.Format32bppArgb)
                {

                    BytesPerPixel = Image.GetPixelFormatSize(bmp.PixelFormat) / 8;

                    rawStride = BytesPerPixel * width;
                }
                else if (bmp.PixelFormat == PixelFormat.Format48bppRgb)
                {
                    BytesPerPixel = Image.GetPixelFormatSize(bmp.PixelFormat) / 8;
                    rawStride = BytesPerPixel * width;
                }
                else
                {                   
                    return null;
                }
                // Declare an array to hold the bytes of the bitmap. 
                raw = new byte[rawStride * height];
                unsafe
                {
                    // Get the address of the first line. //Sets pointer
                    byte* bmpPtr = (byte*)data.Scan0.ToPointer();

                    fixed (byte* rawPtr = raw)
                        if (bmp.PixelFormat == PixelFormat.Format24bppRgb)
                        {
                            //the pixels row by row
                            for (row = 0; row < height; ++row)
                            {
                                for (column = 0; column < rawStride; column += BytesPerPixel)
                                {
                                    rawPtr[row * rawStride + column] = bmpPtr[row * bmpStride + column + 2];   //b
                                    rawPtr[row * rawStride + column + 1] = bmpPtr[row * bmpStride + column + 1]; //g
                                    rawPtr[row * rawStride + column + 2] = bmpPtr[row * bmpStride + column]; ; //r
                                }
                            }                           
                        }
}
}
catch (Exception e)
            {
                return null;
            }
            finally
            {
                bmp.UnlockBits(data);                
            }
            return raw;
}

if have any idea jus share your thoughts
Reply all
Reply to author
Forward
Message has been deleted
0 new messages