comparison Framework/Loaders/DicomVolumeLoader.cpp @ 1228:c471a0aa137b broker

adding the next generation of loaders
author Sebastien Jodogne <s.jodogne@gmail.com>
date Mon, 09 Dec 2019 13:58:37 +0100
parents
children 0ca50d275b9a
comparison
equal deleted inserted replaced
1227:a1c0c9c9f9af 1228:c471a0aa137b
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-2019 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 <Core/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>&>(message.GetUserPayload()).GetValue();
110
111 {
112 ImageBuffer3D::SliceWriter writer(volume_->GetPixelData(), VolumeProjection_Axial, frameIndex);
113 Orthanc::ImageProcessing::Copy(writer.GetAccessor(), message.GetImage());
114 }
115
116 volume_->IncrementRevision();
117
118 {
119 VolumeUpdatedMessage updated(*this, frameIndex);
120 BroadcastMessage(updated);
121 }
122
123 remaining_--;
124
125 if (remaining_ == 0)
126 {
127 VolumeReadyMessage ready(*this);
128 BroadcastMessage(ready);
129 }
130 }
131
132
133 DicomVolumeLoader::Factory::Factory(LoadedDicomResources& instances) :
134 framesFactory_(instances),
135 computeRange_(false)
136 {
137 }
138
139 DicomVolumeLoader::Factory::Factory(const SeriesMetadataLoader::SeriesLoadedMessage& metadata) :
140 framesFactory_(metadata.GetInstances()),
141 computeRange_(false)
142 {
143 SetDicomDir(metadata.GetDicomDirPath(), metadata.GetDicomDir()); // Only useful for DICOMDIR sources
144 }
145
146
147 boost::shared_ptr<IObserver> DicomVolumeLoader::Factory::Create(ILoadersContext::ILock& context)
148 {
149 boost::shared_ptr<SeriesFramesLoader> frames =
150 boost::dynamic_pointer_cast<SeriesFramesLoader>(framesFactory_.Create(context));
151
152 boost::shared_ptr<DicomVolumeLoader> volume(new DicomVolumeLoader(frames, computeRange_));
153 volume->Register<SeriesFramesLoader::FrameLoadedMessage>(*frames, &DicomVolumeLoader::Handle);
154
155 return volume;
156 }
157
158 void DicomVolumeLoader::Start(int priority,
159 const DicomSource& source)
160 {
161 if (started_)
162 {
163 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
164 }
165
166 started_ = true;
167
168 if (IsValid())
169 {
170 for (size_t i = 0; i < GetOrderedFrames().GetFramesCount(); i++)
171 {
172 framesLoader_->ScheduleLoadFrame(priority, source, i, source.GetQualityCount() - 1,
173 new Orthanc::SingleValueObject<size_t>(i));
174 }
175 }
176 else
177 {
178 VolumeReadyMessage ready(*this);
179 BroadcastMessage(ready);
180 }
181 }
182 }