Mercurial > hg > orthanc
diff OrthancServer/Resources/Samples/Python/MicroCTDicomization.py @ 5240:c9e2c6d1cd62
added two Python samples: DicomizeImage.py and MicroCTDicomization.py
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Wed, 05 Apr 2023 17:54:21 +0200 |
parents | |
children | 48b8dae6dc77 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OrthancServer/Resources/Samples/Python/MicroCTDicomization.py Wed Apr 05 17:54:21 2023 +0200 @@ -0,0 +1,190 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- + +# Orthanc - A Lightweight, RESTful DICOM Store +# Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics +# Department, University Hospital of Liege, Belgium +# Copyright (C) 2017-2023 Osimis S.A., Belgium +# Copyright (C) 2021-2023 Sebastien Jodogne, ICTEAM UCLouvain, Belgium +# +# This program is free software: you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + + +# +# This sample Python script illustrates how to DICOM-ize a micro-CT +# acquisition, then to upload it to Orthanc. +# +# This sample assumes that the slices of the micro-CT are encoded as +# TIFF files, that are all stored inside the same ZIP archive. Make +# sure to adapt the parameters of the DICOM-ization below. +# +# The following command-line will install the required libraries: +# +# $ sudo pip3 install libtiff numpy pydicom requests +# + +import datetime +import io +import os +import tempfile +import zipfile + +import libtiff +import numpy +import pydicom +import pydicom._storage_sopclass_uids +import pydicom.datadict +import pydicom.tag +import requests +import requests.auth + + +######################################## +## Parameters for the DICOM-ization ## +######################################## + +ZIP = os.path.join(os.getenv('HOME'), 'Downloads', 'SpyII_mb.zip') + +URL = 'http://localhost:8042/' +USERNAME = 'orthanc' +PASSWORD = 'orthanc' + +VOXEL_WIDTH = 1 +VOXEL_HEIGHT = 1 +VOXEL_DEPTH = 1 + +TAGS = { + 'PatientID' : 'Test', + 'PatientName' : 'Hello^World', + 'StudyDate' : datetime.datetime.now().strftime('%Y%m%d'), + 'StudyTime' : datetime.datetime.now().strftime('%H%M%S'), + + 'AccessionNumber' : None, + 'AcquisitionNumber' : None, + 'KVP' : None, + 'Laterality' : None, + 'Manufacturer' : None, + 'PatientBirthDate' : '', + 'PatientPosition' : None, + 'PatientSex' : 'O', + 'PositionReferenceIndicator' : None, + 'ReferringPhysicianName' : None, + 'SeriesNumber' : 1, + 'StudyID' : 'Test', + } + + + +######################################## +## Application of the DICOM-ization ## +######################################## + +# Add the DICOM unique identifiers +for tag in [ 'StudyInstanceUID', + 'SeriesInstanceUID', + 'FrameOfReferenceUID' ]: + if not tag in TAGS: + TAGS[tag] = pydicom.uid.generate_uid() + + +def CreateDicomDataset(tif, sliceIndex): + image = tif.read_image().astype(numpy.uint16) + + meta = pydicom.Dataset() + meta.MediaStorageSOPClassUID = pydicom._storage_sopclass_uids.CTImageStorage + meta.MediaStorageSOPInstanceUID = pydicom.uid.generate_uid() + meta.TransferSyntaxUID = pydicom.uid.ImplicitVRLittleEndian + + dataset = pydicom.Dataset() + dataset.file_meta = meta + + dataset.is_little_endian = True + dataset.is_implicit_VR = True + dataset.SOPClassUID = meta.MediaStorageSOPClassUID + dataset.SOPInstanceUID = meta.MediaStorageSOPInstanceUID + dataset.Modality = 'CT' + + for (key, value) in TAGS.items(): + tag = pydicom.tag.Tag(key) + vr = pydicom.datadict.dictionary_VR(tag) + dataset.add_new(tag, vr, value) + + assert(image.dtype == numpy.uint16) + dataset.BitsStored = 16 + dataset.BitsAllocated = 16 + dataset.SamplesPerPixel = 1 + dataset.HighBit = 15 + + dataset.Rows = image.shape[0] + dataset.Columns = image.shape[1] + dataset.InstanceNumber = (sliceIndex + 1) + dataset.ImagePositionPatient = r'0\0\%f' % (-float(sliceIndex) * VOXEL_DEPTH) + dataset.ImageOrientationPatient = r'1\0\0\0\-1\0' + dataset.SliceThickness = VOXEL_DEPTH + dataset.ImageType = r'ORIGINAL\PRIMARY\AXIAL' + dataset.RescaleIntercept = '0' + dataset.RescaleSlope = '1' + dataset.PixelSpacing = r'%f\%f' % (VOXEL_HEIGHT, VOXEL_WIDTH) + dataset.PhotometricInterpretation = 'MONOCHROME2' + dataset.PixelRepresentation = 1 + + minValue = numpy.min(image) + maxValue = numpy.max(image) + dataset.WindowWidth = maxValue - minValue + dataset.WindowCenter = (minValue + maxValue) / 2.0 + + pydicom.dataset.validate_file_meta(dataset.file_meta, enforce_standard=True) + dataset.PixelData = image.tobytes() + + return dataset + + +# Create a temporary file, as libtiff is not able to read from BytesIO() +with tempfile.NamedTemporaryFile() as tmp: + sliceIndex = 0 + + # Loop over the files in the ZIP archive, after having sorted them + with zipfile.ZipFile(ZIP, 'r') as z: + for path in sorted(z.namelist()): + + # Ignore folders in the ZIP archive + info = z.getinfo(path) + if info.is_dir(): + continue + + # Extract the current file from the ZIP archive, into the temporary file + print('DICOM-izing: %s' % path) + data = z.read(path) + + with open(tmp.name, 'wb') as f: + f.write(data) + + # Try and decode the TIFF file + try: + tif = libtiff.TIFF.open(tmp.name) + except: + # Not a TIFF file, ignore + continue + + # Create a DICOM dataset from the TIFF + dataset = CreateDicomDataset(tif, sliceIndex) + b = io.BytesIO() + dataset.save_as(b, False) + + # Upload the DICOM dataset to Orthanc + r = requests.post('%s/instances' % URL, b.getvalue(), + auth = requests.auth.HTTPBasicAuth(USERNAME, PASSWORD)) + r.raise_for_status() + + sliceIndex += 1