void mitk::SlicedGeometry3D::InitializeSlicedGeometry( unsigned int slices ) { Superclass::Initialize(); m_Slices = slices; PlaneGeometry::Pointer gnull = nullptr; m_PlaneGeometries.assign( m_Slices, gnull ); Vector3D spacing; spacing.Fill( 1.0 ); this->SetSpacing( spacing ); m_DirectionVector.Fill( 0 ); }
void mitk::ExtrudedContour::BuildGeometry() { if(m_Contour.IsNull()) return; // Initialize(1); Vector3D nullvector; nullvector.Fill(0.0); float xProj[3]; unsigned int i; unsigned int numPts = 20; //m_Contour->GetNumberOfPoints(); mitk::Contour::PathPointer path = m_Contour->GetContourPath(); mitk::Contour::PathType::InputType cstart = path->StartOfInput(); mitk::Contour::PathType::InputType cend = path->EndOfInput(); mitk::Contour::PathType::InputType cstep = (cend-cstart)/numPts; mitk::Contour::PathType::InputType ccur; // Part I: guarantee/calculate legal vectors m_Vector.Normalize(); itk2vtk(m_Vector, m_Normal); // check m_Vector if(mitk::Equal(m_Vector, nullvector) || m_AutomaticVectorGeneration) { if ( m_AutomaticVectorGeneration == false) itkWarningMacro("Extrusion vector is 0 ("<< m_Vector << "); trying to use normal of polygon"); vtkPoints *loopPoints = vtkPoints::New(); //mitk::Contour::PointsContainerIterator pointsIt = m_Contour->GetPoints()->Begin(); double vtkpoint[3]; unsigned int i=0; for(i=0, ccur=cstart; i<numPts; ++i, ccur+=cstep) { itk2vtk(path->Evaluate(ccur), vtkpoint); loopPoints->InsertNextPoint(vtkpoint); } // Make sure points define a loop with a m_Normal vtkPolygon::ComputeNormal(loopPoints, m_Normal); loopPoints->Delete(); vtk2itk(m_Normal, m_Vector); if(mitk::Equal(m_Vector, nullvector)) { itkExceptionMacro("Cannot calculate normal of polygon"); } } // check m_RightVector if((mitk::Equal(m_RightVector, nullvector)) || (mitk::Equal(m_RightVector*m_Vector, 0.0)==false)) { if(mitk::Equal(m_RightVector, nullvector)) { itkDebugMacro("Right vector is 0. Calculating."); } else { itkWarningMacro("Right vector ("<<m_RightVector<<") not perpendicular to extrusion vector "<<m_Vector<<": "<<m_RightVector*m_Vector); } // calculate a legal m_RightVector if( mitk::Equal( m_Vector[1], 0.0f ) == false ) { FillVector3D( m_RightVector, 1.0f, -m_Vector[0]/m_Vector[1], 0.0f ); m_RightVector.Normalize(); } else { FillVector3D( m_RightVector, 0.0f, 1.0f, 0.0f ); } } // calculate down-vector VnlVector rightDV = m_RightVector.GetVnlVector(); rightDV.normalize(); vnl2vtk(rightDV, m_Right); VnlVector downDV = vnl_cross_3d( m_Vector.GetVnlVector(), rightDV ); downDV.normalize(); vnl2vtk(downDV, m_Down); // Part II: calculate plane as base for extrusion, project the contour // on this plane and store as polygon for IsInside test and BoundingBox calculation // initialize m_ProjectionPlane, yet with origin at 0 m_ProjectionPlane->InitializeStandardPlane(rightDV, downDV); // create vtkPolygon from contour and simultaneously determine 2D bounds of // contour projected on m_ProjectionPlane //mitk::Contour::PointsContainerIterator pointsIt = m_Contour->GetPoints()->Begin(); m_Polygon->Points->Reset(); m_Polygon->Points->SetNumberOfPoints(numPts); m_Polygon->PointIds->Reset(); m_Polygon->PointIds->SetNumberOfIds(numPts); mitk::Point2D pt2d; mitk::Point3D pt3d; mitk::Point2D min, max; min.Fill(ScalarTypeNumericTraits::max()); max.Fill(ScalarTypeNumericTraits::min()); xProj[2]=0.0; for(i=0, ccur=cstart; i<numPts; ++i, ccur+=cstep) { pt3d.CastFrom(path->Evaluate(ccur)); m_ProjectionPlane->Map(pt3d, pt2d); xProj[0]=pt2d[0]; if(pt2d[0]<min[0]) min[0]=pt2d[0]; if(pt2d[0]>max[0]) max[0]=pt2d[0]; xProj[1]=pt2d[1]; if(pt2d[1]<min[1]) min[1]=pt2d[1]; if(pt2d[1]>max[1]) max[1]=pt2d[1]; m_Polygon->Points->SetPoint(i, xProj); m_Polygon->PointIds->SetId(i, i); } // shift parametric origin to (0,0) for(i=0; i<numPts; ++i) { double * pt = this->m_Polygon->Points->GetPoint(i); pt[0]-=min[0]; pt[1]-=min[1]; itkDebugMacro( << i << ": (" << pt[0] << "," << pt[1] << "," << pt[2] << ")" ); } this->m_Polygon->GetBounds(m_ProjectedContourBounds); //m_ProjectedContourBounds[4]=-1.0; m_ProjectedContourBounds[5]=1.0; // calculate origin (except translation along the normal) and bounds // of m_ProjectionPlane: // origin is composed of the minimum x-/y-coordinates of the polygon, // bounds from the extent of the polygon, both after projecting on the plane mitk::Point3D origin; m_ProjectionPlane->Map(min, origin); ScalarType bounds[6]={0, max[0]-min[0], 0, max[1]-min[1], 0, 1}; m_ProjectionPlane->SetBounds(bounds); m_ProjectionPlane->SetOrigin(origin); // Part III: initialize geometry if(m_ClippingGeometry.IsNotNull()) { ScalarType min_dist=ScalarTypeNumericTraits::max(), max_dist=ScalarTypeNumericTraits::min(), dist; unsigned char i; for(i=0; i<8; ++i) { dist = m_ProjectionPlane->SignedDistance( m_ClippingGeometry->GetCornerPoint(i) ); if(dist<min_dist) min_dist=dist; if(dist>max_dist) max_dist=dist; } //incorporate translation along the normal into origin origin = origin+m_Vector*min_dist; m_ProjectionPlane->SetOrigin(origin); bounds[5]=max_dist-min_dist; } else bounds[5]=20; itk2vtk(origin, m_Origin); mitk::Geometry3D::Pointer g3d = GetGeometry( 0 ); assert( g3d.IsNotNull() ); g3d->SetBounds(bounds); g3d->SetIndexToWorldTransform(m_ProjectionPlane->GetIndexToWorldTransform()); g3d->TransferItkToVtkTransform(); ProportionalTimeGeometry::Pointer timeGeometry = ProportionalTimeGeometry::New(); timeGeometry->Initialize(g3d,1); SetTimeGeometry(timeGeometry); }
double mitk::SortByImagePositionPatient ::InternalNumericDistance(const mitk::DICOMDatasetAccess* left, const mitk::DICOMDatasetAccess* right, bool& possible) const { // sort by distance to world origin, assuming (almost) equal orientation static const DICOMTag tagImagePositionPatient = DICOMTag(0x0020,0x0032); // Image Position (Patient) static const DICOMTag tagImageOrientation = DICOMTag(0x0020, 0x0037); // Image Orientation Vector3D leftRight; leftRight.Fill(0.0); Vector3D leftUp; leftUp.Fill(0.0); bool leftHasOrientation(false); DICOMStringToOrientationVectors( left->GetTagValueAsString( tagImageOrientation ).value, leftRight, leftUp, leftHasOrientation ); Vector3D rightRight; rightRight.Fill(0.0); Vector3D rightUp; rightUp.Fill(0.0); bool rightHasOrientation(false); DICOMStringToOrientationVectors(right->GetTagValueAsString(tagImageOrientation).value, rightRight, rightUp, rightHasOrientation ); Point3D leftOrigin; leftOrigin.Fill(0.0f); bool leftHasOrigin(false); leftOrigin = DICOMStringToPoint3D(left->GetTagValueAsString(tagImagePositionPatient).value, leftHasOrigin); Point3D rightOrigin; rightOrigin.Fill(0.0f); bool rightHasOrigin(false); rightOrigin = DICOMStringToPoint3D(right->GetTagValueAsString(tagImagePositionPatient).value, rightHasOrigin); // we tolerate very small differences in image orientation, since we got to know about // acquisitions where these values change across a single series (7th decimal digit) // (http://bugs.mitk.org/show_bug.cgi?id=12263) // still, we want to check if our assumption of 'almost equal' orientations is valid for (unsigned int dim = 0; dim < 3; ++dim) { if ( fabs(leftRight[dim] - rightRight[dim]) > 0.0001 || fabs(leftUp[dim] - rightUp[dim]) > 0.0001) { MITK_ERROR << "Dicom images have different orientations."; throw std::logic_error("Dicom images have different orientations. Call GetSeries() first to separate images."); } } Vector3D normal; normal[0] = leftRight[1] * leftUp[2] - leftRight[2] * leftUp[1]; normal[1] = leftRight[2] * leftUp[0] - leftRight[0] * leftUp[2]; normal[2] = leftRight[0] * leftUp[1] - leftRight[1] * leftUp[0]; double leftDistance = 0.0; double rightDistance = 0.0; // this computes the distance from world origin (0,0,0) ALONG THE NORMAL of the image planes for (unsigned int dim = 0; dim < 3; ++dim) { leftDistance += normal[dim] * leftOrigin[dim]; rightDistance += normal[dim] * rightOrigin[dim]; } // if we can sort by just comparing the distance, we do exactly that if ( fabs(leftDistance - rightDistance) >= mitk::eps) { possible = true; // default: compare position return rightDistance - leftDistance; // if (left < right> ==> diff > 0 } else { possible = false; return 0.0; } }
bool mitk::PicHelper::GetSpacing(const mitkIpPicDescriptor* aPic, Vector3D & spacing) { mitkIpPicDescriptor* pic = const_cast<mitkIpPicDescriptor*>(aPic); mitkIpPicTSV_t *tsv; bool pixelSize = false; tsv = mitkIpPicQueryTag( pic, "REAL PIXEL SIZE" ); if(tsv==NULL) { tsv = mitkIpPicQueryTag( pic, "PIXEL SIZE" ); pixelSize = true; } if(tsv) { bool tagFound = false; if((tsv->dim*tsv->n[0]>=3) && (tsv->type==mitkIpPicFloat)) { if(tsv->bpe==32) { FillVector3D(spacing,((mitkIpFloat4_t*)tsv->value)[0], ((mitkIpFloat4_t*)tsv->value)[1],((mitkIpFloat4_t*)tsv->value)[2]); tagFound = true; } else if(tsv->bpe==64) { FillVector3D(spacing,((mitkIpFloat8_t*)tsv->value)[0], ((mitkIpFloat8_t*)tsv->value)[1],((mitkIpFloat8_t*)tsv->value)[2]); tagFound = true; } } if(tagFound && pixelSize) { tsv = mitkIpPicQueryTag( pic, "PIXEL SPACING" ); if(tsv) { mitk::ScalarType zSpacing = 0; if((tsv->dim*tsv->n[0]>=3) && (tsv->type==mitkIpPicFloat)) { if(tsv->bpe==32) { zSpacing = ((mitkIpFloat4_t*)tsv->value)[2]; } else if(tsv->bpe==64) { zSpacing = ((mitkIpFloat8_t*)tsv->value)[2]; } if(zSpacing != 0) { spacing[2] = zSpacing; } } } } if(tagFound) return true; } #ifdef HAVE_IPDICOM tsv = mitkIpPicQueryTag( pic, "SOURCE HEADER" ); if( tsv ) { void *data; mitkIpUInt4_t len; mitkIpFloat8_t spacing_z = 0; mitkIpFloat8_t thickness = 1; mitkIpFloat8_t fx = 1; mitkIpFloat8_t fy = 1; bool ok=false; if( dicomFindElement( (unsigned char *) tsv->value, 0x0018, 0x0088, &data, &len ) ) { ok=true; sscanf( (char *) data, "%lf", &spacing_z ); // itkGenericOutputMacro( "spacing: " << spacing_z << " mm"); } if( dicomFindElement( (unsigned char *) tsv->value, 0x0018, 0x0050, &data, &len ) ) { ok=true; sscanf( (char *) data, "%lf", &thickness ); // itkGenericOutputMacro( "thickness: " << thickness << " mm"); if( thickness == 0 ) thickness = 1; } if( dicomFindElement( (unsigned char *) tsv->value, 0x0028, 0x0030, &data, &len ) && len>0 && ((char *)data)[0] ) { sscanf( (char *) data, "%lf\\%lf", &fy, &fx ); // row / column value // itkGenericOutputMacro( "fx, fy: " << fx << "/" << fy << " mm"); } else ok=false; if(ok) FillVector3D(spacing, fx, fy,( spacing_z > 0 ? spacing_z : thickness)); return ok; } #endif /* HAVE_IPDICOM */ if(spacing[0]<=0 || spacing[1]<=0 || spacing[2]<=0) { itkGenericOutputMacro(<< "illegal spacing by pic tag: " << spacing << ". Setting spacing to (1,1,1)."); spacing.Fill(1); }
void mitk::NormalDirectionConsistencySorter ::Sort() { DICOMDatasetList datasets = GetInput(); if (datasets.size() > 1) { // at some point in the code, there is the expectation that // the direction of the slice normals is the same as the direction between // first and last slice origin. We need to make this sure here, because // we want to feed the files into itk::ImageSeriesReader with the consistent // setting of ReverseOrderOff. static const DICOMTag tagImagePositionPatient = DICOMTag(0x0020,0x0032); // Image Position (Patient) static const DICOMTag tagImageOrientation = DICOMTag(0x0020, 0x0037); // Image Orientation DICOMDatasetAccess* firstDS = datasets.front(); DICOMDatasetAccess* lastDS = datasets.back(); // make sure here that the direction from slice to slice is the direction of // image normals... std::string imageOrientationString = firstDS->GetTagValueAsString( tagImageOrientation ); std::string imagePositionPatientFirst = firstDS->GetTagValueAsString( tagImagePositionPatient ); std::string imagePositionPatientLast = lastDS->GetTagValueAsString( tagImagePositionPatient ); Vector3D right; right.Fill(0.0); Vector3D up; up.Fill(0.0); bool hasOrientation(false); DICOMStringToOrientationVectors( imageOrientationString, right, up, hasOrientation ); Point3D firstOrigin; firstOrigin.Fill(0.0f); bool firstHasOrigin(false); firstOrigin = DICOMStringToPoint3D( imagePositionPatientFirst, firstHasOrigin ); Point3D lastOrigin; lastOrigin.Fill(0.0f); bool lastHasOrigin(false); lastOrigin = DICOMStringToPoint3D( imagePositionPatientLast, lastHasOrigin ); Vector3D normal; normal[0] = right[1] * up[2] - right[2] * up[1]; normal[1] = right[2] * up[0] - right[0] * up[2]; normal[2] = right[0] * up[1] - right[1] * up[0]; normal.Normalize(); Vector3D directionOfSlices; directionOfSlices = lastOrigin - firstOrigin; directionOfSlices.Normalize(); double projection = normal * directionOfSlices; MITK_DEBUG << "Making sense of \norientation '" << imageOrientationString << "'\nfirst position '" << imagePositionPatientFirst << "'\nlast position '" << imagePositionPatientLast << "'"; MITK_DEBUG << "Normal: " << normal; MITK_DEBUG << "Direction of slices: " << directionOfSlices; MITK_DEBUG << "Projection of direction onto slice normal: " << projection; if ( projection < 0.0 ) { MITK_DEBUG << "Need to reverse filenames"; std::reverse( datasets.begin(), datasets.end() ); m_TiltInfo = GantryTiltInformation::MakeFromTagValues( imagePositionPatientLast, imagePositionPatientFirst, imageOrientationString, datasets.size() - 1 ); } else { m_TiltInfo = GantryTiltInformation::MakeFromTagValues( imagePositionPatientFirst, imagePositionPatientLast, imageOrientationString, datasets.size() - 1 ); } } else // just ONE dataset, do not forget to reset tilt information { m_TiltInfo = GantryTiltInformation(); // empty info } this->SetNumberOfOutputs(1); this->SetOutput(0, datasets); }