comparison Applications/StoneWebViewer/WebAssembly/StoneWebViewer.cpp @ 1850:932dc2265baa

Group together in a single "virtual series" all the instances without the tag "NumberOfFrames"
author Sebastien Jodogne <s.jodogne@gmail.com>
date Tue, 29 Jun 2021 14:09:54 +0200
parents 023cce3d7844
children 73a4bee08bb6
comparison
equal deleted inserted replaced
1849:023cce3d7844 1850:932dc2265baa
189 189
190 190
191 class VirtualSeries : public boost::noncopyable 191 class VirtualSeries : public boost::noncopyable
192 { 192 {
193 private: 193 private:
194 class Item 194 class Item : public boost::noncopyable
195 { 195 {
196 private: 196 private:
197 std::string seriesInstanceUid_; 197 std::string seriesInstanceUid_;
198 unsigned int numberOfFrames_; 198 std::list<std::string> sopInstanceUids_;
199 199
200 public: 200 public:
201 Item(const std::string& seriesInstanceUid, 201 Item(const std::string& seriesInstanceUid,
202 unsigned int numberOfFrames) : 202 const std::list<std::string>& sopInstanceUids) :
203 seriesInstanceUid_(seriesInstanceUid), 203 seriesInstanceUid_(seriesInstanceUid),
204 numberOfFrames_(numberOfFrames) 204 sopInstanceUids_(sopInstanceUids)
205 { 205 {
206 if (numberOfFrames == 0)
207 {
208 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
209 }
210 } 206 }
211 207
212 const std::string& GetSeriesInstanceUid() const 208 const std::string& GetSeriesInstanceUid() const
213 { 209 {
214 return seriesInstanceUid_; 210 return seriesInstanceUid_;
215 } 211 }
216 212
217 unsigned int GetNumberOfFrames() const 213 const std::list<std::string>& GetSopInstanceUids() const
218 { 214 {
219 return numberOfFrames_; 215 return sopInstanceUids_;
220 } 216 }
221 }; 217 };
222 218
223 typedef std::map<std::string, Item> Content; 219 typedef std::map<std::string, Item*> Content;
224 220
225 Content content_; 221 Content content_;
226 222
227 const Item& GetItem(const std::string& id) const 223 const Item& GetItem(const std::string& id) const
228 { 224 {
232 { 228 {
233 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); 229 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
234 } 230 }
235 else 231 else
236 { 232 {
237 return found->second; 233 assert(found->second != NULL);
238 } 234 return *found->second;
235 }
239 } 236 }
240 237
241 public: 238 public:
242 std::string Add(const std::string& seriesInstanceUid, 239 ~VirtualSeries()
243 const std::string& sopInstanceUid, 240 {
244 unsigned int numberOfFrames) 241 for (Content::iterator it = content_.begin(); it != content_.end(); ++it)
245 { 242 {
246 if (content_.find(sopInstanceUid) != content_.end()) 243 assert(it->second != NULL);
244 delete it->second;
245 }
246 }
247
248 std::string AddSingleInstance(const std::string& seriesInstanceUid,
249 const std::string& sopInstanceUid)
250 {
251 std::list<std::string> sopInstanceUids;
252 sopInstanceUids.push_back(sopInstanceUid);
253 return AddMultipleInstances(seriesInstanceUid, sopInstanceUids);
254 }
255
256 std::string AddMultipleInstances(const std::string& seriesInstanceUid,
257 const std::list<std::string>& sopInstanceUids)
258 {
259 // Generate a unique identifier for this virtual series
260 const std::string virtualSeriesId = "virtual-" + boost::lexical_cast<std::string>(content_.size());
261
262 if (content_.find(virtualSeriesId) != content_.end())
247 { 263 {
248 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); 264 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
249 } 265 }
250 else 266 else
251 { 267 {
252 content_.insert(std::make_pair(sopInstanceUid, Item(seriesInstanceUid, numberOfFrames))); 268 content_.insert(std::make_pair(virtualSeriesId, new Item(seriesInstanceUid, sopInstanceUids)));
253 return sopInstanceUid; 269 return virtualSeriesId;
254 } 270 }
255 } 271 }
256 272
257 const std::string& GetSeriesInstanceUid(const std::string& id) const 273 const std::string& GetSeriesInstanceUid(const std::string& id) const
258 { 274 {
259 return GetItem(id).GetSeriesInstanceUid(); 275 return GetItem(id).GetSeriesInstanceUid();
260 } 276 }
261 277
262 unsigned int GetNumberOfFrames(const std::string& id) const 278 const std::list<std::string>& GetSopInstanceUids(const std::string& id) const
263 { 279 {
264 return GetItem(id).GetNumberOfFrames(); 280 return GetItem(id).GetSopInstanceUids();
265 } 281 }
266 }; 282 };
267 283
268 284
269 285
302 boost::shared_ptr<OrthancStone::LoadedDicomResources> series_; 318 boost::shared_ptr<OrthancStone::LoadedDicomResources> series_;
303 boost::shared_ptr<OrthancStone::DicomResourcesLoader> resourcesLoader_; 319 boost::shared_ptr<OrthancStone::DicomResourcesLoader> resourcesLoader_;
304 boost::shared_ptr<OrthancStone::SeriesThumbnailsLoader> thumbnailsLoader_; 320 boost::shared_ptr<OrthancStone::SeriesThumbnailsLoader> thumbnailsLoader_;
305 boost::shared_ptr<OrthancStone::SeriesMetadataLoader> metadataLoader_; 321 boost::shared_ptr<OrthancStone::SeriesMetadataLoader> metadataLoader_;
306 std::set<std::string> scheduledVirtualSeriesThumbnails_; 322 std::set<std::string> scheduledVirtualSeriesThumbnails_;
323 VirtualSeries virtualSeries_;
307 324
308 explicit ResourcesLoader(OrthancStone::ILoadersContext& context, 325 explicit ResourcesLoader(OrthancStone::ILoadersContext& context,
309 const OrthancStone::DicomSource& source) : 326 const OrthancStone::DicomSource& source) :
310 context_(context), 327 context_(context),
311 source_(source), 328 source_(source),
604 { 621 {
605 OrthancStone::SeriesMetadataLoader::Accessor accessor(*metadataLoader_, seriesInstanceUid); 622 OrthancStone::SeriesMetadataLoader::Accessor accessor(*metadataLoader_, seriesInstanceUid);
606 return accessor.IsComplete(); 623 return accessor.IsComplete();
607 } 624 }
608 625
609 bool LookupVirtualSeries(VirtualSeries& target /* out */, 626 bool LookupVirtualSeries(std::map<std::string, unsigned int>& virtualSeries /* out */,
610 std::set<std::string>& virtualSeriesIds /* out */,
611 const std::string& seriesInstanceUid) 627 const std::string& seriesInstanceUid)
612 { 628 {
613 OrthancStone::SeriesMetadataLoader::Accessor accessor(*metadataLoader_, seriesInstanceUid); 629 OrthancStone::SeriesMetadataLoader::Accessor accessor(*metadataLoader_, seriesInstanceUid);
614 if (accessor.IsComplete() && 630 if (accessor.IsComplete() &&
615 accessor.GetInstancesCount() >= 2) 631 accessor.GetInstancesCount() >= 2)
626 } 642 }
627 } 643 }
628 644
629 if (hasMultiframe) 645 if (hasMultiframe)
630 { 646 {
647 std::string studyInstanceUid;
648 std::list<std::string> instancesWithoutFrameNumber;
649
631 for (size_t i = 0; i < accessor.GetInstancesCount(); i++) 650 for (size_t i = 0; i < accessor.GetInstancesCount(); i++)
632 { 651 {
633 OrthancStone::DicomInstanceParameters p(accessor.GetInstance(i)); 652 OrthancStone::DicomInstanceParameters p(accessor.GetInstance(i));
634 653
635 std::string virtualSeriesId = target.Add(seriesInstanceUid, p.GetSopInstanceUid(), p.GetNumberOfFrames()); 654 if (p.HasNumberOfFrames())
636 virtualSeriesIds.insert(virtualSeriesId); 655 {
637 656 const std::string virtualSeriesId = virtualSeries_.AddSingleInstance(seriesInstanceUid, p.GetSopInstanceUid());
638 FetchVirtualSeriesThumbnail(virtualSeriesId, p.GetStudyInstanceUid(), p.GetSeriesInstanceUid(), p.GetSopInstanceUid()); 657 if (virtualSeries.find(virtualSeriesId) != virtualSeries.end())
658 {
659 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
660 }
661 else
662 {
663 virtualSeries[virtualSeriesId] = p.GetNumberOfFrames();
664 FetchVirtualSeriesThumbnail(virtualSeriesId, p.GetStudyInstanceUid(), seriesInstanceUid, p.GetSopInstanceUid());
665 }
666 }
667 else
668 {
669 studyInstanceUid = p.GetStudyInstanceUid();
670 instancesWithoutFrameNumber.push_back(p.GetSopInstanceUid());
671 }
672 }
673
674 if (!instancesWithoutFrameNumber.empty())
675 {
676 /**
677 * Group together in a single "virtual series" all the DICOM
678 * instances that have no value for the tag "NumberOfFrames"
679 * (0028,0008). This can happen in US CINE series. New in
680 * Stone Web viewer 2.1.
681 * https://groups.google.com/g/orthanc-users/c/V-vOnlwj06A/m/2sPNwteYAAAJ
682 **/
683 const std::string virtualSeriesId = virtualSeries_.AddMultipleInstances(seriesInstanceUid, instancesWithoutFrameNumber);
684 virtualSeries[virtualSeriesId] = instancesWithoutFrameNumber.size();
685 FetchVirtualSeriesThumbnail(virtualSeriesId, studyInstanceUid, seriesInstanceUid, instancesWithoutFrameNumber.front());
639 } 686 }
640 687
641 return true; 688 return true;
642 } 689 }
643 else 690 else
673 { 720 {
674 return false; 721 return false;
675 } 722 }
676 } 723 }
677 724
678 bool SortMultipartInstanceFrames(OrthancStone::SortedFrames& target, 725 bool SortVirtualSeriesFrames(OrthancStone::SortedFrames& target,
679 const std::string& seriesInstanceUid, 726 const std::string& virtualSeriesId) const
680 const std::string& sopInstanceUid) const 727 {
681 { 728 const std::string& seriesInstanceUid = virtualSeries_.GetSeriesInstanceUid(virtualSeriesId);
729
682 OrthancStone::SeriesMetadataLoader::Accessor accessor(*metadataLoader_, seriesInstanceUid); 730 OrthancStone::SeriesMetadataLoader::Accessor accessor(*metadataLoader_, seriesInstanceUid);
683 731
684 if (accessor.IsComplete()) 732 if (accessor.IsComplete())
685 { 733 {
686 for (size_t i = 0; i < accessor.GetInstancesCount(); i++) 734 const std::list<std::string>& sopInstanceUids = virtualSeries_.GetSopInstanceUids(virtualSeriesId);
687 { 735
688 std::string s; 736 target.Clear();
689 if (accessor.GetInstance(i).LookupStringValue(s, Orthanc::DICOM_TAG_SOP_INSTANCE_UID, false) && 737
690 s == sopInstanceUid) 738 for (std::list<std::string>::const_iterator
739 it = sopInstanceUids.begin(); it != sopInstanceUids.end(); ++it)
740 {
741 Orthanc::DicomMap instance;
742 if (accessor.LookupInstance(instance, *it))
691 { 743 {
692 target.Clear(); 744 target.AddInstance(instance);
693 target.AddInstance(accessor.GetInstance(i));
694 target.Sort();
695 return true;
696 } 745 }
746 else
747 {
748 LOG(ERROR) << "Missing instance: " << *it;
749 }
697 } 750 }
698 751
752 target.Sort();
699 return true; 753 return true;
700 } 754 }
701 else 755 else
702 { 756 {
703 return false; 757 return false;
3356 static std::string stringBuffer_; 3410 static std::string stringBuffer_;
3357 static bool softwareRendering_ = false; 3411 static bool softwareRendering_ = false;
3358 static WebViewerAction leftButtonAction_ = WebViewerAction_Windowing; 3412 static WebViewerAction leftButtonAction_ = WebViewerAction_Windowing;
3359 static WebViewerAction middleButtonAction_ = WebViewerAction_Pan; 3413 static WebViewerAction middleButtonAction_ = WebViewerAction_Pan;
3360 static WebViewerAction rightButtonAction_ = WebViewerAction_Zoom; 3414 static WebViewerAction rightButtonAction_ = WebViewerAction_Zoom;
3361 static VirtualSeries virtualSeries_;
3362 3415
3363 3416
3364 static void FormatTags(std::string& target, 3417 static void FormatTags(std::string& target,
3365 const Orthanc::DicomMap& tags) 3418 const Orthanc::DicomMap& tags)
3366 { 3419 {
3710 { 3763 {
3711 try 3764 try
3712 { 3765 {
3713 std::unique_ptr<OrthancStone::SortedFrames> frames(new OrthancStone::SortedFrames); 3766 std::unique_ptr<OrthancStone::SortedFrames> frames(new OrthancStone::SortedFrames);
3714 3767
3715 const std::string sopInstanceUid = virtualSeriesId; // TODO 3768 if (GetResourcesLoader().SortVirtualSeriesFrames(*frames, virtualSeriesId))
3716
3717 if (GetResourcesLoader().SortMultipartInstanceFrames(
3718 *frames, virtualSeries_.GetSeriesInstanceUid(virtualSeriesId), sopInstanceUid))
3719 { 3769 {
3720 GetViewport(canvas)->SetFrames(frames.release()); 3770 GetViewport(canvas)->SetFrames(frames.release());
3721 return 1; 3771 return 1;
3722 } 3772 }
3723 else 3773 else
4049 EMSCRIPTEN_KEEPALIVE 4099 EMSCRIPTEN_KEEPALIVE
4050 int LookupVirtualSeries(const char* seriesInstanceUid) 4100 int LookupVirtualSeries(const char* seriesInstanceUid)
4051 { 4101 {
4052 try 4102 try
4053 { 4103 {
4054 std::set<std::string> virtualSeriesIds; 4104 typedef std::map<std::string, unsigned int> VirtualSeries;
4055 if (GetResourcesLoader().LookupVirtualSeries(virtualSeries_, virtualSeriesIds, seriesInstanceUid)) 4105
4106 VirtualSeries virtualSeries;
4107 if (GetResourcesLoader().LookupVirtualSeries(virtualSeries, seriesInstanceUid))
4056 { 4108 {
4057 Json::Value json = Json::arrayValue; 4109 Json::Value json = Json::arrayValue;
4058 for (std::set<std::string>::const_iterator it = virtualSeriesIds.begin(); 4110 for (VirtualSeries::const_iterator it = virtualSeries.begin(); it != virtualSeries.end(); ++it)
4059 it != virtualSeriesIds.end(); ++it)
4060 { 4111 {
4061 Json::Value item = Json::objectValue; 4112 Json::Value item = Json::objectValue;
4062 item["ID"] = *it; 4113 item["ID"] = it->first;
4063 item["NumberOfFrames"] = virtualSeries_.GetNumberOfFrames(*it); 4114 item["NumberOfFrames"] = it->second;
4064 json.append(item); 4115 json.append(item);
4065 } 4116 }
4066 4117
4067 stringBuffer_ = json.toStyledString(); 4118 stringBuffer_ = json.toStyledString();
4068 return true; 4119 return true;