Mercurial > hg > orthanc
view OrthancServer/Resources/Samples/Python/MicroCTDicomization.py @ 5296:5053a10da5a2
Fix orphan files remaining in storage when working with MaximumStorageSize
author | Alain Mazy <am@osimis.io> |
---|---|
date | Mon, 22 May 2023 15:00:01 +0200 |
parents | c9e2c6d1cd62 |
children | 48b8dae6dc77 |
line wrap: on
line source
#!/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