OrthancDicomWeb throws exception when 2 resources existing in the system

Hi Authors,

I have an used case when I upload a modified existing study to my Orthanc. The modified study hasa studyInstanceUid which is already existed in my Orthanc. What I modified in dicom file are PatientID, PatientName, Sex, Birthdate … . Everything seems fine, orthanc accepts the storescu DIMSE and save the dicoms in the internal memory. However when I used OHIF viewer to view the uploaded study, but Orthanc reject the request to get the metadata, it’s because there are two existing studyInstanceUid in the Orthanc. I browse the source and read the book (https://book.orthanc-server.com/dicom-guide.html#dicom-identifiers) under DICOM identifier, it seems reasonable. I went to another solution by modifying the studyInstanceUid of uploaded study to get it different from the existing study in Orthanc. Everything seems again fine that Orthanc accepts my STORESCU Dimse and save dicom in its storage. But OHIF is still not able to get the metadata because there are 2 similar series existing in the system. I went another solution by modifying the seriesInstanceUID and Orthanc again accepts the storescu DIMSE but no luck for OHIF since there are 2 similar instances in the system.
May I ask that why dont Orthanc check the level by level first instead of checking the final level. For example, suppose that the system have 2 different studyInstanceUIDs: abc and def, but both of them have the same series "123:. if i query : localhost:8042/dicom-web/studies/abc/series/123/metadata, then Orthanc only check series “123”, and it releases that there 2 series “123” and rejects the request. Why dont it check studies “abc” first then check the series “123” later. By this way, the uniqueness of the dicom is still maintained ?

By the way, this is the real log when I tried to get the metadata

I0508 17:03:13.975095 OrthancPlugins.cpp:1744] Delegating HTTP request to plugin for URI: /dicom-web/studies/2.25.89389877191218798498751634833839325870/series/2.25.68969172077424604495911553310226068036/instances/1.3.46.670589.11.82158.5.0.4140.2020010314590378469/frames/1
W0508 17:03:13.975776 OrthancPlugins.cpp:2353] LookupResource(): Multiple resources match the query (instead of 0 or 1), which indicates your DICOM database breaks the DICOM model of the real world
E0508 17:03:13.975810 PluginsManager.cpp:164] Accessing an inexistent item: Accessing an inexistent instance with WADO-RS: 1.3.46.670589.11.82158.5.0.4140.2020010314590378469
E0508 17:03:13.975829 PluginsErrorDictionary.cpp:111] Exception inside the plugin engine: Accessing an inexistent item
I0508 17:03:13.981682 HttpServer.cpp:823] GET /dicom-web/studies/2.25.89389877191218798498751634833839325870/series/2.25.62708794374211714246800459490494672025/instances/1.3.46.670589.11.82158.5.0.4140.2020010314593706579/frames/1

And this the the code snippet from OrthanC

bool LocateSeries(OrthancPluginRestOutput* output,

std::string& orthancId,

std::string& studyInstanceUid,

std::string& seriesInstanceUid,

const OrthancPluginHttpRequest* request)

{

OrthancPluginContext* context = OrthancPlugins::GetGlobalContext();

if (request->method != OrthancPluginHttpMethod_Get)

{

OrthancPluginSendMethodNotAllowed(context, output, “GET”);

return false;

}

studyInstanceUid = request->groups[0];

seriesInstanceUid = request->groups[1];

bool found = false;

try

{

OrthancPlugins::OrthancString tmp;

tmp.Assign(OrthancPluginLookupSeries(context, seriesInstanceUid.c_str()));

if (tmp.GetContent() != NULL)

{

tmp.ToString(orthancId);

found = true;

}

}

catch (Orthanc::OrthancException&)

{

}

if (!found)

{

throw Orthanc::OrthancException(Orthanc::ErrorCode_InexistentItem,

"Accessing an inexistent series with WADO-RS: " + seriesInstanceUid);

}

Json::Value study;

if (!OrthancPlugins::RestApiGet(study, “/series/” + orthancId + “/study”, false))

{

OrthancPluginSendHttpStatusCode(context, output, 404);

return false;

}

else if (study[“MainDicomTags”][“StudyInstanceUID”].asString() != studyInstanceUid)

{

throw Orthanc::OrthancException(Orthanc::ErrorCode_InexistentItem,

"No series " + seriesInstanceUid + " in study " + studyInstanceUid);

}

else

{

return true;

}

}

Thanks,
Phong

Hi,

The following is not DICOM compliant: The modified study hasa studyInstanceUid which is already existed in my Orthanc. What I modified in dicom file are PatientID, PatientName, Sex, Birthdate
And this is exactly what Orthanc is trying to tell you when logging: your DICOM database breaks the DICOM model of the real world

When you modify such identifier tags, you shall generate a new UIDs.
If it’s for testing purpose, just remove the original study before upload the modified one.
If it’s not for testing but production purpose, then you shall definitely review your workflow which is not compliant with the DICOM model.

Cheers,

Michel

Hi Michel,

