This question already has answers here:
Which DICOM UIDs should be replaced while overwriting pixel data in DICOM?
(1 answer)
Which DICOM tags other than UIDs should be replaced while overwriting pixel data in DICOM?
(2 answers)
Closed 1 year ago.
I have saved the Preprocessed DICOM in a folder total of 300 .dcm files, but when I open this DICOM folder path in RadiANT DICOM Viewer only One slice is displayed, here is my code is attached, Can you please help me how to display the whole scan. I think the main problem in Image Position and Slice location
import os
import numpy as np
import matplotlib.pyplot as plt
import pydicom
from pydicom.encaps import encapsulate
from pydicom.uid import JPEG2000
from imagecodecs import jpeg2k_encode
basepath="/home/hammad/AssementTask/DICOM/"
des_path="/home/hammad/AssementTask/g/"
file_list = [f.path for f in os.scandir(basepath)]
ds = pydicom.dcmread(file_list[0])
for i in range(imgs_after_resamp.shape[0]):
out = imgs_after_resamp[i,:,:]
#Need to copy() to meet jpeg2k_encodes C contiguous requirement
arr_crop = out.copy()
out = out.astype(np.int16)
# jpeg2k_encode to perform JPEG2000 compression
arr_jpeg2k = jpeg2k_encode(arr_crop)
# convert from bytearray to bytes before saving to PixelData
arr_jpeg2k = bytes(arr_jpeg2k)
ds.Rows = arr_crop.shape[0]
ds.Columns = arr_crop.shape[1]
ds[0x0018, 0x0050].value=np.round(spacing[0])
ds[0x0028, 0x0030].value=[np.round(spacing[1]),np.round(spacing[2])]
ds.InstanceNumber = i
ds.PixelData = encapsulate([arr_jpeg2k])
ds.save_as((des_path + str(i) + '.dcm'.format(i)))
I am not familiar with python. But some things seem to be obvious to me, so I will try an answer:
ds = pydicom.dcmread(file_list[0])
for i in range(imgs_after_resamp.shape[0]):
[...]
You are reading one file and use it as a template for all the resampled files. At minimum, you will have to create a new SOP Instance UID (0008,0018) for each file that you save. This is very likely the reason why the viewer only displays one image. The SOP Instance UID uniquely identifies the image. If all your resampled images have the same SOP Instance UID, this will tell the viewer that the same image is loaded over and over again. I.e. the newly loaded image is considered a duplicate.
And yes, to update the geometry information, further attributes need to be set to appropriate values. This partially depends on the type of image (SOP Class UID, 0008,0016). But here are the main suspects:
Image Position Patient (0020,0032)
Image Orientation Patient (0020,0037)
Slice Location (0020,1041)
Furthermore, make sure that the Frame Of Reference UID (0020,0052) is only kept from the original images if both image sets are using the same coordinate system (i.e. an Image Position Patient in your resampled stack must refer to the same origin as in the original images). In case of doubt, assign a new FOR-UID. Must be identical for all images in your stack.
Last point: This depends on the SOP Class even more, so I can just give you a general hint. The resampling is a derivation in terms of DICOM, so the Image Type (0008,0008), must be "DERIVED" in the second component. This unleashes a phletora of other requirements, depending on the SOP Class. Usually, you have to describe the type of derivation and reference the images from which you derived the resampled image.
Not everything of this will be necessary to have the images properly displayed in a viewer. But if you intend to write your implementation in product quality, you need to consider them. Look into the module table for your IOD in DICOM Part 3 as a starting point for updating the header information.
Related
I'm trying to write a script to simplify my everyday life in the lab. I operate one ThermoFisher / FEI scanning electron microscope and I save all my pictures in the TIFF format.
The microscope software is adding an extensive custom TiffTag (code 34682) containing all the microscope / image parameters.
In my script, I would like to open an image, perform some manipulations and then save the data in a new file, including the original FEI metadata. To do so, I would like to use a python script using the tifffile module.
I can open the image file and perform the needed manipulations without problems. Retrieving the FEI metadata from the input file is also working fine.
I was thinking to use the imwrite function to save the output file and using the extratags optional argument to transfer to the output file the original FEI metadata.
This is an extract of the tifffile documentation about the extratags:
extratags : sequence of tuples
Additional tags as [(code, dtype, count, value, writeonce)].
code : int
The TIFF tag Id.
dtype : int or str
Data type of items in 'value'. One of TIFF.DATATYPES.
count : int
Number of data values. Not used for string or bytes values.
value : sequence
'Count' values compatible with 'dtype'.
Bytes must contain count values of dtype packed as binary data.
writeonce : bool
If True, the tag is written to the first page of a series only.
Here is a snippet of my code.
my_extratags = [(input_tags['FEI_HELIOS'].code,
input_tags['FEI_HELIOS'].dtype,
input_tags['FEI_HELIOS'].count,
input_tags['FEI_HELIOS'].value, True)]
tifffile.imwrite('output.tif', data, extratags = my_extratags)
This code is not working and complaining that the value of the extra tag should be ASCII 7-bit encoded. This looks already very strange to me because I haven't touched the metadata and I am just copying it to the output file.
If I convert the metadata tag value in a string as below:
my_extratags = [(input_tags['FEI_HELIOS'].code,
input_tags['FEI_HELIOS'].dtype,
input_tags['FEI_HELIOS'].count,
str(input_tags['FEI_HELIOS'].value), True)]
tifffile.imwrite('output.tif', data, extratags = my_extratags)
the code is working, the image is saved, the metadata corresponding to 'FEI_HELIOS' is created but it is empty!
Can you help me in finding what I am doing wrongly?
I don't need to use tifffile, but I would prefer to use python rather than ImageJ because I have already several other python scripts and I would like to integrate this new one with the others.
Thanks a lot in advance!
toto
ps. I'm a frequent user of stackoverflow, but this is actually my first question!
In principle the approach is correct. However, tifffile parses the raw values of certain tags, including FEI_HELIOS, to dictionaries or other Python types. To get the raw tag value for rewriting, it needs to be read from file again. In these cases, use the internal TiffTag._astuple function to get an extratag compatible tuple of the tag, e.g.:
import tifffile
with tifffile.TiffFile('FEI_SEM.tif') as tif:
assert tif.is_fei
page = tif.pages[0]
image = page.asarray()
... # process image
with tifffile.TiffWriter('copy1.tif') as out:
out.write(
image,
photometric=page.photometric,
compression=page.compression,
planarconfig=page.planarconfig,
rowsperstrip=page.rowsperstrip,
resolution=(
page.tags['XResolution'].value,
page.tags['YResolution'].value,
page.tags['ResolutionUnit'].value,
),
extratags=[page.tags['FEI_HELIOS']._astuple()],
)
This approach does not preserve Exif metadata, which tifffile cannot write.
Another approach, since FEI files seem to be written uncompressed, is to directly memory map the image data in the file to a numpy array and manipulate that array:
import shutil
import tifffile
shutil.copyfile('FEI_SEM.tif', 'copy2.tif')
image = tifffile.memmap('copy2.tif')
... # process image
image.flush()
Finally, consider tifftools for rewriting TIFF files where tifffile is currently failing, e.g. Exif metadata.
builded a healthcare project on python flask framework.I need to duplicate the image or the report that is uploaded by patient and keep the original file away and only visible that duplicated report to show to any unauthorised access attackers.ie The duplicate image should be similar one but not same a little changes must have for the duplicated image. I think now u get the idea what im trying to
Do you mean to load an image file into python array, and create a new copy of that array?
If that's your demand, try using copy():
from PIL import Image
img = Image.open("example.png")
copy_img = img.copy()
And you can do whatever you want with copy_img, which is independent from the original one.
Edit: 2021/6/8 22:35
If you want to copy an image located in a directory, use shutils:
import shutil
upload_directory = "file/upload/upload.jpg"
save_directory = "file/copy/upload_copy.jpg"
shutil.copy(upload_directory, save_directory)
Then you have an copied image file located in your destination directory.
Please let me know it that works.
I am trying to read czi format images, But because they need a lot of memmory I tried reading them in memmap file.
Here is the code I used>
import czifile as czi
fileName = "Zimt3.czi"
# read file to binary
file = czi.CziFile(fileName)
imageArr = file.asarray(out="/media/my drive/Temp/temp.bin")
Now imageArr is a variable with dimensons of (9,3,29584,68084,1) in memmap. These are high resolution microscopic images from Carl Zeiss device.
Here is an screenshot of more specifications.
I think this means that imageArr contains 9 images with the dimention of (29584,68084,3)
But I cant extract this kind of numpy array to visualize as an image.
Can you please help me convert (9,3,29584,68084,1) in memmap to (29584,68084,3) images please.
It looks like a very large file. If you just want to visualize it, you can use slideio python package (http://slideio.com). It makes use of internal image pyramids. You can read the image partially with high resolution or the whole image with low resolution. The code below rescales the image so that the width of the delivered raster will be 500 pixels (the height is computed to keep the image size ratio).
import slideio
import matplotlib.pyplot as plt
slide = slideio.open_slidei(file_path="Zimt3.czi",driver_id="CZI")
scene = slide.get_scene(0)
block = scene.read_block(size=(500,0))
plt.imshow(scene.read_block())
Be aware that matplotlib can display images if they have 1 or 3 channels. A CZI file can have an arbitrary number of channels. In this case you have to select what channels you want to display:
block = scene.read_block(size=(500,0), channel_indices=[0,2,5])
Another problem with visualization can be if your file is a 3 or 4D image. In this case, slideio returns 3d or 4d numpy array. Matplotlib cannot display 3d or 4d images. You will need to look for a specific visualization package or select a z-slice and/or time-frame:
block = scene.read_block(size=(500,0), channel_indices=[0,2,5], slices=(0,1), frames=(1,2))
For more details see the package documentation.
You would think it's quite simple, but the following code doesn't work as I would expect:
from hashlib import md5
from PIL import Image
im = Image.open("/tmp/original.jpg")
im.save("/tmp/new.jpg", quality="keep")
original = Image.open("/tmp/original.jpg")
new = Image.open("/tmp/new.jpg")
assert md5(original.tobytes()).hexdigest() == md5(new.tobytes()).hexdigest()
Why is it that when I'm simply saving an image as a new file, and keeping the quality settings the same, that the image data isn't identical? What am I missing?
Update (Explanation):
My problem is that I have a Pillow JpegImage object being handed to my code as part of a pipeline and I don't have control over the step at which the file is saved to disk:
<magic> → <my code> → <magic that saves to disk>
All I want my code to do is add/update/replace (any of these) the EXIF data for the to-be-saved jpeg image. As this info doesn't appear to be editable on an image object, the only way that I can figure to do this is to save the image to a temporary place (like BytesIO), save it and the re-open it with Image.open() before passing it to the next function in the chain.
Please tell me that there's a smarter, more efficient way to do this?
I dont get why die image differ from each other after this 3 lines of code. In my opinion the images should be identical.
from PIL import Image
phone_img = Image.open("img2.png")
phone_img1 = Image.frombytes(phone_img.mode, phone_img.size, phone_img.tobytes())
phone_img1.save("img2_new.png","PNG")
img2.png: http://666kb.com/i/dk4ykapuzs4wc2e4g.png
img2_new.png: http://666kb.com/i/dk4ykz98cg97grxts.png
I'm not a big PIL/Pillow user, but:
You open your image with Image.open()
The returned object is of type Image
It holds more than the pure pixel-data (as you see by using .mode, .size)
You create a new image by interpreting the full object as pixel-data only!
The last part should probably something like frombytes(phone_img.mode, phone_img.size, phone_img.getdata())
Depending on the lib, one should take care of bit-mode too (8bit vs. 16bit for example)