comparison OrthancStone/Sources/Loaders/DicomVolumeLoader.cpp @ 1512:244ad1e4e76a

reorganization of folders
author Sebastien Jodogne <s.jodogne@gmail.com>
date Tue, 07 Jul 2020 16:21:02 +0200
parents Framework/Loaders/DicomVolumeLoader.cpp@4db187d29731
children 4fb8fdf03314
comparison
equal deleted inserted replaced
1511:9dfeee74c1e6 1512:244ad1e4e76a
1 /**
2 * Stone of Orthanc
3 * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
4 * Department, University Hospital of Liege, Belgium
5 * Copyright (C) 2017-2020 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 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.
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 * Affero General Public License for more details.
16 *
17 * You should have received a copy of the GNU Affero General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 **/
20
21
22 #include "DicomVolumeLoader.h"
23
24 #include <Images/ImageProcessing.h>
25
26 namespace OrthancStone
27 {
28 DicomVolumeLoader::DicomVolumeLoader(boost::shared_ptr<SeriesFramesLoader>& framesLoader,
29 bool computeRange) :
30 framesLoader_(framesLoader),
31 isValid_(false),
32 started_(false),
33 remaining_(0)
34 {
35 volume_.reset(new OrthancStone::DicomVolumeImage);
36
37 const SeriesOrderedFrames& frames = framesLoader_->GetOrderedFrames();
38
39 if (frames.IsRegular3DVolume() &&
40 frames.GetFramesCount() > 0)
41 {
42 // TODO - Is "0" the good choice for the reference frame?
43 // Shouldn't we use "count - 1" depending on the direction
44 // of the normal?
45 const OrthancStone::DicomInstanceParameters& parameters = frames.GetInstanceParameters(0);
46
47 OrthancStone::CoordinateSystem3D plane(frames.GetInstance(0));
48
49 OrthancStone::VolumeImageGeometry geometry;
50 geometry.SetSizeInVoxels(parameters.GetImageInformation().GetWidth(),
51 parameters.GetImageInformation().GetHeight(),
52 static_cast<unsigned int>(frames.GetFramesCount()));
53 geometry.SetAxialGeometry(plane);
54
55 double spacing;
56 if (parameters.GetSopClassUid() == SopClassUid_RTDose)
57 {
58 if (!parameters.ComputeRegularSpacing(spacing))
59 {
60 LOG(WARNING) << "Unable to compute the spacing in a RT-DOSE instance";
61 spacing = frames.GetSpacingBetweenSlices();
62 }
63 }
64 else
65 {
66 spacing = frames.GetSpacingBetweenSlices();
67 }
68
69 geometry.SetVoxelDimensions(parameters.GetPixelSpacingX(),
70 parameters.GetPixelSpacingY(), spacing);
71 volume_->Initialize(geometry, parameters.GetExpectedPixelFormat(), computeRange);
72 volume_->GetPixelData().Clear();
73 volume_->SetDicomParameters(parameters);
74
75 remaining_ = frames.GetFramesCount();
76 isValid_ = true;
77 }
78 else
79 {
80 LOG(WARNING) << "Not a regular 3D volume";
81 }
82 }
83
84
85 void DicomVolumeLoader::Handle(const OrthancStone::SeriesFramesLoader::FrameLoadedMessage& message)
86 {
87 if (remaining_ == 0 ||
88 !message.HasUserPayload())
89 {
90 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
91 }
92
93 if (message.GetImage().GetWidth() != volume_->GetPixelData().GetWidth() ||
94 message.GetImage().GetHeight() != volume_->GetPixelData().GetHeight())
95 {
96 throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageSize);
97 }
98
99 if (message.GetImage().GetFormat() != volume_->GetPixelData().GetFormat())
100 {
101 throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageFormat);
102 }
103
104 if (message.GetFrameIndex() >= volume_->GetPixelData().GetDepth())
105 {
106 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
107 }
108
109 size_t frameIndex = dynamic_cast<const Orthanc::SingleValueObject<size_t>&>
110 (message.GetUserPayload()).GetValue();
111
112 {
113 ImageBuffer3D::SliceWriter writer(volume_->GetPixelData(),
114 VolumeProjection_Axial,
115 static_cast<unsigned int>(frameIndex));
116
117 Orthanc::ImageProcessing::Copy(writer.GetAccessor(), message.GetImage());
118 }
119
120 volume_->IncrementRevision();
121
122 {
123 VolumeUpdatedMessage updated(*this,
124 static_cast<unsigned int>(frameIndex));
125
126 BroadcastMessage(updated);
127 }
128
129 remaining_--;
130
131 if (remaining_ == 0)
132 {
133 VolumeReadyMessage ready(*this);
134 BroadcastMessage(ready);
135 }
136 }
137
138
139 DicomVolumeLoader::Factory::Factory(LoadedDicomResources& instances) :
140 framesFactory_(instances),
141 computeRange_(false)
142 {
143 }
144
145 DicomVolumeLoader::Factory::Factory(const SeriesMetadataLoader::SuccessMessage& metadata) :
146 framesFactory_(metadata.GetInstances()),
147 computeRange_(false)
148 {
149 SetDicomDir(metadata.GetDicomDirPath(), metadata.GetDicomDir()); // Only useful for DICOMDIR sources
150 }
151
152
153 boost::shared_ptr<IObserver> DicomVolumeLoader::Factory::Create(ILoadersContext::ILock& context)
154 {
155 boost::shared_ptr<SeriesFramesLoader> frames =
156 boost::dynamic_pointer_cast<SeriesFramesLoader>(framesFactory_.Create(context));
157
158 boost::shared_ptr<DicomVolumeLoader> volume(new DicomVolumeLoader(frames, computeRange_));
159 volume->Register<SeriesFramesLoader::FrameLoadedMessage>(*frames, &DicomVolumeLoader::Handle);
160
161 return volume;
162 }
163
164 void DicomVolumeLoader::Start(int priority,
165 const DicomSource& source)
166 {
167 if (started_)
168 {
169 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
170 }
171
172 started_ = true;
173
174 if (IsValid())
175 {
176 for (size_t i = 0; i < GetOrderedFrames().GetFramesCount(); i++)
177 {
178 framesLoader_->ScheduleLoadFrame(priority, source, i, source.GetQualityCount() - 1,
179 new Orthanc::SingleValueObject<size_t>(i));
180 }
181 }
182 else
183 {
184 VolumeReadyMessage ready(*this);
185 BroadcastMessage(ready);
186 }
187 }
188 }