Comments (6)
Does Pydicom support setting the PixelData property to a file pointer or stream?
No, that is beyond the scope of pydicom directly. Because DICOM doesn't have "records" of fixed size, the only way to know where a data element begins in file is to write out all the previous ones.
Here are some thoughts (untested) on a workaround:
Create a new dcmwrite
-like method with extra parameters to the call, e.g. a Tag (pixel data in this case, 0x7fe00010) and an Iterable[bytes] generator to give chunks of pixel data at a time. The new method would create a Dataset
, called e.g. pre_tag_ds
(using slicing, i.e. pre_tag_ds = ds[:tag]
) of all the data elements prior to the given Tag, and likewise one for tags after (if any), post_tag_ds = ds[tag+1:]
. Then the method would open the output file, call dcmwrite
with the opened file and pre_tag_ds
. Then the hardest part: the code would have to write the data element tag, VR (if applicable) and length (has to be known ahead, or save the location and write it afterwards), then the pixel data in chunks from the generator, then call dcmwrite
again with the (still open file) to write post_tag_ds
.
One caution for the generator, is that DICOM, IIUC, would require encapsulation for the data -- if you search you will find some previous issues/discussions about that topic; pydicom has some support for encapsulation.
I think that could work. If someone wrote something like that, I think we could incorporate it into pydicom.
from pydicom.
A couple of other thoughts:
- the
pre_tag_ds
should also havefile_meta
copied from the original dataset. - for writing the one data element, that might be a little easier by just using a one with a blank value (0-length value) and then storing the
file_tell
to backtrack and write the correct length later.
I'm also musing about a completely different path -- add a callable (generator) to the DataElement
class, something like generate_value
. The user would have to set the callable to their own generator which returns a chunk of bytes at a time, and raise StopIteration
when complete. Then write_data_element
would have to be modified a little, to check for the callable and loop to write the .value
from a generator rather than just assuming it is bytes in memory. This still leaves writing the correct length in the data element header, but write_data_element
could keep track of the file position and seek back to write that after all the total length of the chunks had been tallied.
from pydicom.
Thanks for the quick reply!
I'm also musing about a completely different path -- add a callable (generator) to the DataElement class, something like generate_value. The user would have to set the callable to their own generator which returns a chunk of bytes at a time, and raise StopIteration when complete. Then write_data_element would have to be modified a little, to check for the callable and loop to write the .value from a generator rather than just assuming it is bytes in memory. This still leaves writing the correct length in the data element header, but write_data_element could keep track of the file position and seek back to write that after all the total length of the chunks had been tallied.
This is what I'm attempting currently! We know the length of bytes ahead of time because we can look at the filesize of the pixel data on disk. So far I've made a class that implements __len__
to read the filesize from disk. The part I'm stuck on is the writing:
def write_OBvalue(fp: DicomIO, elem: DataElement) -> None:
"""Write a data_element with VR of 'other byte' (OB)."""
# len(elem.value) is the filesize of the pixel data file
if len(elem.value) % 2:
# Pad odd length values
fp.write(cast(bytes, elem.value))
fp.write(b'\x00')
else:
fp.write(cast(bytes, elem.value))
It seems this function would need to change a bit as you said to see if there is a value_generator
from pydicom.
Here is the hacked function I came up with:
def write_OBvalue(fp: DicomIO, elem: DataElement) -> None:
"""Write a data_element with VR of 'other byte' (OB)."""
READ_SIZE = 1024*500
if isinstance(elem.value, BufferedReader):
# write chunks at a time
while (bytes_ := elem.value.read(READ_SIZE)):
fp.write(bytes_)
if len(elem.value) % 2:
fp.write(b'\x00')
else:
if len(elem.value) % 2:
# Pad odd length values
fp.write(cast(bytes, elem.value))
fp.write(b'\x00')
else:
fp.write(cast(bytes, elem.value))
Do you think there is a way to support this kind of use case or would this be out of scope? Open to API suggestions too!
from pydicom.
I think that looks pretty good, and I wouldn't mind incorporating that in pydicom, but perhaps without necessarily knowing the length. I wondered about doing it for any element but practically it should only be OB so I'm okay with that.
There would have to be checks in other places in code, like in __str__
and __repr__
to put some kind of placeholder representation for the value, other than that I don't foresee any problems.
from pydicom.
This is the approach I'm going to go with. I'm thinking we can also make it completely compatible with all existing code by making the getter for value
return the entire content of the buffer if the value is a buffer.
Then code can opt-in to the iterator interface by checking DataElement for is_buffered
. That should also achieve " I wondered about doing it for any element".
Thanks for being open to the change and for the guidance!
from pydicom.
Related Issues (20)
- FileSet.__str__ add `SeriesDescription` if present
- Documentation search is broken. HOT 2
- Unexpected result from encapsulate_extended if non-even length frame
- convert_color_space should return copy of array HOT 4
- RGB dicoms "AttributeError: can't set attribute" after upgrading pillow to 10.1.0 HOT 1
- Change Python formatting: black → ruff HOT 2
- Intermittent test failures HOT 2
- Comparing two codes where one is erroneously set as a SRT will throw KeyError
- Add support for encoding JPEG2000 and JPEG-LS
- ValueError: cannot reshape array of size HOT 13
- GDCM fails to decode JPEG-LS pixel data with bits stored 6 or 7 HOT 1
- Decoding failure for JPEG-LS pixel data when pixel representation is 1 and bits stored is less than bits allocated
- Decoding failure for JPEG-LS when Bits Allocated is 16 and Bit Stored <= 8 HOT 1
- The (0028,0101) 'Bits Stored' value (16-bit) doesn't match the JPEG 2000 data (14-bit) HOT 3
- Compressing PixelData does not change the VR from OW to OB HOT 1
- dicom saved can't match the plt.show HOT 1
- can pydicom realize dicom image registration HOT 1
- Return sequence items as a list? HOT 3
- Dataset decompress function does not update length of the pixel data HOT 2
- deepcopy on dataset with private block fails HOT 1
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from pydicom.