Posts Tagged c++

DICOM viewer with .NET

After an application interview at Zeiss Meditec I was given a homework with two excercise. The first is about parsing some patient data from xml files with DTD which is not really new stuff for me. The second excercise is an implementation of a DICOM file viewer for a given example VL dicom file. The task tells to use whether C++, Java or C# and because I didn’t do C# for a while I started looking for a good .NET dicom library.

Looking at some OpenSource projects: they’re horrible! Most of the libs are in a pre- alpha- testing- doNotUseInProductiveEnvironments phase and the example viewers of some libs could not read meditec’s VL dicom file at all. Quite promising appeared GrassRoots as it’s a C++ based lib that is wrapped to C#, Python, etc. using swig. But I encountered some issues and as the documentation is close to zero I continued my lib-search.

Finally with mDCM I found a quite easy-to-use .NET C# implementation which was very fast to integrate into my little viewer app. I used a Utility class to access the library methods using the Dicom.Data namespace. To open a dicom file I used:


bool success = false;
m_fileformat = new DicomFileFormat();
try {
m_fileformat.Load(filename, DicomReadOptions.Default |
DicomReadOptions.KeepGroupLengths |
DicomReadOptions.DeferLoadingLargeElements |
DicomReadOptions.DeferLoadingPixelData);

success = true;
}
catch (Exception e)
{
m_lasterror = "Error parsing file! : " + e.Message + "\n";
return false;
}

return success;

Where m_fileformat is a Dicom.Data.DicomFileFormat data member. After that I read the pixel data to obtain the enclosed image:


DcmPixelData pixeldata = new DcmPixelData(m_fileformat.Dataset);
if (pixeldata.NumberOfFrames == 0)
{
m_lasterror = "No image data found" + "\n";
return false;
}
else if (pixeldata.NumberOfFrames == 1) // currently only first img
{
MemoryStream strm = null;
try
{
strm = new MemoryStream(pixeldata.GetFrameDataU8(0));
img = System.Drawing.Image.FromStream(strm);
}
catch (Exception ex)
{
m_lasterror = "Error reading image from dicom file (" + ex.ToString() + ")" + "\n";
return false;
}
return true;
}

As one can see, most likely only 8bit-per-channel images will be supported here (GetFrameDataU8() returns a plain byte[]). Moreover when testing with other sample dicom files this threw exceptions in many cases, so I guess I should look for another .NET lib or alternatively switch to Java or C++ :-(

=> zdicomviewer sourcecode and documentation

Tags: , , , , ,

Direct3D9 Depth Buffer access

If you have to deal with Depth Buffer reading in Direct3D9, you’ll propably encounter the documentation saying that you cannot read the GPU depth buffer directly. But there’s a workaround with special buffer formats on NVIDIA Cards (RAWZ on older cards and INTZ on G80+ series) and on ATI (DF16/24, I think). There’s also a paragraph in the G80 documentation by NVIDIA (see http://developer.nvidia.com/object/gpu_programming_guide.html). I think GTA4 is a game that uses the formats. Here’s some code I have been playing around with using Microsofts DirectX SDK of November 2008:

// decls

IDirect3DTexture9* m_pSMZTexture;   // rawz for using dss as input for next shader
LPDIRECT3DSURFACE9 m_pBackBuffer;   // default

// creates NVIDIA INTZ Format DSS. Use as DSS and render scene. Then use as input texture for another pass.
BOOL vmD3D::SetupDepthBufferAccess()
{
if(!m_pd3dDevice)
return FALSE;

if(FAILED(m_pd3dDevice->CreateTexture(TEXDEPTH_WIDTH, TEXDEPTH_HEIGHT, 1,
D3DUSAGE_DEPTHSTENCIL, (D3DFORMAT)MAKEFOURCC('I','N','T','Z'),
D3DPOOL_DEFAULT, &m_pSMZTexture, NULL)))
{
return FALSE;
}

return TRUE;
}

This actually creates an INTZ-Format DSS. Be sure to use the same dimensions of the render target you’ll bind on the same drawcall. Next, before your dracall bind the dss:

// setdss (our custom zsurf) -> intz!
// first fetch surface from tex at level 0
IDirect3DSurface9 *pSMZSurf = NULL;
m_pSMZTexture->GetSurfaceLevel(0, &pSMZSurf);
// set dss:
if(FAILED(m_pd3dDevice->SetDepthStencilSurface(pSMZSurf)))
return FALSE;
pSMZSurf->Release();

When drawing, there’s no need for any shader stuff if you simply want to output depth-only values (just write black color for example). But obviosly your vertex shader should do the needed transformations (multiply your WorldProjectionMatrix for example). After drawing with the intz-dss bound, you can use the texture as shader input for a next render pass. Here you can simply render a Full-Screen Quad to visualize the depth values:


if(FAILED(pFxDisplayDepth->SetTexture("DepthMap", m_pSMZTexture)))
return FALSE;

//set render target back to normal back buffer / depth buffer (or disable Z - as it's a dumb quad!)
if(FAILED(m_pd3dDevice->SetRenderTarget(0, m_pBackBuffer)))
return FALSE;

Then just draw the quad, i.e. with lazy UP RHW drawing:


UINT uPasses;
if (SUCCEEDED(pFxDisplayDepth->Begin(&uPasses, 0)))
{
for (UINT uPass = 0; uPass < uPasses; uPass++)
{
pFxDisplayDepth->BeginPass(uPass);
m_pd3dDevice->SetFVF( m_pQuad->FVF );
m_pd3dDevice->DrawPrimitiveUP( D3DPT_TRIANGLESTRIP, 2, m_pQuad->m_Vertex, sizeof( FullScreenQuad::Vertex ) );
pFxDisplayDepth->EndPass();
}
pFxDisplayDepth->End();
}

Be aware that Microsoft PIX cannot handle the INTZ resource, so you won’t be able to see a preview or the metadata of the INTZ-Surface.

Tags: , , , , , , ,