From edf4a992de5f82a6512d451a7c453c1bf273134b Mon Sep 17 00:00:00 2001 From: Ross Date: Mon, 14 Apr 2025 10:38:35 +0100 Subject: [PATCH] test fixing thumbnails --- helpers/pydicom_PIL.py | 88 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 84 insertions(+), 4 deletions(-) diff --git a/helpers/pydicom_PIL.py b/helpers/pydicom_PIL.py index ebf1b82a..e4beb8a5 100644 --- a/helpers/pydicom_PIL.py +++ b/helpers/pydicom_PIL.py @@ -25,7 +25,7 @@ have_PIL = True try: import PIL.Image, PIL.ImageOps - from pydicom.pixel_data_handlers.util import apply_voi_lut, apply_modality_lut + from pydicom.pixels import apply_voi_lut, apply_modality_lut, apply_windowing except ImportError: have_PIL = False @@ -36,6 +36,32 @@ try: except ImportError: have_numpy = False +def lin_stretch_img(img, low_prc, high_prc, do_ignore_minmax=True): + """ + Apply linear "stretch" - low_prc percentile goes to 0, + and high_prc percentile goes to 255. + The result is clipped to [0, 255] and converted to np.uint8 + + Additional feature: + When computing high and low percentiles, ignore the minimum and maximum intensities (assumed to be outliers). + """ + # For ignoring the outliers, replace them with the median value + if do_ignore_minmax: + tmp_img = img.copy() + med = np.median(img) # Compute median + tmp_img[img == img.min()] = med + tmp_img[img == img.max()] = med + else: + tmp_img = img + + lo, hi = np.percentile(tmp_img, (low_prc, high_prc)) # Example: 1% - Low percentile, 99% - High percentile + + if lo == hi: + return np.full(img.shape, 128, np.uint8) # Protection: return gray image if lo = hi. + + stretch_img = (img.astype(float) - lo) * (255/(hi-lo)) # Linear stretch: lo goes to 0, hi to 255. + stretch_img = stretch_img.clip(0, 255).astype(np.uint8) # Clip range to [0, 255] and convert to uint8 + return stretch_img def get_LUT_value(data, window, level): """Apply the RGB Look-Up Table for the given @@ -65,7 +91,7 @@ def get_PIL_image(dataset): # Apply modality lut - array = apply_modality_lut(dataset.pixel_array, dataset) + #array = apply_modality_lut(dataset.pixel_array, dataset) # voi lut doesn't seem to help #array = apply_voi_lut(array, dataset) @@ -89,14 +115,21 @@ def get_PIL_image(dataset): # PIL size = (width, height) size = (dataset.Columns, dataset.Rows) - array = apply_modality_lut(dataset.pixel_array, dataset) + array = dataset.pixel_array + + #array = apply_modality_lut(array, dataset) + + array = apply_voi_lut(array, dataset, index=0) + + #array = apply_windowing(array, dataset) + array = lin_stretch_img(array, 0.1, 99.9) # Apply "linear stretching" (lower percentile 0.1 goes to 0, and percentile 99.9 to 255). - array = apply_voi_lut(array, dataset) if dataset["PhotometricInterpretation"].value == "MONOCHROME1": # We need to invert array = np.invert(array) + return PIL.Image.fromarray(array).convert('L') pixel_data = array.tostring() # Recommended to specify all details # by http://www.pythonware.com/library/pil/handbook/image.htm @@ -116,6 +149,53 @@ def get_PIL_image(dataset): return im +def get_PIL_image2(dataset): + """Get Image object from Python Imaging Library(PIL)""" + if not have_PIL: + raise ImportError("Python Imaging Library is not available. " + "See http://www.pythonware.com/products/pil/ " + "to download and install") + + if ('PixelData' not in dataset): + raise TypeError("Cannot show image -- DICOM dataset does not have " + "pixel data") + # can only apply LUT if these window info exists + if ('WindowWidth' not in dataset) or ('WindowCenter' not in dataset): + bits = dataset.BitsAllocated + samples = dataset.SamplesPerPixel + if bits == 8 and samples == 1: + mode = "L" + elif bits == 8 and samples == 3: + mode = "RGB" + elif bits == 16: + # not sure about this -- PIL source says is 'experimental' + # and no documentation. Also, should bytes swap depending + # on endian of file and system?? + mode = "I;16" + else: + raise TypeError("Don't know PIL mode for %d BitsAllocated " + "and %d SamplesPerPixel" % (bits, samples)) + + # PIL size = (width, height) + size = (dataset.Columns, dataset.Rows) + + # Recommended to specify all details + # by http://www.pythonware.com/library/pil/handbook/image.htm + im = PIL.Image.frombuffer(mode, size, dataset.PixelData, + "raw", mode, 0, 1) + + else: + ew = dataset['WindowWidth'] + ec = dataset['WindowCenter'] + ww = int(ew.value[0] if ew.VM > 1 else ew.value) + wc = int(ec.value[0] if ec.VM > 1 else ec.value) + image = get_LUT_value(dataset.pixel_array, ww, wc) + # Convert mode to L since LUT has only 256 values: + # http://www.pythonware.com/library/pil/handbook/image.htm + im = PIL.Image.fromarray(image).convert('L') + + return im + def show_PIL(dataset): """Display an image using the Python Imaging Library (PIL)""" im = get_PIL_image(dataset)