Thanks for your response. Yes, dicom standard states the global uniqueness of identifiers no matter what of INSTANCE, SERIES, STUDY levels. But sometimes it’s quite hard to guarantee the uniqueness at series level (https://stackoverflow.com/questions/53767557/should-a-dicom-studyinstanceuid-be-unique-to-the-patient) so why dont you combine the study + series as a key to protect the series key ?

Thanks,
Phong

“Why dont you combine the study + series as a key to protect the series key?”

=> This is exactly what Orthanc does, as explained in the Orthanc Book: “Series are identified as the SHA-1 hash of the concatenation of their PatientID tag (0010,0020), their StudyInstanceUID tag (0020,000d) and their SeriesInstanceUID tag (0020,000e).”
https://book.orthanc-server.com/faq/orthanc-ids.html

Thank you Sebastien,

But from the original source code above

OrthancPlugins::OrthancString tmp;

tmp.Assign(OrthancPluginLookupSeries(context, seriesInstanceUid.c_str()));

You look up series first although the function passes the studyInstanceUid also. Then after getting the series uuid, it then looks back the studyInstanceUid.

Json::Value study;

if (!OrthancPlugins::RestApiGet(study, “/series/” + orthancId + “/study”, false))

{

OrthancPluginSendHttpStatusCode(context, output, 404);

return false;

}

else if (study[“MainDicomTags”][“StudyInstanceUID”].asString() != studyInstanceUid)

{

throw Orthanc::OrthancException(Orthanc::ErrorCode_InexistentItem,

"No series " + seriesInstanceUid + " in study " + studyInstanceUid);

}

Why dont you check the studyInstanceUid first then check the seriesInstanceUid later ?

Thanks,
Phong

Sorry, I didn’t get that you were referring to the DICOMweb plugin.

As always, if you want the core developers of Orthanc to inspect an issue, you must provide us with minimal working example, which includes sample DICOM files and command lines to immediately understand and reproduce the problem:
https://book.orthanc-server.com/users/support.html#discussing-a-minimal-working-example

Because any text is ambiguous, a textual description of the issue as you provided is not sufficient and makes us lose time.

Also, I notice that you are a business user of Orthanc who freely takes advantage of our hard work on Orthanc:
https://groups.google.com/d/msg/orthanc-users/vWpHWFkYiWI/5OxxpGY3DAAJ

You should support the development of Orthanc (which includes fixing issues such as you reported) by buying professional, B2B support packs from Osimis, which is indispensable to make Orthanc sustainable in the long-term:
https://www.osimis.io/en/services.html

Yes, I am asking DICOMWeb plugin and the way orthanc plugin looking up internal resource. The attachments (abc.tar.gz, abc1.tar.gz) are two studies I modified from the original studies by changing some information: PatientName, Birthdate, AccessionNumber, StudyInstanceUID. The SeriesInstanceUID of the two modified studies is the same. Steps I did are:

  • Using DCM4CHEE StoreSCU tool for storing 2 studies into the Orthanc.
  • Using the below command to get the series metadata

curl localhost:8045/dicom-web/studies/2.25.273949244622975699290379387024715922828/series/1.3.46.670589.11.82158.5.0.4140.2020010315052095594/metadata
{
“Details” : “Accessing an inexistent series with WADO-RS: 1.3.46.670589.11.82158.5.0.4140.2020010315052095594”,
“HttpError” : “Not Found”,
“HttpStatus” : 404,
“Message” : “Accessing an inexistent item”,
“Method” : “GET”,
“OrthancError” : “Accessing an inexistent item”,
“OrthancStatus” : 7,
“Uri” : “/dicom-web/studies/2.25.273949244622975699290379387024715922828/series/1.3.46.670589.11.82158.5.0.4140.2020010315052095594/metadata”
}

Here are the log in the orthanc with verbose mode

I0511 13:46:14.010278 HttpServer.cpp:823] GET /dicom-web/studies/2.25.273949244622975699290379387024715922828/series/1.3.46.670589.11.82158.5.0.4140.2020010315052095594/metadata
I0511 13:46:14.010372 OrthancPlugins.cpp:1629] Delegating HTTP request to plugin for URI: /dicom-web/studies/2.25.273949244622975699290379387024715922828/series/1.3.46.670589.11.82158.5.0.4140.2020010315052095594/metadata
W0511 13:46:14.010974 OrthancPlugins.cpp:2226] LookupResource(): Multiple resources match the query (instead of 0 or 1), which indicates your DICOM database breaks the DICOM model of the real world
E0511 13:46:14.011505 PluginsManager.cpp:164] Accessing an inexistent item: Accessing an inexistent series with WADO-RS: 1.3.46.670589.11.82158.5.0.4140.2020010315052095594
E0511 13:46:14.013000 PluginsErrorDictionary.cpp:111] Exception inside the plugin engine: Accessing an inexistent item

Orthanc.log (62.1 KB)

abc.tar.gz (432 KB)

abc1.tar.gz (432 KB)

Garbage in, garbage out.

Some of your instances (e.g. the two named “1.3.46.670589.11.82158.5.0.4140.2020010315053332602.dcm”) share the same “SOP Instance UID” and the same “Series Instance UID”, while having different “Study Instance UID”.

This is totally invalid. Your sample instances indeed break the DICOM model of the real world.

Orthanc allows storing 2 different studies with same Series/ Instance but does not allow retrieving them via Wado. Somehow it’s quite strict. So my question is why dont Orthanc check the study level first before checking the series level then instance level

When we designed this function, we could not imagine all the invalid data that would be provided. So we chose ONE implementation.

We, Orthanc developers, can not adapt the software for every invalid DICOM data out there. Feel free to rewrite this function such that it meets your requirements and share your modified function with the community.

Also feel free to configure your modalities/workflows such that they implement the DICOM standard (unique IDs !).

image.gif

image.gif

image.png

image.png

image.png

Thanks Alain and Sebastien. I got it