diff --git a/Dockerfile b/Dockerfile index 4a5e75b..9f6e33c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -10,7 +10,7 @@ FROM alpine:3.20 RUN apk update && \ apk add py3-qt5 py3-numpy py3-pip py3-pillow ttf-freefont mesa-dri-gallium && \ rm -rf /var/cache/apk/* usr/lib/python3.12/EXTERNALLY-MANAGED &&\ - pip3 install pydicom pyqtgraph + pip3 install pydicom pyqtgraph pylibjpeg WORKDIR /dicombrowser diff --git a/dicombrowser/__init__.py b/dicombrowser/__init__.py index b0807ed..8679a45 100644 --- a/dicombrowser/__init__.py +++ b/dicombrowser/__init__.py @@ -17,6 +17,5 @@ # with this program (LICENSE.txt). If not, see - from ._version import __version__, __author__, __copyright__ from .dicombrowser import mainargv diff --git a/dicombrowser/_version.py b/dicombrowser/_version.py index ea681df..ffddf35 100644 --- a/dicombrowser/_version.py +++ b/dicombrowser/_version.py @@ -17,9 +17,8 @@ # with this program (LICENSE.txt). If not, see __appname__ = "DicomBrowser" -__version_info__ = (1, 5, 0) # global application version, major/minor/patch +__version_info__ = (1, 5, 1) # global application version, major/minor/patch __version__ = f"{__version_info__[0]}.{__version_info__[1]}.{__version_info__[2]}" __author__ = "Eric Kerfoot" -__author_email__="eric.kerfoot@kcl.ac.uk" +__author_email__ = "eric.kerfoot@kcl.ac.uk" __copyright__ = "Copyright (c) 2016-22 Eric Kerfoot, King's College London, all rights reserved. Licensed under the GPL (see LICENSE.txt)." - diff --git a/dicombrowser/dicom.py b/dicombrowser/dicom.py index e8add52..cc380da 100644 --- a/dicombrowser/dicom.py +++ b/dicombrowser/dicom.py @@ -21,6 +21,7 @@ from concurrent.futures import ProcessPoolExecutor, as_completed from io import BytesIO from glob import glob +from warnings import warn import numpy as np from pydicom import dicomio, datadict, errors @@ -65,6 +66,30 @@ FULL_NAME_MAP = {v: k for k, v in KEYWORD_NAME_MAP.items()} # maps full names to keywords +def get_2d_equivalent_image(img): + """Given an array `img` of some arbitrary dimensions, attempt to choose a valid 2D gray/RGB/RGBA image from it.""" + ndim = img.ndim + shape = img.shape + color_dim = ndim > 2 and shape[-1] in (1, 3, 4) + + if ndim <= 2 or (ndim == 3 and color_dim): # 0D array, 1D array, 2D grayscale or 2D RGB(A) + if ndim < 2: + warn(f"Image has unusual shape {shape}, attempting to visualise") + + return img + elif ndim == 3: # 3D grayscale + warn(f"Image is volume with shape {shape}, using mid-slice") + elif ndim == 4 and color_dim: + warn(f"Image is RGB(A) volume with shape {shape}, using mid-slice") + else: + warn(f"Image is unknown volume with shape {shape}, using mid-slices") + + # attempt to slice the volume in every dimension that's not height, width, or the channels + stop = 3 if color_dim else 2 # dimensions to not slice in, ie. (height,width) or (height,width,channels) + slices = [s // 2 for s in shape[:-stop]] + [slice(None)] * stop + return img[tuple(slices)] + + def get_scaled_image(dcm): """Return image data from `dcm` scaled using slope and intercept values.""" try: diff --git a/dicombrowser/dicombrowser.py b/dicombrowser/dicombrowser.py index ab871db..6e8fdfe 100755 --- a/dicombrowser/dicombrowser.py +++ b/dicombrowser/dicombrowser.py @@ -34,7 +34,7 @@ from ._version import __version__ from . import res -from .dicom import load_dicom_dir, load_dicom_zip, SERIES_LIST_COLUMNS, ATTR_TREE_COLUMNS +from .dicom import load_dicom_dir, load_dicom_zip, get_2d_equivalent_image, SERIES_LIST_COLUMNS, ATTR_TREE_COLUMNS from .models import AttrItemModel, SeriesTreeModel @@ -49,6 +49,7 @@ class LoadWorker(QtCore.QRunnable): """Loads Dicom data in a separate thread, updating the UI through the given signals.""" + def __init__(self, src, status_signal, update_signal): super().__init__() self.src = src @@ -231,8 +232,8 @@ def set_series_image(self, i, auto_range=False): if img is None: # if the image is None use the default "no image" object img = self.noimg - # elif len(img.shape)==3: # multi-channel or multi-dimensional image, use average of dimensions - # img=np.mean(img,axis=2) + + img = get_2d_equivalent_image(img) # get something renderable in 2D self.image_view.setImage(img.T, autoRange=auto_range, autoLevels=self.autoLevelsCheck.isChecked()) self._fill_attr_view() diff --git a/dicombrowser/models.py b/dicombrowser/models.py index b78d261..a037443 100644 --- a/dicombrowser/models.py +++ b/dicombrowser/models.py @@ -99,6 +99,7 @@ def fill_attrs(self, dcm, columns, regex=None, maxValueSize=256): class SeriesTreeModel(QtGui.QStandardItemModel): """Represents the tree of Dicom series organised under nodes for each source file/directory.""" + def __init__(self, columns, data={}, parent=None): super().__init__(parent) self.columns = columns