Mercurial > hg > orthanc-stone
annotate Framework/Volumes/ImageBuffer3D.cpp @ 47:28956ed68280
agpl license
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Thu, 27 Apr 2017 11:00:15 +0200 |
parents | 7207a407bcd8 |
children | 4a541cd4fa83 4cff7b1ed31d |
rev | line source |
---|---|
0 | 1 /** |
2 * Stone of Orthanc | |
3 * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics | |
4 * Department, University Hospital of Liege, Belgium | |
40
7207a407bcd8
shared copyright with osimis
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
16
diff
changeset
|
5 * Copyright (C) 2017 Osimis, Belgium |
0 | 6 * |
7 * This program is free software: you can redistribute it and/or | |
47 | 8 * modify it under the terms of the GNU Affero 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. | |
0 | 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 | |
47 | 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
15 * Affero General Public License for more details. | |
16 * | |
17 * You should have received a copy of the GNU Affero General Public License | |
0 | 18 * along with this program. If not, see <http://www.gnu.org/licenses/>. |
19 **/ | |
20 | |
21 | |
22 #include "ImageBuffer3D.h" | |
23 | |
16 | 24 #include "../../Resources/Orthanc/Core/Images/ImageProcessing.h" |
25 #include "../../Resources/Orthanc/Core/OrthancException.h" | |
0 | 26 |
27 namespace OrthancStone | |
28 { | |
29 Orthanc::ImageAccessor ImageBuffer3D::GetAxialSliceAccessor(unsigned int slice, | |
30 bool readOnly) | |
31 { | |
32 if (slice >= depth_) | |
33 { | |
34 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); | |
35 } | |
36 | |
37 Orthanc::ImageAccessor accessor; | |
38 | |
39 if (readOnly) | |
40 { | |
41 accessor.AssignReadOnly(format_, width_, height_, image_.GetPitch(), | |
42 image_.GetConstRow(height_ * (depth_ - 1 - slice))); | |
43 } | |
44 else | |
45 { | |
46 accessor.AssignWritable(format_, width_, height_, image_.GetPitch(), | |
47 image_.GetRow(height_ * (depth_ - 1 - slice))); | |
48 } | |
49 | |
50 return accessor; | |
51 } | |
52 | |
53 | |
54 Orthanc::ImageAccessor ImageBuffer3D::GetCoronalSliceAccessor(unsigned int slice, | |
55 bool readOnly) | |
56 { | |
57 if (slice >= height_) | |
58 { | |
59 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); | |
60 } | |
61 | |
62 Orthanc::ImageAccessor accessor; | |
63 | |
64 if (readOnly) | |
65 { | |
66 accessor.AssignReadOnly(format_, width_, depth_, image_.GetPitch() * height_, | |
67 image_.GetConstRow(slice)); | |
68 } | |
69 else | |
70 { | |
71 accessor.AssignWritable(format_, width_, depth_, image_.GetPitch() * height_, | |
72 image_.GetRow(slice)); | |
73 } | |
74 | |
75 return accessor; | |
76 } | |
77 | |
78 | |
79 Orthanc::Image* ImageBuffer3D::ExtractSagittalSlice(unsigned int slice) const | |
80 { | |
81 if (slice >= width_) | |
82 { | |
83 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); | |
84 } | |
85 | |
11 | 86 std::auto_ptr<Orthanc::Image> result(new Orthanc::Image(format_, height_, depth_, false)); |
0 | 87 |
88 unsigned int bytesPerPixel = Orthanc::GetBytesPerPixel(format_); | |
89 | |
90 for (unsigned int z = 0; z < depth_; z++) | |
91 { | |
92 //uint8_t* target = reinterpret_cast<uint8_t*>(result->GetRow(depth_ - 1 - z)); | |
93 uint8_t* target = reinterpret_cast<uint8_t*>(result->GetRow(z)); | |
94 | |
95 for (unsigned int y = 0; y < height_; y++) | |
96 { | |
97 const void* source = (reinterpret_cast<const uint8_t*>(image_.GetConstRow(y + z * height_)) + | |
98 bytesPerPixel * slice); | |
99 | |
100 memcpy(target, source, bytesPerPixel); | |
101 target += bytesPerPixel; | |
102 } | |
103 } | |
104 | |
105 return result.release(); | |
106 } | |
107 | |
108 | |
109 ImageBuffer3D::ImageBuffer3D(Orthanc::PixelFormat format, | |
110 unsigned int width, | |
111 unsigned int height, | |
112 unsigned int depth) : | |
11 | 113 image_(format, width, height * depth, false), |
0 | 114 format_(format), |
115 width_(width), | |
116 height_(height), | |
117 depth_(depth) | |
118 { | |
119 GeometryToolbox::AssignVector(voxelDimensions_, 1, 1, 1); | |
120 } | |
121 | |
122 | |
123 void ImageBuffer3D::Clear() | |
124 { | |
125 WriteLock lock(mutex_); | |
126 Orthanc::ImageProcessing::Set(image_, 0); | |
127 } | |
128 | |
129 | |
130 void ImageBuffer3D::SetAxialGeometry(const SliceGeometry& geometry) | |
131 { | |
132 WriteLock lock(mutex_); | |
133 axialGeometry_ = geometry; | |
134 } | |
135 | |
136 | |
137 void ImageBuffer3D::SetVoxelDimensions(double x, | |
138 double y, | |
139 double z) | |
140 { | |
141 if (x <= 0 || | |
142 y <= 0 || | |
143 z <= 0) | |
144 { | |
145 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); | |
146 } | |
147 | |
148 { | |
149 WriteLock lock(mutex_); | |
150 GeometryToolbox::AssignVector(voxelDimensions_, x, y, z); | |
151 } | |
152 } | |
153 | |
154 | |
155 Vector ImageBuffer3D::GetVoxelDimensions(VolumeProjection projection) | |
156 { | |
157 ReadLock lock(mutex_); | |
158 | |
159 Vector result; | |
160 switch (projection) | |
161 { | |
162 case VolumeProjection_Axial: | |
163 result = voxelDimensions_; | |
164 break; | |
165 | |
166 case VolumeProjection_Coronal: | |
167 GeometryToolbox::AssignVector(result, voxelDimensions_[0], voxelDimensions_[2], voxelDimensions_[1]); | |
168 break; | |
169 | |
170 case VolumeProjection_Sagittal: | |
171 GeometryToolbox::AssignVector(result, voxelDimensions_[1], voxelDimensions_[2], voxelDimensions_[0]); | |
172 break; | |
173 | |
174 default: | |
175 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); | |
176 } | |
177 | |
178 return result; | |
179 } | |
180 | |
181 | |
182 void ImageBuffer3D::GetSliceSize(unsigned int& width, | |
183 unsigned int& height, | |
184 VolumeProjection projection) | |
185 { | |
186 switch (projection) | |
187 { | |
188 case VolumeProjection_Axial: | |
189 width = width_; | |
190 height = height_; | |
191 break; | |
192 | |
193 case VolumeProjection_Coronal: | |
194 width = width_; | |
195 height = depth_; | |
196 break; | |
197 | |
198 case VolumeProjection_Sagittal: | |
199 width = height_; | |
200 height = depth_; | |
201 break; | |
202 | |
203 default: | |
204 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); | |
205 } | |
206 } | |
207 | |
208 | |
209 ParallelSlices* ImageBuffer3D::GetGeometry(VolumeProjection projection) | |
210 { | |
211 std::auto_ptr<ParallelSlices> result(new ParallelSlices); | |
212 | |
213 switch (projection) | |
214 { | |
215 case VolumeProjection_Axial: | |
216 for (unsigned int z = 0; z < depth_; z++) | |
217 { | |
218 Vector origin = axialGeometry_.GetOrigin(); | |
219 origin += static_cast<double>(z) * voxelDimensions_[2] * axialGeometry_.GetNormal(); | |
220 | |
221 result->AddSlice(origin, | |
222 axialGeometry_.GetAxisX(), | |
223 axialGeometry_.GetAxisY()); | |
224 } | |
225 break; | |
226 | |
227 case VolumeProjection_Coronal: | |
228 for (unsigned int y = 0; y < height_; y++) | |
229 { | |
230 Vector origin = axialGeometry_.GetOrigin(); | |
231 origin += static_cast<double>(y) * voxelDimensions_[1] * axialGeometry_.GetAxisY(); | |
232 origin += static_cast<double>(depth_ - 1) * voxelDimensions_[2] * axialGeometry_.GetNormal(); | |
233 | |
234 result->AddSlice(origin, | |
235 axialGeometry_.GetAxisX(), | |
236 -axialGeometry_.GetNormal()); | |
237 } | |
238 break; | |
239 | |
240 case VolumeProjection_Sagittal: | |
241 for (unsigned int x = 0; x < width_; x++) | |
242 { | |
243 Vector origin = axialGeometry_.GetOrigin(); | |
244 origin += static_cast<double>(x) * voxelDimensions_[0] * axialGeometry_.GetAxisX(); | |
245 origin += static_cast<double>(depth_ - 1) * voxelDimensions_[2] * axialGeometry_.GetNormal(); | |
246 | |
247 result->AddSlice(origin, | |
248 axialGeometry_.GetAxisY(), | |
249 -axialGeometry_.GetNormal()); | |
250 } | |
251 break; | |
252 | |
253 default: | |
254 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); | |
255 } | |
256 | |
257 return result.release(); | |
258 } | |
259 | |
260 | |
261 ImageBuffer3D::SliceReader::SliceReader(ImageBuffer3D& that, | |
262 VolumeProjection projection, | |
263 unsigned int slice) : | |
264 lock_(that.mutex_) | |
265 { | |
266 switch (projection) | |
267 { | |
268 case VolumeProjection_Axial: | |
269 accessor_ = that.GetAxialSliceAccessor(slice, true); | |
270 break; | |
271 | |
272 case VolumeProjection_Coronal: | |
273 accessor_ = that.GetCoronalSliceAccessor(slice, true); | |
274 break; | |
275 | |
276 case VolumeProjection_Sagittal: | |
277 sagittal_.reset(that.ExtractSagittalSlice(slice)); | |
278 accessor_ = *sagittal_; | |
279 break; | |
280 | |
281 default: | |
282 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); | |
283 } | |
284 } | |
285 | |
286 | |
287 void ImageBuffer3D::SliceWriter::Flush() | |
288 { | |
289 if (sagittal_.get() != NULL) | |
290 { | |
291 // TODO | |
292 throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); | |
293 } | |
294 } | |
295 | |
296 | |
297 ImageBuffer3D::SliceWriter::SliceWriter(ImageBuffer3D& that, | |
298 VolumeProjection projection, | |
299 unsigned int slice) : | |
300 lock_(that.mutex_) | |
301 { | |
302 switch (projection) | |
303 { | |
304 case VolumeProjection_Axial: | |
305 accessor_ = that.GetAxialSliceAccessor(slice, false); | |
306 break; | |
307 | |
308 case VolumeProjection_Coronal: | |
309 accessor_ = that.GetCoronalSliceAccessor(slice, false); | |
310 break; | |
311 | |
312 case VolumeProjection_Sagittal: | |
313 sagittal_.reset(that.ExtractSagittalSlice(slice)); | |
314 accessor_ = *sagittal_; | |
315 break; | |
316 | |
317 default: | |
318 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); | |
319 } | |
320 } | |
321 } |