# HG changeset patch # User Alain Mazy # Date 1711527425 -3600 # Node ID 0d8089ca81833d5985f99a4bd3e7609a97212630 # Parent e5e6841e13b158f8a92358d362b38f9f09878da4# Parent 63edb430f259b24e6a8cc9a5b246016ba32dd9ec merge diff -r e5e6841e13b1 -r 0d8089ca8183 Sphinx/source/plugins/python.rst --- a/Sphinx/source/plugins/python.rst Wed Mar 27 09:16:33 2024 +0100 +++ b/Sphinx/source/plugins/python.rst Wed Mar 27 09:17:05 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 e5e6841e13b1 -r 0d8089ca8183 Sphinx/source/plugins/python/mosaic.png Binary file Sphinx/source/plugins/python/mosaic.png has changed diff -r e5e6841e13b1 -r 0d8089ca8183 Sphinx/source/plugins/python/mosaic.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Sphinx/source/plugins/python/mosaic.py Wed Mar 27 09:17:05 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)