imagingdatacommons / dicomweb-client Goto Github PK
View Code? Open in Web Editor NEWPython client for DICOMweb RESTful services
Home Page: https://dicomweb-client.readthedocs.io
License: MIT License
Python client for DICOMweb RESTful services
Home Page: https://dicomweb-client.readthedocs.io
License: MIT License
Hi @hackermd . In #27 we had discussed making pillow and numpy optional. The issue was closed after these two dependencies were removed.
#56 reintroduced these required dependencies. #56 explicitly mentions in the description:
The DICOMfileClient depends on the NumPy and Pillow libraries. Therefore, this PR would introduce a dependency on C Python. I don't think this is a major issue and most users will likely have NumPy and Pillow installed anyways.
If this should turn out to be an issue, we could either
- make the dependencies optional (not a fan of that approach)
- put the DICOMfileClient into a separate package (also not ideal because the class is supposed to have the same API as the DICOMwebClient and having both in one package is advantageous in this regard)
As explained in #27:
I'm using this project (the library, not the CLI) as part of an application that will be running under pypy instead of cpython, possibly in an alpine container. I'd have to build both pillow & numpy for a feature I don't intend on using.
I would therefore kindly request that these dependencies are either changed to optional, or that the DICOMfileClient
is split into a separate package (which can still of course be developed in this repo alongside DICOMwebClient
) but installed separately from PYPI.
The 'empty response' warning doesn't seem useful and is very noisy
dicomweb-client/src/dicomweb_client/web.py
Line 538 in 3a97873
get_remaining=True
Any reason to keep it around?
@dimrozakis raised the following issue:
chunked_headers
isn't being used, the subsequent request uses the original headers. Also, according to requests' documentation on chunk-encoded requests, passing an iterator is all that's required so it seems like Transfer-Encoding: chunked
is taken care of automatically by requests.
Hi,
I noticed that the Note
section of the retrieve_instance-method (and in other similar methods) mentions that the default transfer syntax will be Implicit VR Little Endian
. The standard states that the default transfer syntax for dicom web is Explicity VR Little Endian
and that Implicit VR Little Endian
is not allowed.
When returning Iterable
s by the DICOMwebClient methods that are created by a Generator
instead of lists, it would be possible to process the results of each method on the fly (maybe in another thread or process).
Many server support to retrieve metadata by QIDO.
But parameter of dicomweb-client retrieve metadata api is _Transaction.RETRIEVE.
It cause api to get wrong prefix of URI.
Does it should be changed the parameter to _Transaction.SEARCH?
With the emergence of the IHE AIW-I profile, the role of UPS-RS is poised to grow dramatically. While dcm4chee supports the provider interface, support for the user interface is relatively rare in the DICOM community. Given the widespread use of python for ML algorithms, a python-based UPS-RS client implementation would be beneficial for many.
Currently, when downloading a study or series, all instances are loaded and parsed with pydicom. For some use cases it is absolutely fine to process each instance individually and store the result on disk, especially when dealing with very large studies and/or image. Therefore the current functionality should be extended with generator based functions. In order to keep the current exposed functionality, I would propose to write generator based functions and just convert the generator to a list.
Using Micorsoft's dicom web to pull study using study_uuid.
But it is failing by saying 406 error.
It can be resolved by adding a addition header (transfer-syntax=*) before sending the request. But current function doesn't support the same
Hi,
Thanks for the sharing a great job! Could you provide any good example of STOW-RS usage, with either of dcm file or mutlipart mime format generation?
Thanks!
Jinserk
I don't really understand the error I got, maybe you can help me. I didn't have any problem with the library when installing it on my "general" environment (Anaconda, Python 3.7), but when I install dicomweb_client
on a new virtual environment (python 3.7, with dicomweb_client installed only), I get the error below when using this: client.store_instances(datasets=[ds])
HTTPError: 415 Client Error: Unsupported Media Type for URL: http://localhost:8000/studies
I investigated a little with tcpdump
in order to capture the requests. With the general environment, where I didn't have an error, here is the captured request :
h#..h#..POST /studies HTTP/1.1
User-Agent: python-requests/2.22.0
Accept-Encoding: gzip, deflate
Accept: */*
Connection: keep-alive
Host: localhost
Content-Type: multipart/related; type="application/dicom"; boundary=0f3cf5c0-70e0-41ef-baef-c6f9f65ec3e1
Content-Length: 525907
It seems to me as expected. Then, with my new virtual environment with only dicomweb_client (python 3.7), and with the exact same command, here is what I got:
h#~.h#~.POST /studies HTTP/1.1
User-Agent: python-requests/2.23.0
Accept-Encoding: gzip, deflate
Accept: */*
Connection: keep-alive
Host: localhost
Content-Length: 119
Content-Type: application/x-www-form-urlencoded
An I missing something or this is not the expected behavior?
Thank you very much,
Gabriel
Hi,
@hackermd I have a code that pulls full dicom data using:
from dicomweb_client.api import DICOMwebClient
client = DICOMwebClient(url=url)
pydicom_dataset = client.retrieve_instance(
study_instance_uid=study_uid,
series_instance_uid=series_uid,
sop_instance_uid=object_uid,
)
It broke going from 0.55.0 to 0.55.1. (I was able to trace this down by running with different versions)
With 0.55.1 I get errors of this form:
406 Client Error: Not Acceptable for url: http://......../studies/........../series/............../instances/.............
Sorry, I have no idea what the issue is. I just wanted to let you know. I will continue using 0.55.0 for now.
According to DICOM PS3.18 2023e - Web Services Section 8.5 the server can respond with a 206 status code when for example not all images could be retrieved during a WADO-RS request. Is there some way to get this information programmatically when using dicomweb-client (e.g. when calling retrieve_study
)?
I only found one location in web.py where a warning is logged to the console when a warning is in the response.headers
. Is there a way to access the response status after e.g. retrieve_study
is called?
When one passes media_types=('image/jls', '1.2.840.10008.1.2.4.80', )
to retrieve_instance_frames()
, one gets the following error:
ValueError: Media type "image/jls" is not supported for requested resource
The reason for that is that the client still assumes image/x-jls
instead of image/jls
.
In api,py
, the last line of retrieve_instance_metadata()
is:
return self._http_get_application_json(url)[0]
.
Shouldn't it be:
return self._http_get_application_json(url)
?
Are there any open demo servers other than https://dicomcloud.azurewebsites.net
?
As a follow up to #3 (comment) because I'm developing course material that depends on the availability of a server providing a complete tree of DICOM studies-series-instances-frames without any missing links (in the case for https://dicomcloud.azurewebsites.net most series are missing).
Thank you for this great package, it makes it very easy to access data on DICOMweb endpoints. We have been using the dicomweb-client successfully with https://github.com/dcmjs-org/dicomweb-server up until version 0.41.2. All versions after that (0.50.0-0.50.3) fails to retrieve instance with this error: ValueError: Unexpected media type: "application/dicom". Expected "multipart/related".
I'll leave the server accessible for a while so that you can easily reproduce the error.
>>> import dicomweb_client
>>> dicomweb_client.__version__
'0.41.2'
>>>
>>> from dicomweb_client.api import DICOMwebClient
>>> client = DICOMwebClient(url="http://skull.cs.queensu.ca:5995")
>>> study_instance_uid = client.search_for_studies()[0]['0020000D']['Value'][0]
>>> series_instance_uid = client.search_for_series(study_instance_uid)[0]['0020000E']['Value'][0]
>>> sop_instance_uid = client.search_for_instances(study_instance_uid, series_instance_uid)[0]['00080018']['Value'][0]
>>> ds = client.retrieve_instance(study_instance_uid, series_instance_uid, sop_instance_uid)
>>> ds.InstanceNumber
"1"
>>> import dicomweb_client
>>> dicomweb_client.__version__
'0.50.0'
>>>
>>> from dicomweb_client.api import DICOMwebClient
>>> client = DICOMwebClient(url="http://skull.cs.queensu.ca:5995")
>>> study_instance_uid = client.search_for_studies()[0]['0020000D']['Value'][0]
>>> series_instance_uid = client.search_for_series(study_instance_uid)[0]['0020000E']['Value'][0]
>>> sop_instance_uid = client.search_for_instances(study_instance_uid, series_instance_uid)[0]['00080018']['Value'][0]
>>> ds = client.retrieve_instance(study_instance_uid, series_instance_uid, sop_instance_uid)
Traceback (most recent call last):
File "<console>", line 1, in <module>
File "C:\Users\andra\AppData\Local\NA-MIC\Slicer 4.13.0-2020-11-17\lib\Python\Lib\site-packages\dicomweb_client\api.py", line 2442, in retrieve_instance
instances = list(iterator)
File "C:\Users\andra\AppData\Local\NA-MIC\Slicer 4.13.0-2020-11-17\lib\Python\Lib\site-packages\dicomweb_client\api.py", line 1056, in <genexpr>
pydicom.dcmread(BytesIO(part))
File "C:\Users\andra\AppData\Local\NA-MIC\Slicer 4.13.0-2020-11-17\lib\Python\Lib\site-packages\dicomweb_client\api.py", line 725, in _decode_multipart_message
f'Unexpected media type: "{media_type}". '
ValueError: Unexpected media type: "application/dicom". Expected "multipart/related".
>>>
>>>
>>> import dicomweb_client
>>> dicomweb_client.__version__
'0.50.3'
>>>
>>>
>>> from dicomweb_client.api import DICOMwebClient
>>> client = DICOMwebClient(url="http://skull.cs.queensu.ca:5995")
>>> study_instance_uid = client.search_for_studies()[0]['0020000D']['Value'][0]
>>> series_instance_uid = client.search_for_series(study_instance_uid)[0]['0020000E']['Value'][0]
>>> sop_instance_uid = client.search_for_instances(study_instance_uid, series_instance_uid)[0]['00080018']['Value'][0]
>>> ds = client.retrieve_instance(study_instance_uid, series_instance_uid, sop_instance_uid)
Traceback (most recent call last):
File "<console>", line 1, in <module>
File "C:\Users\andra\AppData\Local\NA-MIC\Slicer 4.13.0-2020-11-17\lib\Python\Lib\site-packages\dicomweb_client\api.py", line 2443, in retrieve_instance
instances = list(iterator)
File "C:\Users\andra\AppData\Local\NA-MIC\Slicer 4.13.0-2020-11-17\lib\Python\Lib\site-packages\dicomweb_client\api.py", line 1057, in <genexpr>
pydicom.dcmread(BytesIO(part))
File "C:\Users\andra\AppData\Local\NA-MIC\Slicer 4.13.0-2020-11-17\lib\Python\Lib\site-packages\dicomweb_client\api.py", line 726, in _decode_multipart_message
f'Unexpected media type: "{media_type}". '
ValueError: Unexpected media type: "application/dicom". Expected "multipart/related".
@dimrozakis raised folllowing issue:
In _http_post_multipart_application_dicom
, when the response.content
returned by _http_post
is parsed, Content-Type is checked to decide whether it should be loaded as json or xml. Here, in case of a response with an error status code, content is assumed to be xml. Is it not possible for the content of an error response to be in json?
I lefted another question for the store_instances()
but it's already closed and won't get any further notice to you, so please excuse me to make another new thread. I'm almost newbie to deal with the dicom standard, but the following example code has an error:
import os
import tempfile
import datetime
import pydicom
from pydicom.dataset import Dataset, FileDataset
from pydicom.sequence import Sequence
# Create some temporary filenames
suffix = '.dcm'
filename_little_endian = tempfile.NamedTemporaryFile(suffix=suffix).name
filename_big_endian = tempfile.NamedTemporaryFile(suffix=suffix).name
print("Setting file meta information...")
# Populate required values for file meta information
file_meta = Dataset()
file_meta.MediaStorageSOPClassUID = '1.2.840.10008.5.1.4.1.1.2'
file_meta.MediaStorageSOPInstanceUID = "1.2.3"
file_meta.ImplementationClassUID = "1.2.3.4"
print("Setting dataset values...")
# Create the FileDataset instance (initially no data elements, but file_meta
# supplied)
ds = FileDataset(filename_little_endian, {},
file_meta=file_meta, preamble=b"\0" * 128)
# Add the data elements -- not trying to set all required here. Check DICOM
# standard
ds.PatientName = "Test^Firstname"
ds.PatientID = "123456"
# Set the transfer syntax
ds.is_little_endian = True
ds.is_implicit_VR = True
# Set creation date/time
dt = datetime.datetime.now()
ds.ContentDate = dt.strftime('%Y%m%d')
timeStr = dt.strftime('%H%M%S.%f') # long format with micro seconds
ds.ContentTime = timeStr
print("Writing test file", filename_little_endian)
ds.save_as(filename_little_endian)
print("File saved.")
from dicomweb_client.api import DICOMwebClient
client = DICOMwebClient("http://172.19.2.123:8042/dicom-web")
STUDY_UID = '2.16.840.1.114488.0.4.123489834087.1330071425.0'
client.store_instances(datasets=[ds])
and the error message is:
Setting file meta information...
Setting dataset values...
Writing test file /tmp/tmph64u7s3j.dcm
File saved.
Traceback (most recent call last):
File "test.py", line 56, in <module>
client.store_instances(datasets=[ds])
File "/home/jinserk/.pyenv/versions/3.7.2/lib/python3.7/site-packages/dicomweb_client/api.py", line 1797, in store_instances
self._http_post_multipart_application_dicom(url, encoded_datasets)
File "/home/jinserk/.pyenv/versions/3.7.2/lib/python3.7/site-packages/dicomweb_client/api.py", line 1207, in _http_post_multipart_application_dicom
self._http_post(url, content, headers={'Content-Type': content_type})
File "/home/jinserk/.pyenv/versions/3.7.2/lib/python3.7/site-packages/dicomweb_client/api.py", line 1186, in _http_post
response.raise_for_status()
File "/home/jinserk/.pyenv/versions/3.7.2/lib/python3.7/site-packages/requests/models.py", line 940, in raise_for_status
raise HTTPError(http_error_msg, response=self)
requests.exceptions.HTTPError: 400 Client Error: Bad Request for url: http://172.19.2.123:8042/dicom-web/studies
Here what I wonder that:
We have a need to deal with some equipment that does not expose its full WADO functionality through WADA-RS and requires the use of WADO-URI for some WADO features.
Would you entertain a PR that adds support for the WADO-URI? I think this should be pretty straightforward to implement and we would be happy to work on it.
If so, I would appreciate your thoughts on how the API could look. I envision that we would add the option to use WADO-URI to the existing retrieve_...
methods. This could be done either as an optional parameter to the relevant methods, or as a global configuration option in the client's constructor. I think I favour the former despite it being more verbose. Thoughts? @hackermd
Secure versions of dcm4che arc 5 use keycloak for authentication for both UI and webservices. Can other auth methods be used other than basic auth in DICOMweb-client? In particular client id / token
Thanks
Chris
Currently dcmread
is used without the force
option in dicomweb-client. It would be nice to make this configurable to read also maybe not so valid DICOM files.
Hi, I am trying to test the wado-rs functionality for retrieve instance, the functionality works fine on postman/browser. While testing using dicomweb-client I am getting the following error.
http://127.0.0.1:8000 "GET /api/internal/dicomweb/ec712b95-a5fb-43bc-9de1-3ae5024e7c39/wadors/studies/2.16.124.113543.6003.1719516381.65153.16773.3015806397/series/2.16.124.113543.6003.2297816378.33047.16917.2271512738/instances/2.16.124.113543.6003.3368520785.218.17135.699537048 HTTP/1.1" 406 57
DEBUG 2020-06-30 05:03:10,741 api 77 139832964693320 request status code: 406
Traceback (most recent call last):
File "/usr/local/lib/python3.7/code.py", line 90, in runcode
exec(code, self.locals)
File "<console>", line 1, in <module>
File "/usr/local/lib/python3.7/site-packages/dicomweb_client/api.py", line 2084, in retrieve_instance
return self._http_get_multipart_application_dicom(url)[0]
File "/usr/local/lib/python3.7/site-packages/dicomweb_client/api.py", line 1045, in _http_get_multipart_application_dicom
response = self._http_get(url, params, headers)
File "/usr/local/lib/python3.7/site-packages/dicomweb_client/api.py", line 695, in _http_get
response.raise_for_status()
File "/usr/local/lib/python3.7/site-packages/requests/models.py", line 941, in raise_for_status
raise HTTPError(http_error_msg, response=self)
requests.exceptions.HTTPError: 406 Client Error: Not Acceptable for url: http://127.0.0.1:8000/api/internal/dicomweb/ec712b95-a5fb-43bc-9de1-3ae5024e7c39/wadors/studies/2.16.124.113543.6003.1719516381.65153.16773.3015806397/series/2.16.124.113543.6003.2297816378.33047.16917.2271512738/instances/2.16.124.113543.6003.3368520785.218.17135.699537048
This is how I am testing the wado retrieve instance
from dicomweb_client.api import DICOMwebClient
study_instance_uid = '2.16.124.113543.6003.1719516381.65153.16773.3015806397'
series_instance_uid = '2.16.124.113543.6003.2297816378.33047.16917.2271512738'
sop_instance_uid = '2.16.124.113543.6003.3368520785.218.17135.699537048'
client = DICOMwebClient(url="http://127.0.0.1:8000/api/internal/dicomweb/ec712b95-a5fb-43bc-9de1-3ae5024e7c39", wado_url_prefix="wadors")
client.retrieve_instance(study_instance_uid=study_instance_uid, series_instance_uid=series_instance_uid, sop_instance_uid=sop_instance_uid)
The response from the endpoint has content_type multipart/related;type=application/dicom
. Let me know if I am doing something wrong here.
According to the standard image/dicom-rle
should be used as media type for transfer syntax 1.2.840.10008.1.2.5 RLE Lossless. However DicomWebClient
throws when using it as a media type when fetching frames:
url = 'https://idc-external-006.uc.r.appspot.com/dcm4chee-arc/aets/DCM4CHEE/rs'
dicomweb_client = DICOMwebClient(url)
dicomweb_client.retrieve_instance_frames(
"1.3.6.1.4.1.5962.99.1.3456320082.756362073.1622659023442.3.0",
"1.3.6.1.4.1.5962.1.1.0.0.0.1595262375.18986.35",
"1.3.6.1.4.1.5962.99.1.3456320082.756362073.1622659023442.1478.0",
[1],
("image/dicom-rle",)
)
throws:
File [c:\Users\er-gac\.pyenv\pyenv-win\versions\3.11.2\Lib\site-packages\dicomweb_client\web.py:2961](file:///C:/Users/er-gac/.pyenv/pyenv-win/versions/3.11.2/Lib/site-packages/dicomweb_client/web.py:2961), in DICOMwebClient.retrieve_instance_frames(self, study_instance_uid, series_instance_uid, sop_instance_uid, frame_numbers, media_types)
[2930](file:///C:/Users/er-gac/.pyenv/pyenv-win/versions/3.11.2/Lib/site-packages/dicomweb_client/web.py:2930) def retrieve_instance_frames(
[2931](file:///C:/Users/er-gac/.pyenv/pyenv-win/versions/3.11.2/Lib/site-packages/dicomweb_client/web.py:2931) self,
[2932](file:///C:/Users/er-gac/.pyenv/pyenv-win/versions/3.11.2/Lib/site-packages/dicomweb_client/web.py:2932) study_instance_uid: str,
(...)
[2936](file:///C:/Users/er-gac/.pyenv/pyenv-win/versions/3.11.2/Lib/site-packages/dicomweb_client/web.py:2936) media_types: Optional[Tuple[Union[str, Tuple[str, str]], ...]] = None
[2937](file:///C:/Users/er-gac/.pyenv/pyenv-win/versions/3.11.2/Lib/site-packages/dicomweb_client/web.py:2937) ) -> List[bytes]:
[2938](file:///C:/Users/er-gac/.pyenv/pyenv-win/versions/3.11.2/Lib/site-packages/dicomweb_client/web.py:2938) """Retrieve one or more frames of an image instance.
[2939](file:///C:/Users/er-gac/.pyenv/pyenv-win/versions/3.11.2/Lib/site-packages/dicomweb_client/web.py:2939)
[2940](file:///C:/Users/er-gac/.pyenv/pyenv-win/versions/3.11.2/Lib/site-packages/dicomweb_client/web.py:2940) Parameters
(...)
[2958](file:///C:/Users/er-gac/.pyenv/pyenv-win/versions/3.11.2/Lib/site-packages/dicomweb_client/web.py:2958)
[2959](file:///C:/Users/er-gac/.pyenv/pyenv-win/versions/3.11.2/Lib/site-packages/dicomweb_client/web.py:2959) """ # noqa: E501
[2960](file:///C:/Users/er-gac/.pyenv/pyenv-win/versions/3.11.2/Lib/site-packages/dicomweb_client/web.py:2960) return list(
-> [2961](file:///C:/Users/er-gac/.pyenv/pyenv-win/versions/3.11.2/Lib/site-packages/dicomweb_client/web.py:2961) self._get_instance_frames(
[2962](file:///C:/Users/er-gac/.pyenv/pyenv-win/versions/3.11.2/Lib/site-packages/dicomweb_client/web.py:2962) study_instance_uid=study_instance_uid,
[2963](file:///C:/Users/er-gac/.pyenv/pyenv-win/versions/3.11.2/Lib/site-packages/dicomweb_client/web.py:2963) series_instance_uid=series_instance_uid,
[2964](file:///C:/Users/er-gac/.pyenv/pyenv-win/versions/3.11.2/Lib/site-packages/dicomweb_client/web.py:2964) sop_instance_uid=sop_instance_uid,
[2965](file:///C:/Users/er-gac/.pyenv/pyenv-win/versions/3.11.2/Lib/site-packages/dicomweb_client/web.py:2965) frame_numbers=frame_numbers,
[2966](file:///C:/Users/er-gac/.pyenv/pyenv-win/versions/3.11.2/Lib/site-packages/dicomweb_client/web.py:2966) media_types=media_types,
[2967](file:///C:/Users/er-gac/.pyenv/pyenv-win/versions/3.11.2/Lib/site-packages/dicomweb_client/web.py:2967) stream=False
[2968](file:///C:/Users/er-gac/.pyenv/pyenv-win/versions/3.11.2/Lib/site-packages/dicomweb_client/web.py:2968) )
[2969](file:///C:/Users/er-gac/.pyenv/pyenv-win/versions/3.11.2/Lib/site-packages/dicomweb_client/web.py:2969) )
File [c:\Users\er-gac\.pyenv\pyenv-win\versions\3.11.2\Lib\site-packages\dicomweb_client\web.py:2907](file:///C:/Users/er-gac/.pyenv/pyenv-win/versions/3.11.2/Lib/site-packages/dicomweb_client/web.py:2907), in DICOMwebClient._get_instance_frames(self, study_instance_uid, series_instance_uid, sop_instance_uid, frame_numbers, media_types, stream)
[2901](file:///C:/Users/er-gac/.pyenv/pyenv-win/versions/3.11.2/Lib/site-packages/dicomweb_client/web.py:2901) return self._http_get_multipart_application_octet_stream(
[2902](file:///C:/Users/er-gac/.pyenv/pyenv-win/versions/3.11.2/Lib/site-packages/dicomweb_client/web.py:2902) url,
[2903](file:///C:/Users/er-gac/.pyenv/pyenv-win/versions/3.11.2/Lib/site-packages/dicomweb_client/web.py:2903) media_types=media_types,
[2904](file:///C:/Users/er-gac/.pyenv/pyenv-win/versions/3.11.2/Lib/site-packages/dicomweb_client/web.py:2904) stream=stream
[2905](file:///C:/Users/er-gac/.pyenv/pyenv-win/versions/3.11.2/Lib/site-packages/dicomweb_client/web.py:2905) )
[2906](file:///C:/Users/er-gac/.pyenv/pyenv-win/versions/3.11.2/Lib/site-packages/dicomweb_client/web.py:2906) elif common_media_type.startswith('image'):
-> [2907](file:///C:/Users/er-gac/.pyenv/pyenv-win/versions/3.11.2/Lib/site-packages/dicomweb_client/web.py:2907) return self._http_get_multipart_image(
[2908](file:///C:/Users/er-gac/.pyenv/pyenv-win/versions/3.11.2/Lib/site-packages/dicomweb_client/web.py:2908) url,
[2909](file:///C:/Users/er-gac/.pyenv/pyenv-win/versions/3.11.2/Lib/site-packages/dicomweb_client/web.py:2909) media_types=media_types,
[2910](file:///C:/Users/er-gac/.pyenv/pyenv-win/versions/3.11.2/Lib/site-packages/dicomweb_client/web.py:2910) stream=stream
[2911](file:///C:/Users/er-gac/.pyenv/pyenv-win/versions/3.11.2/Lib/site-packages/dicomweb_client/web.py:2911) )
[2912](file:///C:/Users/er-gac/.pyenv/pyenv-win/versions/3.11.2/Lib/site-packages/dicomweb_client/web.py:2912) elif common_media_type.startswith('video'):
[2913](file:///C:/Users/er-gac/.pyenv/pyenv-win/versions/3.11.2/Lib/site-packages/dicomweb_client/web.py:2913) return self._http_get_multipart_video(
[2914](file:///C:/Users/er-gac/.pyenv/pyenv-win/versions/3.11.2/Lib/site-packages/dicomweb_client/web.py:2914) url,
[2915](file:///C:/Users/er-gac/.pyenv/pyenv-win/versions/3.11.2/Lib/site-packages/dicomweb_client/web.py:2915) media_types=media_types,
[2916](file:///C:/Users/er-gac/.pyenv/pyenv-win/versions/3.11.2/Lib/site-packages/dicomweb_client/web.py:2916) stream=stream
[2917](file:///C:/Users/er-gac/.pyenv/pyenv-win/versions/3.11.2/Lib/site-packages/dicomweb_client/web.py:2917) )
File [c:\Users\er-gac\.pyenv\pyenv-win\versions\3.11.2\Lib\site-packages\dicomweb_client\web.py:1201](file:///C:/Users/er-gac/.pyenv/pyenv-win/versions/3.11.2/Lib/site-packages/dicomweb_client/web.py:1201), in DICOMwebClient._http_get_multipart_image(self, url, media_types, byte_range, params, stream)
[1197](file:///C:/Users/er-gac/.pyenv/pyenv-win/versions/3.11.2/Lib/site-packages/dicomweb_client/web.py:1197) if byte_range is not None:
[1198](file:///C:/Users/er-gac/.pyenv/pyenv-win/versions/3.11.2/Lib/site-packages/dicomweb_client/web.py:1198) headers['Range'] = self._build_range_header_field_value(
[1199](file:///C:/Users/er-gac/.pyenv/pyenv-win/versions/3.11.2/Lib/site-packages/dicomweb_client/web.py:1199) byte_range
[1200](file:///C:/Users/er-gac/.pyenv/pyenv-win/versions/3.11.2/Lib/site-packages/dicomweb_client/web.py:1200) )
-> [1201](file:///C:/Users/er-gac/.pyenv/pyenv-win/versions/3.11.2/Lib/site-packages/dicomweb_client/web.py:1201) headers['Accept'] = self._build_multipart_accept_header_field_value(
[1202](file:///C:/Users/er-gac/.pyenv/pyenv-win/versions/3.11.2/Lib/site-packages/dicomweb_client/web.py:1202) media_types,
[1203](file:///C:/Users/er-gac/.pyenv/pyenv-win/versions/3.11.2/Lib/site-packages/dicomweb_client/web.py:1203) supported_media_types
[1204](file:///C:/Users/er-gac/.pyenv/pyenv-win/versions/3.11.2/Lib/site-packages/dicomweb_client/web.py:1204) )
[1205](file:///C:/Users/er-gac/.pyenv/pyenv-win/versions/3.11.2/Lib/site-packages/dicomweb_client/web.py:1205) response = self._http_get(
[1206](file:///C:/Users/er-gac/.pyenv/pyenv-win/versions/3.11.2/Lib/site-packages/dicomweb_client/web.py:1206) url,
[1207](file:///C:/Users/er-gac/.pyenv/pyenv-win/versions/3.11.2/Lib/site-packages/dicomweb_client/web.py:1207) params=params,
[1208](file:///C:/Users/er-gac/.pyenv/pyenv-win/versions/3.11.2/Lib/site-packages/dicomweb_client/web.py:1208) headers=headers,
[1209](file:///C:/Users/er-gac/.pyenv/pyenv-win/versions/3.11.2/Lib/site-packages/dicomweb_client/web.py:1209) stream=stream
[1210](file:///C:/Users/er-gac/.pyenv/pyenv-win/versions/3.11.2/Lib/site-packages/dicomweb_client/web.py:1210) )
[1211](file:///C:/Users/er-gac/.pyenv/pyenv-win/versions/3.11.2/Lib/site-packages/dicomweb_client/web.py:1211) return self._decode_multipart_message(response, stream=stream)
File [c:\Users\er-gac\.pyenv\pyenv-win\versions\3.11.2\Lib\site-packages\dicomweb_client\web.py:901](file:///C:/Users/er-gac/.pyenv/pyenv-win/versions/3.11.2/Lib/site-packages/dicomweb_client/web.py:901), in DICOMwebClient._build_multipart_accept_header_field_value(cls, media_types, supported_media_types)
[898](file:///C:/Users/er-gac/.pyenv/pyenv-win/versions/3.11.2/Lib/site-packages/dicomweb_client/web.py:898) if media_type not in supported_media_types.values():
[899](file:///C:/Users/er-gac/.pyenv/pyenv-win/versions/3.11.2/Lib/site-packages/dicomweb_client/web.py:899) if not (media_type.endswith('/*') or
[900](file:///C:/Users/er-gac/.pyenv/pyenv-win/versions/3.11.2/Lib/site-packages/dicomweb_client/web.py:900) media_type.endswith('/')):
--> [901](file:///C:/Users/er-gac/.pyenv/pyenv-win/versions/3.11.2/Lib/site-packages/dicomweb_client/web.py:901) raise ValueError(
[902](file:///C:/Users/er-gac/.pyenv/pyenv-win/versions/3.11.2/Lib/site-packages/dicomweb_client/web.py:902) f'Media type "{media_type}" is not supported for '
[903](file:///C:/Users/er-gac/.pyenv/pyenv-win/versions/3.11.2/Lib/site-packages/dicomweb_client/web.py:903) 'requested resource.'
[904](file:///C:/Users/er-gac/.pyenv/pyenv-win/versions/3.11.2/Lib/site-packages/dicomweb_client/web.py:904) )
[905](file:///C:/Users/er-gac/.pyenv/pyenv-win/versions/3.11.2/Lib/site-packages/dicomweb_client/web.py:905) if transfer_syntax_uid is not None:
[906](file:///C:/Users/er-gac/.pyenv/pyenv-win/versions/3.11.2/Lib/site-packages/dicomweb_client/web.py:906) if transfer_syntax_uid != '*':
ValueError: Media type "image/dicom-rle" is not supported for requested resource.
Not that I expect the frame to be available in RLE, but the expected error would be:
HTTPError: 406 Client Error: Not Acceptable for url: https://idc-external-006.uc.r.appspot.com/dcm4chee-arc/aets/DCM4CHEE/rs/studies/1.3.6.1.4.1.5962.99.1.3456320082.756362073.1622659023442.3.0/series/1.3.6.1.4.1.5962.1.1.0.0.0.1595262375.18986.35/instances/1.3.6.1.4.1.5962.99.1.3456320082.756362073.1622659023442.1478.0/frames/1
as I get if I change to media type image/x-dicom-rle
.
Looking at setup.py
I see that numpy & pillow are required dependencies. After a quick search of the codebase, I see that both of them are only used in the retrieve_instance_frames
subparser of cli.py
. Furthermore, in the case of numpy, only its (rather simple) fromstring
function is used and only in the case where the the retrieved frame is compressed with JPEG LS.
Since these are two significant dependencies that may need to be built, and are only useful for a very specific feature, would you be open to making them optional dependencies (for example using setuptools extras)?
In my case, I'm considering using this project (the library, not the CLI) as part of an application that will be running under pypy instead of cpython, possibly in an alpine container. I'd have to build both pillow & numpy for a feature I don't intend on using.
It's totally understandable if you wish to keep these dependencies as required and I could always just build them or use a slightly modified fork that removes these dependencies.
@pieper @chafey As discussed in #56, it would be great to implement a DICOMs3Client
based on DICOMwebProtocol.
In general, I think we should consider the following aspects for the design of an S3 client:
(1) I've been thinking that we could put the metadata into the S3 Object Metadata. The PUT request header is limited to 8 KB in size, but that should be sufficient for the metadata. This could also include the offset to the Pixel Data, Float Pixel Data, or Double Float Pixel Data element (see 2).
(2) If the Pixel Data element contains a Basic Offset Table item, then the client could just read its value and cache the offsets locally. Otherwise the client would have to determine the offset of frame items by reading the header of each frame item. That could result in a lot of HTTP calls, so ideally we would make sure that the Basic Offset Table gets included into image data sets.
(3) To support writes (including parallel writes), we shouldn't store any aggregate study- or series-level information such as Number of Study Related Series in the object metadata, but dynamically compute it client-side.
What do you think?
The retrieve_series_metadata function is exhibiting unexpected behavior where it gets stuck and does not raise a timeout exception even after waiting for an extended period (hours) . This issue occurs when attempting to retrieve metadata for a specific series using the retrieve_series_metadata function with a certain series_uid and study_uid.
To improve the context:
The same series_uid and study_uid hang indefinetly alse with retrieve_series and retrieve_study method.
The same series_uid and study_uid return good result for the search_for_series method (<1s).
For different series_uid all the methods seem to work so a WADO-RS avaliabity issue on the server can be discarded (At least 1000 series tested).
Although we definitely cannot rule out a bug in our end (any suggestions in this regard are welcome) the library should have a method to hang a request that takes too long.
Im getting a JSONDECODE error when trying to use the QIDO against a dicom rs broker (dicomweb -> dicom)
Using the requests module in python if i change the header "Accept : application/json" i am able to get a response but the server i am calling with dicomweb-client throws the error "application/json+dicom" is not accepted
Is there a way to change the header (similar to how the requests module can) from application/json+dicom to just application/json ?
Google suggests using up to 20 concurrent requests for better overall network performance. But I believe we currently only do one at a time with requests
.
It looks like we could switch to asks
for concurrent requests.
When using DICOMfileClient
to retrieve an instance with RLE Lossless encoding (Transfer Syntax UID 1.2.840.10008.1.2.5
), an error is raised even though pydicom
can deal with RLE Lossless encoding just fine. There is no error when using DICOMwebClient
.
dicom-samples
.dicom-samples
folder and run it.from pathlib import Path
from dicomweb_client import DICOMfileClient
from pydicom.dataset import Dataset
samples_path = Path("./dicom-samples")
dicom_client = DICOMfileClient(f"file://{samples_path.absolute()}")
for series_json in dicom_client.search_for_series(get_remaining=True):
series = Dataset.from_json(series_json)
for instance in dicom_client.retrieve_series(
study_instance_uid=series.StudyInstanceUID,
series_instance_uid=series.SeriesInstanceUID,
):
pass
The script succeeds without any output.
Traceback (most recent call last):
File "/.../.venv/lib/python3.11/site-packages/dicomweb_client/file.py", line 3180, in retrieve_instance
expected_media_type = transfer_syntax_uid_lut[transfer_syntax_uid]
~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^
KeyError: '1.2.840.10008.1.2.5'
I was trying to parse the byte array send by store request using this client and I am getting an EOF error of reading. I checked the content from the request body and reading the test dicom file directly and found some difference at the beginning.
I have a similar issue as #41 regarding the current version:
I am using this great package to send data to an Orthanc instance. Until version 0.41.2 this works.
For the current version, I get a response code 200 (this is probably an Orthanc mistake), but no data can be found on the Orthanc instance. I tested it with different Orthanc version and with the Ortanc docker container.
e.g. the current version:
docker run -p 4242:4242 -p 8042:8042 --rm jodogne/orthanc-plugins
Here the code snipped to reproduce:
from dicomweb_client.api import DICOMwebClient
from dicomweb_client.session_utils import create_session_from_auth
from requests.auth import HTTPBasicAuth
auth = HTTPBasicAuth('orthanc', 'orthanc')
session = create_session_from_auth(auth)
client = DICOMwebClient(url="http://localhost:8042/dicom-web", session=session)
path = "" # ADD path to a dicom file
dcmfile = pydicom.dcmread(path)
value = client.store_instances([dcmfile])
With version 0.50.3 there is no data anymore on the server:
http://localhost:8042/app/explorer.html#find-patients (login with orthanc/orthanc), there is no error message ...
Hi, I need to know, how I can set headers request in every dicomweb requests. Nowadays, I have this issue to solve: has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
I'm currently writing a course based on the writing of a Python 3 program that fetches, manipulates and presents images and metadata available at azurewebsites.net. The first thing to figure out is how to traverse the tree of studies,series,instances,frames,etc that is described at
http://dicom.nema.org/medical/dicom/current/output/html/part18.html#table_6.8-1
but some parameter to the call client.retrieve_instance()
in the code below is incorrect and throws and error (in which we can see the resulting error):
https://dicomcloud.azurewebsites.net/wadors/studies/1.3.12.2.1107.5.4.3.4975316777216.19951114.94101.16/series/1.3.12.2.1107.5.4.3.4975316777216.19951114.94101.17/instances/1.3.12.2.1107.5.4.3.284980.19951129.170916.11
What have I done wrong?
Code follows:
#!/usr/bin/env python3
import argparse
from pprint import pprint
from dicomweb_client.api import DICOMwebClient
def main(server_name):
'Main function of application.'
# 1.
print('Defaulting DICOM-server_name to {}'.format(server_name))
client = DICOMwebClient(url=server_name,
qido_url_prefix='qidors',
wado_url_prefix='wadors',
stow_url_prefix='stowrs')
print('Connected to DICOM-server_name {}'.format(server_name))
# 2.
studies = client.search_for_studies()
print('There are {} studies available'.format(len(studies)))
# 3.
for study in studies:
# Ref: http://dicom.nema.org/medical/dicom/current/output/html/part18.html#table_6.7.1-2
study_instance_uid = study['0020000D']['Value'][0] # StudyInstanceUID
print('Study Instance UID: {}'.format(study_instance_uid))
study_metadata = client.retrieve_study_metadata(study_instance_uid)
# pprint(study_metadata)
for series_metadata in study_metadata:
series_instance_uid = series_metadata['0020000E']['Value'][0] # SeriesInstanceUID
sop_class_uid = series_metadata['00080016']['Value'][0] # SOPClassUID
sop_instance_uid = series_metadata['00080018']['Value'][0] # SOPInstanceUID
print('SOP Class UID: ', sop_class_uid)
print('SOP Instance UID: ', sop_instance_uid)
# print('Instance Number: ', series_metadata['00200013']['Value'])
try:
print('Rows: ', series_metadata['00280010']['Value'][0])
print('Columns: ', series_metadata['00280010']['Value'][0])
except:
None
instance = client.retrieve_instance(study_instance_uid,
series_instance_uid,
sop_instance_uid)
# print('Study Metadata:')
# pprint(study_metadata)
# find all series_instance_uid
# 4.
patient_name = input('Enter patient name: ')
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument('--server',
help='DICOMweb server to connect to',
type=str,
default='https://dicomcloud.azurewebsites.net'
)
args = parser.parse_args()
main(server_name=args.server)
Hello,
I get a client error when connecting to an azure dicom webserver. I can see normally all the records and I can as well remove a selected series from the DICOMweb server through Slicer's extension. But when I try to "download and index" it I get:
DownloadSelectedSeries: Error in getting response from DICOMweb server.
HTTP Error:
406 Client Error: Not Acceptable for url: https://xxx-xxx.azurewebsites.net/studies/1.2.826.0.1.xxxxxx.x.xxx.x.x.xxxxxxxxxxx.xxxx.xxxx/series/1.2.826.0.1.xxxxxx.x.x.x.x.xxxx.xxx.xxxxx.xxxxxx/instances/1.2.826.0.1.xxxx.x.xxx.x.x.8xxxxx.xxx.xxxxxxxx.xxxxx
What could possibly go wrong?
OS: Win 10
Slicer version: 4.13.0-2021-03-03 r29745 / fca084e
Regards
Antonios
Currently, Dicom WebClient API has interface for an authentication mechanism. However, when CLI this option is not exposed which limits the usage of CLI.
What is the minimum python requirement when using version 0.50.0? I see it specifies python 3.6 in the classifiers section, but then python_requires
specifies python >3.6.
There seem to be issues when integrating with Orthanc. Orthanc uses SHA-1 hashes of Instance UIDs. This fails DICOM UID verification giving "DICOM UID has invalid format.". It is clearly obvious this is not a valid DICOM UID but support for Orthanc is mentioned.
While it's common practice to accept Sequence[T]
when a function can accept a list, tuple, etc., it's often best to be more specific with return types allowing those invoking the methods to have greater clarity of the type returned. In many of DICOMwebClient
's methods, current type hints provide a return value of Sequence[T]
where List[T]
would be more specific. I propose updating these types to the more specific values provided the API is comfortable committing to these types (which seems like a fairly innocuous commitment).
The link to the standard itself is wrong/old (resulting in 404):
http://www.dicomstandard.org/dicomweb/
(I guess it should be: https://www.dicomstandard.org/current/ )
Found on:
https://dicomweb-client.readthedocs.io/en/latest/introduction.html
I switched from my own aiohttp
based DicomWeb implementation to dicomwebclient
for downloading image studies/series and observe quite often missing data in a single slice per image series (when using retrieve_series
or iter_series
). It can be reproduced for the very same image series and when retrieving more than one series in a row there is also some kind of buffer leakage introduced. The missing image data is replaced with data from probably a different series.
Two different download mechanisms, one based on aiohttp
and the other on Scala and dcm4che, are able to retrieve the image data without any problems so I think it is actually a problem in the multi-part response parsing in this package.
In new conda environment with only dicomweb-client 0.54.2 installed, when using python 3.7.11, following error has occured:
>>> import dicomweb_client.api
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/home/jan/.conda/envs/test/lib/python3.7/site-packages/dicomweb_client/__init__.py", line 3, in <module>
from dicomweb_client.api import DICOMwebClient, DICOMfileClient
File "/home/jan/.conda/envs/test/lib/python3.7/site-packages/dicomweb_client/api.py", line 4, in <module>
from dicomweb_client.protocol import DICOMClient
File "/home/jan/.conda/envs/test/lib/python3.7/site-packages/dicomweb_client/protocol.py", line 1, in <module>
from typing import (
ImportError: cannot import name 'Protocol' from 'typing' (/home/jan/.conda/envs/test/lib/python3.7/typing.py)
I guess, that typing-extensions is not used in case of python versions lower then 3.8.
Hi,
it would be really nice to add an verify_ssl
attribute to the DICOMwebClient
for servers with self-signed certificates.
This should be pretty straightforward to do by adding the verify=True/False
parameter to all requests
calls.
_get_transaction_url function cannot properly check if the value is null or empty.
https://github.com/herrmannlab/dicomweb-client/blob/a9d6b09a347d41b245d0bf2054ec323fdc7ddb33/src/dicomweb_client/web.py#L318
https://github.com/herrmannlab/dicomweb-client/blob/a9d6b09a347d41b245d0bf2054ec323fdc7ddb33/src/dicomweb_client/web.py#L321
this codes always return True, so it adds a double slash to the end of the address even if the url prefix is empty.
Related : Project-MONAI/MONAILabel#1045 (comment)
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.