Storing pixel information of a device context into pixel data array

All other questions regarding DCMTK

Moderator: Moderator Team

Post Reply
Message
Author
Joren
Posts: 5
Joined: Fri, 2005-01-07, 16:34

Storing pixel information of a device context into pixel data array

#1 Post by Joren »

Hello,

I'm pretty new at imaging with C++, so I was hoping some could help me out...

I have an application which displays an image retrieved from a scanner.
So now I want to export these images to DICOM. Can someone please explain how I store the pixel data into the dataset?

I know I have to use the function

Code: Select all

 dataset->putAndInsertUint8Array(DCM_PixelData, pixelData, pixelLength); 
with pixeldata and pixellength being UINT8. But how can I convert the pixels in the DC to such an array, and then how to calculate the pixellength?

Thanks
Last edited by Joren on Wed, 2005-01-19, 11:53, edited 1 time in total.

Joren
Posts: 5
Joined: Fri, 2005-01-07, 16:34

#2 Post by Joren »

Sorry to bump this thread again, but it would really helpful if someone could help me out...

The application is one in a Doc/view architecture. From the active view, I get the device context. And now I want to store the image information in this device context into a DICOM file. But for this, I have to store the pixel information of this device context into an array for the pixel data. Can someone please tell me how to do this?

guy
Posts: 41
Joined: Tue, 2004-11-09, 16:55

convert DC to Dicom file

#3 Post by guy »

If I have well understood what you want to do, it's quiete easy.
For being sure, you want to convert the image in your device context into a dicom file, aren't you ?

I'm sure you are able to set the pixels of your DC in a buffer.
If you have an image R (rows) * C (columns), you will get a buffer with length = R * C * 4 (number of planes by pixel) because DC works in 32 bits. You have to convert your image to 24 bits RGB for a dicom file RGB.

Your new buffer wil have a length of R * C * 3 where one colour of one pixel will be represent by a BYTE (or Uint8).

For setting the pixels of the image in the dicom file, use the function of DcmDataset class : putAndInsertUint8Array (TabPixels, Length) where TabPixels is the tab of your pixels : BYTE* and with a length of R * C * 3.

Finally you have to set the different tags of the dicom file (for example PatientsName...).

Note that Dicomscope doesn't read dicom RGB images, and look if you have to convert your image into a Secondary Capture Dicom File.

Sylvain
In practice in a dicom work

Joren
Posts: 5
Joined: Fri, 2005-01-07, 16:34

#4 Post by Joren »

Thank you very much for your help, but I'm still having problems converting the device context to a 24 bit RGB. I searched through the internet for a solution/example, but was unable to find one... As I said, I'm pretty new when it comes to C++ imaging.. :)

The code I have untill so far:

Code: Select all

 void CVTApp::OnExportDicomfile()
{

	CMDIChildWnd * pChild = ((CMDIFrameWnd*)(AfxGetApp()->m_pMainWnd))->MDIGetActive();

    CVTImgDoc * pDoc = reinterpret_cast<CVTImgDoc *>( pChild->GetActiveDocument());

    CVTImgView * pView = reinterpret_cast<CVTImgView *>(pChild->GetActiveView());

	pView->GetClientRect(&rect);

	rect.normalize();

	int height = rect.Height();
	int width = rect.Width();

	pixelDataLength24bit = height * width * 3; 
	Uint8 * pixelData = new Uint8[pixelDataLength24bit];

	int pixelPos = 0;

	for (int y=height-1; y>=0; y--)
			for (int x=0; x<width; x++)
			{
				COLORREF pixelColor = pView->m_MemoryDC.GetPixel(x,y);

				//pixelData[pixelPos] = (UINT8)pixelColor; //should be converted to 24 bit RGB first

				/*Convert to 24 Bit RGB*/
				//...
				//...
				//...
				
				pixelPos = pixelPos + 1;
			}

	char uid[100];
	DcmFileFormat fileformat;
	DcmDataset *dataset = fileformat.getDataset();
	dataset->putAndInsertString(DCM_SOPClassUID, UID_SecondaryCaptureImageStorage);
	dataset->putAndInsertString(DCM_SOPInstanceUID, dcmGenerateUniqueIdentifier(uid, SITE_INSTANCE_UID_ROOT));
	dataset->putAndInsertString(DCM_PatientsName, "Doe^John");
/* ... */

	dataset->putAndInsertUint8Array(DCM_PixelData, pixelData, pixellength);
	
	OFCondition status = fileformat.saveFile("test.dcm", EXS_LittleEndianExplicit);
	if (status.bad())
	{
		 cerr << "Error: cannot write DICOM file (" << status.text() << ")" << endl;
	}
}
Is this any good up to here? As you can see there is still nothing filled in for the 24 bit conversion. Can you please help me out?[/code]

markw
Posts: 84
Joined: Mon, 2005-01-17, 01:08

#5 Post by markw »

Hi Joren,

I myself have just downloaded the toolkit two days ago. I thought there was a class for specifically writing out color dicom images?

As for getting the pixel colors from your DC, your method will work, what you're missing is in the /*Convert to 24 Bit RGB*/ section obviously. You could use GetRValue() / GetGValue() / GetBValue() to decompose the pixel into its color channel ranges. Keep in mind that while your method of getting the pixel color data will work, GetPixel / SetPixel are extremely slow! Don't you have access to what's being displayed in the DC to begin with? Why jump through this hoop of taking a 'screen shot'?

