0

I am using python 3.6 on Windows to fetch a DICOM file from a database as a string of bytes. Now, I would like to use the Pydicom library to read the content of the file. The dcmread() method takes a file-like object as main argument, however the following piece of code raises an error when trying to access the stored images :

import sqlite3 as sql, io

...
dicom_as_bytes = cursor.fetchone()[0] # read bytes from sqlite3 request
dicom_as_bytes = io.BytesIO(dicom_as_bytes) # convert to file-like format

dcm_file = dcmread(dicom_as_bytes)
images = dcm_file.pixel_array # PermissionError: [Errno 13] Permission denied: 'C:\\Users\\<User>\\AppData\\Local\\Temp\\tmpfhvgc2oo'

There is no problem with the sql request in itself because I was able to write to a persistent file and read it :

tmp_file = open("tmp", "wb")
tmp_file.write(dicom_as_bytes)
tmp_file.close()

dcm_file = dcmread("tmp")
images = dcm_file.pixel_array # no error

Free anonymized DICOM files can be found on the Internet with a bit of search. As I am not sure I have the right to link the one I used, I won't post a link here - sorry.

Here is the pydicom documentation of the dcmread method : link

The above method is working. However, I would prefer to avoid creating a temporary file in the project folder like that. I also took a look at python's tempfile library which doesn't seem to work for me as well, for one reason or another :

import tempfile

tmp_file = tempfile.NamedTemporaryFile()
temp_file.write(dicom_as_bytes)
temp_file.seek(0)

dcm_file = dcmread(temp_file)
images = dcm_file.pixel_array # TypeError: GDCM could not read DICOM image

temp_file.close()

In the end, I would like to know if it is possible to use BytesIO or tempfile to load the images of a DICOM file from sqlite3 bytes of data corresponding exactly to sqlite3.Binary(open("file.dcm", "rb).read()) with "file.dcm" a DICOM file storing more than one image. I would prefer to not use a temporary file explicitely because I should have no reason to use temporary files (no lack of memory, storage space, I am not working on inter-process communication or crash recovery either).

I am wondering if there is a need to somehow encode the received bytes before creating a new BytesIO object or writing to a TemporaryFile. However, this doesn't seem to be the case because I can just write them directly in a file with open(filename, mode).write(dicom_bytes) or TemporaryFile().write(dicom_bytes). It's just that on the one hand, I can access the image array but not on the other (although I can access basic information such as dcm_file.filename without an issue). Maybe converting a BytesIO object to a FileIO object would work, although I have no idea how to do that without creating a new file with open.

SpaceBurger
  • 537
  • 2
  • 12
  • 1
    Which pydicom and GDCM versions are you using? There had been [a related issue](https://github.com/pydicom/pydicom/issues/1153) due to the fact that GDCM > v2.8.8 could not read pixel data from memory. – MrBean Bremen Jan 18 '21 at 12:46
  • I just checked : I use pydicom 2.1.2 and gdcm 2.8.4. I must have downloaded it from [conda forge](https://anaconda.org/conda-forge/pydicom). – SpaceBurger Jan 18 '21 at 13:27
  • So this seems related indeed - this might explain why I can't read images with a BytesIO instance as the decoding source, but v2.1.2 of pydicom should have this "fixed" with [PR 1157](https://github.com/pydicom/pydicom/pull/1157). And indeed, when I try to access the pixel data, python tries to access a temporary file as explained in the fix, but is unable to, which I don't understand why. (`PermissionError: [Errno 13] Permission denied...`) – SpaceBurger Jan 18 '21 at 13:42
  • 1
    I suggest you open an issue in pydicom to investigate this. – MrBean Bremen Jan 18 '21 at 13:46
  • 1
    When trying to reproduce the error to make a MRE for the bug report, I started the whole process again and now it seems to be working fine. I don't know why, maybe the data on my database wasn't cleanly uploaded. Thanks for taking the time to answer, I will now close this topic. – SpaceBurger Jan 18 '21 at 15:03

2 Answers2

2

I use the same way as @SpaceBurger, just one more parameter, force

ds = pydicom.dcmread(BytesIO(buffer_memory), force=True)
Grimmer Kang
  • 235
  • 3
  • 10
0

Trying something like that should work with pydicom 2.1.2 and gdcm 2.8.4 :

from pydicom import dcmread               
from pydicom.data import get_testdata_file
import io, sqlite3 as sql

testdata_filepath = get_testdata_file("MR_small.dcm")
dicom_as_bytes = open(testdata_filepath, "rb")

# to sql, supposing we have one table named "Dicoms" with two columns "name" and "data"
connection = sql.connect("test.sqlite")
cursor = connection.cursor()
cursor.execute("insert into Dicoms values (?, ?);", (dataset_name, sql.Binary(dicom_as_bytes.read())))
connection.commit()
connection.close()
dicom_as_bytes.close()

# from sql
connection = sql.connect("test.sqlite")
cursor = connection.cursor()
cursor.execute("select (data) from Dicoms where name=?;", (dataset_name,))
connection.commit()

res = cursor.fetchone()[0]

connection.close()

dicom_as_bytesio = io.BytesIO(res)
dicom = dcmread(dicom_as_bytesio)
print(dicom.pixel_array)
SpaceBurger
  • 537
  • 2
  • 12