#!/usr/bin/python3
import datetime
import hashlib
import random
import string
import sys
from pathlib import Path
from typing import Any, List, Optional, Union
import pydicom
from pydicom.dataset import Dataset, FileMetaDataset
from pydicom.uid import UID
# File meta info data elements
# def mandelbrot(m: int = 512, n: int = 256) -> Any:
# x = np.linspace(-2, 1, num=m).reshape((1, m))
# y = np.linspace(-1, 1, num=n).reshape((n, 1))
# C = np.tile(x, (n, 1)) + 1j * np.tile(y, (1, m))
# Z = np.zeros((n, m), dtype=complex)
# M = np.full((n, m), True, dtype=bool)
# for i in range(20):
# Z[M] = Z[M] * Z[M] + C[M]
# M[np.abs(Z) > 2] = False
# return M.astype(np.uint8)
# def julia(arg: complex, m: int = 256, n: int = 512) -> Any:
# x = np.linspace(-1, 1, num=m).reshape((1, m))
# y = np.linspace(-2, 2, num=n).reshape((n, 1))
# C = np.tile(x, (n, 1)) + 1j * np.tile(y, (1, m))
# # Z = np.zeros((n, m), dtype=complex)
# M = np.full((n, m), True, dtype=bool)
# K = np.full((n, m), 1, dtype=np.uint16)
# for i in range(20):
# C[M] = C[M] * C[M] + arg
# M[np.abs(C) > 2] = False
# # np.log(np.abs(C)+.1).astype('uint16'))
# np.putmask(K, np.abs(C) > 2, 0)
# # K[np.abs(C) > 2] = np.abs(C)
# return K
[docs]def nums(n: int, source: Optional[str] = None) -> str:
if not source:
return "".join(random.choice(string.digits) for i in range(n))
else:
return "".join([str(int(x)) for x in hashlib.md5(source.encode()).digest()])[0:n]
dt = datetime.datetime.now()
[docs]def generate_file(
study: str,
series: str,
slice_number: int,
acc: str,
study_uid: str,
desc: str,
image: Any,
orientation: List[List[float]] = [[1, 0, 0], [0, 1, 0]],
patient_name: Optional[str] = "Julia^Set",
patient_id: Optional[str] = "JULIATEST",
series_number: int = 1,
) -> Dataset:
# normal_vec = np.cross(orientation[0], orientation[1])
file_meta = FileMetaDataset()
file_meta.FileMetaInformationGroupLength = 200
file_meta.FileMetaInformationVersion = b"\x00\x01"
file_meta.MediaStorageSOPClassUID = UID("1.2.840.10008.5.1.4.1.1.4")
file_meta.MediaStorageSOPInstanceUID = pydicom.uid.generate_uid(prefix="1.2.276.0.7230010.3.1.4.")
file_meta.TransferSyntaxUID = UID("1.2.840.10008.1.2.1")
file_meta.ImplementationClassUID = UID("1.2.276.0.7230010.3.0.3.6.2")
file_meta.ImplementationVersionName = "OFFIS_DCMTK_362"
# Main data elements
ds = Dataset()
ds.preamble = 128 * b"\0"
ds.SOPClassUID = "1.2.840.10008.5.1.4.1.1.4"
ds.SOPInstanceUID = file_meta.MediaStorageSOPInstanceUID
# pydicom.uid.generate_uid(
# prefix='1.2.276.0.7230010.3.1.4.')
ds.StudyDate = dt.strftime("%Y%m%d")
ds.StudyTime = dt.strftime("%H%M")
ds.AccessionNumber = acc
ds.Modality = "MR"
ds.PatientName = patient_name
ds.PatientID = patient_id
ds.PatientBirthDate = "19700101"
ds.PatientSex = "O"
ds.StudyInstanceUID = study_uid
ds.SeriesInstanceUID = series
ds.FrameOfReferenceUID = series
ds.SeriesDescription = desc
ds.SeriesNumber = series_number
ds.InstanceNumber = str(slice_number + 1)
ds.StudyID = study
ds.ImageComments = "NOT FOR DIAGNOSTIC USE"
ds.PatientPosition = "HFS"
ds.ImageOrientationPatient = [*orientation[0], *orientation[1]]
ds.SpacingBetweenSlices = 7.5
# ds.ImagePositionPatient = list([0, 0, 0] + normal_vec * ds.SpacingBetweenSlices * slice_number)
ds.SliceLocation = 7.5 * slice_number + 170
ds.SamplesPerPixel = 1
ds.PhotometricInterpretation = "MONOCHROME2"
ds.SliceThickness = 5
ds.PixelData = b"\x00" * (100 * 100)
ds.NumberOfFrames = "1"
ds.Rows = 100
ds.Columns = 100
ds.PixelSpacing = [2, 2]
# ds.PixelAspectRatio = [1, 1]
ds.BitsAllocated = 16
ds.BitsStored = 16
ds.HighBit = 15
ds.PixelRepresentation = 0
ds.file_meta = file_meta
ds.is_implicit_VR = False
ds.is_little_endian = True
return ds
[docs]def generate_test_series(
pt: complex = -0.3 - 0.0j,
n: int = 10,
orientation: List[List[float]] = [[1, 0, 0], [0, 1, 0]],
accession: Optional[str] = None,
study_id: Optional[str] = None,
patient_name: Optional[str] = None,
patient_id: Optional[str] = None,
series_description: Optional[str] = None,
series_number: int = 1,
) -> List[Dataset]:
acc = accession or nums(7)
study = study_id or nums(8)
series = pydicom.uid.generate_uid(prefix="1.2.276.0.7230010.3.1.3.")
study_uid = pydicom.uid.generate_uid(prefix="1.2.276.0.7230010.3.1.2.", entropy_srcs=[study])
description = series_description or f"Julia set around {pt}"
if patient_name and not patient_id:
patient_id = nums(8, patient_name)
print(f"acc {acc}, study {study}, series {series}, description {description}")
datasets = []
for i in range(n):
# pt_at = pt + 0.1j * (i - n / 2)
# array = julia(pt_at)
# print(array)
datasets.append(
generate_file(
study,
series,
i,
acc,
study_uid,
description,
None,
# array,
orientation,
patient_name,
patient_id,
series_number,
)
)
return datasets
[docs]def generate_series(
k: Union[str, Path],
n: int,
orientation: List[List[float]] = [[1, 0, 0], [0, 1, 0]],
series_description: Optional[str] = "Julia set",
series_number=1,
) -> List[Path]:
f: Path = Path(k)
f.mkdir(parents=True, exist_ok=True)
datasets = generate_test_series(
0.3 - 0.0j, n, orientation, series_description=series_description, series_number=series_number
)
files = []
for i, d in enumerate(datasets):
filename = f / f"slice.{i}.dcm"
d.save_as(filename)
files.append(filename)
return files
[docs]def generate_several_protocols(
base_path: Union[str, Path], protocols=["PROT1", "PROT2", "PROT1_DL", "PROT2_DL"]
) -> List[Path]:
f: Path = Path(base_path)
f.mkdir(parents=True, exist_ok=True)
acc = nums(7)
study = nums(8)
patient_name = "Patient 1"
files = []
for n, p in enumerate(protocols):
datasets = generate_test_series(0.3 - 0.0j, 5, [[1, 0, 0], [0, 1, 0]], acc, study, patient_name, None, p, n)
for i, d in enumerate(datasets):
(f / p).mkdir(exist_ok=True)
filename = f / p / f"slice.{i}.dcm"
d.save_as(filename)
files.append(filename)
return files
if __name__ == "__main__":
generate_series(sys.argv[1], int(sys.argv[2]))
# print(generate_test("/vagrant/blinding_test"))
# result.save_as(f'/vagrant/test_series/slice.{i}.dcm')
# ds.save_as(r'../mandel_from_codify.dcm', write_like_original=False)