1 /** 2 * Orthanc - A Lightweight, RESTful DICOM Store 3 * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics 4 * Department, University Hospital of Liege, Belgium 5 * Copyright (C) 2017-2021 Osimis S.A., Belgium 6 * 7 * This program is free software: you can redistribute it and/or 8 * modify it under the terms of the GNU Lesser General Public License 9 * as published by the Free Software Foundation, either version 3 of 10 * the License, or (at your option) any later version. 11 * 12 * This program is distributed in the hope that it will be useful, but 13 * WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 * Lesser General Public License for more details. 16 * 17 * You should have received a copy of the GNU Lesser General Public 18 * License along with this program. If not, see 19 * <http://www.gnu.org/licenses/>. 20 **/ 21 22 23 #include "../PrecompiledHeaders.h" 24 25 #ifndef NOMINMAX 26 #define NOMINMAX 27 #endif 28 29 #include "DicomIntegerPixelAccessor.h" 30 31 #include "../OrthancException.h" 32 #include <boost/lexical_cast.hpp> 33 #include <limits> 34 #include <cassert> 35 #include <stdio.h> 36 37 namespace Orthanc 38 { DicomIntegerPixelAccessor(const DicomMap & values,const void * pixelData,size_t size)39 DicomIntegerPixelAccessor::DicomIntegerPixelAccessor(const DicomMap& values, 40 const void* pixelData, 41 size_t size) : 42 information_(values), 43 pixelData_(pixelData), 44 size_(size) 45 { 46 if (information_.GetBitsAllocated() > 32 || 47 information_.GetBitsStored() >= 32) 48 { 49 // Not available, as the accessor internally uses int32_t values 50 throw OrthancException(ErrorCode_NotImplemented); 51 } 52 53 frame_ = 0; 54 frameOffset_ = information_.GetFrameSize(); 55 56 if (information_.GetNumberOfFrames() * frameOffset_ > size) 57 { 58 throw OrthancException(ErrorCode_BadFileFormat); 59 } 60 61 if (information_.IsSigned()) 62 { 63 // Pixels are signed 64 mask_ = (1 << (information_.GetBitsStored() - 1)) - 1; 65 signMask_ = (1 << (information_.GetBitsStored() - 1)); 66 } 67 else 68 { 69 // Pixels are unsigned 70 mask_ = (1 << information_.GetBitsStored()) - 1; 71 signMask_ = 0; 72 } 73 74 if (information_.IsPlanar()) 75 { 76 /** 77 * Each color plane shall be sent contiguously. For RGB images, 78 * this means the order of the pixel values sent is R1, R2, R3, 79 * ..., G1, G2, G3, ..., B1, B2, B3, etc. 80 **/ 81 rowOffset_ = information_.GetWidth() * information_.GetBytesPerValue(); 82 } 83 else 84 { 85 /** 86 * The sample values for the first pixel are followed by the 87 * sample values for the second pixel, etc. For RGB images, this 88 * means the order of the pixel values sent shall be R1, G1, B1, 89 * R2, G2, B2, ..., etc. 90 **/ 91 rowOffset_ = information_.GetWidth() * information_.GetBytesPerValue() * information_.GetChannelCount(); 92 } 93 } 94 95 GetExtremeValues(int32_t & min,int32_t & max) const96 void DicomIntegerPixelAccessor::GetExtremeValues(int32_t& min, 97 int32_t& max) const 98 { 99 if (information_.GetHeight() == 0 || information_.GetWidth() == 0) 100 { 101 min = max = 0; 102 return; 103 } 104 105 min = std::numeric_limits<int32_t>::max(); 106 max = std::numeric_limits<int32_t>::min(); 107 108 const unsigned int height = information_.GetHeight(); 109 const unsigned int width = information_.GetWidth(); 110 const unsigned int channels = information_.GetChannelCount(); 111 112 for (unsigned int y = 0; y < height; y++) 113 { 114 for (unsigned int x = 0; x < width; x++) 115 { 116 for (unsigned int c = 0; c < channels; c++) 117 { 118 int32_t v = GetValue(x, y); 119 if (v < min) 120 min = v; 121 if (v > max) 122 max = v; 123 } 124 } 125 } 126 } 127 128 GetValue(unsigned int x,unsigned int y,unsigned int channel) const129 int32_t DicomIntegerPixelAccessor::GetValue(unsigned int x, 130 unsigned int y, 131 unsigned int channel) const 132 { 133 assert(x < information_.GetWidth() && 134 y < information_.GetHeight() && 135 channel < information_.GetChannelCount()); 136 137 const uint8_t* pixel = reinterpret_cast<const uint8_t*>(pixelData_) + 138 y * rowOffset_ + frame_ * frameOffset_; 139 140 // http://dicom.nema.org/medical/dicom/current/output/html/part03.html#sect_C.7.6.3.1.3 141 if (information_.IsPlanar()) 142 { 143 /** 144 * Each color plane shall be sent contiguously. For RGB images, 145 * this means the order of the pixel values sent is R1, R2, R3, 146 * ..., G1, G2, G3, ..., B1, B2, B3, etc. 147 **/ 148 assert(frameOffset_ % information_.GetChannelCount() == 0); 149 pixel += channel * frameOffset_ / information_.GetChannelCount() + x * information_.GetBytesPerValue(); 150 } 151 else 152 { 153 /** 154 * The sample values for the first pixel are followed by the 155 * sample values for the second pixel, etc. For RGB images, this 156 * means the order of the pixel values sent shall be R1, G1, B1, 157 * R2, G2, B2, ..., etc. 158 **/ 159 pixel += channel * information_.GetBytesPerValue() + x * information_.GetChannelCount() * information_.GetBytesPerValue(); 160 } 161 162 uint32_t v; 163 v = pixel[0]; 164 if (information_.GetBytesPerValue() >= 2) 165 v = v + (static_cast<uint32_t>(pixel[1]) << 8); 166 if (information_.GetBytesPerValue() >= 3) 167 v = v + (static_cast<uint32_t>(pixel[2]) << 16); 168 if (information_.GetBytesPerValue() >= 4) 169 v = v + (static_cast<uint32_t>(pixel[3]) << 24); 170 171 v = v >> information_.GetShift(); 172 173 if (v & signMask_) 174 { 175 // Signed value 176 // http://en.wikipedia.org/wiki/Two%27s_complement#Subtraction_from_2N 177 return -static_cast<int32_t>(mask_) + static_cast<int32_t>(v & mask_) - 1; 178 } 179 else 180 { 181 // Unsigned value 182 return static_cast<int32_t>(v & mask_); 183 } 184 } 185 186 SetCurrentFrame(unsigned int frame)187 void DicomIntegerPixelAccessor::SetCurrentFrame(unsigned int frame) 188 { 189 if (frame >= information_.GetNumberOfFrames()) 190 { 191 throw OrthancException(ErrorCode_ParameterOutOfRange); 192 } 193 194 frame_ = frame; 195 } 196 197 } 198