Mercurial > hg > orthanc
diff OrthancFramework/Sources/DicomFormat/DicomImageInformation.cpp @ 4044:d25f4c0fa160 framework
splitting code into OrthancFramework and OrthancServer
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Wed, 10 Jun 2020 20:30:34 +0200 |
parents | Core/DicomFormat/DicomImageInformation.cpp@2a170a8f1faf |
children | eab8010c05fc |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OrthancFramework/Sources/DicomFormat/DicomImageInformation.cpp Wed Jun 10 20:30:34 2020 +0200 @@ -0,0 +1,310 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2020 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" + +#ifndef NOMINMAX +#define NOMINMAX +#endif + +#include "DicomImageInformation.h" + +#include "../Compatibility.h" +#include "../OrthancException.h" +#include "../Toolbox.h" +#include <boost/lexical_cast.hpp> +#include <limits> +#include <cassert> +#include <stdio.h> +#include <memory> + +namespace Orthanc +{ + DicomImageInformation::DicomImageInformation(const DicomMap& values) + { + unsigned int pixelRepresentation; + unsigned int planarConfiguration = 0; + + try + { + std::string p = values.GetValue(DICOM_TAG_PHOTOMETRIC_INTERPRETATION).GetContent(); + Toolbox::ToUpperCase(p); + + if (p == "RGB") + { + photometric_ = PhotometricInterpretation_RGB; + } + else if (p == "MONOCHROME1") + { + photometric_ = PhotometricInterpretation_Monochrome1; + } + else if (p == "MONOCHROME2") + { + photometric_ = PhotometricInterpretation_Monochrome2; + } + else if (p == "PALETTE COLOR") + { + photometric_ = PhotometricInterpretation_Palette; + } + else if (p == "HSV") + { + photometric_ = PhotometricInterpretation_HSV; + } + else if (p == "ARGB") + { + photometric_ = PhotometricInterpretation_ARGB; + } + else if (p == "CMYK") + { + photometric_ = PhotometricInterpretation_CMYK; + } + else if (p == "YBR_FULL") + { + photometric_ = PhotometricInterpretation_YBRFull; + } + else if (p == "YBR_FULL_422") + { + photometric_ = PhotometricInterpretation_YBRFull422; + } + else if (p == "YBR_PARTIAL_420") + { + photometric_ = PhotometricInterpretation_YBRPartial420; + } + else if (p == "YBR_PARTIAL_422") + { + photometric_ = PhotometricInterpretation_YBRPartial422; + } + else if (p == "YBR_ICT") + { + photometric_ = PhotometricInterpretation_YBR_ICT; + } + else if (p == "YBR_RCT") + { + photometric_ = PhotometricInterpretation_YBR_RCT; + } + else + { + photometric_ = PhotometricInterpretation_Unknown; + } + + values.GetValue(DICOM_TAG_COLUMNS).ParseFirstUnsignedInteger(width_); // in some US images, we've seen tag values of "800\0"; that's why we parse the 'first' value + values.GetValue(DICOM_TAG_ROWS).ParseFirstUnsignedInteger(height_); + + bitsAllocated_ = boost::lexical_cast<unsigned int>(values.GetValue(DICOM_TAG_BITS_ALLOCATED).GetContent()); + + try + { + samplesPerPixel_ = boost::lexical_cast<unsigned int>(values.GetValue(DICOM_TAG_SAMPLES_PER_PIXEL).GetContent()); + } + catch (OrthancException&) + { + samplesPerPixel_ = 1; // Assume 1 color channel + } + + try + { + bitsStored_ = boost::lexical_cast<unsigned int>(values.GetValue(DICOM_TAG_BITS_STORED).GetContent()); + } + catch (OrthancException&) + { + bitsStored_ = bitsAllocated_; + } + + try + { + highBit_ = boost::lexical_cast<unsigned int>(values.GetValue(DICOM_TAG_HIGH_BIT).GetContent()); + } + catch (OrthancException&) + { + highBit_ = bitsStored_ - 1; + } + + try + { + pixelRepresentation = boost::lexical_cast<unsigned int>(values.GetValue(DICOM_TAG_PIXEL_REPRESENTATION).GetContent()); + } + catch (OrthancException&) + { + pixelRepresentation = 0; // Assume unsigned pixels + } + + if (samplesPerPixel_ > 1) + { + // The "Planar Configuration" is only set when "Samples per Pixels" is greater than 1 + // http://dicom.nema.org/medical/dicom/current/output/html/part03.html#sect_C.7.6.3.1.3 + try + { + planarConfiguration = boost::lexical_cast<unsigned int>(values.GetValue(DICOM_TAG_PLANAR_CONFIGURATION).GetContent()); + } + catch (OrthancException&) + { + planarConfiguration = 0; // Assume interleaved color channels + } + } + } + catch (boost::bad_lexical_cast&) + { + throw OrthancException(ErrorCode_NotImplemented); + } + catch (OrthancException&) + { + throw OrthancException(ErrorCode_NotImplemented); + } + + if (values.HasTag(DICOM_TAG_NUMBER_OF_FRAMES)) + { + try + { + numberOfFrames_ = boost::lexical_cast<unsigned int>(values.GetValue(DICOM_TAG_NUMBER_OF_FRAMES).GetContent()); + } + catch (boost::bad_lexical_cast&) + { + throw OrthancException(ErrorCode_NotImplemented); + } + } + else + { + numberOfFrames_ = 1; + } + + if (bitsAllocated_ != 8 && bitsAllocated_ != 16 && + bitsAllocated_ != 24 && bitsAllocated_ != 32) + { + throw OrthancException(ErrorCode_IncompatibleImageFormat, "Image not supported: " + boost::lexical_cast<std::string>(bitsAllocated_) + " bits allocated"); + } + else if (numberOfFrames_ == 0) + { + throw OrthancException(ErrorCode_IncompatibleImageFormat, "Image not supported (no frames)"); + } + else if (planarConfiguration != 0 && planarConfiguration != 1) + { + throw OrthancException(ErrorCode_IncompatibleImageFormat, "Image not supported: planar configuration is " + boost::lexical_cast<std::string>(planarConfiguration)); + } + + if (samplesPerPixel_ == 0) + { + throw OrthancException(ErrorCode_IncompatibleImageFormat, "Image not supported: samples per pixel is 0"); + } + + bytesPerValue_ = bitsAllocated_ / 8; + + isPlanar_ = (planarConfiguration != 0 ? true : false); + isSigned_ = (pixelRepresentation != 0 ? true : false); + } + + DicomImageInformation* DicomImageInformation::Clone() const + { + std::unique_ptr<DicomImageInformation> target(new DicomImageInformation); + target->width_ = width_; + target->height_ = height_; + target->samplesPerPixel_ = samplesPerPixel_; + target->numberOfFrames_ = numberOfFrames_; + target->isPlanar_ = isPlanar_; + target->isSigned_ = isSigned_; + target->bytesPerValue_ = bytesPerValue_; + target->bitsAllocated_ = bitsAllocated_; + target->bitsStored_ = bitsStored_; + target->highBit_ = highBit_; + target->photometric_ = photometric_; + + return target.release(); + } + + bool DicomImageInformation::ExtractPixelFormat(PixelFormat& format, + bool ignorePhotometricInterpretation) const + { + if (photometric_ == PhotometricInterpretation_Palette) + { + if (GetBitsStored() == 8 && GetChannelCount() == 1 && !IsSigned()) + { + format = PixelFormat_RGB24; + return true; + } + + if (GetBitsStored() == 16 && GetChannelCount() == 1 && !IsSigned()) + { + format = PixelFormat_RGB48; + return true; + } + } + + if (ignorePhotometricInterpretation || + photometric_ == PhotometricInterpretation_Monochrome1 || + photometric_ == PhotometricInterpretation_Monochrome2) + { + if (GetBitsStored() == 8 && GetChannelCount() == 1 && !IsSigned()) + { + format = PixelFormat_Grayscale8; + return true; + } + + if (GetBitsAllocated() == 16 && GetChannelCount() == 1 && !IsSigned()) + { + format = PixelFormat_Grayscale16; + return true; + } + + if (GetBitsAllocated() == 16 && GetChannelCount() == 1 && IsSigned()) + { + format = PixelFormat_SignedGrayscale16; + return true; + } + + if (GetBitsAllocated() == 32 && GetChannelCount() == 1 && !IsSigned()) + { + format = PixelFormat_Grayscale32; + return true; + } + } + + if (GetBitsStored() == 8 && + GetChannelCount() == 3 && + !IsSigned() && + (ignorePhotometricInterpretation || photometric_ == PhotometricInterpretation_RGB)) + { + format = PixelFormat_RGB24; + return true; + } + + return false; + } + + + size_t DicomImageInformation::GetFrameSize() const + { + return (GetHeight() * + GetWidth() * + GetBytesPerValue() * + GetChannelCount()); + } +}