# HG changeset patch # User Sebastien Jodogne # Date 1711443004 -3600 # Node ID 63edb430f259b24e6a8cc9a5b246016ba32dd9ec # Parent bb92aeb7dbde0fc486f036bfe6cb458d0238c4d4 mosaic diff -r bb92aeb7dbde -r 63edb430f259 Sphinx/source/plugins/python.rst --- a/Sphinx/source/plugins/python.rst Mon Mar 25 18:33:57 2024 +0100 +++ b/Sphinx/source/plugins/python.rst Tue Mar 26 09:50:04 2024 +0100 @@ -397,6 +397,8 @@ :language: python +.. _python-pil-thumbnail: + Rendering a thumbnail using PIL/Pillow ...................................... @@ -899,6 +901,25 @@ the :ref:`Orthanc Explorer 2 ` interface. +.. _python_mosaic: + +Generating a mosaic for a DICOM series +...................................... + +Thanks to the fact that Python plugins have access to :ref:`PIL/Pillow +`, it is quite easy to generate a mosaic from a +DICOM series: + +.. image:: python/mosaic.png + :align: center + :width: 512 + +Here is the source code: + +.. literalinclude:: python/mosaic.py + :language: python + + Performance and concurrency --------------------------- diff -r bb92aeb7dbde -r 63edb430f259 Sphinx/source/plugins/python/mosaic.png Binary file Sphinx/source/plugins/python/mosaic.png has changed diff -r bb92aeb7dbde -r 63edb430f259 Sphinx/source/plugins/python/mosaic.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Sphinx/source/plugins/python/mosaic.py Tue Mar 26 09:50:04 2024 +0100 @@ -0,0 +1,43 @@ +import PIL.Image +import io +import json +import math +import orthanc + +def generate_mosaic(output, uri, **request): + # Sort the slices of the series, using the REST API of Orthanc + seriesId = request['groups'][0] + slices = json.loads(orthanc.RestApiGet('/series/%s/ordered-slices' % seriesId)) ['Slices'] + + # Retrieve the first slice of the mosaic + firstSliceBytes = orthanc.RestApiGet(slices[0] + '/preview') + firstSliceDecoded = PIL.Image.open(io.BytesIO(firstSliceBytes)) + + # Compute the size of the mosaic + sliceWidth, sliceHeight = firstSliceDecoded.size + side = math.ceil(math.sqrt(len(slices))) + + # Create a PIL image to store the mosaic + image = PIL.Image.new(mode = 'L', size = (side * sliceWidth, side * sliceHeight)) + + # Loop over the instances of the series to populate the mosaic + x = 0 + y = 0 + for i in range(len(slices)): + sliceBytes = orthanc.RestApiGet(slices[i] + '/preview') + sliceDecoded = PIL.Image.open(io.BytesIO(sliceBytes)) + + image.paste(sliceDecoded, (x * sliceWidth, y * sliceHeight)) + + x += 1 + if x == side: + x = 0 + y += 1 + + # Answer with the mosaic encoded as a PNG image + with io.BytesIO() as png: + image.save(png, format = 'PNG') + png.seek(0) + output.AnswerBuffer(png.read(), 'image/png') + +orthanc.RegisterRestCallback('/series/(.*)/mosaic', generate_mosaic)