Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,15 @@
import pydicom
from check_axial_alignment import check_axial_alignment
from parse_vendor_specific_cbct_info import get_serie_ssm_data_from_gendex_dicom_files
from tqdm import tqdm


def select_axial_series(series, alignment_limit=0.9):

alignments = []
nr_images = []
for serie in data[selected_study].Series:
axial_alignment = check_axial_alignment(
serie.CompleteMetadata[-1], mode="calc_alignment"
)
axial_alignment = check_axial_alignment(serie.CompleteMetadata[-1], mode="calc_alignment")
alignments.append(np.round(axial_alignment, 2))
nr_images.append(len(serie.CompleteMetadata))

Expand Down Expand Up @@ -68,7 +67,7 @@ def find_scout_strip_indices(serie):
idx_keeps = []

for fp in serie.FilePaths:
tmp = pydicom.read_file(fp)
tmp = pydicom.dcmread(fp)
if "SCOUT" in tmp.ImageType:
idx_keeps.append(False)

Expand All @@ -78,14 +77,14 @@ def find_scout_strip_indices(serie):
return np.asarray(idx_keeps)


# folder
folder = Path(r"F:\Max\U922_mod")
# Enter pathlib path to dcm exports
folder = Path()

procedure_dicts = []

for pat in folder.iterdir():
# print(pat.name)
print(pat.name.split(" ")[0])

data = dit.import_dicom_from_folder(folder=pat)
studies = [study for study in data.keys()]

Expand All @@ -96,16 +95,11 @@ def find_scout_strip_indices(serie):

# remove all non CT series
data[selected_study].Series = [
item
for item in data[selected_study].Series
if isinstance(item, dit.dicom_handlers.ct.CtSeries)
item for item in data[selected_study].Series if isinstance(item, dit.dicom_handlers.ct.CtSeries)
]

# remove Sectra Reconstructions
data[selected_study].Series = [
item
for item in data[selected_study].Series
if item.SeriesDescription != "Sectra Reconstruction"
item for item in data[selected_study].Series if item.SeriesDescription != "Sectra Reconstruction"
]

# strip scouts from CT series
Expand All @@ -119,8 +113,7 @@ def find_scout_strip_indices(serie):

# get series info (for SSM stats for all axial stacks)
axial_series_info = [
get_serie_ssm_data_from_gendex_dicom_files(axial_series[i], alignments[i])
for i in range(len(axial_series))
get_serie_ssm_data_from_gendex_dicom_files(axial_series[i], alignments[i]) for i in range(len(axial_series))
]

