Update analyize_discovery.py
✅ New Features Graphical file selectors for both: Choosing the discovery output file Choosing the name and location of the analysis output file Output saved to a text file formatted for easy copy/paste into Excel Tab-separated values in the summary for compatibility with Excel columns Modality breakdown + exam statistics in one concise report 📝 How to Use It Save the code below as dicom_analysis_tool.py. Run it (python dicom_analysis_tool.py or double-click if .py is registered). Click the button to: Select the discovery results .txt file. Specify the output .txt destination. It will create a text summary that can be easily opened in Excel (tab-delimited).
This commit is contained in:
@@ -1,17 +1,9 @@
|
|||||||
import tkinter as tk
|
import tkinter as tk
|
||||||
from tkinter import filedialog, ttk
|
from tkinter import filedialog, simpledialog
|
||||||
import pandas as pd
|
import pandas as pd
|
||||||
import os
|
import os
|
||||||
|
|
||||||
def load_and_analyze_file():
|
def normalize_modalities(val):
|
||||||
file_path = filedialog.askopenfilename(title="Select DICOM Discovery Output File", filetypes=[("Text Files", "*.txt")])
|
|
||||||
if not file_path:
|
|
||||||
return
|
|
||||||
|
|
||||||
df = pd.read_csv(file_path, delimiter='|', dtype=str).fillna('')
|
|
||||||
|
|
||||||
# Normalize modalities to upper case and flatten list-like strings
|
|
||||||
def normalize_modalities(val):
|
|
||||||
if val.startswith("[") and val.endswith("]"):
|
if val.startswith("[") and val.endswith("]"):
|
||||||
try:
|
try:
|
||||||
return ", ".join(eval(val))
|
return ", ".join(eval(val))
|
||||||
@@ -19,9 +11,25 @@ def load_and_analyze_file():
|
|||||||
return val
|
return val
|
||||||
return val
|
return val
|
||||||
|
|
||||||
|
def analyze_and_save():
|
||||||
|
input_path = filedialog.askopenfilename(
|
||||||
|
title="Select DICOM Discovery Output File",
|
||||||
|
filetypes=[("Text Files", "*.txt")]
|
||||||
|
)
|
||||||
|
if not input_path:
|
||||||
|
return
|
||||||
|
|
||||||
|
output_path = filedialog.asksaveasfilename(
|
||||||
|
title="Save Analysis Output As",
|
||||||
|
defaultextension=".txt",
|
||||||
|
filetypes=[("Text Files", "*.txt")]
|
||||||
|
)
|
||||||
|
if not output_path:
|
||||||
|
return
|
||||||
|
|
||||||
|
df = pd.read_csv(input_path, delimiter='|', dtype=str).fillna('')
|
||||||
df['ModalitiesInStudy'] = df['ModalitiesInStudy'].apply(normalize_modalities)
|
df['ModalitiesInStudy'] = df['ModalitiesInStudy'].apply(normalize_modalities)
|
||||||
|
|
||||||
# Prepare metrics
|
|
||||||
stats = {
|
stats = {
|
||||||
"Distinct StudyInstanceUIDs": df['StudyInstanceUID'].nunique(),
|
"Distinct StudyInstanceUIDs": df['StudyInstanceUID'].nunique(),
|
||||||
"Distinct AccessionNumbers": df['AccessionNumber'].nunique(),
|
"Distinct AccessionNumbers": df['AccessionNumber'].nunique(),
|
||||||
@@ -38,9 +46,9 @@ def load_and_analyze_file():
|
|||||||
"*TEST* in PatientName": df['PatientName'].str.contains('TEST', case=False, na=False).sum()
|
"*TEST* in PatientName": df['PatientName'].str.contains('TEST', case=False, na=False).sum()
|
||||||
}
|
}
|
||||||
|
|
||||||
# Modality breakdown
|
|
||||||
modality_counts = {
|
modality_counts = {
|
||||||
"CR": 0, "DX": 0, "CT": 0, "MR": 0, "NM": 0, "US": 0, "XA": 0, "PR": 0, "SC": 0, "OT": 0, "Other": 0
|
"CR": 0, "DX": 0, "CT": 0, "MR": 0, "NM": 0, "US": 0,
|
||||||
|
"XA": 0, "PR": 0, "SC": 0, "OT": 0, "Other": 0
|
||||||
}
|
}
|
||||||
|
|
||||||
for val in df['ModalitiesInStudy']:
|
for val in df['ModalitiesInStudy']:
|
||||||
@@ -50,30 +58,26 @@ def load_and_analyze_file():
|
|||||||
else:
|
else:
|
||||||
modality_counts["Other"] += 1
|
modality_counts["Other"] += 1
|
||||||
|
|
||||||
# Display results
|
with open(output_path, 'w') as f:
|
||||||
output_window = tk.Toplevel(root)
|
f.write("DICOM Discovery Analysis Summary\n")
|
||||||
output_window.title("DICOM Discovery Analysis")
|
f.write("="*40 + "\n\n")
|
||||||
|
for key, value in stats.items():
|
||||||
row = 0
|
f.write(f"{key}\t{value}\n")
|
||||||
for key, val in stats.items():
|
f.write("\nModality Breakdown\n")
|
||||||
tk.Label(output_window, text=f"{key}:").grid(row=row, column=0, sticky='w', padx=5, pady=2)
|
f.write("-"*40 + "\n")
|
||||||
tk.Label(output_window, text=f"{val}").grid(row=row, column=1, sticky='w', padx=5, pady=2)
|
|
||||||
row += 1
|
|
||||||
|
|
||||||
tk.Label(output_window, text="Modality Breakdown:").grid(row=row, column=0, sticky='w', padx=5, pady=10)
|
|
||||||
row += 1
|
|
||||||
for modality, count in modality_counts.items():
|
for modality, count in modality_counts.items():
|
||||||
tk.Label(output_window, text=f"{modality}:").grid(row=row, column=0, sticky='w', padx=20, pady=2)
|
f.write(f"{modality}\t{count}\n")
|
||||||
tk.Label(output_window, text=f"{count}").grid(row=row, column=1, sticky='w', padx=5, pady=2)
|
|
||||||
row += 1
|
|
||||||
|
|
||||||
# GUI setup
|
tk.messagebox.showinfo("Analysis Complete", f"Analysis saved to:\n{output_path}")
|
||||||
|
|
||||||
|
# GUI
|
||||||
root = tk.Tk()
|
root = tk.Tk()
|
||||||
root.title("DICOM Discovery Analyzer")
|
root.title("DICOM Discovery Analyzer")
|
||||||
|
root.geometry("400x150")
|
||||||
|
|
||||||
frame = tk.Frame(root, padx=10, pady=10)
|
frame = tk.Frame(root, padx=10, pady=20)
|
||||||
frame.pack()
|
frame.pack(expand=True)
|
||||||
|
|
||||||
ttk.Button(frame, text="Select and Analyze Output File", command=load_and_analyze_file).pack()
|
tk.Button(frame, text="Select Input File and Analyze", command=analyze_and_save, width=30).pack(pady=10)
|
||||||
|
|
||||||
root.mainloop()
|
root.mainloop()
|
||||||
|
|||||||
Reference in New Issue
Block a user