Mercurial > hg > orthanc-wsi
view Framework/ImageToolbox.cpp @ 116:a18bfe1fdd62
sync
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Thu, 04 Jan 2018 10:49:34 +0100 |
parents | a51dee6a1515 |
children | a0f9a3df1110 |
line wrap: on
line source
/** * 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 Affero General Public License * as published by the Free Software Foundation, either version 3 of * the License, or (at your option) any later version. * * 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 * Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. **/ #include "PrecompiledHeadersWSI.h" #include "ImageToolbox.h" #include "Jpeg2000Reader.h" #include "Jpeg2000Writer.h" #include "../Resources/Orthanc/Core/OrthancException.h" #include "../Resources/Orthanc/Core/Images/ImageProcessing.h" #include "../Resources/Orthanc/Core/Images/PngReader.h" #include "../Resources/Orthanc/Core/Images/PngWriter.h" #include "../Resources/Orthanc/Core/Images/JpegReader.h" #include "../Resources/Orthanc/Core/Images/JpegWriter.h" #include "../Resources/Orthanc/Core/Logging.h" #include <string.h> #include <memory> namespace OrthancWSI { namespace ImageToolbox { Orthanc::ImageAccessor* Allocate(Orthanc::PixelFormat format, unsigned int width, unsigned int height) { return new Orthanc::Image(format, width, height, false); } void Embed(Orthanc::ImageAccessor& target, const Orthanc::ImageAccessor& source, unsigned int x, unsigned int y) { if (target.GetFormat() != source.GetFormat()) { throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageFormat); } if (x >= target.GetWidth() || y >= target.GetHeight()) { return; } unsigned int h = std::min(source.GetHeight(), target.GetHeight() - y); unsigned int w = std::min(source.GetWidth(), target.GetWidth() - x); Orthanc::ImageAccessor targetRegion = target.GetRegion(x, y, w, h); Orthanc::ImageAccessor sourceRegion = source.GetRegion(0, 0, w, h); Orthanc::ImageProcessing::Copy(targetRegion, sourceRegion); } void Set(Orthanc::ImageAccessor& image, uint8_t r, uint8_t g, uint8_t b) { if (image.GetWidth() == 0 || image.GetHeight() == 0) { return; } uint8_t grayscale = (2126 * static_cast<uint16_t>(r) + 7152 * static_cast<uint16_t>(g) + 0722 * static_cast<uint16_t>(b)) / 10000; switch (image.GetFormat()) { case Orthanc::PixelFormat_Grayscale8: { for (unsigned int y = 0; y < image.GetHeight(); y++) { memset(image.GetRow(y), grayscale, image.GetWidth()); } break; } case Orthanc::PixelFormat_RGB24: { for (unsigned int y = 0; y < image.GetHeight(); y++) { uint8_t* p = reinterpret_cast<uint8_t*>(image.GetRow(y)); for (unsigned int x = 0; x < image.GetWidth(); x++, p += 3) { p[0] = r; p[1] = g; p[2] = b; } } break; } default: throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); } } Orthanc::ImageAccessor* DecodeTile(const std::string& source, ImageCompression compression) { switch (compression) { case ImageCompression_Png: { std::auto_ptr<Orthanc::PngReader> reader(new Orthanc::PngReader); reader->ReadFromMemory(source); return reader.release(); } case ImageCompression_Jpeg: { std::auto_ptr<Orthanc::JpegReader> reader(new Orthanc::JpegReader); reader->ReadFromMemory(source); return reader.release(); } case ImageCompression_Jpeg2000: { std::auto_ptr<Jpeg2000Reader> reader(new Jpeg2000Reader); reader->ReadFromMemory(source); return reader.release(); } default: throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); } } void EncodeTile(std::string& target, const Orthanc::ImageAccessor& source, ImageCompression compression, uint8_t quality) { if (compression == ImageCompression_None) { unsigned int pitch = GetBytesPerPixel(source.GetFormat()) * source.GetWidth(); target.resize(pitch * source.GetHeight()); for (unsigned int i = 0; i < source.GetHeight(); i++) { memcpy(&target[i * pitch], source.GetConstRow(i), pitch); } } else { std::auto_ptr<Orthanc::IImageWriter> writer; switch (compression) { case ImageCompression_Png: writer.reset(new Orthanc::PngWriter); break; case ImageCompression_Jpeg: writer.reset(new Orthanc::JpegWriter); dynamic_cast<Orthanc::JpegWriter&>(*writer).SetQuality(quality); break; case ImageCompression_Jpeg2000: writer.reset(new Jpeg2000Writer); break; default: throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); } writer->WriteToMemory(target, source); } } void ChangeTileCompression(std::string& target, const std::string& source, ImageCompression sourceCompression, ImageCompression targetCompression, uint8_t quality) { if (sourceCompression == targetCompression) { target = source; } else { std::auto_ptr<Orthanc::ImageAccessor> decoded(DecodeTile(source, sourceCompression)); EncodeTile(target, *decoded, targetCompression, quality); } } static uint8_t GetPixelValue(const Orthanc::ImageAccessor& source, unsigned int x, unsigned int y, unsigned int channel, int offsetX, int offsetY, unsigned int bytesPerPixel) { assert(bytesPerPixel == source.GetBytesPerPixel()); assert(channel < bytesPerPixel); assert(source.GetFormat() == Orthanc::PixelFormat_Grayscale8 || source.GetFormat() == Orthanc::PixelFormat_RGB24 || source.GetFormat() == Orthanc::PixelFormat_RGBA32); // 16bpp is unsupported if (static_cast<int>(x) + offsetX < 0) { x = 0; } else { x += offsetX; if (x >= source.GetWidth()) { x = source.GetWidth() - 1; } } if (static_cast<int>(y) + offsetY < 0) { y = 0; } else { y += offsetY; if (y >= source.GetHeight()) { y = source.GetHeight() - 1; } } return *(reinterpret_cast<const uint8_t*>(source.GetConstBuffer()) + y * source.GetPitch() + x * bytesPerPixel + channel); } static uint8_t SmoothPixelValue(const Orthanc::ImageAccessor& source, unsigned int x, unsigned int y, unsigned int channel, unsigned int bytesPerPixel) { static const uint32_t kernel[5] = { 1, 4, 6, 4, 1 }; static const uint32_t normalization = 2 * (1 + 4 + 6 + 4 + 1); uint32_t accumulator = 0; // Horizontal smoothing for (int offset = -2; offset <= 2; offset++) { accumulator += kernel[offset + 2] * GetPixelValue(source, x, y, channel, offset, 0, bytesPerPixel); } // Vertical smoothing for (int offset = -2; offset <= 2; offset++) { accumulator += kernel[offset + 2] * GetPixelValue(source, x, y, channel, 0, offset, bytesPerPixel); } return static_cast<uint8_t>(accumulator / normalization); } Orthanc::ImageAccessor* Halve(const Orthanc::ImageAccessor& source, bool smooth) { if (source.GetWidth() % 2 == 1 || source.GetHeight() % 2 == 1) { throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageSize); } if (source.GetFormat() != Orthanc::PixelFormat_Grayscale8 && source.GetFormat() != Orthanc::PixelFormat_RGB24 && source.GetFormat() != Orthanc::PixelFormat_RGBA32) // 16bpp is not supported (*) { throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); } unsigned int channelsCount = source.GetBytesPerPixel(); // OK tx (*) std::auto_ptr<Orthanc::ImageAccessor> result(Allocate(source.GetFormat(), source.GetWidth() / 2, source.GetHeight() / 2)); unsigned int bytesPerPixel = source.GetBytesPerPixel(); for (unsigned int y = 0; y < source.GetHeight() / 2; y++) { uint8_t* q = reinterpret_cast<uint8_t*>(result->GetRow(y)); for (unsigned int x = 0; x < source.GetWidth() / 2; x++, q += 3) { for (unsigned int c = 0; c < channelsCount; c++) { if (smooth) { q[c] = SmoothPixelValue(source, 2 * x, 2 * y, c, bytesPerPixel); } else { q[c] = GetPixelValue(source, 2 * x, 2 * y, c, 0, 0, bytesPerPixel); } } } } return result.release(); } Orthanc::ImageAccessor* Clone(const Orthanc::ImageAccessor& accessor) { std::auto_ptr<Orthanc::ImageAccessor> result(Allocate(accessor.GetFormat(), accessor.GetWidth(), accessor.GetHeight())); Embed(*result, accessor, 0, 0); return result.release(); } Orthanc::ImageAccessor* Render(ITiledPyramid& pyramid, unsigned int level) { std::auto_ptr<Orthanc::ImageAccessor> result(Allocate(pyramid.GetPixelFormat(), pyramid.GetLevelWidth(level), pyramid.GetLevelHeight(level))); LOG(INFO) << "Rendering a tiled image of size " << result->GetWidth() << "x" << result->GetHeight(); for (unsigned int y = 0; y < result->GetHeight(); y += pyramid.GetTileHeight()) { for (unsigned int x = 0; x < result->GetWidth(); x += pyramid.GetTileWidth()) { std::auto_ptr<Orthanc::ImageAccessor> tile(pyramid.DecodeTile(level, x / pyramid.GetTileWidth(), y / pyramid.GetTileHeight())); Embed(*result, *tile, x, y); } } return result.release(); } } }