# select only one
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": null,
"id": "1b2c113a",
"metadata": {},
"outputs": [],
"source": [
"import numpy as np\n",
"import matplotlib.pyplot as plt\n",
"import pandas as pd\n",
"from pathlib import Path\n",
"import pandas as pd"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "f9ad8838",
"metadata": {},
"outputs": [],
"source": [
"# enter pathlib path (dp) to cleaned notebook\n",
"dp = Path()"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "be6e2739",
"metadata": {},
"outputs": [],
"source": [
"data = pd.read_excel(dp)\n",
"pd.set_option(\"display.max_rows\", 20)\n",
"\n",
"#data[\"Datum\"] = pd.to_datetime(data[\"Datum\"], format=\"%Y%m%d\")\n",
"data[\"datetime\"] = pd.to_datetime(\n",
" data[\"Datum\"].astype(str) + \" \" + data[\"Tid\"].astype(str),\n",
" format=\"%Y%m%d %H:%M:%S\"\n",
")\n",
"cols = [\"datetime\"] + [c for c in data.columns if c != \"datetime\"]\n",
"data = data[cols]\n",
"data = data.sort_values(\"datetime\")\n",
"\n",
"\n",
"columns_to_drop = [\"Modalitet.1\", \"Efternamn\", \"Mellannamn\", \"Indikation\", \"Datum\", \"Tid\", \"Kommentar\", \"Användare\", \"Modalitetens serienummer\", \"Gravid\", \"Datum för föregående röntgenundersökning\", \"Extrafilter\", \"Grader\", \"Exp. Tid\", \"Röntgenrum\", \"Exp.läge\", \"Röntgenhuvud serienummer\", \"Röntgenrör serienummer\", \"Röntgenrörsmodell\"]\n",
"data = data.drop(columns=columns_to_drop)\n",
"\n",
"data[\"Födelsedatum\"] = pd.to_datetime(\n",
" data[\"Id\"]\n",
" .astype(str)\n",
" .str.slice(0, 8),\n",
" format=\"%Y%m%d\",\n",
" errors=\"coerce\"\n",
")\n",
"\n",
"data[\"Ålder\"] = (\n",
" (data[\"datetime\"] - data[\"Födelsedatum\"])\n",
" .dt.days // 365\n",
")\n",
"\n",
"mask = data[\"Kön\"] == \"O\"\n",
"\n",
"digit = pd.to_numeric(\n",
" data.loc[mask, \"Id\"].astype(str).str[-2],\n",
" errors=\"coerce\"\n",
")\n",
"data.loc[mask, \"Kön\"] = digit.mod(2).map({0: \"F\", 1: \"M\"})\n",
"\n",
"data = data.drop(columns=(\"Id\"))\n",
"\n",
"\n",
"data = data[data[\"Modalitet\"] == 'CT']\n",
"\n",
"pd.set_option(\"display.max_rows\", 20)\n",
"\n",
"data\n",
"\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "5bd7c22a",
"metadata": {},
"outputs": [],
"source": [
"pd.set_option(\"display.max_rows\", None)\n",
"\n",
"# Antal US\n",
"for sex in ['F', 'M']:\n",
" for age_range in ((16, 40), (41, 65), (66, 999), (0, 15)):\n",
"\n",
" a = data[\n",
" data[\"datetime\"].between(\n",
" \"2025-01-01\",\n",
" \"2025-12-31 23:59:59\"\n",
" ) &\n",
" (data[\"Modalitet\"] == \"CT\") &\n",
" (data[\"Kön\"] == sex) &\n",
" (data[\"Tänder\"].isin([\"Veraview X800\", \"3D Accuitomo 170\"])) &\n",
" (data[\"Ålder\"].between(age_range[0], age_range[1]))\n",
" ]\n",
" print(f'antal {sex} {age_range[0]}-{age_range[1]}: {a.__len__()}')\n",
"\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "8a29e15a",
"metadata": {},
"outputs": [],
"source": [
"# DAP\n",
"age_ranges = ((16, 999), (0, 15))\n",
"for sex in ('F', 'M'):\n",
" for age_range in age_ranges:\n",
" a = data[\n",
" data[\"datetime\"].between(\n",
" \"2025-01-01\",\n",
" \"2025-12-31 23:59:59\"\n",
" ) &\n",
" (data[\"Modalitet\"] == \"CT\") &\n",
" (data[\"Kön\"] == sex) &\n",
" (data[\"Tänder\"].isin([\"Veraview X800\", \"3D Accuitomo 170\"])) &\n",
" (data[\"Ålder\"].between(age_range[0], age_range[1]))\n",
" ]\n",
"\n",
" dap = np.array(a[\"DAP (mGycm2)\"]) * 1e-3 # DAP i Gycm2\n",
" dap_mean = np.mean(dap)\n",
" dap_median = np.median(dap)\n",
" dap_q1, dap_q3 = np.percentile(dap, [25, 75])\n",
"\n",
" print(f'DAP mean {sex} ({age_range}): {dap_mean}')\n",
" print(f'DAP median {sex} ({age_range}): {dap_median}')\n",
" print(f'DAP Q1 {sex} ({age_range}): {dap_q1}')\n",
" print(f'DAP Q3 {sex} ({age_range}): {dap_q3}')\n",
"\n",
" fig, ax = plt.subplots()\n",
" ax.set_title(f'sex: {sex}, age range: {age_range}')\n",
" ax.plot(dap, '.', label=f'DAP')\n",
" ax.plot((0, a.__len__()), 2*[dap_mean], label='DAP mean')\n",
" ax.plot((0, a.__len__()), 2*[dap_median], label='DAP median')\n",
" ax.plot((0, a.__len__()), 2*[dap_q1], label='DAP Q1')\n",
" ax.plot((0, a.__len__()), 2*[dap_q3], label='DAP Q3')\n",
" ax.legend()\n"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "venv",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.14.2"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
Original file line number Diff line number Diff line change
@@ -1,27 +1,28 @@
import numpy as np
from datetime import datetime


def get_serie_ssm_data_from_morita_dicom_files(serie, alignment, age_lim=16):
res = dict()

common_data = serie.CompleteMetadata[0]

# patient birth date yyyymmdd
birth_date = datetime.strptime(str(common_data.PatientBirthDate), '%Y%m%d')
study_date = datetime.strptime(str(common_data.StudyDate), '%Y%m%d')
birth_date = datetime.strptime(str(common_data.PatientBirthDate), "%Y%m%d")
study_date = datetime.strptime(str(common_data.StudyDate), "%Y%m%d")

age_at_study = study_date - birth_date
age_at_study_years = int(age_at_study.days/(365))
age_at_study_years = int(age_at_study.days / (365))
res["name"] = str(common_data.PatientName)
res["birth_date"] = str(common_data.PatientBirthDate)
res["pat_age"] = age_at_study_years
res["in_correct_age_limit"] = True if age_at_study_years >= age_lim else False
res["in_correct_age_limit"] = True if age_at_study_years >= age_lim else False
res["sex"] = common_data.PatientSex
res["study_description"] = common_data.StudyDescription
res["axial_alignment_percentage"] = np.round(100 * alignment, 2)

if [0x0018, 0x115e] in common_data:
#X-Ray dose, measured in dGy*cm*cm
if [0x0018, 0x115E] in common_data:
# X-Ray dose, measured in dGy*cm*cm
res["DAP"] = float(common_data.ImageAndFluoroscopyAreaDoseProduct)

if [0x0020, 0x4000] in common_data:
Expand All @@ -31,17 +32,17 @@ def get_serie_ssm_data_from_morita_dicom_files(serie, alignment, age_lim=16):
# radius in mm
res["stack_radius"] = float(tmp2[1])

# get instance numbers in stack
# get instance numbers in stack
instance_nrs = [int(image.InstanceNumber) for image in serie.CompleteMetadata]
for image in serie.CompleteMetadata:

for image in serie.CompleteMetadata:
instance_nr = int(image.InstanceNumber)
if instance_nr == min(instance_nrs):
pos_start = image.ImagePositionPatient

if instance_nr == max(instance_nrs):
pos_stop = image.ImagePositionPatient

# stack height im mm
res["stack_height"] = abs(float(pos_start[2]) - float(pos_stop[2]))

Expand All @@ -50,42 +51,46 @@ def get_serie_ssm_data_from_morita_dicom_files(serie, alignment, age_lim=16):

def get_serie_ssm_data_from_gendex_dicom_files(serie, alignment):
res = dict()

common_data = serie.CompleteMetadata[0]

private_tag = str(common_data[0x000d, 0x1000].value)

private_tag = str(common_data[0x000D, 0x1000].value)
split = private_tag.split("Attribute name=")

for i in range(len(split)):
if "fov_width" in split[i]:
fov_width = float(split[i].split(">")[1].split("<")[0])
if "fov_height" in split[i]:
fov_height = float(split[i].split(">")[1].split("<")[0])
if "dose" in split[i]:
dose = float(split[i].split(">")[1].split("<")[0])

# patient birth date yyyymmdd
res["birth_date"] = str(common_data.PatientBirthDate)
#DAP in mGycm2
res["dose"] = dose
birth_date = datetime.strptime(str(common_data.PatientBirthDate), "%Y%m%d")
study_date = datetime.strptime(str(common_data.StudyDate), "%Y%m%d")

age_at_study = study_date - birth_date
age_at_study_years = int(age_at_study.days / (365))
res["pat_age"] = age_at_study_years
# DAP from mGycm2 to Gymc2
res["dose"] = dose * 1e-3
res["fov_width"] = fov_width
res["fov_height"] = fov_height
res["sex"] = common_data.PatientSex
res["study_description"] = common_data.StudyDescription
res["axial_alignment_percentage"] = np.round(100 * alignment, 2)
# get instance numbers in stack

# get instance numbers in stack
instance_nrs = [int(image.InstanceNumber) for image in serie.CompleteMetadata]
for image in serie.CompleteMetadata:

for image in serie.CompleteMetadata:
instance_nr = int(image.InstanceNumber)
if instance_nr == min(instance_nrs):
pos_start = image.ImagePositionPatient

if instance_nr == max(instance_nrs):
pos_stop = image.ImagePositionPatient

# stack height im mm
res["stack_height"] = abs(float(pos_start[2]) - float(pos_stop[2]))

Expand Down