I want to crop a fragment of DICOM (.dcm) file in Python, and then save such an image. I was trying to do such thing using pydicom, openslide or opencv(cv2) library, without success. For example I tried:
import pydicom
ds = pydicom.dcmread(r"path_to_file")
ds=ds[500:1500,500:1500]
ds.save_as(r"path_to_file2")
without success. Let me know if you got some ideas how to fix it, regards
Edit:below are the errors present using above code
Traceback (most recent call last): File "C:\Users\marci\AppData\Roaming\Python\Python313\site-packages\pydicom\dataset.py", line 1047, in getitem tag = Tag(key) File "C:\Users\marci\AppData\Roaming\Python\Python313\site-packages\pydicom\tag.py", line 89, in Tag raise TypeError( ...<2 lines>... ) TypeError: Unable to create an element tag from '(slice(500, 1500, None), slice(500, 1500, None))': both arguments must be the same type and str or int
The above exception was the direct cause of the following exception:
Traceback (most recent call last): File "D:\praca\GUMED\serce\testy2.py", line 3, in ds=ds[500:1500,500:1500] ~~^^^^^^^^^^^^^^^^^^^ File "C:\Users\marci\AppData\Roaming\Python\Python313\site-packages\pydicom\dataset.py", line 1049, in getitem raise KeyError(f"'{key}'") from exc KeyError: "'(slice(500, 1500, None), slice(500, 1500, None))'"
Process finished with exit code 1
Edit2: @LMC by cropping, I meant, cutting a fragment of an image and saving as another file. So, for example
img[500:1500, 500:1500] would give an image from 500th to 1500th pixel "vertically" and from 500th to 1500th pixel "horizontally".
@Konstantin Makarov there are two issues with your codes (both not working for me):
I want to save an image as .dcm file, not .png. The reason why I need a cropped dcm file is because I got many layers from the scanner, each in the different resolutions; and I need them in the same resolution in order to be able to read them in the segmentation program, as ITKSnap or 3DSlicer. As far as I understand your codes, you assumed that I got one .dcm file with many layers - this is not true for me, I got separate files for the each layer. I mean, each layer is lets say in (x,y) coordinates, and the next layer is some distance (in z direction) from the previous one. Could you please edit the code, given the above clarification? @etauger I got numpy. Your code doesnt work for me. It gives an error for the last line of code (saving file). The error is below:
"C:\Program Files\Python\Python313\python.exe" D:\praca\GUMED\serce\testy2.py Traceback (most recent call last):
File "C:\Users\marci\AppData\Roaming\Python\Python313\site-packages\pydicom\tag.py", line 29, in tag_in_exception yield File "C:\Users\marci\AppData\Roaming\Python\Python313\site-packages\pydicom\filewriter.py", line 826, in write_dataset write_data_element(fp, get_item(tag), dataset_encoding) ~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "C:\Users\marci\AppData\Roaming\Python\Python313\site-packages\pydicom\filewriter.py", line 686, in write_data_element raise ValueError( ...<3 lines>... ) ValueError: The (7FE0,0010) 'Pixel Data' element value hasn't been encapsulated as required for a compressed transfer syntax - see pydicom.encaps.encapsulate() for more informationThe above exception was the direct cause of the following exception:
Traceback (most recent call last): File "D:\praca\GUMED\serce\testy2.py", line 10, in ds.save_as(r"D:\praca\GUMED\dicom\mrxs\1_AORTA\AO_1_014_Masson\3_0-test-cropping.dcm") ~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "C:\Users\marci\AppData\Roaming\Python\Python313\site-packages\pydicom\dataset.py", line 2642, in save_as pydicom.dcmwrite( ~~~~~~~~~~~~~~~~^ filename, ^^^^^^^^^ ...<6 lines>... **kwargs, ^^^^^^^^^ ) ^ File "C:\Users\marci\AppData\Roaming\Python\Python313\site-packages\pydicom\filewriter.py", line 1455, in dcmwrite write_dataset(fp, dataset) ~~~~~~~~~~~~~^^^^^^^^^^^^^ File "C:\Users\marci\AppData\Roaming\Python\Python313\site-packages\pydicom\filewriter.py", line 825, in write_dataset with tag_in_exception(tag): ~~~~~~~~~~~~~~~~^^^^^ File "C:\Program Files\Python\Python313\Lib\contextlib.py", line 162, in exit self.gen.throw(value) ~~~~~~~~~~~~~~^^^^^^^ File "C:\Users\marci\AppData\Roaming\Python\Python313\site-packages\pydicom\tag.py", line 33, in tag_in_exception raise type(exc)(msg) from exc ValueError: With tag (7FE0,0010) got exception: The (7FE0,0010) 'Pixel Data' element value hasn't been encapsulated as required for a compressed transfer syntax - see pydicom.encaps.encapsulate() for more information Traceback (most recent call last): File "C:\Users\marci\AppData\Roaming\Python\Python313\site-packages\pydicom\tag.py", line 29, in tag_in_exception yield File "C:\Users\marci\AppData\Roaming\Python\Python313\site-packages\pydicom\filewriter.py", line 826, in write_dataset write_data_element(fp, get_item(tag), dataset_encoding) ~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "C:\Users\marci\AppData\Roaming\Python\Python313\site-packages\pydicom\filewriter.py", line 686, in write_data_element raise ValueError( ...<3 lines>... ) ValueError: The (7FE0,0010) 'Pixel Data' element value hasn't been encapsulated as required for a compressed transfer syntax - see pydicom.encaps.encapsulate() for more information
Process finished with exit code 1
@scaramallion your code from the "second step", ie.
ds = dcmread(r"D:\praca\GUMED\dicom\mrxs\1_AORTA\AO_1_015_HC\3_0")
arr = ds.pixel_array # returns entire *Pixel Data* by default
arr = arr[100:200, 200:350]
# Automatically sets Pixel Data and the values of the associated elements
ds.set_pixel_data(arr, ds.PhotometricInterpretation, ds.BitsStored)
# Save the modified dataset
ds.save_as(r"C:\Users\marci\Desktop\tiles_dcm\3_0-crop.dcm")
seems to work, because it saves a file without an error, however when Im trying to open this file using openslide library with simple command: slide = open_slide(r"C:\Users\marci\Desktop\tiles_dcm\3_0-crop.dcm"), I get an error below, so something wrong is happening during processing this image...
Traceback (most recent call last): File "D:\praca\GUMED\serce\testy2.py", line 24, in slide = open_slide(r"C:\Users\marci\Desktop\tiles_dcm\1.dcm") File "C:\Users\marci\AppData\Roaming\Python\Python313\site-packages\openslide_init_.py", line 497, in open_slide return OpenSlide(filename) File "C:\Users\marci\AppData\Roaming\Python\Python313\site-packages\openslide_init_.py", line 207, in init self._osr = lowlevel.open(filename)
"C:\Users\marci\AppData\Roaming\Python\Python313\site-packages\openslide\lowlevel.py", line 265, in _check_open raise OpenSlideError(err) openslide.lowlevel.OpenSlideError: Reading C:\Users\marci\Desktop\tiles_dcm\1.dcm: Unsupported photometric interpretation YBR_FULL_422 for 1.2.840.10008.1.2.1
========================================================================
I want to crop a fragment of DICOM (.dcm) file in Python, and then save such an image. I was trying to do such thing using pydicom, openslide or opencv(cv2) library, without success. For example I tried:
import pydicom
ds = pydicom.dcmread(r"path_to_file")
ds=ds[500:1500,500:1500]
ds.save_as(r"path_to_file2")
without success. Let me know if you got some ideas how to fix it, regards
Edit:below are the errors present using above code
Traceback (most recent call last): File "C:\Users\marci\AppData\Roaming\Python\Python313\site-packages\pydicom\dataset.py", line 1047, in getitem tag = Tag(key) File "C:\Users\marci\AppData\Roaming\Python\Python313\site-packages\pydicom\tag.py", line 89, in Tag raise TypeError( ...<2 lines>... ) TypeError: Unable to create an element tag from '(slice(500, 1500, None), slice(500, 1500, None))': both arguments must be the same type and str or int
The above exception was the direct cause of the following exception:
Traceback (most recent call last): File "D:\praca\GUMED\serce\testy2.py", line 3, in ds=ds[500:1500,500:1500] ~~^^^^^^^^^^^^^^^^^^^ File "C:\Users\marci\AppData\Roaming\Python\Python313\site-packages\pydicom\dataset.py", line 1049, in getitem raise KeyError(f"'{key}'") from exc KeyError: "'(slice(500, 1500, None), slice(500, 1500, None))'"
Process finished with exit code 1
Edit2: @LMC by cropping, I meant, cutting a fragment of an image and saving as another file. So, for example
img[500:1500, 500:1500] would give an image from 500th to 1500th pixel "vertically" and from 500th to 1500th pixel "horizontally".
@Konstantin Makarov there are two issues with your codes (both not working for me):
I want to save an image as .dcm file, not .png. The reason why I need a cropped dcm file is because I got many layers from the scanner, each in the different resolutions; and I need them in the same resolution in order to be able to read them in the segmentation program, as ITKSnap or 3DSlicer. As far as I understand your codes, you assumed that I got one .dcm file with many layers - this is not true for me, I got separate files for the each layer. I mean, each layer is lets say in (x,y) coordinates, and the next layer is some distance (in z direction) from the previous one. Could you please edit the code, given the above clarification? @etauger I got numpy. Your code doesnt work for me. It gives an error for the last line of code (saving file). The error is below:
"C:\Program Files\Python\Python313\python.exe" D:\praca\GUMED\serce\testy2.py Traceback (most recent call last):
File "C:\Users\marci\AppData\Roaming\Python\Python313\site-packages\pydicom\tag.py", line 29, in tag_in_exception yield File "C:\Users\marci\AppData\Roaming\Python\Python313\site-packages\pydicom\filewriter.py", line 826, in write_dataset write_data_element(fp, get_item(tag), dataset_encoding) ~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "C:\Users\marci\AppData\Roaming\Python\Python313\site-packages\pydicom\filewriter.py", line 686, in write_data_element raise ValueError( ...<3 lines>... ) ValueError: The (7FE0,0010) 'Pixel Data' element value hasn't been encapsulated as required for a compressed transfer syntax - see pydicom.encaps.encapsulate() for more informationThe above exception was the direct cause of the following exception:
Traceback (most recent call last): File "D:\praca\GUMED\serce\testy2.py", line 10, in ds.save_as(r"D:\praca\GUMED\dicom\mrxs\1_AORTA\AO_1_014_Masson\3_0-test-cropping.dcm") ~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "C:\Users\marci\AppData\Roaming\Python\Python313\site-packages\pydicom\dataset.py", line 2642, in save_as pydicom.dcmwrite( ~~~~~~~~~~~~~~~~^ filename, ^^^^^^^^^ ...<6 lines>... **kwargs, ^^^^^^^^^ ) ^ File "C:\Users\marci\AppData\Roaming\Python\Python313\site-packages\pydicom\filewriter.py", line 1455, in dcmwrite write_dataset(fp, dataset) ~~~~~~~~~~~~~^^^^^^^^^^^^^ File "C:\Users\marci\AppData\Roaming\Python\Python313\site-packages\pydicom\filewriter.py", line 825, in write_dataset with tag_in_exception(tag): ~~~~~~~~~~~~~~~~^^^^^ File "C:\Program Files\Python\Python313\Lib\contextlib.py", line 162, in exit self.gen.throw(value) ~~~~~~~~~~~~~~^^^^^^^ File "C:\Users\marci\AppData\Roaming\Python\Python313\site-packages\pydicom\tag.py", line 33, in tag_in_exception raise type(exc)(msg) from exc ValueError: With tag (7FE0,0010) got exception: The (7FE0,0010) 'Pixel Data' element value hasn't been encapsulated as required for a compressed transfer syntax - see pydicom.encaps.encapsulate() for more information Traceback (most recent call last): File "C:\Users\marci\AppData\Roaming\Python\Python313\site-packages\pydicom\tag.py", line 29, in tag_in_exception yield File "C:\Users\marci\AppData\Roaming\Python\Python313\site-packages\pydicom\filewriter.py", line 826, in write_dataset write_data_element(fp, get_item(tag), dataset_encoding) ~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "C:\Users\marci\AppData\Roaming\Python\Python313\site-packages\pydicom\filewriter.py", line 686, in write_data_element raise ValueError( ...<3 lines>... ) ValueError: The (7FE0,0010) 'Pixel Data' element value hasn't been encapsulated as required for a compressed transfer syntax - see pydicom.encaps.encapsulate() for more information
Process finished with exit code 1
@scaramallion your code from the "second step", ie.
ds = dcmread(r"D:\praca\GUMED\dicom\mrxs\1_AORTA\AO_1_015_HC\3_0")
arr = ds.pixel_array # returns entire *Pixel Data* by default
arr = arr[100:200, 200:350]
# Automatically sets Pixel Data and the values of the associated elements
ds.set_pixel_data(arr, ds.PhotometricInterpretation, ds.BitsStored)
# Save the modified dataset
ds.save_as(r"C:\Users\marci\Desktop\tiles_dcm\3_0-crop.dcm")
seems to work, because it saves a file without an error, however when Im trying to open this file using openslide library with simple command: slide = open_slide(r"C:\Users\marci\Desktop\tiles_dcm\3_0-crop.dcm"), I get an error below, so something wrong is happening during processing this image...
Traceback (most recent call last): File "D:\praca\GUMED\serce\testy2.py", line 24, in slide = open_slide(r"C:\Users\marci\Desktop\tiles_dcm\1.dcm") File "C:\Users\marci\AppData\Roaming\Python\Python313\site-packages\openslide_init_.py", line 497, in open_slide return OpenSlide(filename) File "C:\Users\marci\AppData\Roaming\Python\Python313\site-packages\openslide_init_.py", line 207, in init self._osr = lowlevel.open(filename)
"C:\Users\marci\AppData\Roaming\Python\Python313\site-packages\openslide\lowlevel.py", line 265, in _check_open raise OpenSlideError(err) openslide.lowlevel.OpenSlideError: Reading C:\Users\marci\Desktop\tiles_dcm\1.dcm: Unsupported photometric interpretation YBR_FULL_422 for 1.2.840.10008.1.2.1
========================================================================
Share Improve this question edited Mar 27 at 14:10 Marcin Nowak asked Mar 25 at 14:15 Marcin NowakMarcin Nowak 94 bronze badges 9 | Show 4 more comments3 Answers
Reset to default 1Here is a small example that creates new DICOM file from DICOM file frames, where each next frame is scaled up and cropped:
import pydicom
import cv2
import numpy as np
ds = pydicom.dcmread(r"path_to_file.dcm")
shapes = len(ds.pixel_array.shape)
pixel_array = None
if shapes == 3: pixel_array = ds.pixel_array #Many frames
elif shapes == 2: pixel_array = np.array([ds.pixel_array]) #One frame
if pixel_array is not None:
MAX_SCALE = 2
steps, h, w = pixel_array.shape
h2, w2 = h//2, w//2
frames = []
for fr, img in enumerate(pixel_array):
sc = MAX_SCALE**(fr/steps)
nh = int(img.shape[0]*sc)
nw = int(img.shape[1]*sc)
top = nh//2 - h2
left = nw//2 - w2
frames.append(
cv2.resize(img, (nw, nh),
interpolation = cv2.INTER_AREA)
[top:top + h, left:left + w]) #Cropping
ds.set_pixel_data(
np.array(frames),
ds.PhotometricInterpretation, ds.BitsStored)
ds.save_as(r"path_to_file2.dcm")
You have to slice the pixel_array
attribute of the DICOM dataset, but set its PixelData
attribute. Also, you have to manually adjust attributes relevant to this cropping operation, i.e. the Rows
and Columns
:
import pydicom
ds = pydicom.dcmread(r"path_to_file")
ds.PixelData = ds.pixel_array[500:1500, 500:1500].tobytes()
ds.Rows = 1000
ds.Columns = 1000
ds.save_as(r"path_to_file2")
This assumes you have numpy
installed on your system. Please see https://pydicom.github.io/pydicom/1.1/working_with_pixel_data.html for more information.
For all the following I'm going to assume you're using v3.X - which you really, really should be as it contains many fixes and improvements over v2.X. I'm also not going to go into converting to PNG or some other image format as that adds an additional layer of complexity.
First step: Check the Transfer Syntax UID
>>> from pydicom import dcmread, examples
>>> ds = dcmread(examples.get_path("ct"))
>>> ds.file_meta.TransferSyntaxUID.name
'Explicit VR Little Endian'
>>> ds.file_meta.TransferSyntaxUID.is_compressed
False
>>> ds = dcmread(examples.get_path("jpeg2k"))
>>> ds.file_meta.TransferSyntaxUID.name
'JPEG 2000 Image Compression (Lossless Only)'
>>> ds.file_meta.TransferSyntaxUID.is_compressed
True
The Transfer Syntax UID tells you whether or not a dataset's Pixel Data has been compressed or not. In the above we see example datasets with both compressed and uncompressed Pixel Data.
This is important for two reasons:
- Compressed Pixel Data is encapulated - essentially each compressed frame is put in it's own container
- pydicom may need third-party package(s) installed to decompress such data
Looking at your exceptions, your original dataset uses a compressed transfer syntax, although I don't know which one. This page in the pydicom documentation will tell you which third-party packages are needed to decompress the Pixel Data (if any).
Second step: decompression, conversion and modification
Assuming you have the required third-party packages there are three main ways to convert compressed (and uncompressed) Pixel Data to a NumPy ndarray; the FileDataset.pixel_array property and the pixel_array() and iter_pixels() functions. For simplicity I'll use the FileDataset.pixel_array
property but I recommend you check the other two functions out since they're more flexible and potentially much more memory-efficient.
from pydicom import dcmread, examples
path = examples.get_path("ct")
ds = dcmread(path)
arr = ds.pixel_array # returns entire *Pixel Data* by default
arr = arr[100:200, 200:350]
# By default Dataset.pixel_array converts a YCbCr image to RGB
pi = ds.PhotometricInterpretation
if "YBR" in ds.PhotometricInterpretation:
pi = "RGB"
# Automatically sets Pixel Data and the values of the associated elements
ds.set_pixel_data(arr, pi, ds.BitsStored)
# Save the modified dataset
ds.save_as("modified.dcm")
I've also use the FileDataset.set_pixel_data() method to update the Pixel Data and associated group 0x0028 element values. This will also set the Transfer Syntax UID to Explicit VR Little Endian, which is an appropriate value for uncompressed Pixel Data, and update the SOP Instance UID (you can disable this by passing generate_instance_uid=False
).
You may need to adjust the array modification line depending on whether or not your dataset has multiple frames and/or is multi-sample: arr = arr[..., 100:200, 200:350, ...]
If the dataset uses one of the JPEG transfer syntaxes and the image was originally in a YCbCr colour space then you may need to explicitly pass "RGB"
as the photometric interpretation instead of ds.PhotometricInterpretation
.
At this point you can either save the dataset as-is, with uncompressed Pixel Data, or continue on to see how to compress it.
(Optional) Third step: compression
Optionally you may wish to compress your Pixel Data prior to saving. As of v3.0, pydicom supports compression using RLE, JPEG-LS and JPEG 2000 as long as you have the required third-party packages installed (listed here). If you're going to compress the Pixel Data then I recommend you think carefully about what the dataset will be used for, as downstream third-party DICOM applications can vary wildly in their support for compressed transfer syntaxes. I also recommend reading pydicom's corresponding encoding guide.
# Continuing on from above...
from pydicom.uid import JPEG2000Lossless
# Compress the Pixel Data losslessly and update the dataset in-place
dspress(JPEG2000Lossless)
# Save the modified and compressed dataset
ds.save_as("modified_compressed.dcm")
This will compress each frame of Pixel Data using the lossless JPEG 2000 compression method, encapsulate the resulting codestream and update the dataset in-place with appropriate values for the corresponding elements and the transfer syntax.
发布者:admin,转转请注明出处:http://www.yc00.com/questions/1744190300a4562403.html
dtype
when it loaded? Did it save? Where is the image? What is your OS platform? What versions of software are you using? – Mark Setchell Commented Mar 25 at 15:11