If you really don't have access to what's being displayed in the DC, you may want to consider BitBlt()'ing the DC into a CImage object. CImage has methods for direct pixel access which will be lightyears faster than your current method. Also, why are you reading the y direction from bottom to top?

I have to take a look at read/writing color dicom images myself later this week, I will post my results if you're still stuck.

Goodluck!

Mark

markw
Posts: 84
Joined: Mon, 2005-01-17, 01:08

#6 Post by markw »

Just thought of one other thing - I haven't done color dicom images in awhile, but I believe the order you're writing the color data to the file should be represented by the tag Planar Configuration.

If you're writing the pixels out like RGB RGB RGB etc..
then the planar configuration should be 0.

Otherwise if you're writing out the pixels like RRR GGG BBB etc..
the planar configuration should be 1.

I think, just something to keep in mind.

Joren
Posts: 5
Joined: Fri, 2005-01-07, 16:34

#7 Post by Joren »

Hello Mark,

Thank you for your response. You were right about the reading upside down, but that was because I was studying a BMP example.. And since BMP's are stored upside down, I forgot to reverse it for the DC...

I know that GetPixel/SetPixel is a slow operation, but frankly I don't really know how I could convert it to 24 bit otherwise... Uptill now, I have only written console applications, so using the DC is kinda new to me...

I now tried to do it like this:

Code: Select all

int pixelPos = 0;
UINT8 rColor, gColor, bColor;

	for (int y=0; y<height; y++)
			for (int x=0; x<width; x++)
			{
				pixelColor = pView->m_MemoryDC.GetPixel(x,y);

				rColor = GetRValue((UINT8) pixelColor);
				pixelData[pixelPos++] = rColor;

				gColor = GetGValue((UINT8) pixelColor);
				pixelData[pixelPos++] = gColor;

				bColor = GetBValue((UINT8) pixelColor);
				pixelData[pixelPos++] = bColor;
 			}
			
....
dataset->putAndInsertUint8Array(DCM_PixelData, pixelData, pixelDataLength24bit);
but this doesn't seem to work. In my watches I can see that the gColor and bColor always return 0 (grayscale image), so that's not correct... Also the "image" that I get doesn't really look like the original one, but this could also has to do with an miscalculation of the rows and columns of the DC. I have to further look into it.

But I'm not really seeing what I'm doing wrong...: Get the individual colors of the color value, put those in an uint8 array, and pass that array to the method with the length of that array...

And if you can find the time, could you then please tell me what in your opinion would be the most performant way to convert it to 24 bit and store in the pixel data array?

Thanks

markw
Posts: 84
Joined: Mon, 2005-01-17, 01:08

#8 Post by markw »

Hi Joren,

To me it looks ok, except that I can't see if m_MemoryDC is actually initialized with anything. Can you write it out to a bmp or something else to make sure you've actually got it loaded and aren't reading random bits of memory?

Also, are you positive that you've set the correct DICOM tags that are characteristic for a color image of this bit depth etc? For example, samples per pixel, high bit, bits stored, planar configuration, pixel representation are just a few that need to be correctly defined or else a viewer will interpret the image completely wrong.

I think the fastest way to get direct pixel access in your case since you're already using MFC is to copy the DC into an instance of a CImage. This class has methods for getting the pixels directly. It would be a few extra steps but would speed this process up a whole lot. Check msdn for CImage.

markw
Posts: 84
Joined: Mon, 2005-01-17, 01:08

#9 Post by markw »

By the way, you've defined pixelColor as type COLORREF right?

guy
Posts: 41
Joined: Tue, 2004-11-09, 16:55

"""

#10 Post by guy »

There are some attributes you have to set, view the PS3 A.8 concerning the secondary captures.

For Secondary Capture RGB for example:

UID = "1.2.840.10008.5.1.4.1.1.7.4"; --> Multi-Frame True Color SC
PhotometricInterpretation = "RGB";
SamplesPerPixel = 3;
BitsAllocated = 8;
BitsStored = 8;
HightBit = 7;
PixelRepresentation = 0;
PlanarConfiguration = 0;

I don't understand why you use the method GetPixel, there are some other method like GetBitmapBits or GetDIBits which set directly the pixels in a buffer you pass to.

Enjoy Dicom!

Joren
Posts: 5
Joined: Fri, 2005-01-07, 16:34

#11 Post by Joren »

Got it working now :D

Especially the mandatory attributes where necessary off course...

I will now try to get it done by GetBitmapBits or GetDIBits.

Thanks!

cabbage
Posts: 1
Joined: Wed, 2005-10-19, 04:22

Mr Joren, would you please post some of your working code?

#12 Post by cabbage »

Code: Select all

char uid[100]; 
   DcmFileFormat fileformat; 
   DcmDataset *dataset = fileformat.getDataset(); 
   dataset->putAndInsertString(DCM_SOPClassUID, UID_SecondaryCaptureImageStorage); 
   dataset->putAndInsertString(DCM_SOPInstanceUID, dcmGenerateUniqueIdentifier(uid, SITE_INSTANCE_UID_ROOT)); 
   dataset->putAndInsertString(DCM_PatientsName, "Doe^John"); 

/* ... */ [color=red]//I've added the mandatory tags here, but i can not make it work[/color]

   dataset->putAndInsertUint8Array(DCM_PixelData, pixelData, pixellength); 
    
   OFCondition status = fileformat.saveFile("test.dcm", EXS_LittleEndianExplicit); 
Or could you give me some hint.
thank you very much.

Post Reply

Who is online

Users browsing this forum: Baidu [Spider], Bing [Bot], Google [Bot] and 1 guest