iv3d-users

Text archives Help


[IV3D-USERS] Re: Re: Improving NRRD/skewed volumes


Chronological Thread 
  • From: Tim Holy <holy@wustl.edu>
  • To: iv3d-users@sci.utah.edu
  • Cc: Zhongsheng Guo <zsguo@pcg.wustl.edu>, Pei Sabrina Xu <xupei@wustl.edu>
  • Subject: [IV3D-USERS] Re: Re: Improving NRRD/skewed volumes
  • Date: Sun, 16 Oct 2011 14:36:13 -0500

Hi Tom,

OK, I got to this a bit sooner than expected. As it turns out, most of the 
work was already done: there was already code supporting the "space 
directions" tag, though it took a couple of bug fixes to get it working 
properly.

I also transitioned from FLOATVECTOR3 vVolumeAspect to a FLOATMATRIX3 with 
the 
name "mGrid2Phys" (meaning, the matrix that you multiply any vector of grid 
displacements by to convert to the displacement in physical units). Feel free 
to suggest an alternate name, or of course any other changes to the patch.

While I did test an intermediate version of the patch (fixing the "space 
directions" parsing), it has not been tested in its final form, because it 
involves an API change in other code. But I think it would be wise to get 
your 
feedback before taking further steps.

Best,
--Tim


On Monday, October 10, 2011 02:29:22 pm tom fogal wrote:
> Hi Tim,
> 
> On 10/10/2011 01:01 PM, Tim Holy wrote:
> > I'm writing with a question and proposal. We have new data volumes in
> > which the coordinate axes, in physical space, are not orthogonal; the
> > pixels are therefore "skewed." By using the (new?) UVFReader tool,
> 
> It's existed for a while, but we don't distribute it.  It's really just
> for us developers to debug things.
> 
> > I stumbled across the fact that UVF files appear to permit a generic
> > transformation matrix to be supplied. If so, this would seem to
> > suggest that such volumes can be rendered faithfully, as long as the
> > proper transformation matrix is supplied.
> > 
> > First and foremost, is this true? I.e., is the support in Tuvok there
> > for this?
> 
> Hrm, it's not in Dataset.h, so I don't think the renderer side can even
> query this.
> 
> Anyway... the only thing that needs to be done is multiply the modelView
> by the given matrix (probably in GLRenderer::ComputeViewAndProjection),
> so this is simple for me to fix.
> 
> > If so, then here's the proposal:
> [snip]
> 
> All sounds good.  If you do that IO side, I will fix 'Dataset' + the
> renderer.
> 
> Cheers,
> 
> -tom
Index: NRRDConverter.h
===================================================================
--- NRRDConverter.h	(revision 1660)
+++ NRRDConverter.h	(working copy)
@@ -53,13 +53,13 @@
                             const std::string& strTempDir, bool bNoUserInteraction,
                             UINT64& iHeaderSkip, UINT64& iComponentSize, UINT64& iComponentCount,
                             bool& bConvertEndianess, bool& bSigned, bool& bIsFloat, UINT64VECTOR3& vVolumeSize,
-                            FLOATVECTOR3& vVolumeAspect, std::string& strTitle,
+                            FLOATMATRIX3& mGrid2Phys, std::string& strTitle,
                             UVFTables::ElementSemanticTable& eType, std::string& strIntermediateFile,
                             bool& bDeleteIntermediateFile);
 
   virtual bool ConvertToNative(const std::string& strRawFilename, const std::string& strTargetFilename, UINT64 iHeaderSkip,
                                UINT64 iComponentSize, UINT64 iComponentCount, bool bSigned, bool bFloatingPoint,
-                               UINT64VECTOR3 vVolumeSize,FLOATVECTOR3 vVolumeAspect, bool bNoUserInteraction,
+                               UINT64VECTOR3 vVolumeSize,FLOATMATRIX3 mGrid2Phys, bool bNoUserInteraction,
                                const bool bQuantizeTo8Bit);
 
   virtual bool CanExportData() const {return true;}
Index: NRRDConverter.cpp
===================================================================
--- NRRDConverter.cpp	(revision 1660)
+++ NRRDConverter.cpp	(working copy)
@@ -58,7 +58,7 @@
                                  UINT64& iComponentCount,
                                  bool& bConvertEndianess, bool& bSigned,
                                  bool& bIsFloat, UINT64VECTOR3& vVolumeSize,
-                                 FLOATVECTOR3& vVolumeAspect,
+                                 FLOATMATRIX3& mGrid2Phys,
                                  std::string& strTitle,
                                  UVFTables::ElementSemanticTable& eType,
                                  std::string& strIntermediateFile,
@@ -91,7 +91,7 @@
   bSigned=false;
   bool bBigEndian=false;
   vVolumeSize = UINT64VECTOR3(1,1,1);
-  vVolumeAspect = FLOATVECTOR3(1,1,1);
+  mGrid2Phys = FLOATMATRIX3(1,0,0,0,1,0,0,0,1);
   string        strRAWFile;
 
   // read data
@@ -221,7 +221,7 @@
     for (size_t i = 0;i<kvpSizes->vuiValue.size();i++) {
       if (kvpSpacings->vfValue.size() <= i) break;
       if (kvpSizes->vuiValue[i] > 1) {
-        vVolumeAspect[j] = kvpSpacings->vfValue[i];
+        mGrid2Phys[4*j] = kvpSpacings->vfValue[i];  // set just the diagonal
         j++;
       }
     }
