Mercurial > hg > orthanc
changeset 2699:52217dc47a4e
new image/pam MIME TYPE supported in /instances/../frames/../image-uint8... routes
author | am@osimis.io |
---|---|
date | Thu, 05 Jul 2018 12:25:00 +0200 |
parents | 2c684841da15 |
children | be6d7acf4e94 |
files | .hgignore Core/DicomParsing/Internals/DicomImageDecoder.cpp Core/DicomParsing/Internals/DicomImageDecoder.h Core/Images/JpegWriter.cpp Core/Images/JpegWriter.h Core/Images/PamReader.cpp Core/Images/PamReader.h Core/Images/PamWriter.cpp Core/Images/PamWriter.h Core/Images/PngWriter.cpp Core/Images/PngWriter.h NEWS OrthancServer/OrthancRestApi/OrthancRestResources.cpp OrthancServer/main.cpp Resources/CMake/OrthancFrameworkConfiguration.cmake UnitTestsSources/ImageTests.cpp |
diffstat | 16 files changed, 715 insertions(+), 10 deletions(-) [+] |
line wrap: on
line diff
--- a/.hgignore Thu Jun 28 12:24:45 2018 +0200 +++ b/.hgignore Thu Jul 05 12:25:00 2018 +0200 @@ -1,2 +1,5 @@ syntax: glob -ThirdPartyDownloads/ \ No newline at end of file +ThirdPartyDownloads/ +CMakeLists.txt.user +*.cpp.orig +*.h.orig
--- a/Core/DicomParsing/Internals/DicomImageDecoder.cpp Thu Jun 28 12:24:45 2018 +0200 +++ b/Core/DicomParsing/Internals/DicomImageDecoder.cpp Thu Jul 05 12:25:00 2018 +0200 @@ -93,6 +93,7 @@ #if ORTHANC_ENABLE_JPEG == 1 # include "../../Images/JpegWriter.h" #endif +#include "../../Images/PamWriter.h" #include <boost/lexical_cast.hpp> @@ -952,6 +953,17 @@ } + void DicomImageDecoder::ExtractPamImage(std::string& result, + std::auto_ptr<ImageAccessor>& image, + ImageExtractionMode mode, + bool invert) + { + ApplyExtractionMode(image, mode, invert); + + PamWriter writer; + writer.WriteToMemory(result, *image); + } + #if ORTHANC_ENABLE_PNG == 1 void DicomImageDecoder::ExtractPngImage(std::string& result, std::auto_ptr<ImageAccessor>& image,
--- a/Core/DicomParsing/Internals/DicomImageDecoder.h Thu Jun 28 12:24:45 2018 +0200 +++ b/Core/DicomParsing/Internals/DicomImageDecoder.h Thu Jul 05 12:25:00 2018 +0200 @@ -101,6 +101,11 @@ static ImageAccessor *Decode(ParsedDicomFile& dicom, unsigned int frame); + static void ExtractPamImage(std::string& result, + std::auto_ptr<ImageAccessor>& image, + ImageExtractionMode mode, + bool invert); + #if ORTHANC_ENABLE_PNG == 1 static void ExtractPngImage(std::string& result, std::auto_ptr<ImageAccessor>& image,
--- a/Core/Images/JpegWriter.cpp Thu Jun 28 12:24:45 2018 +0200 +++ b/Core/Images/JpegWriter.cpp Thu Jul 05 12:25:00 2018 +0200 @@ -166,7 +166,6 @@ #endif -#if ORTHANC_SANDBOXED == 0 void JpegWriter::WriteToMemoryInternal(std::string& jpeg, unsigned int width, unsigned int height, @@ -211,5 +210,4 @@ jpeg.assign(reinterpret_cast<const char*>(data), size); free(data); } -#endif }
--- a/Core/Images/JpegWriter.h Thu Jun 28 12:24:45 2018 +0200 +++ b/Core/Images/JpegWriter.h Thu Jul 05 12:25:00 2018 +0200 @@ -48,21 +48,21 @@ class JpegWriter : public IImageWriter { protected: +#if ORTHANC_SANDBOXED == 0 virtual void WriteToFileInternal(const std::string& filename, unsigned int width, unsigned int height, unsigned int pitch, PixelFormat format, const void* buffer); +#endif -#if ORTHANC_SANDBOXED == 0 virtual void WriteToMemoryInternal(std::string& jpeg, unsigned int width, unsigned int height, unsigned int pitch, PixelFormat format, const void* buffer); -#endif private: uint8_t quality_;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Core/Images/PamReader.cpp Thu Jul 05 12:25:00 2018 +0200 @@ -0,0 +1,184 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2018 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#include "../PrecompiledHeaders.h" +#include "PamReader.h" + +#include "../OrthancException.h" +#include "../Toolbox.h" +#include <istream> +#include <sstream> +#include <fstream> +#include <endian.h> +#if ORTHANC_SANDBOXED == 0 +# include "../SystemToolbox.h" +#endif + +#include <string.h> + +namespace Orthanc +{ + namespace + { + void GetPixelFormat(PixelFormat& format, unsigned int& bytesPerChannel, const unsigned int& maxValue, const unsigned int& channelCount, const char* tupleType) + { + if (strcmp(tupleType, "GRAYSCALE") == 0 && channelCount == 1) + { + if (maxValue == 255) + { + format = PixelFormat_Grayscale8; + bytesPerChannel = 1; + return; + } + else if (maxValue == 65535) + { + format = PixelFormat_Grayscale16; + bytesPerChannel = 2; + return; + } + } + else if (strcmp(tupleType, "RGB") == 0 && channelCount == 3) + { + if (maxValue == 255) + { + format = PixelFormat_RGB24; + bytesPerChannel = 1; + return; + } + else if (maxValue == 65535) + { + format = PixelFormat_RGB48; + bytesPerChannel = 2; + return; + } + } + throw OrthancException(ErrorCode_NotImplemented); + } + + void ReadDelimiter(std::istream& input, const char* expectedDelimiter) + { + std::string delimiter; + input >> delimiter; + if (delimiter != expectedDelimiter) + { + throw OrthancException(ErrorCode_BadFileFormat); + } + } + + unsigned int ReadKeyValueUint(std::istream& input, const char* expectedKey) + { + std::string key; + unsigned int value; + input >> key >> value; + if (key != expectedKey) + { + throw OrthancException(ErrorCode_BadFileFormat); + } + return value; + } + + std::string ReadKeyValueString(std::istream& input, const char* expectedKey) + { + std::string key; + std::string value; + input >> key >> value; + if (key != expectedKey) + { + throw OrthancException(ErrorCode_BadFileFormat); + } + return value; + } + } + + void PamReader::ReadFromStream(std::istream& input) + { + ReadDelimiter(input, "P7"); + unsigned int width = ReadKeyValueUint(input, "WIDTH"); + unsigned int height = ReadKeyValueUint(input, "HEIGHT"); + unsigned int channelCount = ReadKeyValueUint(input, "DEPTH"); + unsigned int maxValue = ReadKeyValueUint(input, "MAXVAL"); + std::string tupleType = ReadKeyValueString(input, "TUPLTYPE"); + ReadDelimiter(input, "ENDHDR"); + // skip last EOL + char tmp[16]; + input.getline(tmp, 16); + + unsigned int bytesPerChannel; + PixelFormat format; + GetPixelFormat(format, bytesPerChannel, maxValue, channelCount, tupleType.c_str()); + + // read the pixels data + unsigned int sizeInBytes = width * height * channelCount * bytesPerChannel; + data_.reserve(sizeInBytes); + input.read(data_.data(), sizeInBytes); + + AssignWritable(format, width, height, width * channelCount * bytesPerChannel, data_.data()); + + // swap bytes + if (Toolbox::DetectEndianness() == Endianness_Little && bytesPerChannel == 2) + { + uint16_t* pixel = NULL; + for (unsigned int h = 0; h < height; ++h) + { + pixel = reinterpret_cast<uint16_t*> (data_.data() + h * width * channelCount * bytesPerChannel); + for (unsigned int w = 0; w < (width * channelCount); ++w, ++pixel) + { + *pixel = htobe16(*pixel); + } + } + } + + } + +#if ORTHANC_SANDBOXED == 0 + void PamReader::ReadFromFile(const std::string& filename) + { + std::ifstream inputStream(filename, std::ofstream::binary); + ReadFromStream(inputStream); + } +#endif + + void PamReader::ReadFromMemory(const void* buffer, + size_t size) + { + std::istringstream inputStream(std::string(reinterpret_cast<const char*>(buffer), size)); + ReadFromStream(inputStream); + } + + void PamReader::ReadFromMemory(const std::string& buffer) + { + std::istringstream inputStream(buffer); + ReadFromStream(inputStream); + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Core/Images/PamReader.h Thu Jul 05 12:25:00 2018 +0200 @@ -0,0 +1,83 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2018 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#if !defined(ORTHANC_ENABLE_PNG) +# error The macro ORTHANC_ENABLE_PNG must be defined +#endif + +#if ORTHANC_ENABLE_PNG != 1 +# error PNG support must be enabled to include this file +#endif + +#include "ImageAccessor.h" + +#include "../Enumerations.h" + +#include <vector> +#include <stdint.h> +#include <boost/shared_ptr.hpp> +#include <boost/noncopyable.hpp> +#include <istream> + +#if !defined(ORTHANC_SANDBOXED) +# error The macro ORTHANC_SANDBOXED must be defined +#endif + +namespace Orthanc +{ + class PamReader : + public ImageAccessor, + public boost::noncopyable + { + private: + std::vector<char> data_; + + public: + PamReader() {} + virtual ~PamReader() {} + +#if ORTHANC_SANDBOXED == 0 + void ReadFromFile(const std::string& filename); +#endif + + void ReadFromMemory(const void* buffer, + size_t size); + + void ReadFromMemory(const std::string& buffer); + + protected: + void ReadFromStream(std::istream& input); + }; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Core/Images/PamWriter.cpp Thu Jul 05 12:25:00 2018 +0200 @@ -0,0 +1,168 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2018 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#include "../PrecompiledHeaders.h" +#include "PamWriter.h" + +#include <vector> +#include <stdint.h> +#include <iostream> +#include <sstream> +#include <fstream> +#include <endian.h> +#include "../OrthancException.h" +#include "../ChunkedBuffer.h" +#include "../Toolbox.h" + +#if ORTHANC_SANDBOXED == 0 +# include "../SystemToolbox.h" +#endif + + +namespace Orthanc +{ + namespace + { + void GetPixelFormatInfo(const PixelFormat& format, unsigned int& maxValue, unsigned int& channelCount, unsigned int& bytesPerChannel, const char*& tupleType) { + maxValue = 255; + channelCount = 1; + bytesPerChannel = 1; + tupleType = NULL; + + switch (format) { + case PixelFormat_Grayscale8: + maxValue = 255; + channelCount = 1; + bytesPerChannel = 1; + tupleType = "GRAYSCALE"; + break; + case PixelFormat_Grayscale16: + maxValue = 65535; + channelCount = 1; + bytesPerChannel = 2; + tupleType = "GRAYSCALE"; + break; + case PixelFormat_RGB24: + maxValue = 255; + channelCount = 3; + bytesPerChannel = 1; + tupleType = "RGB"; + break; + case PixelFormat_RGB48: + maxValue = 255; + channelCount = 3; + bytesPerChannel = 2; + tupleType = "RGB"; + break; + default: + throw OrthancException(ErrorCode_NotImplemented); + } + + } + + void WriteToStream(std::ostream& output, + unsigned int width, + unsigned int height, + unsigned int pitch, + PixelFormat format, + const void* buffer) + { + unsigned int maxValue = 255; + unsigned int channelCount = 1; + unsigned int bytesPerChannel = 1; + const char* tupleType = "GRAYSCALE"; + GetPixelFormatInfo(format, maxValue, channelCount, bytesPerChannel, tupleType); + + output << "P7" << "\n"; + output << "WIDTH " << width << "\n"; + output << "HEIGHT " << height << "\n"; + output << "DEPTH " << channelCount << "\n"; + output << "MAXVAL " << maxValue << "\n"; + output << "TUPLTYPE " << tupleType << "\n"; + output << "ENDHDR" << "\n"; + + if (Toolbox::DetectEndianness() == Endianness_Little && bytesPerChannel == 2) + { + uint16_t tmp; + const uint16_t* pixel = NULL; + for (unsigned int h = 0; h < height; ++h) + { + pixel = reinterpret_cast<const uint16_t*> (reinterpret_cast<const uint8_t*>(buffer) + h * pitch); + for (unsigned int w = 0; w < (width * channelCount); ++w, ++pixel) + { + tmp = htobe16(*pixel); + output.write(reinterpret_cast<const char*>(&tmp), 2); + } + } + } + else + { + for (unsigned int h = 0; h < height; ++h) + { + output.write(reinterpret_cast<const char*>(reinterpret_cast<const uint8_t*>(buffer) + h * pitch), channelCount * bytesPerChannel * width); + } + } + + } + + } + +#if ORTHANC_SANDBOXED == 0 + void PamWriter::WriteToFileInternal(const std::string& filename, + unsigned int width, + unsigned int height, + unsigned int pitch, + PixelFormat format, + const void* buffer) + { + std::ofstream outfile (filename, std::ofstream::binary); + + WriteToStream(outfile, width, height, pitch, format, buffer); + outfile.close(); + } +#endif + + + void PamWriter::WriteToMemoryInternal(std::string& output, + unsigned int width, + unsigned int height, + unsigned int pitch, + PixelFormat format, + const void* buffer) + { + std::ostringstream outStream; // todo: try to write directly in output and avoid copy + + WriteToStream(outStream, width, height, pitch, format, buffer); + output = outStream.str(); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Core/Images/PamWriter.h Thu Jul 05 12:25:00 2018 +0200 @@ -0,0 +1,65 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2018 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "IImageWriter.h" + +namespace Orthanc +{ + // https://en.wikipedia.org/wiki/Netpbm#PAM_graphics_format + class PamWriter : public IImageWriter + { + protected: +#if ORTHANC_SANDBOXED == 0 + virtual void WriteToFileInternal(const std::string& filename, + unsigned int width, + unsigned int height, + unsigned int pitch, + PixelFormat format, + const void* buffer); +#endif + + virtual void WriteToMemoryInternal(std::string& png, + unsigned int width, + unsigned int height, + unsigned int pitch, + PixelFormat format, + const void* buffer); + + public: + PamWriter() {} + + virtual ~PamWriter() {} + }; +}
--- a/Core/Images/PngWriter.cpp Thu Jun 28 12:24:45 2018 +0200 +++ b/Core/Images/PngWriter.cpp Thu Jul 05 12:25:00 2018 +0200 @@ -248,7 +248,6 @@ -#if ORTHANC_SANDBOXED == 0 void PngWriter::WriteToMemoryInternal(std::string& png, unsigned int width, unsigned int height, @@ -272,5 +271,4 @@ chunks.Flatten(png); } -#endif }
--- a/Core/Images/PngWriter.h Thu Jun 28 12:24:45 2018 +0200 +++ b/Core/Images/PngWriter.h Thu Jul 05 12:25:00 2018 +0200 @@ -50,21 +50,21 @@ class PngWriter : public IImageWriter { protected: +#if ORTHANC_SANDBOXED == 0 virtual void WriteToFileInternal(const std::string& filename, unsigned int width, unsigned int height, unsigned int pitch, PixelFormat format, const void* buffer); +#endif -#if ORTHANC_SANDBOXED == 0 virtual void WriteToMemoryInternal(std::string& png, unsigned int width, unsigned int height, unsigned int pitch, PixelFormat format, const void* buffer); -#endif private: struct PImpl;
--- a/NEWS Thu Jun 28 12:24:45 2018 +0200 +++ b/NEWS Thu Jul 05 12:25:00 2018 +0200 @@ -22,6 +22,9 @@ for data already in Orthanc, you'll need to reconstruct the data by sending a POST request to the ".../reconstruct" URI. This change triggered an update of ORTHANC_API_VERSION from 1.0 to 1.1 +* "/instances/.../frame/../image-uint8 and friends now accepts a + "image/pam" MIME type to retrieve images in PAM format + (https://en.wikipedia.org/wiki/Netpbm#PAM_graphics_format) Maintenance -----------
--- a/OrthancServer/OrthancRestApi/OrthancRestResources.cpp Thu Jun 28 12:24:45 2018 +0200 +++ b/OrthancServer/OrthancRestApi/OrthancRestResources.cpp Thu Jul 05 12:25:00 2018 +0200 @@ -364,6 +364,12 @@ DicomImageDecoder::ExtractPngImage(answer_, image_, mode_, invert_); } + void EncodeUsingPam() + { + format_ = "image/pam"; + DicomImageDecoder::ExtractPamImage(answer_, image_, mode_, invert_); + } + void EncodeUsingJpeg(uint8_t quality) { format_ = "image/jpeg"; @@ -390,6 +396,25 @@ } }; + class EncodePam : public HttpContentNegociation::IHandler + { + private: + ImageToEncode& image_; + + public: + EncodePam(ImageToEncode& image) : image_(image) + { + } + + virtual void Handle(const std::string& type, + const std::string& subtype) + { + assert(type == "image"); + assert(subtype == "pam"); + image_.EncodeUsingPam(); + } + }; + class EncodeJpeg : public HttpContentNegociation::IHandler { private: @@ -526,6 +551,7 @@ HttpContentNegociation negociation; EncodePng png(image); negociation.Register("image/png", png); EncodeJpeg jpeg(image, call); negociation.Register("image/jpeg", jpeg); + EncodePam pam(image); negociation.Register("image/pam", pam); if (negociation.Apply(call.GetHttpHeaders())) {
--- a/OrthancServer/main.cpp Thu Jun 28 12:24:45 2018 +0200 +++ b/OrthancServer/main.cpp Thu Jul 05 12:25:00 2018 +0200 @@ -1057,7 +1057,7 @@ database.Open(); unsigned int currentVersion = database.GetDatabaseVersion(); - + if (upgradeDatabase) { UpgradeDatabase(database, storageArea);
--- a/Resources/CMake/OrthancFrameworkConfiguration.cmake Thu Jun 28 12:24:45 2018 +0200 +++ b/Resources/CMake/OrthancFrameworkConfiguration.cmake Thu Jul 05 12:25:00 2018 +0200 @@ -137,6 +137,8 @@ ${ORTHANC_ROOT}/Core/Images/ImageAccessor.cpp ${ORTHANC_ROOT}/Core/Images/ImageBuffer.cpp ${ORTHANC_ROOT}/Core/Images/ImageProcessing.cpp + ${ORTHANC_ROOT}/Core/Images/PamReader.cpp + ${ORTHANC_ROOT}/Core/Images/PamWriter.cpp ${ORTHANC_ROOT}/Core/Logging.cpp ${ORTHANC_ROOT}/Core/Toolbox.cpp ${ORTHANC_ROOT}/Core/WebServiceParameters.cpp
--- a/UnitTestsSources/ImageTests.cpp Thu Jun 28 12:24:45 2018 +0200 +++ b/UnitTestsSources/ImageTests.cpp Thu Jul 05 12:25:00 2018 +0200 @@ -41,6 +41,8 @@ #include "../Core/Images/JpegWriter.h" #include "../Core/Images/PngReader.h" #include "../Core/Images/PngWriter.h" +#include "../Core/Images/PamReader.h" +#include "../Core/Images/PamWriter.h" #include "../Core/Toolbox.h" #include "../Core/TemporaryFile.h" #include "../OrthancServer/OrthancInitialization.h" // For the FontRegistry @@ -269,3 +271,159 @@ w.WriteToFile("UnitTestsResults/font.png", s); } +TEST(PamWriter, ColorPattern) +{ + Orthanc::PamWriter w; + unsigned int width = 17; + unsigned int height = 61; + unsigned int pitch = width * 3; + + std::vector<uint8_t> image(height * pitch); + for (unsigned int y = 0; y < height; y++) + { + uint8_t *p = &image[0] + y * pitch; + for (unsigned int x = 0; x < width; x++, p += 3) + { + p[0] = (y % 3 == 0) ? 255 : 0; + p[1] = (y % 3 == 1) ? 255 : 0; + p[2] = (y % 3 == 2) ? 255 : 0; + } + } + + Orthanc::ImageAccessor accessor; + accessor.AssignReadOnly(Orthanc::PixelFormat_RGB24, width, height, pitch, &image[0]); + + w.WriteToFile("UnitTestsResults/ColorPattern.pam", accessor); + + std::string f, md5; + Orthanc::SystemToolbox::ReadFile(f, "UnitTestsResults/ColorPattern.pam"); + Orthanc::Toolbox::ComputeMD5(md5, f); + ASSERT_EQ("81a3441754e88969ebbe53e69891e841", md5); +} + +TEST(PamWriter, Gray8Pattern) +{ + Orthanc::PamWriter w; + int width = 17; + int height = 256; + int pitch = width; + + std::vector<uint8_t> image(height * pitch); + for (int y = 0; y < height; y++) + { + uint8_t *p = &image[0] + y * pitch; + for (int x = 0; x < width; x++, p++) + { + *p = y; + } + } + + Orthanc::ImageAccessor accessor; + accessor.AssignReadOnly(Orthanc::PixelFormat_Grayscale8, width, height, pitch, &image[0]); + + w.WriteToFile("UnitTestsResults/Gray8Pattern.pam", accessor); + + std::string f, md5; + Orthanc::SystemToolbox::ReadFile(f, "UnitTestsResults/Gray8Pattern.pam"); + Orthanc::Toolbox::ComputeMD5(md5, f); + ASSERT_EQ("7873c408d26a9d11dd1c1de5e69cc0a3", md5); +} + +TEST(PamWriter, Gray16Pattern) +{ + Orthanc::PamWriter w; + int width = 256; + int height = 256; + int pitch = width * 2 + 16; + + std::vector<uint8_t> image(height * pitch); + + int v = 0; + for (int y = 0; y < height; y++) + { + uint16_t *p = reinterpret_cast<uint16_t*>(&image[0] + y * pitch); + for (int x = 0; x < width; x++, p++, v++) + { + *p = v; + } + } + + Orthanc::ImageAccessor accessor; + accessor.AssignReadOnly(Orthanc::PixelFormat_Grayscale16, width, height, pitch, &image[0]); + w.WriteToFile("UnitTestsResults/Gray16Pattern.pam", accessor); + + std::string f, md5; + Orthanc::SystemToolbox::ReadFile(f, "UnitTestsResults/Gray16Pattern.pam"); + Orthanc::Toolbox::ComputeMD5(md5, f); + ASSERT_EQ("b268772bf28f3b2b8520ff21c5e3dcb6", md5); +} + +TEST(PamWriter, EndToEnd) +{ + Orthanc::PamWriter w; + unsigned int width = 256; + unsigned int height = 256; + unsigned int pitch = width * 2 + 16; + + std::vector<uint8_t> image(height * pitch); + + int v = 0; + for (unsigned int y = 0; y < height; y++) + { + uint16_t *p = reinterpret_cast<uint16_t*>(&image[0] + y * pitch); + for (unsigned int x = 0; x < width; x++, p++, v++) + { + *p = v; + } + } + + Orthanc::ImageAccessor accessor; + accessor.AssignReadOnly(Orthanc::PixelFormat_Grayscale16, width, height, pitch, &image[0]); + + std::string s; + w.WriteToMemory(s, accessor); + + { + Orthanc::PamReader r; + r.ReadFromMemory(s); + + ASSERT_EQ(r.GetFormat(), Orthanc::PixelFormat_Grayscale16); + ASSERT_EQ(r.GetWidth(), width); + ASSERT_EQ(r.GetHeight(), height); + + v = 0; + for (unsigned int y = 0; y < height; y++) + { + const uint16_t *p = reinterpret_cast<const uint16_t*>((const uint8_t*) r.GetConstBuffer() + y * r.GetPitch()); + ASSERT_EQ(p, r.GetConstRow(y)); + for (unsigned int x = 0; x < width; x++, p++, v++) + { + ASSERT_EQ(v, *p); + } + } + } + + { + Orthanc::TemporaryFile tmp; + Orthanc::SystemToolbox::WriteFile(s, tmp.GetPath()); + + Orthanc::PamReader r2; + r2.ReadFromFile(tmp.GetPath()); + + ASSERT_EQ(r2.GetFormat(), Orthanc::PixelFormat_Grayscale16); + ASSERT_EQ(r2.GetWidth(), width); + ASSERT_EQ(r2.GetHeight(), height); + + v = 0; + for (unsigned int y = 0; y < height; y++) + { + const uint16_t *p = reinterpret_cast<const uint16_t*>((const uint8_t*) r2.GetConstBuffer() + y * r2.GetPitch()); + ASSERT_EQ(p, r2.GetConstRow(y)); + for (unsigned int x = 0; x < width; x++, p++, v++) + { + ASSERT_EQ(*p, v); + } + } + } +} +