changeset 1941:d7b176f7dd1b

test dicom patterns
author Sebastien Jodogne <s.jodogne@gmail.com>
date Fri, 25 Mar 2016 11:27:38 +0100
parents 5514d37176b4
children cc9901a98099
files NEWS OrthancServer/ParsedDicomFile.cpp UnitTestsSources/FromDcmtkTests.cpp
diffstat 3 files changed, 197 insertions(+), 8 deletions(-) [+]
line wrap: on
line diff
--- a/NEWS	Fri Mar 25 11:08:07 2016 +0100
+++ b/NEWS	Fri Mar 25 11:27:38 2016 +0100
@@ -11,6 +11,7 @@
 * Support decoding of RLE Lossless transfer syntax
 * Fix issue 11 (is_regular_file() fails for FILE_ATTRIBUTE_REPARSE_POINT)
 * Upgrade to Boost 1.60.0 for static builds
+* Support of signed 16bpp images in ParsedDicomFile
 
 
 Version 1.0.0 (2015/12/15)
--- a/OrthancServer/ParsedDicomFile.cpp	Fri Mar 25 11:08:07 2016 +0100
+++ b/OrthancServer/ParsedDicomFile.cpp	Fri Mar 25 11:27:38 2016 +0100
@@ -933,6 +933,7 @@
   {
     if (accessor.GetFormat() != PixelFormat_Grayscale8 &&
         accessor.GetFormat() != PixelFormat_Grayscale16 &&
+        accessor.GetFormat() != PixelFormat_SignedGrayscale16 &&
         accessor.GetFormat() != PixelFormat_RGB24 &&
         accessor.GetFormat() != PixelFormat_RGBA32)
     {
@@ -951,28 +952,42 @@
     Replace(DICOM_TAG_ROWS, boost::lexical_cast<std::string>(accessor.GetHeight()));
     Replace(DICOM_TAG_SAMPLES_PER_PIXEL, "1");
     Replace(DICOM_TAG_NUMBER_OF_FRAMES, "1");
-    Replace(DICOM_TAG_PIXEL_REPRESENTATION, "0");  // Unsigned pixels
+
+    if (accessor.GetFormat() == PixelFormat_SignedGrayscale16)
+    {
+      Replace(DICOM_TAG_PIXEL_REPRESENTATION, "1");
+    }
+    else
+    {
+      Replace(DICOM_TAG_PIXEL_REPRESENTATION, "0");  // Unsigned pixels
+    }
+
     Replace(DICOM_TAG_PLANAR_CONFIGURATION, "0");  // Color channels are interleaved
     Replace(DICOM_TAG_PHOTOMETRIC_INTERPRETATION, "MONOCHROME2");
-    Replace(DICOM_TAG_BITS_ALLOCATED, "8");
-    Replace(DICOM_TAG_BITS_STORED, "8");
-    Replace(DICOM_TAG_HIGH_BIT, "7");
 
-    unsigned int bytesPerPixel = 1;
+    unsigned int bytesPerPixel = 0;
 
     switch (accessor.GetFormat())
     {
+      case PixelFormat_Grayscale8:
+        Replace(DICOM_TAG_BITS_ALLOCATED, "8");
+        Replace(DICOM_TAG_BITS_STORED, "8");
+        Replace(DICOM_TAG_HIGH_BIT, "7");
+        bytesPerPixel = 1;
+        break;
+
       case PixelFormat_RGB24:
       case PixelFormat_RGBA32:
         Replace(DICOM_TAG_PHOTOMETRIC_INTERPRETATION, "RGB");
         Replace(DICOM_TAG_SAMPLES_PER_PIXEL, "3");
+        Replace(DICOM_TAG_BITS_ALLOCATED, "8");
+        Replace(DICOM_TAG_BITS_STORED, "8");
+        Replace(DICOM_TAG_HIGH_BIT, "7");
         bytesPerPixel = 3;
         break;
 
-      case PixelFormat_Grayscale8:
-        break;
-
       case PixelFormat_Grayscale16:
+      case PixelFormat_SignedGrayscale16:
         Replace(DICOM_TAG_BITS_ALLOCATED, "16");
         Replace(DICOM_TAG_BITS_STORED, "16");
         Replace(DICOM_TAG_HIGH_BIT, "15");
@@ -983,6 +998,8 @@
         throw OrthancException(ErrorCode_NotImplemented);
     }
 
+    assert(bytesPerPixel != 0);
+
     DcmTag key(DICOM_TAG_PIXEL_DATA.GetGroup(), 
                DICOM_TAG_PIXEL_DATA.GetElement());
 
--- a/UnitTestsSources/FromDcmtkTests.cpp	Fri Mar 25 11:08:07 2016 +0100
+++ b/UnitTestsSources/FromDcmtkTests.cpp	Fri Mar 25 11:27:38 2016 +0100
@@ -768,6 +768,121 @@
 
 
 
+TEST(TestImages, PatternGrayscale8)
+{
+  static const char* PATH = "UnitTestsResults/PatternGrayscale8.dcm";
+
+  Orthanc::Image image(Orthanc::PixelFormat_Grayscale8, 256, 256);
+
+  for (int y = 0; y < 256; y++)
+  {
+    uint8_t *p = reinterpret_cast<uint8_t*>(image.GetRow(y));
+    for (int x = 0; x < 256; x++, p++)
+    {
+      *p = y;
+    }
+  }
+
+  Orthanc::ImageAccessor r = image.GetRegion(32, 32, 64, 192);
+  Orthanc::ImageProcessing::Set(r, 0); 
+  r = image.GetRegion(160, 32, 64, 192);
+  Orthanc::ImageProcessing::Set(r, 255); 
+
+  {
+    ParsedDicomFile f(true);
+    f.Replace(DICOM_TAG_SOP_CLASS_UID, "1.2.840.10008.5.1.4.1.1.7");
+    f.Replace(DICOM_TAG_STUDY_INSTANCE_UID, "1.2.276.0.7230010.3.1.2.2831176407.321.1458901422.884998");
+    f.Replace(DICOM_TAG_PATIENT_ID, "ORTHANC");
+    f.Replace(DICOM_TAG_PATIENT_NAME, "Orthanc");
+    f.Replace(DICOM_TAG_STUDY_DESCRIPTION, "Patterns");
+    f.Replace(DICOM_TAG_SERIES_DESCRIPTION, "Grayscale8");
+    f.EmbedImage(image);
+
+    f.SaveToFile(PATH);
+  }
+
+  {
+    std::string s;
+    Orthanc::Toolbox::ReadFile(s, PATH);
+    Orthanc::ParsedDicomFile f(s);
+    
+    std::auto_ptr<Orthanc::ImageAccessor> decoded(Orthanc::DicomImageDecoder::Decode(f, 0));
+    ASSERT_EQ(256, decoded->GetWidth());
+    ASSERT_EQ(256, decoded->GetHeight());
+    ASSERT_EQ(Orthanc::PixelFormat_Grayscale8, decoded->GetFormat());
+
+    for (int y = 0; y < 256; y++)
+    {
+      const void* a = image.GetConstRow(y);
+      const void* b = decoded->GetConstRow(y);
+      ASSERT_EQ(0, memcmp(a, b, 256));
+    }
+  }
+}
+
+
+TEST(TestImages, PatternRGB)
+{
+  static const char* PATH = "UnitTestsResults/PatternRGB24.dcm";
+
+  Orthanc::Image image(Orthanc::PixelFormat_RGB24, 384, 256);
+
+  for (int y = 0; y < 256; y++)
+  {
+    uint8_t *p = reinterpret_cast<uint8_t*>(image.GetRow(y));
+    for (int x = 0; x < 128; x++, p += 3)
+    {
+      p[0] = y;
+      p[1] = 0;
+      p[2] = 0;
+    }
+    for (int x = 128; x < 128 * 2; x++, p += 3)
+    {
+      p[0] = 0;
+      p[1] = 255 - y;
+      p[2] = 0;
+    }
+    for (int x = 128 * 2; x < 128 * 3; x++, p += 3)
+    {
+      p[0] = 0;
+      p[1] = 0;
+      p[2] = y;
+    }
+  }
+
+  {
+    ParsedDicomFile f(true);
+    f.Replace(DICOM_TAG_SOP_CLASS_UID, "1.2.840.10008.5.1.4.1.1.7");
+    f.Replace(DICOM_TAG_STUDY_INSTANCE_UID, "1.2.276.0.7230010.3.1.2.2831176407.321.1458901422.884998");
+    f.Replace(DICOM_TAG_PATIENT_ID, "ORTHANC");
+    f.Replace(DICOM_TAG_PATIENT_NAME, "Orthanc");
+    f.Replace(DICOM_TAG_STUDY_DESCRIPTION, "Patterns");
+    f.Replace(DICOM_TAG_SERIES_DESCRIPTION, "RGB24");
+    f.EmbedImage(image);
+
+    f.SaveToFile(PATH);
+  }
+
+  {
+    std::string s;
+    Orthanc::Toolbox::ReadFile(s, PATH);
+    Orthanc::ParsedDicomFile f(s);
+    
+    std::auto_ptr<Orthanc::ImageAccessor> decoded(Orthanc::DicomImageDecoder::Decode(f, 0));
+    ASSERT_EQ(384, decoded->GetWidth());
+    ASSERT_EQ(256, decoded->GetHeight());
+    ASSERT_EQ(Orthanc::PixelFormat_RGB24, decoded->GetFormat());
+
+    for (int y = 0; y < 256; y++)
+    {
+      const void* a = image.GetConstRow(y);
+      const void* b = decoded->GetConstRow(y);
+      ASSERT_EQ(0, memcmp(a, b, 3 * 384));
+    }
+  }
+}
+
+
 TEST(TestImages, PatternUint16)
 {
   static const char* PATH = "UnitTestsResults/PatternGrayscale16.dcm";
@@ -791,6 +906,8 @@
 
   {
     ParsedDicomFile f(true);
+    f.Replace(DICOM_TAG_SOP_CLASS_UID, "1.2.840.10008.5.1.4.1.1.7");
+    f.Replace(DICOM_TAG_STUDY_INSTANCE_UID, "1.2.276.0.7230010.3.1.2.2831176407.321.1458901422.884998");
     f.Replace(DICOM_TAG_PATIENT_ID, "ORTHANC");
     f.Replace(DICOM_TAG_PATIENT_NAME, "Orthanc");
     f.Replace(DICOM_TAG_STUDY_DESCRIPTION, "Patterns");
@@ -818,3 +935,57 @@
     }
   }
 }
+
+
+TEST(TestImages, PatternInt16)
+{
+  static const char* PATH = "UnitTestsResults/PatternSignedGrayscale16.dcm";
+
+  Orthanc::Image image(Orthanc::PixelFormat_SignedGrayscale16, 256, 256);
+
+  int16_t v = -32768;
+  for (int y = 0; y < 256; y++)
+  {
+    int16_t *p = reinterpret_cast<int16_t*>(image.GetRow(y));
+    for (int x = 0; x < 256; x++, v++, p++)
+    {
+      *p = htole16(v);   // Orthanc uses Little-Endian transfer syntax to encode images
+    }
+  }
+
+  Orthanc::ImageAccessor r = image.GetRegion(32, 32, 64, 192);
+  Orthanc::ImageProcessing::Set(r, -32768); 
+  r = image.GetRegion(160, 32, 64, 192);
+  Orthanc::ImageProcessing::Set(r, 32767); 
+
+  {
+    ParsedDicomFile f(true);
+    f.Replace(DICOM_TAG_SOP_CLASS_UID, "1.2.840.10008.5.1.4.1.1.7");
+    f.Replace(DICOM_TAG_STUDY_INSTANCE_UID, "1.2.276.0.7230010.3.1.2.2831176407.321.1458901422.884998");
+    f.Replace(DICOM_TAG_PATIENT_ID, "ORTHANC");
+    f.Replace(DICOM_TAG_PATIENT_NAME, "Orthanc");
+    f.Replace(DICOM_TAG_STUDY_DESCRIPTION, "Patterns");
+    f.Replace(DICOM_TAG_SERIES_DESCRIPTION, "SignedGrayscale16");
+    f.EmbedImage(image);
+
+    f.SaveToFile(PATH);
+  }
+
+  {
+    std::string s;
+    Orthanc::Toolbox::ReadFile(s, PATH);
+    Orthanc::ParsedDicomFile f(s);
+    
+    std::auto_ptr<Orthanc::ImageAccessor> decoded(Orthanc::DicomImageDecoder::Decode(f, 0));
+    ASSERT_EQ(256, decoded->GetWidth());
+    ASSERT_EQ(256, decoded->GetHeight());
+    ASSERT_EQ(Orthanc::PixelFormat_SignedGrayscale16, decoded->GetFormat());
+
+    for (int y = 0; y < 256; y++)
+    {
+      const void* a = image.GetConstRow(y);
+      const void* b = decoded->GetConstRow(y);
+      ASSERT_EQ(0, memcmp(a, b, 512));
+    }
+  }
+}