Mercurial > hg > orthanc-webviewer
changeset 172:330ecfd96aec
sync
line wrap: on
line diff
--- a/CMakeLists.txt Thu Aug 24 19:49:20 2017 +0200 +++ b/CMakeLists.txt Tue Oct 10 12:53:46 2017 +0200 @@ -37,8 +37,8 @@ set(USE_SYSTEM_ORTHANC_SDK ON CACHE BOOL "Use the system version of the Orthanc plugin SDK") # Distribution-specific settings -set(USE_GTEST_DEBIAN_SOURCE_PACKAGE OFF CACHE BOOL "Use the sources of Google Test shipped with libgtest-dev (Debian only)") -mark_as_advanced(USE_GTEST_DEBIAN_SOURCE_PACKAGE) +set(USE_GOOGLE_TEST_DEBIAN_PACKAGE OFF CACHE BOOL "Use the sources of Google Test shipped with libgtest-dev (Debian only)") +mark_as_advanced(USE_GOOGLE_TEST_DEBIAN_PACKAGE) set(ORTHANC_ROOT ${CMAKE_SOURCE_DIR}/Orthanc) set(ORTHANC_DISABLE_PATCH ON) # No need for the "patch" command-line tool @@ -92,8 +92,10 @@ ) add_definitions( + -DORTHANC_DEFAULT_DICOM_ENCODING=Encoding_Latin1 -DORTHANC_ENABLE_LOCALE=0 -DORTHANC_ENABLE_PUGIXML=0 + -DORTHANC_ENABLE_SQLITE=1 -DORTHANC_SQLITE_STANDALONE=1 ) @@ -201,7 +203,7 @@ add_executable(UnitTests ${CORE_SOURCES} - ${GTEST_SOURCES} + ${GOOGLE_TEST_SOURCES} ${JSONCPP_SOURCES} UnitTestsSources/UnitTestsMain.cpp )
--- a/Orthanc/Core/DicomFormat/DicomMap.cpp Thu Aug 24 19:49:20 2017 +0200 +++ b/Orthanc/Core/DicomFormat/DicomMap.cpp Tue Oct 10 12:53:46 2017 +0200 @@ -36,7 +36,9 @@ #include <stdio.h> #include <memory> + #include "../Endianness.h" +#include "../Logging.h" #include "../OrthancException.h" @@ -781,4 +783,193 @@ return true; } + + + static std::string ValueAsString(const DicomMap& summary, + const DicomTag& tag) + { + const DicomValue& value = summary.GetValue(tag); + if (value.IsNull()) + { + return "(null)"; + } + else + { + return value.GetContent(); + } + } + + + void DicomMap::LogMissingTagsForStore() const + { + std::string s, t; + + if (HasTag(DICOM_TAG_PATIENT_ID)) + { + if (t.size() > 0) + t += ", "; + t += "PatientID=" + ValueAsString(*this, DICOM_TAG_PATIENT_ID); + } + else + { + if (s.size() > 0) + s += ", "; + s += "PatientID"; + } + + if (HasTag(DICOM_TAG_STUDY_INSTANCE_UID)) + { + if (t.size() > 0) + t += ", "; + t += "StudyInstanceUID=" + ValueAsString(*this, DICOM_TAG_STUDY_INSTANCE_UID); + } + else + { + if (s.size() > 0) + s += ", "; + s += "StudyInstanceUID"; + } + + if (HasTag(DICOM_TAG_SERIES_INSTANCE_UID)) + { + if (t.size() > 0) + t += ", "; + t += "SeriesInstanceUID=" + ValueAsString(*this, DICOM_TAG_SERIES_INSTANCE_UID); + } + else + { + if (s.size() > 0) + s += ", "; + s += "SeriesInstanceUID"; + } + + if (HasTag(DICOM_TAG_SOP_INSTANCE_UID)) + { + if (t.size() > 0) + t += ", "; + t += "SOPInstanceUID=" + ValueAsString(*this, DICOM_TAG_SOP_INSTANCE_UID); + } + else + { + if (s.size() > 0) + s += ", "; + s += "SOPInstanceUID"; + } + + if (t.size() == 0) + { + LOG(ERROR) << "Store has failed because all the required tags (" << s << ") are missing (is it a DICOMDIR file?)"; + } + else + { + LOG(ERROR) << "Store has failed because required tags (" << s << ") are missing for the following instance: " << t; + } + } + + + bool DicomMap::CopyToString(std::string& result, + const DicomTag& tag, + bool allowBinary) const + { + const DicomValue* value = TestAndGetValue(tag); + + if (value == NULL) + { + return false; + } + else + { + return value->CopyToString(result, allowBinary); + } + } + + bool DicomMap::ParseInteger32(int32_t& result, + const DicomTag& tag) const + { + const DicomValue* value = TestAndGetValue(tag); + + if (value == NULL) + { + return false; + } + else + { + return value->ParseInteger32(result); + } + } + + bool DicomMap::ParseInteger64(int64_t& result, + const DicomTag& tag) const + { + const DicomValue* value = TestAndGetValue(tag); + + if (value == NULL) + { + return false; + } + else + { + return value->ParseInteger64(result); + } + } + + bool DicomMap::ParseUnsignedInteger32(uint32_t& result, + const DicomTag& tag) const + { + const DicomValue* value = TestAndGetValue(tag); + + if (value == NULL) + { + return false; + } + else + { + return value->ParseUnsignedInteger32(result); + } + } + + bool DicomMap::ParseUnsignedInteger64(uint64_t& result, + const DicomTag& tag) const + { + const DicomValue* value = TestAndGetValue(tag); + + if (value == NULL) + { + return false; + } + else + { + return value->ParseUnsignedInteger64(result); + } + } + + bool DicomMap::ParseFloat(float& result, + const DicomTag& tag) const + { + const DicomValue* value = TestAndGetValue(tag); + + if (value == NULL) + { + return false; + } + else + { + return value->ParseFloat(result); + } + } + + bool DicomMap::ParseDouble(double& result, + const DicomTag& tag) const + { + const DicomValue* value = TestAndGetValue(tag); + + if (value == NULL) + { + return false; + } + else + { + return value->ParseDouble(result); + } + } }
--- a/Orthanc/Core/DicomFormat/DicomMap.h Thu Aug 24 19:49:20 2017 +0200 +++ b/Orthanc/Core/DicomFormat/DicomMap.h Tue Oct 10 12:53:46 2017 +0200 @@ -181,5 +181,29 @@ static bool ParseDicomMetaInformation(DicomMap& result, const char* dicom, size_t size); + + void LogMissingTagsForStore() const; + + bool CopyToString(std::string& result, + const DicomTag& tag, + bool allowBinary) const; + + bool ParseInteger32(int32_t& result, + const DicomTag& tag) const; + + bool ParseInteger64(int64_t& result, + const DicomTag& tag) const; + + bool ParseUnsignedInteger32(uint32_t& result, + const DicomTag& tag) const; + + bool ParseUnsignedInteger64(uint64_t& result, + const DicomTag& tag) const; + + bool ParseFloat(float& result, + const DicomTag& tag) const; + + bool ParseDouble(double& result, + const DicomTag& tag) const; }; }
--- a/Orthanc/Core/DicomFormat/DicomTag.h Thu Aug 24 19:49:20 2017 +0200 +++ b/Orthanc/Core/DicomFormat/DicomTag.h Tue Oct 10 12:53:46 2017 +0200 @@ -173,6 +173,17 @@ static const DicomTag DICOM_TAG_ACQUISITION_DEVICE_PROCESSING_DESCRIPTION(0x0018, 0x1400); static const DicomTag DICOM_TAG_CONTRAST_BOLUS_AGENT(0x0018, 0x0010); + // Tags used within the Stone of Orthanc + static const DicomTag DICOM_TAG_FRAME_INCREMENT_POINTER(0x0028, 0x0009); + static const DicomTag DICOM_TAG_GRID_FRAME_OFFSET_VECTOR(0x3004, 0x000c); + static const DicomTag DICOM_TAG_PIXEL_SPACING(0x0028, 0x0030); + static const DicomTag DICOM_TAG_RESCALE_INTERCEPT(0x0028, 0x1052); + static const DicomTag DICOM_TAG_RESCALE_SLOPE(0x0028, 0x1053); + static const DicomTag DICOM_TAG_SLICE_THICKNESS(0x0018, 0x0050); + static const DicomTag DICOM_TAG_WINDOW_CENTER(0x0028, 0x1050); + static const DicomTag DICOM_TAG_WINDOW_WIDTH(0x0028, 0x1051); + static const DicomTag DICOM_TAG_DOSE_GRID_SCALING(0x3004, 0x000e); + // Counting patients, studies and series // https://www.medicalconnections.co.uk/kb/Counting_Studies_Series_and_Instances static const DicomTag DICOM_TAG_NUMBER_OF_PATIENT_RELATED_STUDIES(0x0020, 0x1200);
--- a/Orthanc/Core/DicomFormat/DicomValue.cpp Thu Aug 24 19:49:20 2017 +0200 +++ b/Orthanc/Core/DicomFormat/DicomValue.cpp Tue Oct 10 12:53:46 2017 +0200 @@ -37,6 +37,8 @@ #include "../OrthancException.h" #include "../Toolbox.h" +#include <boost/lexical_cast.hpp> + namespace Orthanc { DicomValue::DicomValue(const DicomValue& other) : @@ -91,4 +93,104 @@ } #endif + + template <typename T, + bool allowSigned> + static bool ParseValue(T& result, + const DicomValue& source) + { + if (source.IsBinary() || + source.IsNull()) + { + return false; + } + + try + { + std::string value = Toolbox::StripSpaces(source.GetContent()); + if (value.empty()) + { + return false; + } + + if (!allowSigned && + value[0] == '-') + { + return false; + } + + result = boost::lexical_cast<T>(value); + return true; + } + catch (boost::bad_lexical_cast&) + { + return false; + } + } + + bool DicomValue::ParseInteger32(int32_t& result) const + { + int64_t tmp; + if (ParseValue<int64_t, true>(tmp, *this)) + { + result = static_cast<int32_t>(tmp); + return (tmp == static_cast<int64_t>(result)); // Check no overflow occurs + } + else + { + return false; + } + } + + bool DicomValue::ParseInteger64(int64_t& result) const + { + return ParseValue<int64_t, true>(result, *this); + } + + bool DicomValue::ParseUnsignedInteger32(uint32_t& result) const + { + uint64_t tmp; + if (ParseValue<uint64_t, false>(tmp, *this)) + { + result = static_cast<uint32_t>(tmp); + return (tmp == static_cast<uint64_t>(result)); // Check no overflow occurs + } + else + { + return false; + } + } + + bool DicomValue::ParseUnsignedInteger64(uint64_t& result) const + { + return ParseValue<uint64_t, false>(result, *this); + } + + bool DicomValue::ParseFloat(float& result) const + { + return ParseValue<float, true>(result, *this); + } + + bool DicomValue::ParseDouble(double& result) const + { + return ParseValue<double, true>(result, *this); + } + + bool DicomValue::CopyToString(std::string& result, + bool allowBinary) const + { + if (IsNull()) + { + return false; + } + else if (IsBinary() && !allowBinary) + { + return false; + } + else + { + result.assign(content_); + return true; + } + } }
--- a/Orthanc/Core/DicomFormat/DicomValue.h Thu Aug 24 19:49:20 2017 +0200 +++ b/Orthanc/Core/DicomFormat/DicomValue.h Tue Oct 10 12:53:46 2017 +0200 @@ -33,6 +33,7 @@ #pragma once +#include <stdint.h> #include <string> #include <boost/noncopyable.hpp> @@ -93,5 +94,20 @@ FormatDataUriScheme(target, "application/octet-stream"); } #endif + + bool CopyToString(std::string& result, + bool allowBinary) const; + + bool ParseInteger32(int32_t& result) const; + + bool ParseInteger64(int64_t& result) const; + + bool ParseUnsignedInteger32(uint32_t& result) const; + + bool ParseUnsignedInteger64(uint64_t& result) const; + + bool ParseFloat(float& result) const; + + bool ParseDouble(double& result) const; }; }
--- a/Orthanc/Core/Endianness.h Thu Aug 24 19:49:20 2017 +0200 +++ b/Orthanc/Core/Endianness.h Tue Oct 10 12:53:46 2017 +0200 @@ -35,10 +35,10 @@ /******************************************************************** - ** LINUX ARCHITECTURES + ** LINUX-LIKE ARCHITECTURES ********************************************************************/ -#if defined(__linux__) +#if defined(__linux__) || defined(__EMSCRIPTEN__) # define ORTHANC_HAS_BUILTIN_BYTE_SWAP 1 # include <endian.h> #endif
--- a/Orthanc/Core/Enumerations.cpp Thu Aug 24 19:49:20 2017 +0200 +++ b/Orthanc/Core/Enumerations.cpp Tue Oct 10 12:53:46 2017 +0200 @@ -38,6 +38,7 @@ #include "Toolbox.h" #include "Logging.h" +#include <boost/thread/mutex.hpp> #include <string.h> #include <cassert> @@ -752,12 +753,128 @@ case PixelFormat_Float32: return "Grayscale (float 32bpp)"; + case PixelFormat_Grayscale32: + return "Grayscale (unsigned 32bpp)"; + + case PixelFormat_RGB48: + return "RGB48"; + default: throw OrthancException(ErrorCode_ParameterOutOfRange); } } + const char* EnumerationToString(ModalityManufacturer manufacturer) + { + switch (manufacturer) + { + case ModalityManufacturer_Generic: + return "Generic"; + + case ModalityManufacturer_GenericNoWildcardInDates: + return "GenericNoWildcardInDates"; + + case ModalityManufacturer_GenericNoUniversalWildcard: + return "GenericNoUniversalWildcard"; + + case ModalityManufacturer_StoreScp: + return "StoreScp"; + + case ModalityManufacturer_ClearCanvas: + return "ClearCanvas"; + + case ModalityManufacturer_Dcm4Chee: + return "Dcm4Chee"; + + case ModalityManufacturer_Vitrea: + return "Vitrea"; + + default: + throw OrthancException(ErrorCode_ParameterOutOfRange); + } + } + + + const char* EnumerationToString(DicomRequestType type) + { + switch (type) + { + case DicomRequestType_Echo: + return "Echo"; + break; + + case DicomRequestType_Find: + return "Find"; + break; + + case DicomRequestType_Get: + return "Get"; + break; + + case DicomRequestType_Move: + return "Move"; + break; + + case DicomRequestType_Store: + return "Store"; + break; + + default: + throw OrthancException(ErrorCode_ParameterOutOfRange); + } + } + + + const char* EnumerationToString(TransferSyntax syntax) + { + switch (syntax) + { + case TransferSyntax_Deflated: + return "Deflated"; + + case TransferSyntax_Jpeg: + return "JPEG"; + + case TransferSyntax_Jpeg2000: + return "JPEG2000"; + + case TransferSyntax_JpegLossless: + return "JPEG Lossless"; + + case TransferSyntax_Jpip: + return "JPIP"; + + case TransferSyntax_Mpeg2: + return "MPEG2"; + + case TransferSyntax_Rle: + return "RLE"; + + default: + throw OrthancException(ErrorCode_ParameterOutOfRange); + } + } + + + const char* EnumerationToString(DicomVersion version) + { + switch (version) + { + case DicomVersion_2008: + return "2008"; + break; + + case DicomVersion_2017c: + return "2017c"; + break; + + default: + throw OrthancException(ErrorCode_ParameterOutOfRange); + } + } + + Encoding StringToEncoding(const char* encoding) { std::string s(encoding); @@ -1127,6 +1244,86 @@ } + ModalityManufacturer StringToModalityManufacturer(const std::string& manufacturer) + { + ModalityManufacturer result; + bool obsolete = false; + + if (manufacturer == "Generic") + { + return ModalityManufacturer_Generic; + } + else if (manufacturer == "GenericNoWildcardInDates") + { + return ModalityManufacturer_GenericNoWildcardInDates; + } + else if (manufacturer == "GenericNoUniversalWildcard") + { + return ModalityManufacturer_GenericNoUniversalWildcard; + } + else if (manufacturer == "ClearCanvas") + { + return ModalityManufacturer_ClearCanvas; + } + else if (manufacturer == "StoreScp") + { + return ModalityManufacturer_StoreScp; + } + else if (manufacturer == "Dcm4Chee") + { + return ModalityManufacturer_Dcm4Chee; + } + else if (manufacturer == "Vitrea") + { + return ModalityManufacturer_Vitrea; + } + else if (manufacturer == "AgfaImpax" || + manufacturer == "SyngoVia") + { + result = ModalityManufacturer_GenericNoWildcardInDates; + obsolete = true; + } + else if (manufacturer == "EFilm2" || + manufacturer == "MedInria") + { + result = ModalityManufacturer_Generic; + obsolete = true; + } + else + { + throw OrthancException(ErrorCode_ParameterOutOfRange); + } + + if (obsolete) + { + LOG(WARNING) << "The \"" << manufacturer << "\" manufacturer is obsolete since " + << "Orthanc 1.3.0. To guarantee compatibility with future Orthanc " + << "releases, you should replace it by \"" + << EnumerationToString(result) + << "\" in your configuration file."; + } + + return result; + } + + + DicomVersion StringToDicomVersion(const std::string& version) + { + if (version == "2008") + { + return DicomVersion_2008; + } + else if (version == "2017c") + { + return DicomVersion_2017c; + } + else + { + throw OrthancException(ErrorCode_ParameterOutOfRange); + } + } + + unsigned int GetBytesPerPixel(PixelFormat format) { switch (format) @@ -1143,12 +1340,16 @@ case PixelFormat_RGBA32: case PixelFormat_BGRA32: + case PixelFormat_Grayscale32: return 4; case PixelFormat_Float32: assert(sizeof(float) == 4); return 4; + case PixelFormat_RGB48: + return 6; + default: throw OrthancException(ErrorCode_ParameterOutOfRange); } @@ -1225,8 +1426,15 @@ { encoding = Encoding_Japanese; } - else if (s == "GB18030") + else if (s == "GB18030" || s == "GBK") { + /** + * According to tumashu@163.com, "In China, many dicom file's + * 0008,0005 tag is set as "GBK", instead of "GB18030", GBK is a + * subset of GB18030, and which is used frequently in China, + * suggest support it." + * https://groups.google.com/d/msg/orthanc-users/WMM8LMbjpUc/02-1f_yFCgAJ + **/ encoding = Encoding_Chinese; } /* @@ -1475,5 +1683,28 @@ default: throw OrthancException(ErrorCode_ParameterOutOfRange); } + } + + + static boost::mutex defaultEncodingMutex_; // Should not be necessary + static Encoding defaultEncoding_ = ORTHANC_DEFAULT_DICOM_ENCODING; + + Encoding GetDefaultDicomEncoding() + { + boost::mutex::scoped_lock lock(defaultEncodingMutex_); + return defaultEncoding_; } + + void SetDefaultDicomEncoding(Encoding encoding) + { + std::string name = EnumerationToString(encoding); + + { + boost::mutex::scoped_lock lock(defaultEncodingMutex_); + defaultEncoding_ = encoding; + } + + LOG(INFO) << "Default encoding for DICOM was changed to: " << name; + } + }
--- a/Orthanc/Core/Enumerations.h Thu Aug 24 19:49:20 2017 +0200 +++ b/Orthanc/Core/Enumerations.h Tue Oct 10 12:53:46 2017 +0200 @@ -199,8 +199,21 @@ **/ PixelFormat_Float32 = 6, - // This is the memory layout for Cairo - PixelFormat_BGRA32 = 7 + // This is the memory layout for Cairo (for internal use in Stone of Orthanc) + PixelFormat_BGRA32 = 7, + + /** + * {summary}{Graylevel, unsigned 32bpp image.} + * {description}{The image is graylevel. Each pixel is unsigned and stored in 4 bytes.} + **/ + PixelFormat_Grayscale32 = 8, + + /** + * {summary}{Color image in RGB48 format.} + * {description}{This format describes a color image. The pixels are stored in 6 + * consecutive bytes. The memory layout is RGB.} + **/ + PixelFormat_RGB48 = 9 }; @@ -443,6 +456,81 @@ ValueRepresentation_NotSupported // Not supported by Orthanc, or tag not in dictionary }; + enum DicomReplaceMode + { + DicomReplaceMode_InsertIfAbsent, + DicomReplaceMode_ThrowIfAbsent, + DicomReplaceMode_IgnoreIfAbsent + }; + + enum DicomToJsonFormat + { + DicomToJsonFormat_Full, + DicomToJsonFormat_Short, + DicomToJsonFormat_Human + }; + + enum DicomToJsonFlags + { + DicomToJsonFlags_IncludeBinary = (1 << 0), + DicomToJsonFlags_IncludePrivateTags = (1 << 1), + DicomToJsonFlags_IncludeUnknownTags = (1 << 2), + DicomToJsonFlags_IncludePixelData = (1 << 3), + DicomToJsonFlags_ConvertBinaryToAscii = (1 << 4), + DicomToJsonFlags_ConvertBinaryToNull = (1 << 5), + + // Some predefined combinations + DicomToJsonFlags_None = 0, + DicomToJsonFlags_Default = (DicomToJsonFlags_IncludeBinary | + DicomToJsonFlags_IncludePixelData | + DicomToJsonFlags_IncludePrivateTags | + DicomToJsonFlags_IncludeUnknownTags | + DicomToJsonFlags_ConvertBinaryToNull) + }; + + enum DicomFromJsonFlags + { + DicomFromJsonFlags_DecodeDataUriScheme = (1 << 0), + DicomFromJsonFlags_GenerateIdentifiers = (1 << 1) + }; + + enum DicomVersion + { + DicomVersion_2008, + DicomVersion_2017c + }; + + enum ModalityManufacturer + { + ModalityManufacturer_Generic, + ModalityManufacturer_GenericNoWildcardInDates, + ModalityManufacturer_GenericNoUniversalWildcard, + ModalityManufacturer_StoreScp, + ModalityManufacturer_ClearCanvas, + ModalityManufacturer_Dcm4Chee, + ModalityManufacturer_Vitrea + }; + + enum DicomRequestType + { + DicomRequestType_Echo, + DicomRequestType_Find, + DicomRequestType_Get, + DicomRequestType_Move, + DicomRequestType_Store + }; + + enum TransferSyntax + { + TransferSyntax_Deflated, + TransferSyntax_Jpeg, + TransferSyntax_Jpeg2000, + TransferSyntax_JpegLossless, + TransferSyntax_Jpip, + TransferSyntax_Mpeg2, + TransferSyntax_Rle + }; + /** * WARNING: Do not change the explicit values in the enumerations @@ -513,6 +601,14 @@ const char* EnumerationToString(PixelFormat format); + const char* EnumerationToString(ModalityManufacturer manufacturer); + + const char* EnumerationToString(DicomRequestType type); + + const char* EnumerationToString(TransferSyntax syntax); + + const char* EnumerationToString(DicomVersion version); + Encoding StringToEncoding(const char* encoding); ResourceType StringToResourceType(const char* type); @@ -525,6 +621,10 @@ bool throwIfUnsupported); PhotometricInterpretation StringToPhotometricInterpretation(const char* value); + + ModalityManufacturer StringToModalityManufacturer(const std::string& manufacturer); + + DicomVersion StringToDicomVersion(const std::string& version); unsigned int GetBytesPerPixel(PixelFormat format); @@ -544,4 +644,8 @@ bool IsUserContentType(FileContentType type); bool IsBinaryValueRepresentation(ValueRepresentation vr); + + Encoding GetDefaultDicomEncoding(); + + void SetDefaultDicomEncoding(Encoding encoding); }
--- a/Orthanc/Core/FileStorage/FilesystemStorage.h Thu Aug 24 19:49:20 2017 +0200 +++ b/Orthanc/Core/FileStorage/FilesystemStorage.h Tue Oct 10 12:53:46 2017 +0200 @@ -33,6 +33,14 @@ #pragma once +#if !defined(ORTHANC_SANDBOXED) +# error The macro ORTHANC_SANDBOXED must be defined +#endif + +#if ORTHANC_SANDBOXED == 1 +# error The class FilesystemStorage cannot be used in sandboxed environments +#endif + #include "IStorageArea.h" #include <stdint.h>
--- a/Orthanc/Core/Images/ImageAccessor.cpp Thu Aug 24 19:49:20 2017 +0200 +++ b/Orthanc/Core/Images/ImageAccessor.cpp Tue Oct 10 12:53:46 2017 +0200 @@ -218,6 +218,10 @@ ToMatlabStringInternal<uint16_t>(buffer, *this); break; + case PixelFormat_Grayscale32: + ToMatlabStringInternal<uint32_t>(buffer, *this); + break; + case PixelFormat_SignedGrayscale16: ToMatlabStringInternal<int16_t>(buffer, *this); break;
--- a/Orthanc/Core/Images/ImageProcessing.cpp Thu Aug 24 19:49:20 2017 +0200 +++ b/Orthanc/Core/Images/ImageProcessing.cpp Tue Oct 10 12:53:46 2017 +0200 @@ -413,6 +413,13 @@ } if (target.GetFormat() == PixelFormat_Float32 && + source.GetFormat() == PixelFormat_Grayscale32) + { + ConvertGrayscaleToFloat<uint32_t>(target, source); + return; + } + + if (target.GetFormat() == PixelFormat_Float32 && source.GetFormat() == PixelFormat_SignedGrayscale16) { ConvertGrayscaleToFloat<int16_t>(target, source); @@ -561,6 +568,26 @@ return; } + if (target.GetFormat() == PixelFormat_RGB24 && + source.GetFormat() == PixelFormat_RGB48) + { + for (unsigned int y = 0; y < source.GetHeight(); y++) + { + const uint16_t* p = reinterpret_cast<const uint16_t*>(source.GetConstRow(y)); + uint8_t* q = reinterpret_cast<uint8_t*>(target.GetRow(y)); + for (unsigned int x = 0; x < source.GetWidth(); x++) + { + q[0] = p[0] >> 8; + q[1] = p[1] >> 8; + q[2] = p[2] >> 8; + p += 3; + q += 3; + } + } + + return; + } + throw OrthancException(ErrorCode_NotImplemented); } @@ -579,6 +606,10 @@ SetInternal<uint16_t>(image, value); return; + case PixelFormat_Grayscale32: + SetInternal<uint32_t>(image, value); + return; + case PixelFormat_SignedGrayscale16: SetInternal<int16_t>(image, value); return; @@ -664,9 +695,9 @@ } - void ImageProcessing::GetMinMaxValue(int64_t& minValue, - int64_t& maxValue, - const ImageAccessor& image) + void ImageProcessing::GetMinMaxIntegerValue(int64_t& minValue, + int64_t& maxValue, + const ImageAccessor& image) { switch (image.GetFormat()) { @@ -688,6 +719,15 @@ break; } + case PixelFormat_Grayscale32: + { + uint32_t a, b; + GetMinMaxValueInternal<uint32_t>(a, b, image); + minValue = a; + maxValue = b; + break; + } + case PixelFormat_SignedGrayscale16: { int16_t a, b; @@ -703,6 +743,28 @@ } + void ImageProcessing::GetMinMaxFloatValue(float& minValue, + float& maxValue, + const ImageAccessor& image) + { + switch (image.GetFormat()) + { + case PixelFormat_Float32: + { + assert(sizeof(float) == 32); + float a, b; + GetMinMaxValueInternal<float>(a, b, image); + minValue = a; + maxValue = b; + break; + } + + default: + throw OrthancException(ErrorCode_NotImplemented); + } + } + + void ImageProcessing::AddConstant(ImageAccessor& image, int64_t value)
--- a/Orthanc/Core/Images/ImageProcessing.h Thu Aug 24 19:49:20 2017 +0200 +++ b/Orthanc/Core/Images/ImageProcessing.h Tue Oct 10 12:53:46 2017 +0200 @@ -60,9 +60,13 @@ static void ShiftRight(ImageAccessor& target, unsigned int shift); - static void GetMinMaxValue(int64_t& minValue, - int64_t& maxValue, - const ImageAccessor& image); + static void GetMinMaxIntegerValue(int64_t& minValue, + int64_t& maxValue, + const ImageAccessor& image); + + static void GetMinMaxFloatValue(float& minValue, + float& maxValue, + const ImageAccessor& image); static void AddConstant(ImageAccessor& image, int64_t value);
--- a/Orthanc/Core/PrecompiledHeaders.h Thu Aug 24 19:49:20 2017 +0200 +++ b/Orthanc/Core/PrecompiledHeaders.h Tue Oct 10 12:53:46 2017 +0200 @@ -50,7 +50,7 @@ #include <json/value.h> #if ORTHANC_ENABLE_PUGIXML == 1 -#include <pugixml.hpp> +# include <pugixml.hpp> #endif #include "Enumerations.h" @@ -58,4 +58,21 @@ #include "OrthancException.h" #include "Toolbox.h" +#if ORTHANC_ENABLE_DCMTK == 1 +# include "DicomParsing/ParsedDicomFile.h" + +// Headers from DCMTK used in Orthanc headers +# include <dcmtk/dcmdata/dcdatset.h> +# include <dcmtk/dcmdata/dcfilefo.h> +# include <dcmtk/dcmdata/dcmetinf.h> +# include <dcmtk/dcmdata/dcpixseq.h> #endif + +#if ORTHANC_ENABLE_DCMTK_NETWORKING == 1 +# include "DicomNetworking/DicomServer.h" + +// Headers from DCMTK used in Orthanc headers +# include <dcmtk/dcmnet/dimse.h> +#endif + +#endif
--- a/Orthanc/Core/SQLite/OrthancSQLiteException.h Thu Aug 24 19:49:20 2017 +0200 +++ b/Orthanc/Core/SQLite/OrthancSQLiteException.h Tue Oct 10 12:53:46 2017 +0200 @@ -38,6 +38,11 @@ #pragma once +#if ORTHANC_ENABLE_SQLITE != 1 +# error Macro ORTHANC_ENABLE_SQLITE must be set to 1 to use SQLite +#endif + + #if ORTHANC_SQLITE_STANDALONE == 1 #include <stdexcept>
--- a/Orthanc/Core/SQLite/Statement.h Thu Aug 24 19:49:20 2017 +0200 +++ b/Orthanc/Core/SQLite/Statement.h Tue Oct 10 12:53:46 2017 +0200 @@ -46,7 +46,7 @@ #include <stdint.h> #if ORTHANC_BUILD_UNIT_TESTS == 1 -#include <gtest/gtest_prod.h> +# include <gtest/gtest_prod.h> #endif
--- a/Orthanc/Core/Toolbox.cpp Thu Aug 24 19:49:20 2017 +0200 +++ b/Orthanc/Core/Toolbox.cpp Tue Oct 10 12:53:46 2017 +0200 @@ -397,6 +397,7 @@ #endif +#if ORTHANC_ENABLE_LOCALE == 1 static const char* GetBoostLocaleEncoding(const Encoding sourceEncoding) { switch (sourceEncoding) @@ -463,6 +464,7 @@ throw OrthancException(ErrorCode_NotImplemented); } } +#endif #if ORTHANC_ENABLE_LOCALE == 1
--- a/Orthanc/Plugins/Samples/GdcmDecoder/GdcmImageDecoder.cpp Thu Aug 24 19:49:20 2017 +0200 +++ b/Orthanc/Plugins/Samples/GdcmDecoder/GdcmImageDecoder.cpp Tue Oct 10 12:53:46 2017 +0200 @@ -32,6 +32,17 @@ #include <boost/iostreams/device/array.hpp> +// This is for compatibility with Orthanc SDK <= 1.3.0 +#if !defined(ORTHANC_PLUGINS_VERSION_IS_ABOVE) +#define ORTHANC_PLUGINS_VERSION_IS_ABOVE(major, minor, revision) \ + (ORTHANC_PLUGINS_MINIMAL_MAJOR_NUMBER > major || \ + (ORTHANC_PLUGINS_MINIMAL_MAJOR_NUMBER == major && \ + (ORTHANC_PLUGINS_MINIMAL_MINOR_NUMBER > minor || \ + (ORTHANC_PLUGINS_MINIMAL_MINOR_NUMBER == minor && \ + ORTHANC_PLUGINS_MINIMAL_REVISION_NUMBER >= revision)))) +#endif + + namespace OrthancPlugins { struct GdcmImageDecoder::PImpl @@ -199,6 +210,13 @@ case gdcm::PixelFormat::UINT8: return OrthancPluginPixelFormat_RGB24; + case gdcm::PixelFormat::UINT16: +#if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 3, 1) + return OrthancPluginPixelFormat_RGB48; +#else + throw std::runtime_error("RGB48 pixel format is only supported by Orthanc >= 1.3.1"); +#endif + default: break; }
--- a/Orthanc/Plugins/Samples/GdcmDecoder/GdcmImageDecoder.h Thu Aug 24 19:49:20 2017 +0200 +++ b/Orthanc/Plugins/Samples/GdcmDecoder/GdcmImageDecoder.h Tue Oct 10 12:53:46 2017 +0200 @@ -26,6 +26,18 @@ #include <boost/noncopyable.hpp> #include <boost/shared_ptr.hpp> + +// This is for compatibility with Orthanc SDK <= 1.3.0 +#if !defined(ORTHANC_PLUGINS_VERSION_IS_ABOVE) +#define ORTHANC_PLUGINS_VERSION_IS_ABOVE(major, minor, revision) \ + (ORTHANC_PLUGINS_MINIMAL_MAJOR_NUMBER > major || \ + (ORTHANC_PLUGINS_MINIMAL_MAJOR_NUMBER == major && \ + (ORTHANC_PLUGINS_MINIMAL_MINOR_NUMBER > minor || \ + (ORTHANC_PLUGINS_MINIMAL_MINOR_NUMBER == minor && \ + ORTHANC_PLUGINS_MINIMAL_REVISION_NUMBER >= revision)))) +#endif + + namespace OrthancPlugins { class GdcmImageDecoder : public boost::noncopyable
--- a/Orthanc/Resources/CMake/Compiler.cmake Thu Aug 24 19:49:20 2017 +0200 +++ b/Orthanc/Resources/CMake/Compiler.cmake Tue Oct 10 12:53:46 2017 +0200 @@ -179,6 +179,9 @@ message(FATAL_ERROR "Please install the uuid-dev package") endif() +elseif (CMAKE_SYSTEM_NAME STREQUAL "Emscripten") + message("Building using Emscripten (for WebAssembly or asm.js targets)") + else() message(FATAL_ERROR "Support your platform here") endif()
--- a/Orthanc/Resources/CMake/GoogleTestConfiguration.cmake Thu Aug 24 19:49:20 2017 +0200 +++ b/Orthanc/Resources/CMake/GoogleTestConfiguration.cmake Tue Oct 10 12:53:46 2017 +0200 @@ -1,5 +1,5 @@ -if (USE_GTEST_DEBIAN_SOURCE_PACKAGE) - find_path(GTEST_DEBIAN_SOURCES_DIR +if (USE_GOOGLE_TEST_DEBIAN_PACKAGE) + find_path(GOOGLE_TEST_DEBIAN_SOURCES_DIR NAMES src/gtest-all.cc PATHS /usr/src/gtest @@ -7,37 +7,40 @@ PATH_SUFFIXES src ) - find_path(GTEST_DEBIAN_INCLUDE_DIR + find_path(GOOGLE_TEST_DEBIAN_INCLUDE_DIR NAMES gtest.h PATHS /usr/include/gtest ) - message("Path to the Debian Google Test sources: ${GTEST_DEBIAN_SOURCES_DIR}") - message("Path to the Debian Google Test includes: ${GTEST_DEBIAN_INCLUDE_DIR}") + message("Path to the Debian Google Test sources: ${GOOGLE_TEST_DEBIAN_SOURCES_DIR}") + message("Path to the Debian Google Test includes: ${GOOGLE_TEST_DEBIAN_INCLUDE_DIR}") - set(GTEST_SOURCES ${GTEST_DEBIAN_SOURCES_DIR}/src/gtest-all.cc) - include_directories(${GTEST_DEBIAN_SOURCES_DIR}) + set(GOOGLE_TEST_SOURCES + ${GOOGLE_TEST_DEBIAN_SOURCES_DIR}/src/gtest-all.cc + ) - if (NOT EXISTS ${GTEST_SOURCES} OR - NOT EXISTS ${GTEST_DEBIAN_INCLUDE_DIR}/gtest.h) + include_directories(${GOOGLE_TEST_DEBIAN_SOURCES_DIR}) + + if (NOT EXISTS ${GOOGLE_TEST_SOURCES} OR + NOT EXISTS ${GOOGLE_TEST_DEBIAN_INCLUDE_DIR}/gtest.h) message(FATAL_ERROR "Please install the libgtest-dev package") endif() elseif (STATIC_BUILD OR NOT USE_SYSTEM_GOOGLE_TEST) - set(GTEST_SOURCES_DIR ${CMAKE_BINARY_DIR}/gtest-1.7.0) - set(GTEST_URL "http://www.orthanc-server.com/downloads/third-party/gtest-1.7.0.zip") - set(GTEST_MD5 "2d6ec8ccdf5c46b05ba54a9fd1d130d7") + set(GOOGLE_TEST_SOURCES_DIR ${CMAKE_BINARY_DIR}/gtest-1.7.0) + set(GOOGLE_TEST_URL "http://www.orthanc-server.com/downloads/third-party/gtest-1.7.0.zip") + set(GOOGLE_TEST_MD5 "2d6ec8ccdf5c46b05ba54a9fd1d130d7") - DownloadPackage(${GTEST_MD5} ${GTEST_URL} "${GTEST_SOURCES_DIR}") + DownloadPackage(${GOOGLE_TEST_MD5} ${GOOGLE_TEST_URL} "${GOOGLE_TEST_SOURCES_DIR}") include_directories( - ${GTEST_SOURCES_DIR}/include - ${GTEST_SOURCES_DIR} + ${GOOGLE_TEST_SOURCES_DIR}/include + ${GOOGLE_TEST_SOURCES_DIR} ) - set(GTEST_SOURCES - ${GTEST_SOURCES_DIR}/src/gtest-all.cc + set(GOOGLE_TEST_SOURCES + ${GOOGLE_TEST_SOURCES_DIR}/src/gtest-all.cc ) # https://code.google.com/p/googletest/issues/detail?id=412 @@ -45,14 +48,16 @@ add_definitions(/D _VARIADIC_MAX=10) endif() - source_group(ThirdParty\\GoogleTest REGULAR_EXPRESSION ${GTEST_SOURCES_DIR}/.*) + source_group(ThirdParty\\GoogleTest REGULAR_EXPRESSION ${GOOGLE_TEST_SOURCES_DIR}/.*) else() include(FindGTest) - if (NOT GTEST_FOUND) + if (NOT GOOGLE_TEST_FOUND) message(FATAL_ERROR "Unable to find GoogleTest") endif() - include_directories(${GTEST_INCLUDE_DIRS}) - link_libraries(${GTEST_LIBRARIES}) + include_directories(${GOOGLE_TEST_INCLUDE_DIRS}) + + # The variable GOOGLE_TEST_LIBRARIES contains the shared library of + # Google Test endif()
--- a/Plugin/DecodedImageAdapter.cpp Thu Aug 24 19:49:20 2017 +0200 +++ b/Plugin/DecodedImageAdapter.cpp Tue Oct 10 12:53:46 2017 +0200 @@ -206,7 +206,7 @@ case PixelFormat_SignedGrayscale16: { int64_t a, b; - Orthanc::ImageProcessing::GetMinMaxValue(a, b, accessor); + Orthanc::ImageProcessing::GetMinMaxIntegerValue(a, b, accessor); result["minPixelValue"] = (a < 0 ? static_cast<int32_t>(a) : 0); result["maxPixelValue"] = (b > 0 ? static_cast<int32_t>(b) : 1); result["color"] = false; @@ -414,7 +414,7 @@ converted = buffer->GetAccessor(); int64_t a, b; - Orthanc::ImageProcessing::GetMinMaxValue(a, b, accessor); + Orthanc::ImageProcessing::GetMinMaxIntegerValue(a, b, accessor); result["Orthanc"]["StretchLow"] = static_cast<int32_t>(a); result["Orthanc"]["StretchHigh"] = static_cast<int32_t>(b);
--- a/Plugin/ViewerToolbox.cpp Thu Aug 24 19:49:20 2017 +0200 +++ b/Plugin/ViewerToolbox.cpp Tue Oct 10 12:53:46 2017 +0200 @@ -24,6 +24,9 @@ #include "../Orthanc/Core/OrthancException.h" #include "../Orthanc/Core/Toolbox.h" +// Gain access to ORTHANC_PLUGINS_VERSION_IS_ABOVE if Orthanc SDK <= 1.3.0 +#include "../Orthanc/Plugins/Samples/GdcmDecoder/GdcmImageDecoder.h" + #include <json/reader.h> #include <stdexcept> #include <boost/lexical_cast.hpp> @@ -283,6 +286,11 @@ case Orthanc::PixelFormat_RGB24: return OrthancPluginPixelFormat_RGB24; +#if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 3, 1) + case Orthanc::PixelFormat_RGB48: + return OrthancPluginPixelFormat_RGB48; +#endif + case Orthanc::PixelFormat_RGBA32: return OrthancPluginPixelFormat_RGBA32; @@ -308,6 +316,11 @@ case OrthancPluginPixelFormat_RGB24: return Orthanc::PixelFormat_RGB24; +#if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 3, 1) + case OrthancPluginPixelFormat_RGB48: + return Orthanc::PixelFormat_RGB48; +#endif + case OrthancPluginPixelFormat_RGBA32: return Orthanc::PixelFormat_RGBA32;