# HG changeset patch # User Sebastien Jodogne # Date 1458901658 -3600 # Node ID d7b176f7dd1bc5437c1f6c1c32136e613fa6c1e8 # Parent 5514d37176b4442db75f5ca472bf4bd71889debd test dicom patterns diff -r 5514d37176b4 -r d7b176f7dd1b NEWS --- 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) diff -r 5514d37176b4 -r d7b176f7dd1b OrthancServer/ParsedDicomFile.cpp --- 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(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()); diff -r 5514d37176b4 -r d7b176f7dd1b UnitTestsSources/FromDcmtkTests.cpp --- 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(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 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(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 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(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 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)); + } + } +}