# HG changeset patch
# User Sebastien Jodogne <s.jodogne@gmail.com>
# Date 1734362464 -3600
# Node ID 9e43494cb0543185880164d075331c7078e0cd25
# Parent  bef0d7030f2d1a501703fcf630581575c7456223# Parent  a5241defb36f800c7dc362985b61ed773f1881a5
integration mainline->find-refactoring

diff -r bef0d7030f2d -r 9e43494cb054 NEWS
--- a/NEWS	Fri Sep 27 18:49:59 2024 +0200
+++ b/NEWS	Mon Dec 16 16:21:04 2024 +0100
@@ -1,11 +1,13 @@
 Pending changes in the mainline
 ===============================
 
-* Added a "Server" entry in the DICOMWeb job content.
-* Fixed parsing of numerical values in QIDO-RS response that prevented, amongst other,
-  the retrieval of NumberOfStudyRelatedInstances, NumberOfStudyRelatedSeries, ...
-* Fixed non latin PatientName values that were empty in some QIDO-RS responses.
-* Optimization when running with an Orthanc that supports the ExtendedFind.
+* Added a "Server" entry in the DICOMweb job content.
+* Fixed parsing of numerical values in QIDO-RS response that prevented, among other,
+  the retrieval of "NumberOfStudyRelatedInstances", "NumberOfStudyRelatedSeries",...
+* Fixed non-Latin "PatientName" values that were empty in some QIDO-RS responses.
+* Optimized the retrieval of single frame in WADO-RS when no transcoding is required.
+  This greatly improves the download time of multi-frame images in OHIF.
+* Optimization when running with an Orthanc that supports the "ExtendedFind" primitive.
 * Added support for Orthanc running in "ReadOnly" mode.
 
 
diff -r bef0d7030f2d -r 9e43494cb054 Plugin/WadoRsRetrieveFrames.cpp
--- a/Plugin/WadoRsRetrieveFrames.cpp	Fri Sep 27 18:49:59 2024 +0200
+++ b/Plugin/WadoRsRetrieveFrames.cpp	Mon Dec 16 16:21:04 2024 +0100
@@ -433,6 +433,43 @@
 }
 
 
+static void AnswerFrame(OrthancPluginRestOutput* output,
+                        const OrthancPluginHttpRequest* request,
+                        const OrthancPlugins::MemoryBuffer& instanceContent,
+                        const std::string& studyInstanceUid,
+                        const std::string& seriesInstanceUid,
+                        const std::string& sopInstanceUid,
+                        unsigned int frame,
+                        Orthanc::DicomTransferSyntax outputSyntax)
+{
+  if (OrthancPluginStartMultipartAnswer(
+        OrthancPlugins::GetGlobalContext(),
+        output, "related", GetMimeType(outputSyntax)) != OrthancPluginErrorCode_Success)
+  {
+    throw Orthanc::OrthancException(Orthanc::ErrorCode_Plugin,
+                                    "Cannot start a multipart answer");
+  }
+
+  OrthancPluginErrorCode error;
+
+#if HAS_SEND_MULTIPART_ITEM_2 == 1
+  const std::string base = OrthancPlugins::Configuration::GetBasePublicUrl(request);
+  std::string location = (
+    OrthancPlugins::Configuration::GetWadoUrl(base, studyInstanceUid, seriesInstanceUid, sopInstanceUid) +
+    "frames/" + boost::lexical_cast<std::string>(frame + 1));
+  const char *keys[] = { "Content-Location" };
+  const char *values[] = { location.c_str() };
+  error = OrthancPluginSendMultipartItem2(OrthancPlugins::GetGlobalContext(), output, instanceContent.GetData(), instanceContent.GetSize(), 1, keys, values);
+#else
+  error = OrthancPluginSendMultipartItem(OrthancPlugins::GetGlobalContext(), instanceContent.GetData(), instanceContent.GetSize(), size);
+#endif
+
+  if (error != OrthancPluginErrorCode_Success)
+  {
+    throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol);
+  }
+}
+
 static void RetrieveFrames(OrthancPluginRestOutput* output,
                            const OrthancPluginHttpRequest* request,
                            bool allFrames,
@@ -499,9 +536,23 @@
       {
         throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError, "DICOMWeb: Unable to get transcoded file for instance " + orthancId);
       }
+
+      // TODO-OPTI: this takes a huge amount of time; e.g: 1.5s for a 600MB file while the DicomInstance usually already exists in the Orthanc core
+      //            call /instances/../frames/../transcoded (to be implemented in future Orthanc release)
       instance.reset(new OrthancPlugins::DicomInstance(content.GetData(), content.GetSize()));
     }
-    else // pre 1.12.2 code (or no transcoding needed)
+    else if (!allFrames && frames.size() == 1 && !transcodeThisInstance) // no transcoding needed, let's retrieve the raw frame directly from the core to avoid Orthanc to recreate a DicomInstance for every frame
+    {
+      if (!content.RestApiGet("/instances/" + orthancId + "/frames/" + boost::lexical_cast<std::string>(frames.front()) + "/raw", false))
+      {
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError, "DICOMWeb: Unable to get file for instance " + orthancId);
+      }
+
+      AnswerFrame(output, request, content, studyInstanceUid, seriesInstanceUid,
+                  sopInstanceUid, frames.front(), targetSyntax);
+      return;
+    }
+    else
     {
       if (!content.RestApiGet("/instances/" + orthancId + "/file", false))
       {
diff -r bef0d7030f2d -r 9e43494cb054 TODO
--- a/TODO	Fri Sep 27 18:49:59 2024 +0200
+++ b/TODO	Mon Dec 16 16:21:04 2024 +0100
@@ -10,6 +10,8 @@
   curl -H "Accept: multipart/related; type=application/octet-stream" http://localhost:8043/dicom-web/studies/1.2.156.112536.1.2143.25015081191207.14610300430.5/series/1.2.156.112536.1.2143.25015081191207.14610300430.6/instances/1.2.156.112536.1.2143.25015081191207.14610309990.44/frames/3 --output /tmp/out.bin
   check for these logs: DICOMweb RetrieveFrames: Transcoding instance a7aec17a-e296e51f-2abe8ad8-bc95d57b-4de269d0 to transfer syntax 1.2.840.10008.1.2.1
 
+  Note: This has been partially handled in 1.18: Tf no transcoding is needed, we avoid downloading the full instance from Orthanc
+
   We should very likely implement a cache in the DicomWEB plugin and make sure that, if 3 clients are requesting the same instance at the same time, we only
   request one transcoding.
 
@@ -18,6 +20,7 @@
   https://discourse.orthanc-server.org/t/possible-memory-leak-with-multiframe-dicom-orthanc-ohif/3988/12
 
 
+
 * Implement capabilities: https://www.dicomstandard.org/using/dicomweb/capabilities/
   from https://groups.google.com/d/msgid/orthanc-users/c60227f2-c6da-4fd9-9b03-3ce9bf7d1af5n%40googlegroups.com?utm_medium=email&utm_source=footer