@@ -230,13 +230,16 @@
   try {
     KeyValPair* kvpSpaceDirs = parser.GetData("SPACE DIRECTIONS");
     if (kvpSpaceDirs != NULL) {
+      if (kvpSpacings != NULL)
+	WARNING("Found both 'spacings' and 'space directions' fields, using 'space directions'.");
+
       std::vector<std::string> dirs = kvpSpaceDirs->vstrValue;
         
       if (dirs.size() == 3) {
       
         for (size_t dim = 0;dim<3;dim++) {
-          FLOATVECTOR3 axis;
-
+	  bool success;
+	  // Find the beginning of a number
           size_t iStart = 0;
 
           for (size_t vDim = 0;vDim<3;vDim++) {
@@ -251,6 +254,7 @@
               }
             }
 
+	    // Find the end of the number
             size_t iEnd = iStart+1;
             while (isdigit(dirs[dim][iEnd]) ||
                    '.' == dirs[dim][iEnd] ||
@@ -261,12 +265,14 @@
                 throw tuvok::io::DSParseFailed("Ignoring malformed 'space directions' tag.");
               }
             }
-            
-            axis[vDim] = SysTools::FromString<float>(dirs[dim].substr(iStart, 1+iStart-iEnd));
 
+	    // Convert from ASCII to a numeric value
+            success = SysTools::FromString<float>(mGrid2Phys[3*dim+vDim],dirs[dim].substr(iStart, iEnd-iStart));
+	    if (!success)
+	      throw tuvok::io::DSParseFailed("Error parsing floating point value for 'space directions' tag.");
+
             iStart = iEnd;
           }
-          vVolumeAspect[dim] *= axis.length();
         }
 
       } else {
@@ -416,7 +422,7 @@
 
 bool NRRDConverter::ConvertToNative(const std::string& strRawFilename, const std::string& strTargetFilename, UINT64 iHeaderSkip,
                              UINT64 iComponentSize, UINT64 iComponentCount, bool bSigned, bool bFloatingPoint,
-                             UINT64VECTOR3 vVolumeSize,FLOATVECTOR3 vVolumeAspect, bool bNoUserInteraction,
+                             UINT64VECTOR3 vVolumeSize,FLOATMATRIX3 mGrid2Phys, bool bNoUserInteraction,
                              const bool bQuantizeTo8Bit) {
 
   bool bDetached = SysTools::ToLowerCase(SysTools::GetExt(strTargetFilename)) == "nhdr";
@@ -468,11 +474,36 @@
     return false;
   }
 
-  fAsciiTarget << "NRRD0001" << endl;
+  // Are there any off-diagonal elements in the transformation?
+  bool bDiagonal = (mGrid2Phys.m12 == 0 && 
+		    mGrid2Phys.m13 == 0 &&
+		    mGrid2Phys.m21 == 0 &&
+		    mGrid2Phys.m23 == 0 &&
+		    mGrid2Phys.m31 == 0 &&
+		    mGrid2Phys.m32 == 0);
+
+  if (bDiagonal)
+    fAsciiTarget << "NRRD0001" << endl;
+  else
+    fAsciiTarget << "NRRD0004" << endl;
   fAsciiTarget << "type: " << strFormat << endl;
   fAsciiTarget << "dimension: 3" << endl;
   fAsciiTarget << "sizes:     " << vVolumeSize.x << " " << vVolumeSize.y << " "<< vVolumeSize.z << endl;
-  fAsciiTarget << "spacings: " << vVolumeAspect.x << " " << vVolumeAspect.y << " "<< vVolumeAspect.z << endl;
+  if (bDiagonal)
+    fAsciiTarget << "spacings: " << mGrid2Phys.m11 << " " << mGrid2Phys.m22 << " "<< mGrid2Phys.m33 << endl;
+  else {
+    fAsciiTarget << "space directions: ";
+    for (int dim = 0; dim < 3; dim++) {
+      fAsciiTarget << "(";
+      for (int dim2 = 0; dim2 < 3; dim2++) {
+	fAsciiTarget << mGrid2Phys.array[3*dim+dim2];
+	if (dim2 < 2)
+	  fAsciiTarget << ",";
+      }
+      fAsciiTarget << ") ";
+    }
+    fAsciiTarget << endl;
+  }
   fAsciiTarget << "endian: little" << endl;
   fAsciiTarget << "encoding: raw" << endl;
 
@@ -484,7 +515,7 @@
     // copy RAW file using the parent's call
     bool bRAWSuccess = RAWConverter::ConvertToNative(strRawFilename, strTargetRAWFilename, iHeaderSkip,
                                                      iComponentSize, iComponentCount, bSigned, bFloatingPoint,
-                                                     vVolumeSize, vVolumeAspect, bNoUserInteraction, bQuantizeTo8Bit);
+                                                     vVolumeSize, mGrid2Phys, bNoUserInteraction, bQuantizeTo8Bit);
 
     if (bRAWSuccess) {
       return true;
@@ -505,7 +536,7 @@
     if (bRAWSuccess) {
       return true;
     } else {
-      T_ERROR("Error appaneding raw data to header file %s.", strTargetFilename.c_str());
+      T_ERROR("Error appending raw data to header file %s.", strTargetFilename.c_str());
       remove(strTargetFilename.c_str());
       return false;
     }



Archive powered by MHonArc 2.6.16.

Top of page