| Finding | Assessment |
|---|---|
| Acute ischemic stroke | No evidence on DWI |
| Mass lesion / tumor | Not identified |
| Hemorrhage | Not visible (T2*/GRE sequence not shown) |
| White matter changes | Mild periventricular hyperintensities (small vessel disease) |
| Ventricular system | Normal size and symmetry |
| Midline shift | None |
Important disclaimer: This is an AI interpretation of a photograph of a printed MRI film - not a formal radiological report. A definitive diagnosis requires a qualified radiologist reviewing original DICOM images alongside the patient's clinical history. Sequences like T1, T2, and post-contrast (gadolinium) images are not shown here and may provide additional information.
Explain changes where and where with pictures
periventricular white matter hyperintensities FLAIR MRI leukoaraiosis location diagram small vessel disease


| Location in Brain | FLAIR (rows 1-3) | DWI (rows 4-6) |
|---|---|---|
| Periventricular white matter | Small bright patches = chronic WMH | No bright signal = NOT an acute stroke |
| Deep white matter | Mild scattered hyperintensities | Normal/dark = no restriction |
| Brainstem / cerebellum | Structurally normal | Normal |
| Lateral ventricles | Mildly prominent; dark CSF = normal suppression | Normal |
| Cortex | Mild sulcal widening (mild atrophy) | Normal |
| Risk Factor | Effect |
|---|---|
| Hypertension | Thickens/stiffens small arterial walls |
| Diabetes | Damages endothelial lining of small vessels |
| Age | Arteries naturally stiffen over time |
| Smoking | Promotes atherosclerosis in small vessels |
This MRI analysis is educational. A formal radiological report from a qualified radiologist reviewing the original DICOM files remains essential for clinical decisions.
Create a summary PDF with labeled MRI diagrams explaining WMH locations
mkdir -p /tmp/workspace/wmh-pdf/images && cd /tmp/workspace/wmh-pdf/images && curl -s -o svd_diagram.jpg "https://prod-images-static.radiopaedia.org/images/65745315/082337e05e6ac11db3fe272cdf455711bcca4a12f65bae8eef3336a6156c4b9f_gallery.jpeg" && curl -s -o wmh_progression.jpg "https://cdn.ncbi.nlm.nih.gov/pmc/blobs/cb49/4599520/9722c1d7f2db/jah30004-e001140-f3.jpg" && curl -s -o patient_mri.jpg "https://d361swluvexzqs.cloudfront.net/chats-attachments/photo-1784127210054-1784127210698-04ouvh.jpg" && ls -lh
~/wmh-pdf/build_pdf.py
import os
from reportlab.lib.pagesizes import A4
from reportlab.lib import colors
from reportlab.lib.units import cm, mm
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.enums import TA_LEFT, TA_CENTER, TA_JUSTIFY
from reportlab.platypus import (
SimpleDocTemplate, Paragraph, Spacer, Image, Table, TableStyle,
HRFlowable, PageBreak, KeepTogether
)
from reportlab.graphics.shapes import Drawing, Rect, String, Circle, Line, Ellipse, Arrow
from reportlab.graphics import renderPDF
from reportlab.graphics.shapes import Group
from PIL import Image as PILImage
import io
BASE = "/tmp/workspace/wmh-pdf"
OUT = os.path.join(BASE, "WMH_Brain_MRI_Summary.pdf")
# ── Colour palette ─────────────────────────────────────────────────────────
NAVY = colors.HexColor("#1a2a4a")
BLUE = colors.HexColor("#2e5fa3")
LIGHTBLUE = colors.HexColor("#d6e4f7")
TEAL = colors.HexColor("#1d7874")
ORANGE = colors.HexColor("#e07b39")
YELLOW = colors.HexColor("#f5c842")
RED = colors.HexColor("#c0392b")
WHITE = colors.white
GRAY = colors.HexColor("#666666")
LIGHTGRAY = colors.HexColor("#f2f4f7")
DARKGRAY = colors.HexColor("#333333")
W, H = A4 # 595.27 x 841.89
# ── Styles ──────────────────────────────────────────────────────────────────
styles = getSampleStyleSheet()
def style(name, **kw):
s = ParagraphStyle(name, **kw)
return s
H1 = style("H1", fontSize=26, fontName="Helvetica-Bold", textColor=NAVY,
spaceAfter=6, spaceBefore=4, alignment=TA_CENTER)
H2 = style("H2", fontSize=16, fontName="Helvetica-Bold", textColor=BLUE,
spaceAfter=4, spaceBefore=12)
H3 = style("H3", fontSize=12, fontName="Helvetica-Bold", textColor=NAVY,
spaceAfter=3, spaceBefore=8)
BODY = style("BODY", fontSize=10, fontName="Helvetica", textColor=DARKGRAY,
spaceAfter=4, leading=15, alignment=TA_JUSTIFY)
CAPTION = style("CAPTION", fontSize=8.5, fontName="Helvetica-Oblique",
textColor=GRAY, spaceAfter=6, alignment=TA_CENTER)
BULLET = style("BULLET", fontSize=10, fontName="Helvetica", textColor=DARKGRAY,
spaceAfter=3, leading=15, leftIndent=14, bulletIndent=4)
LABEL_ST = style("LABEL", fontSize=8, fontName="Helvetica-Bold",
textColor=WHITE, alignment=TA_CENTER)
DISCLAIMER = style("DISCLAIMER", fontSize=8, fontName="Helvetica-Oblique",
textColor=GRAY, alignment=TA_CENTER, spaceAfter=4)
SUBTITLE = style("SUBTITLE", fontSize=12, fontName="Helvetica",
textColor=BLUE, alignment=TA_CENTER, spaceAfter=6)
FINDING_TITLE = style("FT", fontSize=11, fontName="Helvetica-Bold",
textColor=NAVY, spaceAfter=2)
# ── Helper: fit image keeping aspect ratio ──────────────────────────────────
def fit_image(path, max_w, max_h):
with PILImage.open(path) as im:
iw, ih = im.size
ratio = min(max_w / iw, max_h / ih)
return Image(path, width=iw * ratio, height=ih * ratio)
# ── Helper: coloured badge ──────────────────────────────────────────────────
def badge(text, bg=BLUE, fg=WHITE, w=120, h=18):
d = Drawing(w, h)
d.add(Rect(0, 0, w, h, rx=4, ry=4, fillColor=bg, strokeColor=None))
d.add(String(w/2, 4, text, fontName="Helvetica-Bold", fontSize=9,
fillColor=fg, textAnchor="middle"))
return d
# ── Annotated brain cross-section drawing ───────────────────────────────────
def brain_cross_section_drawing(dw=480, dh=320):
"""Draw a schematic axial brain cross-section with WMH labels."""
d = Drawing(dw, dh)
cx, cy = dw / 2, dh / 2 + 10
# skull outline
d.add(Ellipse(cx, cy, 200, 160,
fillColor=colors.HexColor("#e8e0d0"),
strokeColor=colors.HexColor("#9e8c70"), strokeWidth=3))
# brain parenchyma
d.add(Ellipse(cx, cy, 185, 148,
fillColor=colors.HexColor("#d8cfc0"),
strokeColor=colors.HexColor("#b0a090"), strokeWidth=1.5))
# gray matter rim
d.add(Ellipse(cx, cy, 185, 148,
fillColor=None,
strokeColor=colors.HexColor("#c0a080"), strokeWidth=8))
# white matter zone (inner)
d.add(Ellipse(cx, cy, 148, 118,
fillColor=colors.HexColor("#ede8df"),
strokeColor=colors.HexColor("#c8bfb0"), strokeWidth=1))
# lateral ventricles (left)
d.add(Ellipse(cx - 42, cy + 8, 28, 14,
fillColor=colors.HexColor("#7fb8d4"),
strokeColor=colors.HexColor("#5a9ab8"), strokeWidth=1.5))
# lateral ventricles (right)
d.add(Ellipse(cx + 42, cy + 8, 28, 14,
fillColor=colors.HexColor("#7fb8d4"),
strokeColor=colors.HexColor("#5a9ab8"), strokeWidth=1.5))
# third ventricle
d.add(Rect(cx - 5, cy - 5, 10, 22,
fillColor=colors.HexColor("#7fb8d4"),
strokeColor=colors.HexColor("#5a9ab8"), strokeWidth=1))
# ── WMH patches (periventricular) ──────────────────────────────────────
wmh_positions = [
(cx - 70, cy + 18, 18, 10), # left periventricular anterior
(cx - 72, cy - 10, 14, 9), # left periventricular posterior
(cx + 52, cy + 18, 18, 10), # right periventricular anterior
(cx + 55, cy - 8, 14, 9), # right periventricular posterior
]
for (ex, ey, ew, eh) in wmh_positions:
d.add(Ellipse(ex, ey, ew, eh,
fillColor=colors.HexColor("#fffacd"),
strokeColor=YELLOW, strokeWidth=1.5))
# ── Deep white matter WMH ─────────────────────────────────────────────
deep_wmh = [
(cx - 110, cy + 25, 12, 7),
(cx + 100, cy - 20, 10, 6),
(cx - 90, cy - 30, 10, 6),
]
for (ex, ey, ew, eh) in deep_wmh:
d.add(Ellipse(ex, ey, ew, eh,
fillColor=colors.HexColor("#fff0a0"),
strokeColor=ORANGE, strokeWidth=1.2))
# ── Basal ganglia ────────────────────────────────────────────────────
d.add(Ellipse(cx - 28, cy - 5, 18, 12,
fillColor=colors.HexColor("#b8a898"),
strokeColor=colors.HexColor("#908070"), strokeWidth=1))
d.add(Ellipse(cx + 28, cy - 5, 18, 12,
fillColor=colors.HexColor("#b8a898"),
strokeColor=colors.HexColor("#908070"), strokeWidth=1))
# ── Annotations with lines ──────────────────────────────────────────
anno_font = "Helvetica-Bold"
anno_small = "Helvetica"
def annotate(x1, y1, x2, y2, text, sub="", col=NAVY):
d.add(Line(x1, y1, x2, y2, strokeColor=col, strokeWidth=1.2))
d.add(Circle(x1, y1, 3, fillColor=col, strokeColor=None))
d.add(String(x2 + 4, y2 - 2, text, fontName=anno_font,
fontSize=8.5, fillColor=col))
if sub:
d.add(String(x2 + 4, y2 - 13, sub, fontName=anno_small,
fontSize=7.5, fillColor=GRAY))
# Ventricles label
annotate(cx - 42, cy + 8, cx - 42, cy + 68, "Lateral Ventricle", "(CSF - dark on FLAIR)", col=colors.HexColor("#2a7aac"))
# Periventricular WMH label
annotate(cx - 72, cy + 18, cx - 165, cy + 55,
"Periventricular WMH", "(bright spots on FLAIR)", col=colors.HexColor("#b8860b"))
# Deep WMH label
annotate(cx - 110, cy + 25, cx - 195, cy - 5,
"Deep White Matter WMH", "(scattered bright foci)", col=ORANGE)
# White matter label
annotate(cx, cy - 85, cx + 60, cy - 105,
"White Matter", "(myelinated axons)", col=GRAY)
# Cortex label
annotate(cx + 155, cy + 20, cx + 200, cy + 60,
"Cortex / Gray Matter", "(normal)", col=colors.HexColor("#6b8e23"))
# Basal ganglia
annotate(cx + 28, cy - 5, cx + 160, cy - 35,
"Basal Ganglia", "(normal)", col=GRAY)
# Legend boxes at bottom
lx, ly = 30, 12
# WMH periventricular
d.add(Rect(lx, ly, 14, 10, fillColor=colors.HexColor("#fffacd"),
strokeColor=YELLOW, strokeWidth=1.5))
d.add(String(lx + 18, ly + 2, "Periventricular WMH",
fontName="Helvetica", fontSize=7.5, fillColor=DARKGRAY))
# WMH deep
d.add(Rect(lx + 145, ly, 14, 10, fillColor=colors.HexColor("#fff0a0"),
strokeColor=ORANGE, strokeWidth=1.2))
d.add(String(lx + 163, ly + 2, "Deep White Matter WMH",
fontName="Helvetica", fontSize=7.5, fillColor=DARKGRAY))
# Ventricle
d.add(Rect(lx + 320, ly, 14, 10, fillColor=colors.HexColor("#7fb8d4"),
strokeColor=colors.HexColor("#5a9ab8"), strokeWidth=1.5))
d.add(String(lx + 338, ly + 2, "CSF / Ventricle",
fontName="Helvetica", fontSize=7.5, fillColor=DARKGRAY))
return d
# ── Fazekas scale drawing ────────────────────────────────────────────────────
def fazekas_drawing(dw=480, dh=110):
"""Illustrate the 3-grade Fazekas scale for WMH severity."""
d = Drawing(dw, dh)
labels = ["Grade 0\nNo WMH", "Grade 1\nPunctate\n(Mild)", "Grade 2\nBeginning\nConfluence\n(Moderate)", "Grade 3\nLarge\nConfluent\n(Severe)"]
wmh_counts = [0, 1, 3, 6]
wmh_large = [False, False, True, True]
box_w = 100
gap = 20
start_x = (dw - (4 * box_w + 3 * gap)) / 2
for i, (label, wcount, wlarge) in enumerate(zip(labels, wmh_counts, wmh_large)):
bx = start_x + i * (box_w + gap)
by = 28
# brain oval
d.add(Ellipse(bx + box_w/2, by + 36, 40, 32,
fillColor=colors.HexColor("#d8cfc0"),
strokeColor=colors.HexColor("#b0a090"), strokeWidth=1.5))
# ventricle
d.add(Ellipse(bx + box_w/2 - 10, by + 38, 10, 6,
fillColor=colors.HexColor("#7fb8d4"),
strokeColor=None))
d.add(Ellipse(bx + box_w/2 + 10, by + 38, 10, 6,
fillColor=colors.HexColor("#7fb8d4"),
strokeColor=None))
# draw WMH patches
wmh_locs = [
(bx + box_w/2 - 22, by + 42),
(bx + box_w/2 + 18, by + 42),
(bx + box_w/2 - 30, by + 28),
(bx + box_w/2 + 24, by + 28),
(bx + box_w/2 - 20, by + 28),
(bx + box_w/2 + 12, by + 55),
]
size = 9 if wlarge else 5
for j in range(wcount):
lx2, ly2 = wmh_locs[j]
d.add(Ellipse(lx2, ly2, size, size * 0.6,
fillColor=YELLOW,
strokeColor=ORANGE, strokeWidth=0.8))
# label
col = [GRAY, colors.HexColor("#8B7000"), ORANGE, RED][i]
d.add(Rect(bx + 5, 4, box_w - 10, 18, rx=3, ry=3,
fillColor=col, strokeColor=None))
for li, lline in enumerate(label.split("\n")):
d.add(String(bx + box_w/2, 79 - li * 9, lline,
fontName="Helvetica-Bold", fontSize=7.5,
fillColor=WHITE, textAnchor="middle"))
return d
# ── FLAIR vs DWI comparison drawing ─────────────────────────────────────────
def flair_dwi_drawing(dw=480, dh=170):
d = Drawing(dw, dh)
cx1 = 120
cx2 = 360
cy = 100
for cx, label, bg, wmh_col, wmh_bright, csf_col, title_col, title in [
(cx1, "FLAIR", colors.HexColor("#0a1a3a"), colors.HexColor("#fffacd"),
True, colors.HexColor("#0a1a3a"), colors.HexColor("#5599dd"), "FLAIR Sequence"),
(cx2, "DWI", colors.HexColor("#050e1f"), colors.HexColor("#050e1f"),
False, colors.HexColor("#101828"), colors.HexColor("#44aacc"), "DWI Sequence"),
]:
# background circle (MRI dark background)
d.add(Ellipse(cx, cy, 88, 72,
fillColor=bg, strokeColor=colors.HexColor("#222244"),
strokeWidth=2))
# brain parenchyma
d.add(Ellipse(cx, cy, 76, 62,
fillColor=colors.HexColor("#1e3a5a") if label == "FLAIR" else colors.HexColor("#0d2035"),
strokeColor=None))
# gray matter rim brighter
d.add(Ellipse(cx, cy, 76, 62,
fillColor=None,
strokeColor=colors.HexColor("#4a7ab8") if label == "FLAIR" else colors.HexColor("#2a4a6a"),
strokeWidth=6))
# ventricles - dark on FLAIR, dark on DWI
d.add(Ellipse(cx - 18, cy + 4, 12, 7,
fillColor=colors.HexColor("#050e1f"),
strokeColor=None))
d.add(Ellipse(cx + 18, cy + 4, 12, 7,
fillColor=colors.HexColor("#050e1f"),
strokeColor=None))
# WMH patches - bright on FLAIR, dark/absent on DWI
wmh_spots = [(cx - 30, cy + 8), (cx + 22, cy + 8),
(cx - 38, cy - 8), (cx + 32, cy - 6)]
spot_col = colors.HexColor("#fffacd") if wmh_bright else colors.HexColor("#1a2a3a")
spot_stroke = ORANGE if wmh_bright else colors.HexColor("#1a2a3a")
for (sx, sy) in wmh_spots:
d.add(Ellipse(sx, sy, 8, 5,
fillColor=spot_col,
strokeColor=spot_stroke, strokeWidth=0.8))
# title bar
d.add(Rect(cx - 80, cy + 82, 160, 20, rx=4, ry=4,
fillColor=title_col, strokeColor=None))
d.add(String(cx, cy + 88, title,
fontName="Helvetica-Bold", fontSize=10,
fillColor=WHITE, textAnchor="middle"))
# Arrow between them
d.add(Line(210, cy, 270, cy, strokeColor=BLUE, strokeWidth=2))
d.add(String(240, cy + 6, "compare", fontName="Helvetica", fontSize=8,
fillColor=BLUE, textAnchor="middle"))
# Labels below FLAIR
d.add(String(cx1, cy - 90, "WMH = BRIGHT (chronic)",
fontName="Helvetica-Bold", fontSize=8.5,
fillColor=colors.HexColor("#c8a800"), textAnchor="middle"))
d.add(String(cx1, cy - 102, "Ventricles = DARK (suppressed)",
fontName="Helvetica", fontSize=7.5,
fillColor=colors.HexColor("#8899aa"), textAnchor="middle"))
# Labels below DWI
d.add(String(cx2, cy - 90, "No acute infarct (no bright spots)",
fontName="Helvetica-Bold", fontSize=8.5,
fillColor=colors.HexColor("#44cc88"), textAnchor="middle"))
d.add(String(cx2, cy - 102, "Normal = all dark/uniform",
fontName="Helvetica", fontSize=7.5,
fillColor=colors.HexColor("#8899aa"), textAnchor="middle"))
return d
# ── Build document ───────────────────────────────────────────────────────────
doc = SimpleDocTemplate(
OUT,
pagesize=A4,
leftMargin=2*cm, rightMargin=2*cm,
topMargin=2.2*cm, bottomMargin=2*cm,
title="Brain MRI – White Matter Hyperintensities Summary",
author="Orris Medical AI"
)
story = []
# ═══════════════════════════════════════════════════════════════════════
# PAGE 1 – COVER
# ═══════════════════════════════════════════════════════════════════════
# Top colour bar
story.append(HRFlowable(width="100%", thickness=8, color=NAVY, spaceAfter=10))
story.append(Paragraph("Brain MRI Analysis Report", H1))
story.append(Paragraph("White Matter Hyperintensities (WMH) – Location & Clinical Significance", SUBTITLE))
story.append(HRFlowable(width="100%", thickness=2, color=BLUE, spaceAfter=16))
# Key info box
info_data = [
[Paragraph("<b>Imaging Modality</b>", BODY), Paragraph("MRI Brain – FLAIR + DWI sequences", BODY)],
[Paragraph("<b>Primary Finding</b>", BODY), Paragraph("Mild periventricular & deep white matter hyperintensities", BODY)],
[Paragraph("<b>Clinical Significance</b>", BODY), Paragraph("Chronic small vessel ischemic disease (leukoaraiosis)", BODY)],
[Paragraph("<b>Acute Finding</b>", BODY), Paragraph("None – No restricted diffusion on DWI (no acute stroke)", BODY)],
[Paragraph("<b>Report Date</b>", BODY), Paragraph("July 15, 2026", BODY)],
]
info_table = Table(info_data, colWidths=[5*cm, 11*cm])
info_table.setStyle(TableStyle([
("BACKGROUND", (0, 0), (0, -1), LIGHTBLUE),
("BACKGROUND", (1, 0), (1, -1), colors.HexColor("#f8faff")),
("TEXTCOLOR", (0, 0), (-1, -1), DARKGRAY),
("FONTNAME", (0, 0), (-1, -1), "Helvetica"),
("FONTSIZE", (0, 0), (-1, -1), 10),
("GRID", (0, 0), (-1, -1), 0.5, colors.HexColor("#c0ccdd")),
("ROWBACKGROUNDS", (0, 0), (-1, -1), [LIGHTBLUE, colors.HexColor("#f8faff")]),
("TOPPADDING", (0, 0), (-1, -1), 7),
("BOTTOMPADDING", (0, 0), (-1, -1), 7),
("LEFTPADDING", (0, 0), (-1, -1), 10),
("ROUNDEDCORNERS", [4]),
]))
story.append(info_table)
story.append(Spacer(1, 0.5*cm))
# Patient MRI thumbnail on cover
story.append(Paragraph("Patient MRI Film", H3))
story.append(fit_image("/tmp/workspace/wmh-pdf/images/patient_mri.jpg", 14*cm, 11*cm))
story.append(Paragraph(
"Figure 1. Original patient MRI film (MEDVISION). Top three rows: FLAIR sequence showing "
"white matter hyperintensities. Bottom three rows: DWI sequence with no evidence of acute infarction.",
CAPTION
))
story.append(HRFlowable(width="100%", thickness=1, color=LIGHTBLUE, spaceBefore=8, spaceAfter=6))
story.append(Paragraph(
"DISCLAIMER: This report is generated by Orris AI for educational purposes only. It does not constitute "
"a formal radiological diagnosis. All clinical decisions must be made by a qualified physician reviewing "
"original DICOM images alongside patient history.",
DISCLAIMER
))
story.append(PageBreak())
# ═══════════════════════════════════════════════════════════════════════
# PAGE 2 – WHAT ARE WMH? + LABELED DIAGRAM
# ═══════════════════════════════════════════════════════════════════════
story.append(HRFlowable(width="100%", thickness=5, color=NAVY, spaceAfter=8))
story.append(Paragraph("1. What Are White Matter Hyperintensities?", H2))
story.append(Paragraph(
"White Matter Hyperintensities (WMH), also called <b>leukoaraiosis</b>, are areas of the brain "
"that appear abnormally bright (hyperintense) on FLAIR and T2-weighted MRI sequences. They represent "
"damage to the white matter – the deep brain tissue made up of myelinated nerve fibers (axons) that "
"connect different brain regions.", BODY))
story.append(Paragraph(
"WMH are extremely common in older adults and in patients with cardiovascular risk factors. "
"They are a hallmark of <b>cerebral small vessel disease (SVD)</b> – chronic damage to the "
"tiny arteries (arterioles) that supply the deep brain white matter.", BODY))
# Two-column bullet table
bullet_data = [
[Paragraph("<b>Location in the Brain</b>", H3), Paragraph("<b>What They Represent</b>", H3)],
[
Paragraph(
"• <b>Periventricular</b> – immediately surrounding the lateral ventricles<br/>"
"• <b>Deep white matter</b> – frontal, parietal, temporal lobes<br/>"
"• <b>Subcortical</b> – just beneath the cortex<br/>"
"• <b>Infratentorial</b> – brainstem, cerebellum (less common)", BULLET),
Paragraph(
"• Demyelination (loss of myelin sheath around axons)<br/>"
"• Axonal loss and gliosis (scar tissue formation)<br/>"
"• Enlarged perivascular spaces<br/>"
"• Chronic ischemia from small artery thickening", BULLET)
],
]
btable = Table(bullet_data, colWidths=[8*cm, 8*cm])
btable.setStyle(TableStyle([
("BACKGROUND", (0, 0), (-1, 0), NAVY),
("TEXTCOLOR", (0, 0), (-1, 0), WHITE),
("BACKGROUND", (0, 1), (0, 1), colors.HexColor("#eef4ff")),
("BACKGROUND", (1, 1), (1, 1), colors.HexColor("#fff8ee")),
("GRID", (0, 0), (-1, -1), 0.5, colors.HexColor("#c0ccdd")),
("TOPPADDING", (0, 0), (-1, -1), 8),
("BOTTOMPADDING", (0, 0), (-1, -1), 8),
("LEFTPADDING", (0, 0), (-1, -1), 8),
("VALIGN", (0, 0), (-1, -1), "TOP"),
]))
story.append(btable)
story.append(Spacer(1, 0.4*cm))
story.append(Paragraph("Labeled Brain Cross-Section – WMH Locations", H3))
story.append(brain_cross_section_drawing(480, 320))
story.append(Paragraph(
"Figure 2. Schematic axial cross-section of the brain showing the two main locations of WMH: "
"periventricular (immediately adjacent to the lateral ventricles, shown in pale yellow) and "
"deep white matter (scattered foci, shown in orange-yellow). The lateral ventricles appear "
"blue (CSF). Basal ganglia are shown in gray.",
CAPTION
))
story.append(PageBreak())
# ═══════════════════════════════════════════════════════════════════════
# PAGE 3 – FLAIR vs DWI + FAZEKAS SCALE
# ═══════════════════════════════════════════════════════════════════════
story.append(HRFlowable(width="100%", thickness=5, color=NAVY, spaceAfter=8))
story.append(Paragraph("2. How WMH Look on MRI – FLAIR vs DWI", H2))
story.append(Paragraph(
"Two key sequences are used to characterise WMH and distinguish them from acute pathology:", BODY))
story.append(flair_dwi_drawing(480, 170))
story.append(Paragraph(
"Figure 3. Schematic comparison of FLAIR (left) and DWI (right) appearances. "
"On FLAIR, WMH appear as bright (hyperintense) foci; ventricles are dark. "
"On DWI, the same WMH are dark – absence of bright signal confirms no acute stroke.",
CAPTION
))
story.append(Spacer(1, 0.3*cm))
seq_data = [
[Paragraph("<b>Sequence</b>", LABEL_ST), Paragraph("<b>WMH Appearance</b>", LABEL_ST),
Paragraph("<b>Why It Matters</b>", LABEL_ST)],
[Paragraph("FLAIR", BODY), Paragraph("Bright / hyperintense", BODY),
Paragraph("Best for detecting WMH, periventricular halos, subcortical lesions", BODY)],
[Paragraph("DWI", BODY), Paragraph("Dark / isointense", BODY),
Paragraph("Normal DWI confirms lesions are CHRONIC, not an acute stroke", BODY)],
[Paragraph("T1", BODY), Paragraph("Dark / hypointense", BODY),
Paragraph("Old lacunar infarcts appear as black holes on T1", BODY)],
[Paragraph("T2", BODY), Paragraph("Bright / hyperintense", BODY),
Paragraph("Shows same changes as FLAIR but with CSF also bright (less contrast)", BODY)],
]
seq_table = Table(seq_data, colWidths=[3*cm, 4.5*cm, 8.5*cm])
seq_table.setStyle(TableStyle([
("BACKGROUND", (0, 0), (-1, 0), NAVY),
("TEXTCOLOR", (0, 0), (-1, 0), WHITE),
("ROWBACKGROUNDS", (0, 1), (-1, -1), [colors.HexColor("#f8faff"), WHITE]),
("GRID", (0, 0), (-1, -1), 0.5, colors.HexColor("#c0ccdd")),
("TOPPADDING", (0, 0), (-1, -1), 7),
("BOTTOMPADDING", (0, 0), (-1, -1), 7),
("LEFTPADDING", (0, 0), (-1, -1), 8),
("FONTNAME", (0, 0), (-1, 0), "Helvetica-Bold"),
("FONTSIZE", (0, 0), (-1, -1), 9.5),
("VALIGN", (0, 0), (-1, -1), "MIDDLE"),
]))
story.append(seq_table)
story.append(Spacer(1, 0.5*cm))
story.append(Paragraph("3. Grading WMH Severity – Fazekas Scale", H2))
story.append(Paragraph(
"The <b>Fazekas scale</b> is the most widely used system to grade WMH severity on MRI. "
"This patient's scan shows Grade 1-2 changes (mild-to-moderate punctate/beginning confluence).", BODY))
story.append(fazekas_drawing(480, 110))
story.append(Paragraph(
"Figure 4. Schematic Fazekas grading scale for WMH severity. Yellow foci = WMH patches. "
"Grade 0 = normal; Grade 1 = punctate foci (mild); Grade 2 = beginning confluence (moderate); "
"Grade 3 = large confluent areas (severe). This patient's scan corresponds to Grade 1-2.",
CAPTION
))
story.append(Spacer(1, 0.4*cm))
# Reference diagram from Radiopaedia
story.append(Paragraph("Reference: Small Vessel Disease Brain Anatomy", H3))
story.append(fit_image("/tmp/workspace/wmh-pdf/images/svd_diagram.jpg", 10*cm, 8*cm))
story.append(Paragraph(
"Figure 5. Anatomical diagram (Radiopaedia) illustrating features of cerebral small vessel disease "
"including WMH location, lacunes, microbleeds, and perivascular spaces in an axial brain cross-section. "
"This patient's scan shows WMH only (no lacunes, no microbleeds identified on available sequences).",
CAPTION
))
story.append(PageBreak())
# ═══════════════════════════════════════════════════════════════════════
# PAGE 4 – CAUSES, CLINICAL SIGNIFICANCE, MANAGEMENT
# ═══════════════════════════════════════════════════════════════════════
story.append(HRFlowable(width="100%", thickness=5, color=NAVY, spaceAfter=8))
story.append(Paragraph("4. Causes and Risk Factors", H2))
causes_data = [
[Paragraph("<b>Risk Factor</b>", LABEL_ST),
Paragraph("<b>Mechanism</b>", LABEL_ST),
Paragraph("<b>Effect on White Matter</b>", LABEL_ST)],
[Paragraph("Hypertension", BODY),
Paragraph("Arteriolosclerosis – thickening/stiffening of small artery walls", BODY),
Paragraph("Reduced perfusion to deep white matter watershed zones", BODY)],
[Paragraph("Diabetes mellitus", BODY),
Paragraph("Endothelial dysfunction; glycation of vessel walls", BODY),
Paragraph("Microangiopathy; accelerated small vessel damage", BODY)],
[Paragraph("Age (>60 years)", BODY),
Paragraph("Natural stiffening of arteries (arteriosclerosis)", BODY),
Paragraph("Progressive ischemic white matter rarefaction", BODY)],
[Paragraph("Hyperlipidaemia", BODY),
Paragraph("Lipid deposits in arterial walls", BODY),
Paragraph("Promotes atheromatous change in small vessels", BODY)],
[Paragraph("Smoking", BODY),
Paragraph("Oxidative stress; vasospasm; accelerated atherosclerosis", BODY),
Paragraph("Worsens all above mechanisms", BODY)],
[Paragraph("Atrial fibrillation", BODY),
Paragraph("Microemboli from cardiac source", BODY),
Paragraph("Small embolic infarcts appear as WMH", BODY)],
]
causes_table = Table(causes_data, colWidths=[3.5*cm, 6.5*cm, 6*cm])
causes_table.setStyle(TableStyle([
("BACKGROUND", (0, 0), (-1, 0), TEAL),
("TEXTCOLOR", (0, 0), (-1, 0), WHITE),
("ROWBACKGROUNDS", (0, 1), (-1, -1), [colors.HexColor("#f0faf8"), WHITE]),
("GRID", (0, 0), (-1, -1), 0.5, colors.HexColor("#aacccc")),
("TOPPADDING", (0, 0), (-1, -1), 7),
("BOTTOMPADDING", (0, 0), (-1, -1), 7),
("LEFTPADDING", (0, 0), (-1, -1), 8),
("FONTNAME", (0, 0), (-1, 0), "Helvetica-Bold"),
("FONTSIZE", (0, 0), (-1, -1), 9),
("VALIGN", (0, 0), (-1, -1), "TOP"),
]))
story.append(causes_table)
story.append(Spacer(1, 0.5*cm))
story.append(Paragraph("5. Clinical Significance", H2))
story.append(Paragraph(
"WMH are not merely incidental findings – they carry important clinical implications, "
"especially when moderate to severe:", BODY))
sig_items = [
("<b>Cognitive impairment:</b>", "WMH disrupt white matter tracts connecting frontal-subcortical circuits. "
"Moderate-severe WMH are associated with slowed processing speed, executive dysfunction, and vascular dementia."),
("<b>Stroke risk:</b>", "WMH independently increase future stroke risk (both ischemic and hemorrhagic). "
"Presence of WMH roughly doubles stroke risk."),
("<b>Gait disturbance:</b>", "Periventricular WMH affecting motor tracts can cause small-stepped gait and falls."),
("<b>Mood disorders:</b>", "Deep white matter lesions are associated with late-onset depression ('vascular depression')."),
("<b>Bladder dysfunction:</b>", "Frontal WMH can cause urinary urgency/incontinence by interrupting inhibitory pathways."),
]
for title_text, body_text in sig_items:
story.append(Paragraph(f"{title_text} {body_text}", BULLET))
story.append(Spacer(1, 0.4*cm))
story.append(Paragraph("6. Management Recommendations", H2))
mgmt_data = [
[Paragraph("<b>Goal</b>", LABEL_ST), Paragraph("<b>Action</b>", LABEL_ST)],
[Paragraph("Control hypertension", BODY),
Paragraph("Target BP <130/80 mmHg; first-line agents: ACE inhibitor or ARB", BODY)],
[Paragraph("Manage diabetes", BODY),
Paragraph("HbA1c <7%; GLP-1 agonists have additional vascular benefit", BODY)],
[Paragraph("Statin therapy", BODY),
Paragraph("If dyslipidaemia present; LDL-C <1.8 mmol/L in high-risk patients", BODY)],
[Paragraph("Antiplatelet therapy", BODY),
Paragraph("Aspirin 75-100 mg/day if cerebrovascular disease confirmed", BODY)],
[Paragraph("Lifestyle modification", BODY),
Paragraph("Smoking cessation, aerobic exercise, Mediterranean diet", BODY)],
[Paragraph("Cognitive monitoring", BODY),
Paragraph("Annual cognitive screening (MoCA); neuropsychology if symptomatic", BODY)],
[Paragraph("Follow-up MRI", BODY),
Paragraph("Repeat MRI in 2-3 years to monitor WMH progression", BODY)],
]
mgmt_table = Table(mgmt_data, colWidths=[4.5*cm, 11.5*cm])
mgmt_table.setStyle(TableStyle([
("BACKGROUND", (0, 0), (-1, 0), NAVY),
("TEXTCOLOR", (0, 0), (-1, 0), WHITE),
("ROWBACKGROUNDS", (0, 1), (-1, -1), [colors.HexColor("#f8faff"), WHITE]),
("GRID", (0, 0), (-1, -1), 0.5, colors.HexColor("#c0ccdd")),
("TOPPADDING", (0, 0), (-1, -1), 7),
("BOTTOMPADDING", (0, 0), (-1, -1), 7),
("LEFTPADDING", (0, 0), (-1, -1), 8),
("FONTNAME", (0, 0), (-1, 0), "Helvetica-Bold"),
("FONTSIZE", (0, 0), (-1, -1), 9.5),
("VALIGN", (0, 0), (-1, -1), "TOP"),
]))
story.append(mgmt_table)
story.append(Spacer(1, 0.5*cm))
story.append(HRFlowable(width="100%", thickness=1, color=LIGHTBLUE, spaceAfter=6))
# WMH progression diagram
story.append(Paragraph("7. How WMH Progress Over Time", H2))
story.append(fit_image("/tmp/workspace/wmh-pdf/images/wmh_progression.jpg", 13*cm, 9*cm))
story.append(Paragraph(
"Figure 6. Dynamic progression model of WMH and small vessel disease (Wardlaw et al., JAH 2015). "
"Periventricular and deep WMH (pale yellow) can increase, occasionally shrink, and lead to atrophy "
"of overlying cortex (blue). Acute small subcortical infarcts (white) may cavitate, persist as WMH caps, "
"or cause downstream white matter tract damage.",
CAPTION
))
story.append(PageBreak())
# ═══════════════════════════════════════════════════════════════════════
# PAGE 5 – SUMMARY FINDINGS TABLE + KEY MESSAGES
# ═══════════════════════════════════════════════════════════════════════
story.append(HRFlowable(width="100%", thickness=5, color=NAVY, spaceAfter=8))
story.append(Paragraph("8. Summary of MRI Findings – This Patient", H2))
summary_data = [
[Paragraph("<b>Brain Region</b>", LABEL_ST),
Paragraph("<b>Sequence</b>", LABEL_ST),
Paragraph("<b>Finding</b>", LABEL_ST),
Paragraph("<b>Significance</b>", LABEL_ST)],
[Paragraph("Periventricular white matter", BODY),
Paragraph("FLAIR", BODY),
Paragraph("Small bright hyperintensities adjacent to lateral ventricles", BODY),
Paragraph("Chronic small vessel disease", BODY)],
[Paragraph("Deep white matter\n(frontal/parietal)", BODY),
Paragraph("FLAIR", BODY),
Paragraph("Scattered mild hyperintense foci", BODY),
Paragraph("Chronic microvascular ischemia", BODY)],
[Paragraph("Lateral ventricles", BODY),
Paragraph("FLAIR", BODY),
Paragraph("Normal size, symmetric; CSF correctly dark", BODY),
Paragraph("No hydrocephalus", BODY)],
[Paragraph("Cerebral hemispheres", BODY),
Paragraph("DWI", BODY),
Paragraph("No restricted diffusion (no bright areas)", BODY),
Paragraph("No acute ischemic stroke", BODY)],
[Paragraph("Brainstem/cerebellum", BODY),
Paragraph("FLAIR + DWI", BODY),
Paragraph("Structurally intact; no focal lesion", BODY),
Paragraph("Normal posterior fossa", BODY)],
[Paragraph("Cortex / sulci", BODY),
Paragraph("FLAIR", BODY),
Paragraph("Mild sulcal widening (age-appropriate)", BODY),
Paragraph("Mild diffuse cortical atrophy", BODY)],
[Paragraph("Basal ganglia / thalami", BODY),
Paragraph("FLAIR", BODY),
Paragraph("No focal signal change", BODY),
Paragraph("Normal deep grey matter", BODY)],
[Paragraph("Midline structures", BODY),
Paragraph("FLAIR", BODY),
Paragraph("No shift; corpus callosum intact", BODY),
Paragraph("No mass effect", BODY)],
]
summary_table = Table(summary_data, colWidths=[4*cm, 2.5*cm, 6*cm, 3.5*cm])
summary_table.setStyle(TableStyle([
("BACKGROUND", (0, 0), (-1, 0), NAVY),
("TEXTCOLOR", (0, 0), (-1, 0), WHITE),
("ROWBACKGROUNDS", (0, 1), (-1, -1), [colors.HexColor("#fff9e6"), WHITE]),
("GRID", (0, 0), (-1, -1), 0.5, colors.HexColor("#c0ccdd")),
("TOPPADDING", (0, 0), (-1, -1), 6),
("BOTTOMPADDING", (0, 0), (-1, -1), 6),
("LEFTPADDING", (0, 0), (-1, -1), 6),
("FONTNAME", (0, 0), (-1, 0), "Helvetica-Bold"),
("FONTSIZE", (0, 0), (-1, -1), 8.5),
("VALIGN", (0, 0), (-1, -1), "TOP"),
]))
story.append(summary_table)
story.append(Spacer(1, 0.5*cm))
story.append(Paragraph("Key Clinical Messages", H2))
key_msgs = [
("No emergency finding.", "There is no acute stroke, hemorrhage, tumor, or midline shift on this scan."),
("Chronic vascular change.", "The WMH represent cumulative damage from long-standing small vessel disease – "
"not a new event."),
("Modifiable risk factors.", "Most causes of WMH (hypertension, diabetes, smoking) are treatable. "
"Aggressive risk factor control can slow progression."),
("Cognitive vigilance.", "Even mild WMH warrant monitoring for early cognitive or gait changes."),
("Follow-up imaging.", "A repeat MRI in 2-3 years is recommended to track WMH evolution "
"and ensure no new lesions develop."),
]
for num, (msg_title, msg_body) in enumerate(key_msgs, 1):
d = Drawing(480, 38)
d.add(Rect(0, 4, 480, 32, rx=5, ry=5,
fillColor=LIGHTBLUE if num % 2 == 0 else colors.HexColor("#fff8e8"),
strokeColor=BLUE, strokeWidth=0.5))
d.add(Rect(0, 4, 8, 32, rx=0, ry=0,
fillColor=BLUE if num % 2 == 0 else ORANGE, strokeColor=None))
d.add(String(20, 24, f"{num}. {msg_title}", fontName="Helvetica-Bold",
fontSize=9.5, fillColor=NAVY))
d.add(String(20, 11, msg_body, fontName="Helvetica",
fontSize=8.5, fillColor=DARKGRAY))
story.append(d)
story.append(Spacer(1, 2))
story.append(Spacer(1, 0.6*cm))
story.append(HRFlowable(width="100%", thickness=2, color=NAVY, spaceAfter=8))
story.append(Paragraph(
"References: Wardlaw JM et al. (2013) Neuroimaging standards for research into small vessel disease. "
"Lancet Neurol 12(8):822-38. | Fazekas F et al. (1987) CT and MRI rating of white matter lesions. "
"AJNR 8(2):421-6. | Hachinski V et al. (1987) Leukoaraiosis. Arch Neurol 44(1):21-3.",
DISCLAIMER
))
story.append(Paragraph(
"DISCLAIMER: This document is generated by Orris AI for educational/informational purposes only. "
"It is NOT a substitute for formal radiological reporting by a qualified radiologist. "
"Clinical decisions must be based on complete clinical assessment.",
DISCLAIMER
))
doc.build(story)
print(f"PDF saved to: {OUT}")
cd /tmp/workspace/wmh-pdf && python build_pdf.py 2>&1
~/wmh-pdf/build_pdf.py
Successfully replaced 1 instance(s) of the string in '/tmp/workspace/wmh-pdf/build_pdf.py'
cd /tmp/workspace/wmh-pdf && python build_pdf.py 2>&1
ls -lh /tmp/workspace/wmh-pdf/WMH_Brain_MRI_Summary.pdf
WMH Brain MRI Summary
PDF Document · PDF
| Page | Content |
|---|---|
| 1 – Cover | Summary info table + your original patient MRI film |
| 2 – What are WMH? | Custom labeled brain cross-section diagram showing periventricular vs. deep white matter WMH locations, with annotated anatomy |
| 3 – FLAIR vs DWI + Fazekas Scale | Schematic comparison of how WMH appear on each sequence; Fazekas grading illustrations (Grade 0-3); reference SVD anatomy diagram |
| 4 – Causes & Management | Detailed tables covering risk factors (hypertension, diabetes, etc.), clinical significance (cognition, stroke risk, gait), and management recommendations |
| 5 – Findings Table + Key Messages | Complete findings by brain region; 5 highlighted key clinical takeaway panels |