207 lines
8.2 KiB
Python
207 lines
8.2 KiB
Python
import base64
|
|
import hashlib
|
|
import mimetypes
|
|
|
|
from io import BytesIO
|
|
|
|
from PIL import Image, ImageFile
|
|
|
|
import pydicom
|
|
import sys
|
|
import glob
|
|
|
|
import itertools
|
|
|
|
try:
|
|
from .pydicom_PIL import get_PIL_image
|
|
except:
|
|
from pydicom_PIL import get_PIL_image
|
|
|
|
def image_as_base64(image_file):
|
|
"""
|
|
:param `image_file` for the complete path of image.
|
|
:param `format` is format for image, eg: `png` or `jpg`.
|
|
"""
|
|
# if not os.path.isfile(image_file):
|
|
# return None
|
|
|
|
try:
|
|
encoded_string = ""
|
|
# with open(image_file, 'rb') as img_f:
|
|
# encoded_string = base64.b64encode(img_f.read())
|
|
encoded_string = base64.b64encode(image_file.file.read())
|
|
mimetype, enc = mimetypes.guess_type(image_file.path)
|
|
# Treat unknown files as dicom
|
|
if None == mimetype:
|
|
return "data:application/dicom;base64,{}".format(encoded_string.decode("utf-8"))
|
|
if "dicom" in mimetype or "octet-stream" in mimetype:
|
|
return "data:{};base64,{}".format(mimetype, encoded_string.decode("utf-8"))
|
|
return "data:image/{};base64,{}".format(mimetype, encoded_string.decode("utf-8"))
|
|
except FileNotFoundError:
|
|
return "data:image/png;base64,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"
|
|
|
|
|
|
|
|
|
|
def pil_dicom_image(source, exif_orientation=True, **options):
|
|
"""
|
|
Try to open the source file directly using PIL, ignoring any errors.
|
|
exif_orientation
|
|
If EXIF orientation data is present, perform any required reorientation
|
|
before passing the data along the processing pipeline.
|
|
"""
|
|
# Use a BytesIO wrapper because if the source is an incomplete file like
|
|
# object, PIL may have problems with it. For example, some image types
|
|
# require tell and seek methods that are not present on all storage
|
|
# File objects.
|
|
#if not source:
|
|
# return
|
|
#source = BytesIO(source.read())
|
|
|
|
# open file with pydicom
|
|
ds = pydicom.read_file(source)
|
|
|
|
# return the image file
|
|
return get_PIL_image(ds)
|
|
|
|
#with Image.open(source) as image:
|
|
# # Fully load the image now to catch any problems with the image contents.
|
|
# try:
|
|
# ImageFile.LOAD_TRUNCATED_IMAGES = True
|
|
# image.load()
|
|
# finally:
|
|
# ImageFile.LOAD_TRUNCATED_IMAGES = False
|
|
|
|
#if exif_orientation:
|
|
# image = utils.exif_orientation(image)
|
|
|
|
|
|
|
|
def show_PIL(dataset):
|
|
"""Display an image using the Python Imaging Library (PIL)"""
|
|
im = get_PIL_image(dataset)
|
|
im.show()
|
|
|
|
|
|
|
|
|
|
def get_dicom_order(path):
|
|
# load the DICOM files
|
|
files = []
|
|
|
|
for fname in glob.glob(path, recursive=False):
|
|
print("loading: {}".format(fname))
|
|
files.append((fname, pydicom.dcmread(fname)))
|
|
|
|
print("file count: {}".format(len(files)))
|
|
|
|
# skip files with no SliceLocation (eg scout views)
|
|
slices = []
|
|
map = {}
|
|
skipcount = 0
|
|
for fname, f in files:
|
|
print(dir(f))
|
|
print(fname)
|
|
if hasattr(f, 'SliceLocation'):
|
|
slices.append(f)
|
|
map[f.SliceLocation] = fname
|
|
else:
|
|
skipcount = skipcount + 1
|
|
|
|
print("skipped, no SliceLocation: {}".format(skipcount))
|
|
|
|
# ensure they are in the correct order
|
|
slices = sorted(slices, key=lambda s: s.SliceLocation)
|
|
|
|
#print(slices)
|
|
for i in slices:
|
|
print(map[i.SliceLocation])
|
|
|
|
#get_dicom_order("/home/ross/Downloads/DICOM HEAD/STD4/SER1/*.dcm")
|
|
|
|
def pretty_print_dicom(dataset, indent=0):
|
|
l = print_dicom(dataset, indent)
|
|
print(l)
|
|
return "".join(l)
|
|
|
|
|
|
def print_dicom(dataset, indent=0):
|
|
"""Go through all items in the dataset and print them with custom format
|
|
|
|
Modelled after Dataset._pretty_str()
|
|
"""
|
|
dont_print = ['Pixel Data', 'File Meta Information Version']
|
|
|
|
indent_string = "---" * indent
|
|
next_indent_string = "---" * (indent + 1)
|
|
|
|
l = []
|
|
|
|
for data_element in dataset:
|
|
if data_element.VR == "SQ": # a sequence
|
|
l.append(f"{indent_string}{data_element.name}")
|
|
for sequence_item in data_element.value:
|
|
l.append(print_dicom(sequence_item, indent + 1))
|
|
l.append(next_indent_string + "---------")
|
|
else:
|
|
if data_element.name in dont_print:
|
|
l.append("""item not printed -- in the "don't print" list""")
|
|
else:
|
|
repr_value = repr(data_element.value)
|
|
if len(repr_value) > 50:
|
|
repr_value = repr_value[:50] + "..."
|
|
l.append("{0:s} {1:s} = {2:s}".format(indent_string,
|
|
data_element.name,
|
|
repr_value))
|
|
return "\n<br/>".join(l)
|
|
|
|
def get_image_dicom_hash(img) -> (str, bool):
|
|
dataset = pydicom.dcmread(img)
|
|
md5 = hashlib.md5()
|
|
first = True
|
|
for i in dataset.pixel_array.astype(str).flatten():
|
|
if first:
|
|
first = False
|
|
md5.update(f"{i}".encode())
|
|
else:
|
|
md5.update(f",{i}".encode())
|
|
|
|
hash = md5.hexdigest()
|
|
return hash
|
|
|
|
def get_image_hash(img) -> (str, bool):
|
|
is_dicom = False
|
|
# Try and read the file as a dicom
|
|
hash = "1234"
|
|
try:
|
|
# and generate a hash from the pixel data
|
|
# TODO: improve?
|
|
dataset = pydicom.dcmread(img)
|
|
# flatten = dataset.pixel_array.astype(str).flatten()
|
|
# print("flatteded")
|
|
# pre_join = ",".join(flatten)
|
|
# print(pre_join)
|
|
# hash = hashlib.md5(pre_join.encode()).hexdigest()
|
|
# ----
|
|
md5 = hashlib.md5()
|
|
first = True
|
|
for i in dataset.pixel_array.astype(str).flatten():
|
|
if first:
|
|
first = False
|
|
md5.update(f"{i}".encode())
|
|
else:
|
|
md5.update(f",{i}".encode())
|
|
|
|
hash = md5.hexdigest()
|
|
is_dicom = True
|
|
# ----
|
|
|
|
except pydicom.errors.InvalidDicomError as e:
|
|
try: # This is horrible (but needed for current unit tests)
|
|
# (we use a temporary file that breaks here)
|
|
img.file.open()
|
|
hash = hashlib.md5(img.read()).hexdigest()
|
|
except AttributeError:
|
|
return hash, False
|
|
|
|
return hash, is_dicom |