Mercurial > hg > orthanc
changeset 853:839be3022203 jpeg
DicomImageInformation
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Fri, 06 Jun 2014 11:45:16 +0200 |
parents | 5944b8b80842 |
children | ff530685e46a |
files | CMakeLists.txt Core/DicomFormat/DicomImageInformation.cpp Core/DicomFormat/DicomImageInformation.h Core/DicomFormat/DicomIntegerPixelAccessor.cpp Core/DicomFormat/DicomIntegerPixelAccessor.h Core/Enumerations.h Core/ImageFormats/ImageBuffer.cpp Core/ImageFormats/ImageBuffer.h Core/ImageFormats/ImageProcessing.cpp Core/ImageFormats/ImageProcessing.h Core/OrthancException.cpp OrthancServer/FromDcmtkBridge.cpp OrthancServer/Internals/DicomImageDecoder.cpp OrthancServer/ParsedDicomFile.cpp UnitTestsSources/ImageProcessingTests.cpp |
diffstat | 15 files changed, 595 insertions(+), 167 deletions(-) [+] |
line wrap: on
line diff
--- a/CMakeLists.txt Fri Jun 06 10:28:02 2014 +0200 +++ b/CMakeLists.txt Fri Jun 06 11:45:16 2014 +0200 @@ -68,6 +68,7 @@ Core/DicomFormat/DicomArray.cpp Core/DicomFormat/DicomMap.cpp Core/DicomFormat/DicomTag.cpp + Core/DicomFormat/DicomImageInformation.cpp Core/DicomFormat/DicomIntegerPixelAccessor.cpp Core/DicomFormat/DicomInstanceHasher.cpp Core/Enumerations.cpp @@ -94,6 +95,7 @@ Core/MultiThreading/ThreadedCommandProcessor.cpp Core/ImageFormats/ImageAccessor.cpp Core/ImageFormats/ImageBuffer.cpp + Core/ImageFormats/ImageProcessing.cpp Core/ImageFormats/PngReader.cpp Core/ImageFormats/PngWriter.cpp Core/SQLite/Connection.cpp @@ -164,6 +166,7 @@ UnitTestsSources/Lua.cpp UnitTestsSources/MultiThreading.cpp UnitTestsSources/UnitTestsMain.cpp + UnitTestsSources/ImageProcessingTests.cpp UnitTestsSources/JpegLossless.cpp )
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Core/DicomFormat/DicomImageInformation.cpp Fri Jun 06 11:45:16 2014 +0200 @@ -0,0 +1,150 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, + * 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" + +#ifndef NOMINMAX +#define NOMINMAX +#endif + +#include "DicomImageInformation.h" + +#include "../OrthancException.h" +#include <boost/lexical_cast.hpp> +#include <limits> +#include <cassert> +#include <stdio.h> + +namespace Orthanc +{ + DicomImageInformation::DicomImageInformation(const DicomMap& values) + { + unsigned int pixelRepresentation; + unsigned int planarConfiguration = 0; + + try + { + width_ = boost::lexical_cast<unsigned int>(values.GetValue(DICOM_TAG_COLUMNS).AsString()); + height_ = boost::lexical_cast<unsigned int>(values.GetValue(DICOM_TAG_ROWS).AsString()); + samplesPerPixel_ = boost::lexical_cast<unsigned int>(values.GetValue(DICOM_TAG_SAMPLES_PER_PIXEL).AsString()); + bitsAllocated_ = boost::lexical_cast<unsigned int>(values.GetValue(DICOM_TAG_BITS_ALLOCATED).AsString()); + + try + { + bitsStored_ = boost::lexical_cast<unsigned int>(values.GetValue(DICOM_TAG_BITS_STORED).AsString()); + } + catch (OrthancException&) + { + bitsStored_ = bitsAllocated_; + } + + try + { + highBit_ = boost::lexical_cast<unsigned int>(values.GetValue(DICOM_TAG_HIGH_BIT).AsString()); + } + catch (OrthancException&) + { + highBit_ = bitsStored_ - 1; + } + + try + { + pixelRepresentation = boost::lexical_cast<unsigned int>(values.GetValue(DICOM_TAG_PIXEL_REPRESENTATION).AsString()); + } + catch (OrthancException&) + { + pixelRepresentation = 0; // Assume unsigned pixels + } + + if (samplesPerPixel_ > 1) + { + // The "Planar Configuration" is only set when "Samples per Pixels" is greater than 1 + // https://www.dabsoft.ch/dicom/3/C.7.6.3.1.3/ + try + { + planarConfiguration = boost::lexical_cast<unsigned int>(values.GetValue(DICOM_TAG_PLANAR_CONFIGURATION).AsString()); + } + catch (OrthancException&) + { + planarConfiguration = 0; // Assume interleaved color channels + } + } + } + catch (boost::bad_lexical_cast&) + { + throw OrthancException(ErrorCode_NotImplemented); + } + catch (OrthancException&) + { + throw OrthancException(ErrorCode_NotImplemented); + } + + try + { + numberOfFrames_ = boost::lexical_cast<unsigned int>(values.GetValue(DICOM_TAG_NUMBER_OF_FRAMES).AsString()); + } + catch (OrthancException) + { + // If the tag "NumberOfFrames" is absent, assume there is a single frame + numberOfFrames_ = 1; + } + catch (boost::bad_lexical_cast) + { + throw OrthancException(ErrorCode_NotImplemented); + } + + if ((bitsAllocated_ != 8 && bitsAllocated_ != 16 && + bitsAllocated_ != 24 && bitsAllocated_ != 32) || + numberOfFrames_ == 0 || + (planarConfiguration != 0 && planarConfiguration != 1)) + { + throw OrthancException(ErrorCode_NotImplemented); + } + + if (bitsAllocated_ > 32 || + bitsStored_ >= 32) + { + // Not available, as the accessor internally uses int32_t values + throw OrthancException(ErrorCode_NotImplemented); + } + + if (samplesPerPixel_ == 0) + { + throw OrthancException(ErrorCode_NotImplemented); + } + + bytesPerPixel_ = bitsAllocated_ / 8; + + isPlanar_ = (planarConfiguration != 0 ? true : false); + isSigned_ = (pixelRepresentation != 0 ? true : false); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Core/DicomFormat/DicomImageInformation.h Fri Jun 06 11:45:16 2014 +0200 @@ -0,0 +1,120 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, + * 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 "DicomMap.h" + +#include <stdint.h> + +namespace Orthanc +{ + class DicomImageInformation + { + private: + unsigned int width_; + unsigned int height_; + unsigned int samplesPerPixel_; + unsigned int numberOfFrames_; + + bool isPlanar_; + bool isSigned_; + size_t bytesPerPixel_; + + unsigned int bitsAllocated_; + unsigned int bitsStored_; + unsigned int highBit_; + + public: + DicomImageInformation(const DicomMap& values); + + unsigned int GetWidth() const + { + return width_; + } + + unsigned int GetHeight() const + { + return height_; + } + + unsigned int GetNumberOfFrames() const + { + return numberOfFrames_; + } + + unsigned int GetChannelCount() const + { + return samplesPerPixel_; + } + + unsigned int GetBitsStored() const + { + return bitsStored_; + } + + size_t GetBytesPerPixel() const + { + return bytesPerPixel_; + } + + bool IsSigned() const + { + return isSigned_; + } + + unsigned int GetBitsAllocated() const + { + return bitsAllocated_; + } + + unsigned int GetHighBit() const + { + return highBit_; + } + + unsigned int GetSamplesPerPixel() const + { + return samplesPerPixel_; + } + + bool IsPlanar() const + { + return isPlanar_; + } + + unsigned int GetShift() const + { + return highBit_ + 1 - bitsStored_; + } + }; +}
--- a/Core/DicomFormat/DicomIntegerPixelAccessor.cpp Fri Jun 06 10:28:02 2014 +0200 +++ b/Core/DicomFormat/DicomIntegerPixelAccessor.cpp Fri Jun 06 11:45:16 2014 +0200 @@ -49,102 +49,33 @@ DicomIntegerPixelAccessor::DicomIntegerPixelAccessor(const DicomMap& values, const void* pixelData, size_t size) : + information_(values), pixelData_(pixelData), size_(size) { - unsigned int bitsAllocated; - unsigned int bitsStored; - unsigned int highBit; - unsigned int pixelRepresentation; - planarConfiguration_ = 0; - - try - { - width_ = boost::lexical_cast<unsigned int>(values.GetValue(DICOM_TAG_COLUMNS).AsString()); - height_ = boost::lexical_cast<unsigned int>(values.GetValue(DICOM_TAG_ROWS).AsString()); - samplesPerPixel_ = boost::lexical_cast<unsigned int>(values.GetValue(DICOM_TAG_SAMPLES_PER_PIXEL).AsString()); - bitsAllocated = boost::lexical_cast<unsigned int>(values.GetValue(DICOM_TAG_BITS_ALLOCATED).AsString()); - bitsStored = boost::lexical_cast<unsigned int>(values.GetValue(DICOM_TAG_BITS_STORED).AsString()); - highBit = boost::lexical_cast<unsigned int>(values.GetValue(DICOM_TAG_HIGH_BIT).AsString()); - pixelRepresentation = boost::lexical_cast<unsigned int>(values.GetValue(DICOM_TAG_PIXEL_REPRESENTATION).AsString()); - - if (samplesPerPixel_ > 1) - { - // The "Planar Configuration" is only set when "Samples per Pixels" is greater than 1 - // https://www.dabsoft.ch/dicom/3/C.7.6.3.1.3/ - planarConfiguration_ = boost::lexical_cast<unsigned int>(values.GetValue(DICOM_TAG_PLANAR_CONFIGURATION).AsString()); - } - } - catch (boost::bad_lexical_cast) - { - throw OrthancException(ErrorCode_NotImplemented); - } - catch (OrthancException) - { - throw OrthancException(ErrorCode_NotImplemented); - } + frame_ = 0; + frameOffset_ = (information_.GetHeight() * information_.GetWidth() * + information_.GetBytesPerPixel() * information_.GetSamplesPerPixel()); - frame_ = 0; - try - { - numberOfFrames_ = boost::lexical_cast<unsigned int>(values.GetValue(DICOM_TAG_NUMBER_OF_FRAMES).AsString()); - } - catch (OrthancException) - { - // If the tag "NumberOfFrames" is absent, assume there is a single frame - numberOfFrames_ = 1; - } - catch (boost::bad_lexical_cast) - { - throw OrthancException(ErrorCode_NotImplemented); - } - - if ((bitsAllocated != 8 && bitsAllocated != 16 && - bitsAllocated != 24 && bitsAllocated != 32) || - numberOfFrames_ == 0 || - (planarConfiguration_ != 0 && planarConfiguration_ != 1)) - { - throw OrthancException(ErrorCode_NotImplemented); - } - - if (bitsAllocated > 32 || - bitsStored >= 32) - { - // Not available, as the accessor internally uses int32_t values - throw OrthancException(ErrorCode_NotImplemented); - } - - if (samplesPerPixel_ == 0) - { - throw OrthancException(ErrorCode_NotImplemented); - } - - bytesPerPixel_ = bitsAllocated / 8; - shift_ = highBit + 1 - bitsStored; - frameOffset_ = height_ * width_ * bytesPerPixel_ * samplesPerPixel_; - - if (numberOfFrames_ * frameOffset_ > size) + if (information_.GetNumberOfFrames() * frameOffset_ > size) { throw OrthancException(ErrorCode_BadFileFormat); } - /*printf("%d %d %d %d %d %d %d %d\n", width_, height_, samplesPerPixel_, bitsAllocated, - bitsStored, highBit, pixelRepresentation, numberOfFrames_);*/ - - if (pixelRepresentation) + if (information_.IsSigned()) { // Pixels are signed - mask_ = (1 << (bitsStored - 1)) - 1; - signMask_ = (1 << (bitsStored - 1)); + mask_ = (1 << (information_.GetBitsStored() - 1)) - 1; + signMask_ = (1 << (information_.GetBitsStored() - 1)); } else { // Pixels are unsigned - mask_ = (1 << bitsStored) - 1; + mask_ = (1 << information_.GetBitsStored()) - 1; signMask_ = 0; } - if (planarConfiguration_ == 0) + if (information_.IsPlanar()) { /** * The sample values for the first pixel are followed by the @@ -152,7 +83,7 @@ * means the order of the pixel values sent shall be R1, G1, B1, * R2, G2, B2, ..., etc. **/ - rowOffset_ = width_ * bytesPerPixel_ * samplesPerPixel_; + rowOffset_ = information_.GetWidth() * information_.GetBytesPerPixel() * information_.GetSamplesPerPixel(); } else { @@ -161,7 +92,7 @@ * this means the order of the pixel values sent is R1, R2, R3, * ..., G1, G2, G3, ..., B1, B2, B3, etc. **/ - rowOffset_ = width_ * bytesPerPixel_; + rowOffset_ = information_.GetWidth() * information_.GetBytesPerPixel(); } } @@ -169,7 +100,7 @@ void DicomIntegerPixelAccessor::GetExtremeValues(int32_t& min, int32_t& max) const { - if (height_ == 0 || width_ == 0) + if (information_.GetHeight() == 0 || information_.GetWidth() == 0) { min = max = 0; return; @@ -178,11 +109,11 @@ min = std::numeric_limits<int32_t>::max(); max = std::numeric_limits<int32_t>::min(); - for (unsigned int y = 0; y < height_; y++) + for (unsigned int y = 0; y < information_.GetHeight(); y++) { - for (unsigned int x = 0; x < width_; x++) + for (unsigned int x = 0; x < information_.GetWidth(); x++) { - for (unsigned int c = 0; c < GetChannelCount(); c++) + for (unsigned int c = 0; c < information_.GetChannelCount(); c++) { int32_t v = GetValue(x, y); if (v < min) @@ -199,13 +130,13 @@ unsigned int y, unsigned int channel) const { - assert(x < width_ && y < height_ && channel < samplesPerPixel_); + assert(x < information_.GetWidth() && y < information_.GetHeight() && channel < information_.GetSamplesPerPixel()); const uint8_t* pixel = reinterpret_cast<const uint8_t*>(pixelData_) + y * rowOffset_ + frame_ * frameOffset_; // https://www.dabsoft.ch/dicom/3/C.7.6.3.1.3/ - if (planarConfiguration_ == 0) + if (information_.IsPlanar() == 0) { /** * The sample values for the first pixel are followed by the @@ -213,7 +144,7 @@ * means the order of the pixel values sent shall be R1, G1, B1, * R2, G2, B2, ..., etc. **/ - pixel += channel * bytesPerPixel_ + x * samplesPerPixel_ * bytesPerPixel_; + pixel += channel * information_.GetBytesPerPixel() + x * information_.GetSamplesPerPixel() * information_.GetBytesPerPixel(); } else { @@ -222,20 +153,20 @@ * this means the order of the pixel values sent is R1, R2, R3, * ..., G1, G2, G3, ..., B1, B2, B3, etc. **/ - assert(frameOffset_ % samplesPerPixel_ == 0); - pixel += channel * frameOffset_ / samplesPerPixel_ + x * bytesPerPixel_; + assert(frameOffset_ % information_.GetSamplesPerPixel() == 0); + pixel += channel * frameOffset_ / information_.GetSamplesPerPixel() + x * information_.GetBytesPerPixel(); } uint32_t v; v = pixel[0]; - if (bytesPerPixel_ >= 2) + if (information_.GetBytesPerPixel() >= 2) v = v + (static_cast<uint32_t>(pixel[1]) << 8); - if (bytesPerPixel_ >= 3) + if (information_.GetBytesPerPixel() >= 3) v = v + (static_cast<uint32_t>(pixel[2]) << 16); - if (bytesPerPixel_ >= 4) + if (information_.GetBytesPerPixel() >= 4) v = v + (static_cast<uint32_t>(pixel[3]) << 24); - v = v >> shift_; + v = v >> information_.GetShift(); if (v & signMask_) { @@ -253,7 +184,7 @@ void DicomIntegerPixelAccessor::SetCurrentFrame(unsigned int frame) { - if (frame >= numberOfFrames_) + if (frame >= information_.GetNumberOfFrames()) { throw OrthancException(ErrorCode_ParameterOutOfRange); }
--- a/Core/DicomFormat/DicomIntegerPixelAccessor.h Fri Jun 06 10:28:02 2014 +0200 +++ b/Core/DicomFormat/DicomIntegerPixelAccessor.h Fri Jun 06 11:45:16 2014 +0200 @@ -34,6 +34,8 @@ #include "DicomMap.h" +#include "DicomImageInformation.h" + #include <stdint.h> namespace Orthanc @@ -41,20 +43,14 @@ class DicomIntegerPixelAccessor { private: - unsigned int width_; - unsigned int height_; - unsigned int samplesPerPixel_; - unsigned int numberOfFrames_; - unsigned int planarConfiguration_; + DicomImageInformation information_; + + uint32_t signMask_; + uint32_t mask_; + const void* pixelData_; size_t size_; - - uint8_t shift_; - uint32_t signMask_; - uint32_t mask_; - size_t bytesPerPixel_; unsigned int frame_; - size_t frameOffset_; size_t rowOffset_; @@ -63,19 +59,9 @@ const void* pixelData, size_t size); - unsigned int GetWidth() const - { - return width_; - } - - unsigned int GetHeight() const + const DicomImageInformation GetInformation() const { - return height_; - } - - unsigned int GetNumberOfFrames() const - { - return numberOfFrames_; + return information_; } unsigned int GetCurrentFrame() const @@ -88,11 +74,6 @@ void GetExtremeValues(int32_t& min, int32_t& max) const; - unsigned int GetChannelCount() const - { - return samplesPerPixel_; - } - int32_t GetValue(unsigned int x, unsigned int y, unsigned int channel = 0) const; }; }
--- a/Core/Enumerations.h Fri Jun 06 10:28:02 2014 +0200 +++ b/Core/Enumerations.h Fri Jun 06 11:45:16 2014 +0200 @@ -69,7 +69,9 @@ ErrorCode_FullStorage, ErrorCode_CorruptedFile, ErrorCode_InexistentTag, - ErrorCode_ReadOnly + ErrorCode_ReadOnly, + ErrorCode_IncompatibleImageFormat, + ErrorCode_IncompatibleImageSize }; /**
--- a/Core/ImageFormats/ImageBuffer.cpp Fri Jun 06 10:28:02 2014 +0200 +++ b/Core/ImageFormats/ImageBuffer.cpp Fri Jun 06 11:45:16 2014 +0200 @@ -63,7 +63,18 @@ } - ImageBuffer::ImageBuffer() : changed_(false) + ImageBuffer::ImageBuffer(unsigned int width, + unsigned int height, + PixelFormat format) + { + Initialize(); + SetWidth(width); + SetHeight(height); + SetFormat(format); + } + + + void ImageBuffer::Initialize() { changed_ = false; forceMinimalPitch_ = true;
--- a/Core/ImageFormats/ImageBuffer.h Fri Jun 06 10:28:02 2014 +0200 +++ b/Core/ImageFormats/ImageBuffer.h Fri Jun 06 11:45:16 2014 +0200 @@ -51,11 +51,20 @@ unsigned int height_; unsigned int pitch_; uint8_t *buffer_; + + void Initialize(); void Allocate(); public: - ImageBuffer(); + ImageBuffer(unsigned int width, + unsigned int height, + PixelFormat format); + + ImageBuffer() + { + Initialize(); + } PixelFormat GetFormat() const {
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Core/ImageFormats/ImageProcessing.cpp Fri Jun 06 11:45:16 2014 +0200 @@ -0,0 +1,82 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, + * 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 "ImageProcessing.h" + +#include "../OrthancException.h" + +#include <cassert> +#include <string.h> + +namespace Orthanc +{ + void ImageProcessing::Copy(ImageAccessor& target, + const ImageAccessor& source) + { + if (target.GetWidth() != source.GetWidth() || + target.GetHeight() != source.GetHeight()) + { + throw OrthancException(ErrorCode_IncompatibleImageSize); + } + + if (target.GetFormat() != source.GetFormat()) + { + throw OrthancException(ErrorCode_IncompatibleImageFormat); + } + + unsigned int lineSize = GetBytesPerPixel(source.GetFormat()) * source.GetWidth(); + + assert(source.GetPitch() >= lineSize && target.GetPitch() >= lineSize); + + for (unsigned int y = 0; y < source.GetHeight(); y++) + { + memcpy(target.GetRow(y), source.GetRow(y), lineSize); + } + } + + void ImageProcessing::Convert(ImageAccessor& target, + const ImageAccessor& source) + { + if (target.GetWidth() != source.GetWidth() || + target.GetHeight() != source.GetHeight()) + { + throw OrthancException(ErrorCode_IncompatibleImageSize); + } + + if (source.GetFormat() == target.GetFormat()) + { + Copy(target, source); + return; + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Core/ImageFormats/ImageProcessing.h Fri Jun 06 11:45:16 2014 +0200 @@ -0,0 +1,48 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, + * 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 "ImageAccessor.h" + +namespace Orthanc +{ + class ImageProcessing + { + public: + static void Copy(ImageAccessor& target, + const ImageAccessor& source); + + static void Convert(ImageAccessor& target, + const ImageAccessor& source); + }; +}
--- a/Core/OrthancException.cpp Fri Jun 06 10:28:02 2014 +0200 +++ b/Core/OrthancException.cpp Fri Jun 06 11:45:16 2014 +0200 @@ -115,6 +115,12 @@ case ErrorCode_ReadOnly: return "Cannot modify a read-only data structure"; + case ErrorCode_IncompatibleImageSize: + return "Incompatible size of the images"; + + case ErrorCode_IncompatibleImageFormat: + return "Incompatible format of the images"; + case ErrorCode_Custom: default: return "???";
--- a/OrthancServer/FromDcmtkBridge.cpp Fri Jun 06 10:28:02 2014 +0200 +++ b/OrthancServer/FromDcmtkBridge.cpp Fri Jun 06 11:45:16 2014 +0200 @@ -431,15 +431,15 @@ static void ExtractPngImageColorPreview(std::string& result, DicomIntegerPixelAccessor& accessor) { - assert(accessor.GetChannelCount() == 3); + assert(accessor.GetInformation().GetChannelCount() == 3); PngWriter w; - std::vector<uint8_t> image(accessor.GetWidth() * accessor.GetHeight() * 3, 0); + std::vector<uint8_t> image(accessor.GetInformation().GetWidth() * accessor.GetInformation().GetHeight() * 3, 0); uint8_t* pixel = &image[0]; - for (unsigned int y = 0; y < accessor.GetHeight(); y++) + for (unsigned int y = 0; y < accessor.GetInformation().GetHeight(); y++) { - for (unsigned int x = 0; x < accessor.GetWidth(); x++) + for (unsigned int x = 0; x < accessor.GetInformation().GetWidth(); x++) { for (unsigned int c = 0; c < 3; c++, pixel++) { @@ -454,27 +454,27 @@ } } - w.WriteToMemory(result, accessor.GetWidth(), accessor.GetHeight(), - accessor.GetWidth() * 3, PixelFormat_RGB24, &image[0]); + w.WriteToMemory(result, accessor.GetInformation().GetWidth(), accessor.GetInformation().GetHeight(), + accessor.GetInformation().GetWidth() * 3, PixelFormat_RGB24, &image[0]); } static void ExtractPngImageGrayscalePreview(std::string& result, DicomIntegerPixelAccessor& accessor) { - assert(accessor.GetChannelCount() == 1); + assert(accessor.GetInformation().GetChannelCount() == 1); PngWriter w; int32_t min, max; accessor.GetExtremeValues(min, max); - std::vector<uint8_t> image(accessor.GetWidth() * accessor.GetHeight(), 0); + std::vector<uint8_t> image(accessor.GetInformation().GetWidth() * accessor.GetInformation().GetHeight(), 0); if (min != max) { uint8_t* pixel = &image[0]; - for (unsigned int y = 0; y < accessor.GetHeight(); y++) + for (unsigned int y = 0; y < accessor.GetInformation().GetHeight(); y++) { - for (unsigned int x = 0; x < accessor.GetWidth(); x++, pixel++) + for (unsigned int x = 0; x < accessor.GetInformation().GetWidth(); x++, pixel++) { int32_t v = accessor.GetValue(x, y); *pixel = static_cast<uint8_t>( @@ -484,8 +484,8 @@ } } - w.WriteToMemory(result, accessor.GetWidth(), accessor.GetHeight(), - accessor.GetWidth(), PixelFormat_Grayscale8, &image[0]); + w.WriteToMemory(result, accessor.GetInformation().GetWidth(), accessor.GetInformation().GetHeight(), + accessor.GetInformation().GetWidth(), PixelFormat_Grayscale8, &image[0]); } @@ -494,15 +494,15 @@ DicomIntegerPixelAccessor& accessor, PixelFormat format) { - assert(accessor.GetChannelCount() == 1); + assert(accessor.GetInformation().GetChannelCount() == 1); PngWriter w; - std::vector<T> image(accessor.GetWidth() * accessor.GetHeight(), 0); + std::vector<T> image(accessor.GetInformation().GetWidth() * accessor.GetInformation().GetHeight(), 0); T* pixel = &image[0]; - for (unsigned int y = 0; y < accessor.GetHeight(); y++) + for (unsigned int y = 0; y < accessor.GetInformation().GetHeight(); y++) { - for (unsigned int x = 0; x < accessor.GetWidth(); x++, pixel++) + for (unsigned int x = 0; x < accessor.GetInformation().GetWidth(); x++, pixel++) { int32_t v = accessor.GetValue(x, y); if (v < static_cast<int32_t>(std::numeric_limits<T>::min())) @@ -514,8 +514,8 @@ } } - w.WriteToMemory(result, accessor.GetWidth(), accessor.GetHeight(), - accessor.GetWidth() * sizeof(T), format, &image[0]); + w.WriteToMemory(result, accessor.GetInformation().GetWidth(), accessor.GetInformation().GetHeight(), + accessor.GetInformation().GetWidth() * sizeof(T), format, &image[0]); } @@ -564,7 +564,7 @@ PixelFormat format; bool supported = false; - if (accessor->GetChannelCount() == 1) + if (accessor->GetInformation().GetChannelCount() == 1) { switch (mode) { @@ -593,7 +593,7 @@ break; } } - else if (accessor->GetChannelCount() == 3) + else if (accessor->GetInformation().GetChannelCount() == 3) { switch (mode) { @@ -614,8 +614,8 @@ } if (accessor.get() == NULL || - accessor->GetWidth() == 0 || - accessor->GetHeight() == 0) + accessor->GetInformation().GetWidth() == 0 || + accessor->GetInformation().GetHeight() == 0) { PngWriter w; w.WriteToMemory(result, 0, 0, 0, format, NULL);
--- a/OrthancServer/Internals/DicomImageDecoder.cpp Fri Jun 06 10:28:02 2014 +0200 +++ b/OrthancServer/Internals/DicomImageDecoder.cpp Fri Jun 06 11:45:16 2014 +0200 @@ -99,12 +99,17 @@ { private: std::string psmct_; - std::auto_ptr<DicomIntegerPixelAccessor> accessor_; - + std::auto_ptr<DicomIntegerPixelAccessor> slowAccessor_; + std::auto_ptr<ImageAccessor> fastAccessor_; + public: void Setup(DcmDataset& dataset, unsigned int frame) { + psmct_.clear(); + slowAccessor_.reset(NULL); + fastAccessor_.reset(NULL); + // See also: http://support.dcmtk.org/wiki/dcmtk/howto/accessing-compressed-data DicomMap m; @@ -121,7 +126,7 @@ Uint8* pixData = NULL; if (e->getUint8Array(pixData) == EC_Normal) { - accessor_.reset(new DicomIntegerPixelAccessor(m, pixData, e->getLength())); + slowAccessor_.reset(new DicomIntegerPixelAccessor(m, pixData, e->getLength())); } } else if (DicomImageDecoder::DecodePsmctRle1(psmct_, dataset)) @@ -133,27 +138,63 @@ pixData = reinterpret_cast<Uint8*>(&psmct_[0]); } - accessor_.reset(new DicomIntegerPixelAccessor(m, pixData, psmct_.size())); + slowAccessor_.reset(new DicomIntegerPixelAccessor(m, pixData, psmct_.size())); } - if (accessor_.get() == NULL) + if (slowAccessor_.get() == NULL) { throw OrthancException(ErrorCode_BadFileFormat); } - accessor_->SetCurrentFrame(frame); + slowAccessor_->SetCurrentFrame(frame); + + + /** + * If possible, create a fast ImageAccessor to the image buffer. + **/ + + + } + + unsigned int GetWidth() const + { + assert(slowAccessor_.get() != NULL); + return slowAccessor_->GetInformation().GetWidth(); + } + + unsigned int GetHeight() const + { + assert(slowAccessor_.get() != NULL); + return slowAccessor_->GetInformation().GetHeight(); + } + + unsigned int GetBytesPerPixel() const + { + assert(slowAccessor_.get() != NULL); + return slowAccessor_->GetInformation().GetBytesPerPixel(); } unsigned int GetChannelCount() const { - assert(accessor_.get() != NULL); - return accessor_->GetChannelCount(); + assert(slowAccessor_.get() != NULL); + return slowAccessor_->GetInformation().GetChannelCount(); } const DicomIntegerPixelAccessor GetAccessor() const { - assert(accessor_.get() != NULL); - return *accessor_; + assert(slowAccessor_.get() != NULL); + return *slowAccessor_; + } + + bool HasFastAccessor() const + { + return fastAccessor_.get() != NULL; + } + + const ImageAccessor& GetFastAccessor() const + { + assert(HasFastAccessor()); + return *fastAccessor_; } }; @@ -362,12 +403,12 @@ const PixelType minValue = std::numeric_limits<PixelType>::min(); const PixelType maxValue = std::numeric_limits<PixelType>::max(); - for (unsigned int y = 0; y < source.GetHeight(); y++) + for (unsigned int y = 0; y < source.GetInformation().GetHeight(); y++) { PixelType* pixel = reinterpret_cast<PixelType*>(target.GetRow(y)); - for (unsigned int x = 0; x < source.GetWidth(); x++) + for (unsigned int x = 0; x < source.GetInformation().GetWidth(); x++) { - for (unsigned int c = 0; c < source.GetChannelCount(); c++, pixel++) + for (unsigned int c = 0; c < source.GetInformation().GetChannelCount(); c++, pixel++) { int32_t v = source.GetValue(x, y, c); if (v < static_cast<int32_t>(minValue))
--- a/OrthancServer/ParsedDicomFile.cpp Fri Jun 06 10:28:02 2014 +0200 +++ b/OrthancServer/ParsedDicomFile.cpp Fri Jun 06 11:45:16 2014 +0200 @@ -999,15 +999,15 @@ DicomIntegerPixelAccessor& accessor, PixelFormat format) { - assert(accessor.GetChannelCount() == 1); + assert(accessor.GetInformation().GetChannelCount() == 1); PngWriter w; - std::vector<T> image(accessor.GetWidth() * accessor.GetHeight(), 0); + std::vector<T> image(accessor.GetInformation().GetWidth() * accessor.GetInformation().GetHeight(), 0); T* pixel = &image[0]; - for (unsigned int y = 0; y < accessor.GetHeight(); y++) + for (unsigned int y = 0; y < accessor.GetInformation().GetHeight(); y++) { - for (unsigned int x = 0; x < accessor.GetWidth(); x++, pixel++) + for (unsigned int x = 0; x < accessor.GetInformation().GetWidth(); x++, pixel++) { int32_t v = accessor.GetValue(x, y); if (v < static_cast<int32_t>(std::numeric_limits<T>::min())) @@ -1019,8 +1019,8 @@ } } - w.WriteToMemory(result, accessor.GetWidth(), accessor.GetHeight(), - accessor.GetWidth() * sizeof(T), format, &image[0]); + w.WriteToMemory(result, accessor.GetInformation().GetWidth(), accessor.GetInformation().GetHeight(), + accessor.GetInformation().GetWidth() * sizeof(T), format, &image[0]); }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/UnitTestsSources/ImageProcessingTests.cpp Fri Jun 06 11:45:16 2014 +0200 @@ -0,0 +1,44 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, + * 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 "PrecompiledHeadersUnitTests.h" +#include "gtest/gtest.h" + +#include "../Core/ImageFormats/ImageBuffer.h" +#include "../Core/ImageFormats/ImageProcessing.h" + +using namespace Orthanc; + + +TEST(ImageProcessing, Copy) +{ +}