// The 'computeSliceSpacing' method double LoadSeriesThread::computeSliceSpacing(OrthancClient::Series& series) const { OrthancClient::Instance instance = series.GetInstance(0); instance.LoadTagContent("0020-0032"); // ImagePositionPatient QString pos1 = instance.GetLoadedTagContent().c_str(); QStringList pos1s = pos1.split("\\"); Vector3D pos1vector(pos1s.at(0).toDouble(), pos1s.at(1).toDouble(), pos1s.at(2).toDouble()); instance.LoadTagContent("0020-0037"); // ImageOrientationPatient QString or1 = instance.GetLoadedTagContent().c_str(); QStringList or1s = or1.split("\\"); Vector3D v(or1s.at(0).toDouble(), or1s.at(1).toDouble(), or1s.at(2).toDouble()); Vector3D w(or1s.at(3).toDouble(), or1s.at(4).toDouble(), or1s.at(5).toDouble()); Vector3D n = v.crossProduct(w); n.normalize(); double min = -1; for(unsigned int i = 1 ; i < series.GetInstanceCount() ; i++) // TODO normally one can be enough but some bug forced me to search for the minest { OrthancClient::Instance instance2 = series.GetInstance(i); instance2.LoadTagContent("0020-0032"); QString pos2 = instance2.GetLoadedTagContent().c_str(); QStringList pos2s = pos2.split("\\"); Vector3D pos2vector(pos2s.at(0).toDouble(), pos2s.at(1).toDouble(), pos2s.at(2).toDouble()); double dist = fabs(n.dotProduct(pos1vector-pos2vector)); if(dist < min || min < 0) min = dist; } return min; }
void Display(OrthancClient::Series& series) { /** * Load the 3D image from Orthanc into VTK. **/ vtkSmartPointer<vtkImageData> image = vtkSmartPointer<vtkImageData>::New(); image->SetDimensions(series.GetWidth(), series.GetHeight(), series.GetInstanceCount()); image->SetScalarType(VTK_SHORT); image->AllocateScalars(); if (series.GetWidth() != 0 && series.GetHeight() != 0 && series.GetInstanceCount() != 0) { DisplayProgress listener; series.Load3DImage(image->GetScalarPointer(0, 0, 0), Orthanc::PixelFormat_SignedGrayscale16, 2 * series.GetWidth(), 2 * series.GetHeight() * series.GetWidth(), listener); } float sx, sy, sz; series.GetVoxelSize(sx, sy, sz); image->SetSpacing(sx, sy, sz); /** * The following code is based on the VTK sample for MIP * http://www.vtk.org/Wiki/VTK/Examples/Cxx/VolumeRendering/MinIntensityRendering **/ // Create a transfer function mapping scalar value to opacity double range[2]; image->GetScalarRange(range); vtkSmartPointer<vtkPiecewiseFunction> opacityTransfer = vtkSmartPointer<vtkPiecewiseFunction>::New(); opacityTransfer->AddSegment(range[0], 0.0, range[1], 1.0); vtkSmartPointer<vtkColorTransferFunction> colorTransfer = vtkSmartPointer<vtkColorTransferFunction>::New(); colorTransfer->AddRGBPoint(0, 1.0, 1.0, 1.0); colorTransfer->AddRGBPoint(range[1], 1.0, 1.0, 1.0); vtkSmartPointer<vtkVolumeProperty> property = vtkSmartPointer<vtkVolumeProperty>::New(); property->SetScalarOpacity(opacityTransfer); property->SetColor(colorTransfer); property->SetInterpolationTypeToLinear(); // Create a Maximum Intensity Projection rendering vtkSmartPointer<vtkFixedPointVolumeRayCastMapper> mapper = vtkSmartPointer<vtkFixedPointVolumeRayCastMapper>::New(); mapper->SetBlendModeToMaximumIntensity(); mapper->SetInput(image); vtkSmartPointer<vtkVolume> volume = vtkSmartPointer<vtkVolume>::New(); volume->SetMapper(mapper); volume->SetProperty(property); vtkSmartPointer<vtkRenderer> renderer = vtkSmartPointer<vtkOpenGLRenderer>::New(); renderer->AddViewProp(volume); renderer->SetBackground(0.1, 0.2, 0.3); // Background color dark blue vtkSmartPointer<vtkInteractorStyleTrackballCamera> style = vtkSmartPointer<vtkInteractorStyleTrackballCamera>::New(); vtkSmartPointer<vtkRenderWindow> window = vtkSmartPointer<vtkRenderWindow>::New(); window->AddRenderer(renderer); vtkSmartPointer<vtkRenderWindowInteractor> interactor = vtkSmartPointer<vtkRenderWindowInteractor>::New(); interactor->SetRenderWindow(window); interactor->SetInteractorStyle(style); interactor->Start(); }