/*========================================================================= Program: GDCM (Grassroots DICOM). A DICOM library Copyright (c) 2006-2011 Mathieu Malaterre All rights reserved. See Copyright.txt or http://gdcm.sourceforge.net/Copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notice for more information. =========================================================================*/ package examples; import vtk.*; //import gdcm.*; import vtk.util.VtkPanelContainer; import vtk.util.VtkPanelUtil; import vtk.util.VtkUtil; import java.util.ArrayList; import javax.swing.*; import java.awt.*; import java.io.File; /** * * This class should show how to read an image and display it * using gdcm and vtk, similar to gdcmorthoplanes * * used to test the transition from vtk 5.6 to vtk 5.9 * @author mmroden */ public class AWTMedical3 extends JComponent implements VtkPanelContainer { private vtkPanel renWin; vtkImageData ReadDataFile(File inSelectedFile){ vtkImageData outImageData = null; Directory theDir = new Directory(); String theInputDirectory = inSelectedFile.getPath(); theDir.Load(theInputDirectory); Scanner theScanner = new Scanner(); Tag theStudyTag = new Tag(0x0020,0x000d); Tag theSeriesTag = new Tag(0x0020,0x000e); theScanner.AddTag(theStudyTag);//get studies, theScanner.AddTag(theSeriesTag);//get studies, theScanner.Scan(theDir.GetFilenames()); FilenamesType theStudyValues = theScanner.GetOrderedValues(theStudyTag); long theNumStudies = theStudyValues.size(); //for now, take the first study, and nothing else. //and the return is actually not FilenamesType, just a //vector of strings if (theNumStudies != 1) return outImageData; String theStudyVal = theStudyValues.get(0); //now, get all the values from the scanner that are in that //study, then from that get their different series FilenamesType theFilenames = theScanner.GetAllFilenamesFromTagToValue(theStudyTag, theStudyVal); //from that set of filenames, isolate individual series //conclude that singleton series = RT struct (can do further //checking for things like MIPs and the like) //and multiple series entries = volumetric data theScanner.Scan(theFilenames); FilenamesType theSeriesValues = theScanner.GetOrderedValues(theSeriesTag); String studyUID = theScanner.GetValue(theScanner.GetFilenames().get(0), theStudyTag); long theNumSeries = theSeriesValues.size(); for (int i = 0; i < theNumSeries; i++) { FilenamesType theSeriesFiles = theScanner.GetAllFilenamesFromTagToValue(theSeriesTag, theSeriesValues.get(i)); long theNumFilesInSeries = theSeriesFiles.size(); if (theNumFilesInSeries > 1) {//assume it's CT or volumetric data //for now, assume a single volume //could have multiples, like PET and CT IPPSorter sorter = new IPPSorter(); sorter.SetComputeZSpacing(true); sorter.SetZSpacingTolerance(0.001); Boolean sorted = sorter.Sort(theSeriesFiles); if (!sorted){ //need some better way to handle failures here return outImageData; } FilenamesType sortedFT = sorter.GetFilenames(); long theSize = sortedFT.size(); vtkStringArray sa = new vtkStringArray(); ArrayList theStrings = new ArrayList(); vtkGDCMImageReader gdcmReader = new vtkGDCMImageReader(); for (int j = 0; j < theSize; j++) { String theFileName = sortedFT.get(j); if (gdcmReader.CanReadFile(theFileName) > 0){ theStrings.add(theFileName); sa.InsertNextValue(theFileName); } else { //this is a busted series //need some more appropriate error here return outImageData; } } gdcmReader.SetFileNames(sa); gdcmReader.Update(); outImageData = gdcmReader.GetOutput();//the zeroth output should be the image } } String theImageInfo = ""; if (outImageData != null){ theImageInfo = outImageData.Print(); } return outImageData; } //this function is a rewrite of Medical3 to see if data can //be loaded via gdcm easily public AWTMedical3(File inFile) { // Create the buttons. renWin = new vtkPanel(); vtkImageData theImageData = ReadDataFile(inFile); // An isosurface, or contour value of 500 is known to correspond to the // skin of the patient. Once generated, a vtkPolyDataNormals filter is // is used to create normals for smooth surface shading during rendering. // The triangle stripper is used to create triangle strips from the // isosurface these render much faster on some systems. vtkContourFilter skinExtractor = new vtkContourFilter(); skinExtractor.SetInput(theImageData); skinExtractor.SetValue(0, 500); vtkPolyDataNormals skinNormals = new vtkPolyDataNormals(); skinNormals.SetInput(skinExtractor.GetOutput()); skinNormals.SetFeatureAngle(60.0); // vtkStripper skinStripper = new vtkStripper(); // skinStripper.SetInput(skinNormals.GetOutput()); vtkPolyDataMapper skinMapper = new vtkPolyDataMapper(); skinMapper.SetInput(skinNormals.GetOutput()); skinMapper.ScalarVisibilityOff(); vtkActor skin = new vtkActor(); skin.SetMapper(skinMapper); skin.GetProperty().SetDiffuseColor(1, .49, .25); skin.GetProperty().SetSpecular(.3); skin.GetProperty().SetSpecularPower(20); // An isosurface, or contour value of 1150 is known to correspond to the // skin of the patient. Once generated, a vtkPolyDataNormals filter is // is used to create normals for smooth surface shading during rendering. // The triangle stripper is used to create triangle strips from the // isosurface these render much faster on some systems. vtkContourFilter boneExtractor = new vtkContourFilter(); boneExtractor.SetInput(theImageData); boneExtractor.SetValue(0, 1150); vtkPolyDataNormals boneNormals = new vtkPolyDataNormals(); boneNormals.SetInput(boneExtractor.GetOutput()); boneNormals.SetFeatureAngle(60.0); vtkStripper boneStripper = new vtkStripper(); boneStripper.SetInput(boneNormals.GetOutput()); vtkPolyDataMapper boneMapper = new vtkPolyDataMapper(); boneMapper.SetInput(boneStripper.GetOutput()); boneMapper.ScalarVisibilityOff(); vtkActor bone = new vtkActor(); bone.SetMapper(boneMapper); bone.GetProperty().SetDiffuseColor(1, 1, .9412); // An outline provides context around the data. vtkOutlineFilter outlineData = new vtkOutlineFilter(); outlineData.SetInput(theImageData); vtkPolyDataMapper mapOutline = new vtkPolyDataMapper(); mapOutline.SetInput(outlineData.GetOutput()); vtkActor outline = new vtkActor(); outline.SetMapper(mapOutline); outline.GetProperty().SetColor(0, 0, 0); // Now we are creating three orthogonal planes passing through the // volume. Each plane uses a different texture map and therefore has // diferent coloration. // Start by creatin a black/white lookup table. vtkLookupTable bwLut = new vtkLookupTable(); bwLut.SetTableRange(0, 2000); bwLut.SetSaturationRange(0, 0); bwLut.SetHueRange(0, 0); bwLut.SetValueRange(0, 1); bwLut.Build(); // Now create a lookup table that consists of the full hue circle (from // HSV);. vtkLookupTable hueLut = new vtkLookupTable(); hueLut.SetTableRange(0, 2000); hueLut.SetHueRange(0, 1); hueLut.SetSaturationRange(1, 1); hueLut.SetValueRange(1, 1); hueLut.Build(); // Finally, create a lookup table with a single hue but having a range // in the saturation of the hue. vtkLookupTable satLut = new vtkLookupTable(); satLut.SetTableRange(0, 2000); satLut.SetHueRange(.6, .6); satLut.SetSaturationRange(0, 1); satLut.SetValueRange(1, 1); satLut.Build(); // Create the first of the three planes. The filter vtkImageMapToColors // maps the data through the corresponding lookup table created above. // The vtkImageActor is a type of vtkProp and conveniently displays an // image on a single quadrilateral plane. It does this using texture // mapping and as a result is quite fast. (Note: the input image has to // be unsigned char values, which the vtkImageMapToColors produces.); // Note also that by specifying the DisplayExtent, the pipeline // requests data of this extent and the vtkImageMapToColors only // processes a slice of data. vtkImageMapToColors saggitalColors = new vtkImageMapToColors(); saggitalColors.SetInput(theImageData); saggitalColors.SetLookupTable(bwLut); vtkImageActor saggital = new vtkImageActor(); saggital.SetInput(saggitalColors.GetOutput()); saggital.SetDisplayExtent(32, 32, 0, 63, 0, 92); // Create the second (axial); plane of the three planes. We use the same // approach as before except that the extent differs. vtkImageMapToColors axialColors = new vtkImageMapToColors(); axialColors.SetInput(theImageData); axialColors.SetLookupTable(hueLut); vtkImageActor axial = new vtkImageActor(); axial.SetInput(axialColors.GetOutput()); axial.SetDisplayExtent(0, 63, 0, 63, 46, 46); // Create the third (coronal); plane of the three planes. We use the same // approach as before except that the extent differs. vtkImageMapToColors coronalColors = new vtkImageMapToColors(); coronalColors.SetInput(theImageData); coronalColors.SetLookupTable(satLut); vtkImageActor coronal = new vtkImageActor(); coronal.SetInput(coronalColors.GetOutput()); coronal.SetDisplayExtent(0, 63, 32, 32, 0, 92); // It is convenient to create an initial view of the data. The FocalPoint // and Position form a vector direction. Later on (ResetCamera() method) // this vector is used to position the camera to look at the data in // this direction. vtkCamera aCamera = new vtkCamera(); aCamera.SetViewUp(0, 0, -1); aCamera.SetPosition(0, 1, 0); aCamera.SetFocalPoint(0, 0, 0); aCamera.ComputeViewPlaneNormal(); // Actors are added to the renderer. An initial camera view is created. // The Dolly() method moves the camera towards the FocalPoint, // thereby enlarging the image. renWin.GetRenderer().AddActor(saggital); renWin.GetRenderer().AddActor(axial); renWin.GetRenderer().AddActor(coronal); renWin.GetRenderer().AddActor(outline); renWin.GetRenderer().AddActor(skin); renWin.GetRenderer().AddActor(bone); // Turn off bone for this example. bone.VisibilityOff(); // Set skin to semi-transparent. skin.GetProperty().SetOpacity(0.5); // An initial camera view is created. The Dolly() method moves // the camera towards the FocalPoint, thereby enlarging the image. renWin.GetRenderer().SetActiveCamera(aCamera); renWin.GetRenderer().ResetCamera(); aCamera.Dolly(1.5); // Set a background color for the renderer and set the size of the // render window (expressed in pixels). renWin.GetRenderer().SetBackground(1, 1, 1); VtkPanelUtil.setSize(renWin, 640, 480); // Note that when camera movement occurs (as it does in the Dolly() // method), the clipping planes often need adjusting. Clipping planes // consist of two planes: near and far along the view direction. The // near plane clips out objects in front of the plane the far plane // clips out objects behind the plane. This way only what is drawn // between the planes is actually rendered. renWin.GetRenderer().ResetCameraClippingRange(); // Setup panel setLayout(new BorderLayout()); add(renWin, BorderLayout.CENTER); } public vtkPanel getRenWin() { return renWin; } public static void main(String s[]) { if (s.length == 0){ return; //need a filename here } File theFile = new File(s[0]); //File theFile = new File("/Users/mmroden/Documents/MVSDownloadDirectory/Documents/1.2.840.113704.1.111.3384.1271766367.5/"); AWTMedical3 panel = new AWTMedical3(theFile); JFrame frame = new JFrame("AWTMedical3"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.getContentPane().add("Center", panel); frame.pack(); frame.setVisible(true); } }