Hi
Tell me every sequence used in mri mrcp with detailed planning with pictures and the uses of the sequences used
MRI MRCP sequences planning detailed protocol 2024
MRCP MRI protocol sequences biliary pancreatic
https://mrimaster.com/plan-mrcp
MRCP MRI sequences TrueFISP HASTE SPACE T1 VIBE planning images diagram site:mrimaster.com
https://mrimaster.com/plan-mrcp
MRCP MRI protocol sequences planning diagram biliary pancreatic duct T2 HASTE SPACE TrueFISP

| TR | TE | Flip | NEX | Slice | Matrix | FOV | Phase |
|---|---|---|---|---|---|---|---|
| ~4-6 ms | ~2 ms | 70-80° | 1 | 5-8 mm | 256x256 | 400 | A>P |
| TR | TE | Flip | NEX | Slice | Matrix | FOV | Phase | Fat Sat |
|---|---|---|---|---|---|---|---|---|
| 4-5 ms | 2 ms | 70° | 1 | 4 mm | 320x320 | 400 | R>L | Yes |
| TR | TE | Flip | NEX | Slice | Matrix | FOV | Phase | Oversample |
|---|---|---|---|---|---|---|---|---|
| 2500-3000 | 90-110 ms | 130° | 1 | 4-5 mm | 256x256 | 350 | R>L | 50% |
Important planning tip: All gated and 3D sequences must be planned on the breath-hold HASTE or TrueFISP (NOT the initial localizer), because the diaphragm pushes the liver downward during inhalation, shifting the liver position from the initial scans.
| TR | TE | Flip | NEX | Slice Thickness | Matrix | FOV | Phase | Oversample |
|---|---|---|---|---|---|---|---|---|
| 4000-5000 | 500 ms | 150° | 1 | 40 mm | 320x320 | 350-450 | R>L | 50% |
| TR | TE | Flip | NEX | Slice | Matrix | FOV | Phase | Oversample | Trigger |
|---|---|---|---|---|---|---|---|---|---|
| 2000-3000 | 200 ms | 12° | 1 | 1 mm | 320x320 | 350 | R>L | 50% | YES (gated) |
| TR | TE | Flip | NEX | Slice | Matrix | FOV | Phase | Fat Sat |
|---|---|---|---|---|---|---|---|---|
| 5-6 ms | 2.5 ms | 12° | 1 | 3-4 mm | 256x256 | 350 | A>P | Yes (SPAIR) |
| TR | TE (in-phase) | TE (out-of-phase) | Flip | NEX | Slice |
|---|---|---|---|---|---|
| 150-200 ms | 4.76 ms | 2.38 ms | 70° | 1 | 5-6 mm |
| TR | TE | b-values | NEX | Slice | Matrix | FOV |
|---|---|---|---|---|---|---|
| 3000-5000 | 60-80 ms | 50, 400, 800 | 4-6 | 5-6 mm | 128x128 | 350 |
| # | Sequence | Plane | Technique | Breathing | Key Use |
|---|---|---|---|---|---|
| 1 | TrueFISP 3-plane | 3-plane | bSSFP | Breath-hold | Localizer, anatomy overview |
| 2 | T2 TrueFISP FS Coronal | Coronal | bSSFP + FS | Breath-hold | Survey biliary anatomy; planning reference |
| 3 | T2 HASTE FS Axial | Axial | HASTE + FS | Breath-hold | Main diagnostic axial; stones, dilation, strictures |
| 4 | T2 HASTE Thick Slab LAO | Coronal oblique | HASTE | Breath-hold | Gallbladder + CBD projection |
| 5 | T2 HASTE Thick Slab RAO | Coronal oblique | HASTE | Breath-hold | CBD + pancreatic duct projection |
| 6 | T2 HASTE Radial Thick Slab | Radial | HASTE | Breath-hold (each) | Multiple-angle biliary projections |
| 7 | T2 SPACE 3D 1mm Coronal | Coronal | 3D TSE | Respiratory gated | Gold standard - high-res MPR/MIP reconstructions |
| 8 | T1 VIBE FS Axial ± Gd | Axial | 3D GRE + FS | Breath-hold | Focal lesion characterization, enhancement |
| 9 | T1 In/Out-of-Phase | Axial | GRE dual-echo | Breath-hold | Steatosis, iron, lipid-containing lesions |
| 10 | T2 HASTE Axial (no FS) | Axial | HASTE | Breath-hold | Parenchyma, cystic lesion characterization |
| 11 | DWI | Axial | EPI | Free-breathing | Malignancy detection, restricted diffusion |
| Condition | Key Sequences |
|---|---|
| Choledocholithiasis (CBD stones) | HASTE FS axial + SPACE 3D + thick slab |
| Primary Sclerosing Cholangitis (PSC) | SPACE 3D (multifocal strictures + "beading") |
| Cholangiocarcinoma | SPACE 3D + DWI + T1 VIBE dynamic |
| Pancreatic cancer (PDAC) | T1 VIBE dynamic + DWI + SPACE 3D |
| Chronic pancreatitis | Thick slab + SPACE 3D (duct dilation, stones) |
| IPMN | SPACE 3D + T2 HASTE |
| Choledochal cyst | SPACE 3D (Todani classification) |
| Acute cholecystitis | T2 HASTE FS + DWI |
| Post-surgical anatomy (post-Whipple, Roux-en-Y) | SPACE 3D + T1 VIBE |
| IgG4-related cholangiopathy | SPACE 3D + T1 VIBE dynamic |
Create a visual MRCP protocol flowchart as a PDF reference guide
mkdir -p /home/daytona/workspace/mrcp-flowchart && ls /home/daytona/workspace/mr…
~/mrcp-flowchart/generate_mrcp_pdf.py
#!/usr/bin/env python3
"""
MRCP MRI Protocol Flowchart - Visual Reference Guide PDF
Generates a 2-page A4 PDF with:
Page 1: Protocol flowchart (sequence order + planning steps)
Page 2: Parameters table + clinical indications
"""
from reportlab.lib.pagesizes import A4
from reportlab.lib import colors
from reportlab.lib.units import mm, cm
from reportlab.pdfgen import canvas
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.platypus import Paragraph
from reportlab.lib.enums import TA_CENTER, TA_LEFT
import textwrap
OUTPUT = "/home/daytona/workspace/mrcp-flowchart/MRCP_Protocol_Reference.pdf"
W, H = A4 # 595 x 842 pts
# ── Colour palette ────────────────────────────────────────────────────────────
NAVY = colors.HexColor("#0D2B55")
TEAL = colors.HexColor("#1A7A8A")
GOLD = colors.HexColor("#E8A020")
LIGHT_BLUE = colors.HexColor("#D6EEF5")
PALE_GREEN = colors.HexColor("#D8F0E4")
PALE_AMBER = colors.HexColor("#FEF3DC")
PALE_PINK = colors.HexColor("#FCE4EC")
PALE_LAVEN = colors.HexColor("#EDE7F6")
WHITE = colors.white
DARK_GREY = colors.HexColor("#333333")
MED_GREY = colors.HexColor("#666666")
LIGHT_GREY = colors.HexColor("#F0F4F8")
RED_DARK = colors.HexColor("#B71C1C")
GREEN_DARK = colors.HexColor("#1B5E20")
PURPLE = colors.HexColor("#4527A0")
def draw_rounded_rect(c, x, y, w, h, r=4, fill_color=WHITE, stroke_color=NAVY, stroke_width=1):
c.setFillColor(fill_color)
c.setStrokeColor(stroke_color)
c.setLineWidth(stroke_width)
c.roundRect(x, y, w, h, r, fill=1, stroke=1)
def draw_diamond(c, cx, cy, hw, hh, fill_color=PALE_AMBER, stroke_color=GOLD):
"""Draw a diamond shape centred at cx,cy with half-widths hw,hh."""
c.setFillColor(fill_color)
c.setStrokeColor(stroke_color)
c.setLineWidth(1.5)
path = c.beginPath()
path.moveTo(cx, cy + hh)
path.lineTo(cx + hw, cy)
path.lineTo(cx, cy - hh)
path.lineTo(cx - hw, cy)
path.close()
c.drawPath(path, fill=1, stroke=1)
def centred_text(c, x, y, text, font="Helvetica-Bold", size=9, color=DARK_GREY):
c.setFont(font, size)
c.setFillColor(color)
c.drawCentredString(x, y, text)
def left_text(c, x, y, text, font="Helvetica", size=8, color=DARK_GREY):
c.setFont(font, size)
c.setFillColor(color)
c.drawString(x, y, text)
def arrow(c, x1, y1, x2, y2, color=NAVY, width=1.2):
"""Draw a vertical arrow from (x1,y1) to (x2,y2)."""
c.setStrokeColor(color)
c.setFillColor(color)
c.setLineWidth(width)
c.line(x1, y1, x2, y2)
# arrowhead
aw, ah = 4, 5
c.setLineWidth(0.5)
path = c.beginPath()
path.moveTo(x2, y2)
path.lineTo(x2 - aw, y2 + ah)
path.lineTo(x2 + aw, y2 + ah)
path.close()
c.drawPath(path, fill=1, stroke=0)
def wrap_in_box(c, x, y, w, h, lines, fill_color, stroke_color,
title=None, title_color=WHITE, title_bg=None,
font="Helvetica", size=7.5, line_spacing=9.5,
radius=5, stroke_width=1):
draw_rounded_rect(c, x, y, w, h, r=radius, fill_color=fill_color,
stroke_color=stroke_color, stroke_width=stroke_width)
if title:
bg = title_bg if title_bg else stroke_color
c.setFillColor(bg)
# top banner
path = c.beginPath()
path.moveTo(x + radius, y + h)
path.lineTo(x + w - radius, y + h)
path.arcTo(x + w - 2*radius, y + h - 2*radius, x + w, y + h, 0, 90)
path.lineTo(x + w, y + h - 14)
path.lineTo(x, y + h - 14)
path.lineTo(x, y + h - radius)
path.arcTo(x, y + h - 2*radius, x + 2*radius, y + h, 90, 90)
path.close()
c.drawPath(path, fill=1, stroke=0)
c.setFont("Helvetica-Bold", 8)
c.setFillColor(title_color)
c.drawCentredString(x + w/2, y + h - 10, title)
text_y = y + h - 22
else:
text_y = y + h - 11
c.setFont(font, size)
c.setFillColor(DARK_GREY)
for line in lines:
c.drawString(x + 6, text_y, line)
text_y -= line_spacing
# ══════════════════════════════════════════════════════════════════════════════
# PAGE 1 – FLOWCHART
# ══════════════════════════════════════════════════════════════════════════════
def page1(c):
# ── Background ───────────────────────────────────────────────────────────
c.setFillColor(LIGHT_GREY)
c.rect(0, 0, W, H, fill=1, stroke=0)
# ── Header banner ─────────────────────────────────────────────────────────
c.setFillColor(NAVY)
c.rect(0, H - 52, W, 52, fill=1, stroke=0)
c.setFillColor(TEAL)
c.rect(0, H - 56, W, 5, fill=1, stroke=0)
c.setFont("Helvetica-Bold", 17)
c.setFillColor(WHITE)
c.drawCentredString(W/2, H - 28, "MRI MRCP Protocol – Sequence Flowchart")
c.setFont("Helvetica", 9)
c.setFillColor(GOLD)
c.drawCentredString(W/2, H - 43, "Magnetic Resonance Cholangiopancreatography | Visual Reference Guide | July 2026")
# ── Two-column layout ────────────────────────────────────────────────────
col1_x = 18
col2_x = W/2 + 4
col_w = W/2 - 26
top_y = H - 68
# ──────────────────────── COLUMN 1 ───────────────────────────────────────
# Block dimensions
bw = col_w
bh_start = 36 # START block
bh_normal = 56 # normal sequence boxes
bh_opt = 50 # optional boxes (shorter)
gap = 8 # gap between boxes (for arrow)
cx = col1_x + bw/2 # centre x of col 1
# ── START: Patient Prep & Positioning ────────────────────────────────────
y = top_y - bh_start
draw_rounded_rect(c, col1_x, y, bw, bh_start, r=18,
fill_color=NAVY, stroke_color=TEAL, stroke_width=2)
c.setFont("Helvetica-Bold", 9)
c.setFillColor(WHITE)
c.drawCentredString(cx, y + 22, "PATIENT PREP & POSITIONING")
c.setFont("Helvetica", 7.5)
c.setFillColor(GOLD)
c.drawCentredString(cx, y + 11, "Supine HFS | Body coil nipple→iliac crest | Laser: xiphoid | Fast 4-6 h")
y_cursor = y
def seq_box(c, y_top, title, title_bg, fill_color, stroke_color,
lines, seq_num=None):
draw_rounded_rect(c, col1_x, y_top - bh_normal, bw, bh_normal,
r=5, fill_color=fill_color,
stroke_color=stroke_color, stroke_width=1.5)
# coloured left accent bar
c.setFillColor(stroke_color)
c.roundRect(col1_x, y_top - bh_normal, 6, bh_normal, 3, fill=1, stroke=0)
# seq number badge
if seq_num:
c.setFillColor(stroke_color)
c.circle(col1_x + 18, y_top - 10, 8, fill=1, stroke=0)
c.setFont("Helvetica-Bold", 8)
c.setFillColor(WHITE)
c.drawCentredString(col1_x + 18, y_top - 13, str(seq_num))
# title
c.setFont("Helvetica-Bold", 8.5)
c.setFillColor(stroke_color)
tx = col1_x + (28 if seq_num else 10)
c.drawString(tx, y_top - 13, title)
# lines
c.setFont("Helvetica", 7.5)
c.setFillColor(DARK_GREY)
ly = y_top - 25
for ln in lines:
c.drawString(col1_x + 10, ly, ln)
ly -= 9.5
return y_top - bh_normal
sequences_col1 = [
# (title, title_bg, fill, stroke, [lines], seq_num)
("LOCALIZER – 3-Plane TrueFISP", TEAL, LIGHT_BLUE, TEAL,
["Plane: Axial + Coronal + Sagittal | BH < 25 s",
"TR/TE: ~5/2 ms | Flip: 70° | Slice: 5-8 mm",
"Use: Anatomy overview; base for all planning"], 1),
("T2 TrueFISP FS Coronal (4 mm BH)", TEAL, PALE_GREEN, TEAL,
["Plan on axial localizer; cover ant. wall → post. wall",
"TR/TE: 4-5/2 ms | Flip: 70° | FS: Yes | BH",
"Use: Survey biliary anatomy; planning reference for gated Seq"], 2),
("T2 HASTE FS Axial (4-5 mm BH)", TEAL, LIGHT_BLUE, TEAL,
["Plan on coronal TrueFISP; diaphragm → C-loop duodenum",
"TR/TE: 2500/90-110 ms | Flip: 130° | 50% oversample",
"Use: KEY diagnostic axial; stones=dark void, bile=bright"], 3),
("T2 HASTE Thick Slab LAO (40 mm BH)", GOLD, PALE_AMBER, GOLD,
["Plan on axial HASTE; rotate 20-30° clockwise (LAO)",
"TR/TE: 4500/500 ms | Flip: 150° | Slice: 40 mm",
"Use: ERCP-like view of CBD + Gallbladder"], 4),
("T2 HASTE Thick Slab RAO (40 mm BH)", GOLD, PALE_AMBER, GOLD,
["Plan on axial HASTE; rotate 20-30° counter-CW (RAO)",
"TR/TE: 4500/500 ms | Flip: 150° | Slice: 40 mm",
"Use: CBD + Pancreatic Duct projection view"], 5),
]
y_cursor = y # just below patient prep box
for (title, tbg, fill, stroke, lines, snum) in sequences_col1:
# arrow
arrow(c, cx, y_cursor, cx, y_cursor - gap + 5, color=stroke)
y_cursor = y_cursor - gap
y_cursor = seq_box(c, y_cursor, title, tbg, fill, stroke, lines, snum)
# ── OPTIONAL box at bottom of col 1 ──────────────────────────────────────
arrow(c, cx, y_cursor, cx, y_cursor - gap + 5, color=PURPLE)
y_cursor -= gap
opt_h = 42
draw_rounded_rect(c, col1_x, y_cursor - opt_h, bw, opt_h, r=5,
fill_color=PALE_LAVEN, stroke_color=PURPLE, stroke_width=1.5)
c.setFillColor(PURPLE)
c.roundRect(col1_x, y_cursor - opt_h, 6, opt_h, 3, fill=1, stroke=0)
c.circle(col1_x + 18, y_cursor - 10, 8, fill=1, stroke=0)
c.setFont("Helvetica-Bold", 8)
c.setFillColor(WHITE)
c.drawCentredString(col1_x + 18, y_cursor - 13, "OPT")
c.setFont("Helvetica-Bold", 8.5)
c.setFillColor(PURPLE)
c.drawString(col1_x + 28, y_cursor - 13, "T2 HASTE Radial Thick Slab (40 mm BH)")
c.setFont("Helvetica", 7.5)
c.setFillColor(DARK_GREY)
c.drawString(col1_x + 10, y_cursor - 26, "Plan radial slices on axial HASTE; pivot on CBD; each angle = 1 BH")
c.drawString(col1_x + 10, y_cursor - 36, "Use: Multiple-angle projections; tortuous/complex ductal anatomy")
# ──────────────────────── COLUMN 2 ───────────────────────────────────────
cx2 = col2_x + bw/2
sequences_col2 = [
("T2 SPACE 3D Coronal Gated – 1 mm", TEAL, PALE_GREEN, TEAL,
["Plan on axial HASTE (NOT free-breathing localizer)",
"Navigator: half liver seg 8, half lung at R diaphragm dome",
"TR/TE: 2500/200 ms | Flip: 12° | 1 mm iso | GATED",
"Use: GOLD STANDARD – MPR/MIP reconstructions, PSC, stones"], 6),
("T1 VIBE FS Axial – Pre-contrast (3-4 mm BH)", RED_DARK, PALE_PINK, RED_DARK,
["Plan on coronal TrueFISP; liver dome → inf. kidneys",
"TR/TE: 5/2.5 ms | Flip: 12° | Fat sat: SPAIR | BH",
"Use: T1-bright lesions: haemorrhage, proteinaceous bile,",
" fatty liver, hemochromatosis; baseline for contrast"], 7),
("T1 VIBE FS Axial – Dynamic Post-Gd", RED_DARK, PALE_PINK, RED_DARK,
["Same planning as pre-contrast T1 VIBE",
"Phases: Arterial (20-25 s) → Portal venous (60-70 s) → Delayed (3-5 min)",
"Use: HCC/PDAC/cholangioCA enhancement; pancreatic NET;",
" biliary wall enhancement (cholangitis, IgG4)"], 8),
("T1 In/Out-of-Phase Dixon Axial (BH)", PURPLE, PALE_LAVEN, PURPLE,
["TR/TE in: ~150/4.76 ms | TE out: 2.38 ms | Flip: 70°",
"Single breath-hold dual-echo acquisition",
"Use: Hepatic steatosis; adrenal adenoma; lipid-containing",
" tumours; iron deposition"], 9),
("T2 HASTE Axial – No Fat Sat (4-5 mm BH)", TEAL, LIGHT_BLUE, TEAL,
["Same planning as fat-sat HASTE axial",
"TR/TE: 2500/90 ms | Flip: 130° | No fat saturation",
"Use: Cystic vs solid lesions; liver parenchyma; cirrhosis;",
" haemangioma (very bright T2); GB wall & pericholecystic fluid"], 10),
("DWI Axial (Free-breathing, 5-6 mm)", GREEN_DARK, PALE_GREEN, GREEN_DARK,
["b-values: 50, 400, 800 | ADC map auto-generated",
"TR/TE: 4000/70 ms | EPI readout | Free-breathing",
"Use: Malignancy (PDAC, CCA, mets) – restricted diffusion;",
" biliary stricture characterisation; liver abscess"], 11),
]
y_cursor2 = top_y
first = True
for (title, tbg, fill, stroke, lines, snum) in sequences_col2:
if not first:
arrow(c, cx2, y_cursor2, cx2, y_cursor2 - gap + 5, color=stroke)
y_cursor2 -= gap
# taller box if >3 lines
bh = bh_normal + (len(lines) - 3) * 9.5
draw_rounded_rect(c, col2_x, y_cursor2 - bh, bw, bh,
r=5, fill_color=fill, stroke_color=stroke, stroke_width=1.5)
c.setFillColor(stroke)
c.roundRect(col2_x, y_cursor2 - bh, 6, bh, 3, fill=1, stroke=0)
c.circle(col2_x + 18, y_cursor2 - 10, 8, fill=1, stroke=0)
c.setFont("Helvetica-Bold", 8)
c.setFillColor(WHITE)
c.drawCentredString(col2_x + 18, y_cursor2 - 13, str(snum))
c.setFont("Helvetica-Bold", 8.5)
c.setFillColor(stroke)
c.drawString(col2_x + 28, y_cursor2 - 13, title)
c.setFont("Helvetica", 7.5)
c.setFillColor(DARK_GREY)
ly = y_cursor2 - 25
for ln in lines:
c.drawString(col2_x + 10, ly, ln)
ly -= 9.5
y_cursor2 = y_cursor2 - bh
first = False
# ── COLUMN HEADERS ────────────────────────────────────────────────────────
for cx_h, label in [(cx, "COLUMN 1 – Localizer & T2 Sequences"), (cx2, "COLUMN 2 – 3D, T1 & Advanced Sequences")]:
c.setFillColor(TEAL)
c.roundRect(cx_h - 110, top_y + 2, 220, 14, 4, fill=1, stroke=0)
c.setFont("Helvetica-Bold", 7.5)
c.setFillColor(WHITE)
c.drawCentredString(cx_h, top_y + 7, label)
# ── CONNECTIVITY ARROW (col1 → col2) ─────────────────────────────────────
# Draw a horizontal dashed line between the two columns at mid-page
mid_y = H/2 - 10
c.setStrokeColor(TEAL)
c.setDash(4, 3)
c.setLineWidth(1)
c.line(col1_x + bw, mid_y, col2_x, mid_y)
c.setDash()
c.setFillColor(TEAL)
c.setFont("Helvetica-Oblique", 7)
c.drawCentredString((col1_x + bw + col2_x)/2, mid_y + 3, "continue →")
# ── KEY PLANNING TIPS BOX ──────────────────────────────────────────────────
tips = [
"1. Plan gated/3D sequences on BH HASTE coronal – NOT the free-breathing localizer",
"2. Navigator: half liver (segment 8) / half lung at R hemidiaphragm dome",
"3. Vessels: DARK in HASTE | BRIGHT in TrueFISP (key duct vs. vessel distinction)",
"4. 50% phase oversample on all sequences to prevent wrap artifacts",
"5. 40 mm thick slab = ERCP-like projection | 1 mm SPACE = MPR/MIP dataset",
"6. Saturation bands above + below axial HASTE block to suppress pulsation artefacts",
]
tip_h = 8 + len(tips) * 10.5
tip_y = 30
draw_rounded_rect(c, 18, tip_y, W - 36, tip_h, r=6,
fill_color=PALE_AMBER, stroke_color=GOLD, stroke_width=1.5)
c.setFillColor(GOLD)
c.roundRect(18, tip_y + tip_h - 16, W - 36, 16, 5, fill=1, stroke=0)
c.setFont("Helvetica-Bold", 8.5)
c.setFillColor(NAVY)
c.drawCentredString(W/2, tip_y + tip_h - 11, "KEY PLANNING TIPS")
c.setFont("Helvetica", 7.8)
c.setFillColor(DARK_GREY)
ty = tip_y + tip_h - 26
for t in tips:
c.drawString(24, ty, t)
ty -= 10.5
# ── Footer ────────────────────────────────────────────────────────────────
c.setFillColor(NAVY)
c.rect(0, 0, W, 22, fill=1, stroke=0)
c.setFont("Helvetica", 7)
c.setFillColor(colors.lightgrey)
c.drawString(18, 8, "MRI MRCP Protocol Reference | For educational use | Sources: mrimaster.com, JAJ Gastrohepatology 2024")
c.setFillColor(GOLD)
c.drawRightString(W - 18, 8, "Page 1 / 2")
# ══════════════════════════════════════════════════════════════════════════════
# PAGE 2 – PARAMETERS TABLE + CLINICAL INDICATIONS
# ══════════════════════════════════════════════════════════════════════════════
def page2(c):
# Background
c.setFillColor(LIGHT_GREY)
c.rect(0, 0, W, H, fill=1, stroke=0)
# Header
c.setFillColor(NAVY)
c.rect(0, H - 52, W, 52, fill=1, stroke=0)
c.setFillColor(TEAL)
c.rect(0, H - 56, W, 5, fill=1, stroke=0)
c.setFont("Helvetica-Bold", 17)
c.setFillColor(WHITE)
c.drawCentredString(W/2, H - 28, "MRI MRCP Protocol – Parameters & Clinical Indications")
c.setFont("Helvetica", 9)
c.setFillColor(GOLD)
c.drawCentredString(W/2, H - 43, "Magnetic Resonance Cholangiopancreatography | Visual Reference Guide | July 2026")
# ── PARAMETERS TABLE ─────────────────────────────────────────────────────
tbl_x = 18
tbl_y = H - 68
col_widths = [22, 105, 52, 50, 42, 42, 48, 50, 48, 45] # pts
headers = ["#", "Sequence", "TR (ms)", "TE (ms)", "Flip", "Slice", "Matrix", "FOV", "Phase", "Breathing"]
rows = [
["1", "TrueFISP 3-plane Localizer", "4-6", "~2", "70°", "5-8 mm", "256×256", "400", "A>P", "BH"],
["2", "T2 TrueFISP FS Coronal", "4-5", "2", "70°", "4 mm", "320×320", "400", "R>L", "BH"],
["3", "T2 HASTE FS Axial", "2500", "90-110", "130°", "4-5 mm", "256×256", "350", "R>L", "BH"],
["4", "T2 HASTE Thick Slab LAO 40 mm", "4500", "500", "150°", "40 mm", "320×320", "450", "R>L", "BH"],
["5", "T2 HASTE Thick Slab RAO 40 mm", "4500", "500", "150°", "40 mm", "320×320", "450", "R>L", "BH"],
["OPT","T2 HASTE Radial Slab (optional)", "4500", "500", "150°", "40 mm", "320×320", "450", "R>L", "BH (each)"],
["6", "T2 SPACE 3D Coronal (GATED)", "2500", "200", "12°", "1 mm iso","320×320", "350", "R>L", "GATED"],
["7", "T1 VIBE FS Axial Pre-Gd", "5-6", "2.5", "12°", "3-4 mm", "256×256", "350", "A>P", "BH"],
["8", "T1 VIBE FS Axial Dynamic Post-Gd", "5-6", "2.5", "12°", "3-4 mm", "256×256", "350", "A>P", "BH"],
["9", "T1 In/Out-of-Phase Dixon", "150", "4.76/2.38","70°","5-6 mm", "256×256", "350", "A>P", "BH"],
["10","T2 HASTE Axial No Fat Sat", "2500", "90", "130°", "4-5 mm", "256×256", "350", "R>L", "BH"],
["11","DWI Axial (b=50/400/800)", "4000", "70", "90°", "5-6 mm", "128×128", "350", "R>L", "Free BH"],
]
row_h = 14
header_h = 17
tbl_w = sum(col_widths)
tbl_total_h = header_h + len(rows) * row_h + 2
# Header row
draw_rounded_rect(c, tbl_x, tbl_y - header_h, tbl_w, header_h, r=5,
fill_color=NAVY, stroke_color=NAVY)
c.setFont("Helvetica-Bold", 7.5)
c.setFillColor(WHITE)
xp = tbl_x
for i, (hdr, cw) in enumerate(zip(headers, col_widths)):
c.drawCentredString(xp + cw/2, tbl_y - 11, hdr)
xp += cw
# Data rows
alt_colors = [WHITE, LIGHT_BLUE]
row_fills = {
"6": PALE_GREEN,
"7": PALE_PINK,
"8": PALE_PINK,
"OPT": PALE_LAVEN,
}
y_row = tbl_y - header_h
for ridx, row in enumerate(rows):
bg = row_fills.get(row[0], alt_colors[ridx % 2])
c.setFillColor(bg)
c.setStrokeColor(colors.HexColor("#CCDDEE"))
c.setLineWidth(0.4)
c.rect(tbl_x, y_row - row_h, tbl_w, row_h, fill=1, stroke=1)
xp = tbl_x
for ci, (cell, cw) in enumerate(zip(row, col_widths)):
font = "Helvetica-Bold" if ci <= 1 else "Helvetica"
c.setFont(font, 7.2)
c.setFillColor(NAVY if ci == 0 else DARK_GREY)
if ci == 0:
c.drawCentredString(xp + cw/2, y_row - row_h + 4, cell)
elif ci == 1:
c.drawString(xp + 3, y_row - row_h + 4, cell)
else:
c.drawCentredString(xp + cw/2, y_row - row_h + 4, cell)
xp += cw
y_row -= row_h
# table caption
c.setFont("Helvetica-Oblique", 7)
c.setFillColor(MED_GREY)
c.drawString(tbl_x, y_row - 5, "BH = Breath-hold | GATED = Respiratory navigator gated | OPT = Optional | FS = Fat Saturated | *Seq 6 marked green = Gold Standard MRCP sequence")
# ── CLINICAL INDICATIONS ──────────────────────────────────────────────────
ind_y = y_row - 20
c.setFillColor(NAVY)
c.roundRect(tbl_x, ind_y - 16, tbl_w, 16, 4, fill=1, stroke=0)
c.setFont("Helvetica-Bold", 9)
c.setFillColor(WHITE)
c.drawCentredString(W/2, ind_y - 11, "CLINICAL INDICATIONS & SEQUENCE PRIORITIES")
ind_data = [
# (condition, primary_sequences, colour)
("Choledocholithiasis (CBD stones)",
"HASTE FS Axial (3) + SPACE 3D (6) + Thick Slab LAO/RAO (4,5)",
LIGHT_BLUE, TEAL),
("Primary Sclerosing Cholangitis (PSC)",
"SPACE 3D (6) — multifocal strictures + beaded pattern; T1 VIBE Dynamic (8)",
PALE_GREEN, GREEN_DARK),
("Cholangiocarcinoma (CCA)",
"SPACE 3D (6) + DWI (11) + T1 VIBE Dynamic (8); restricted diffusion at stricture",
PALE_PINK, RED_DARK),
("Pancreatic Ductal Adenocarcinoma (PDAC)",
"T1 VIBE Dynamic (7,8) + DWI (11) + SPACE 3D (6); double-duct sign",
PALE_PINK, RED_DARK),
("Chronic Pancreatitis",
"Thick Slab (4,5) + SPACE 3D (6); duct dilation, stones, strictures",
PALE_AMBER, GOLD),
("IPMN (Intraductal Papillary Mucinous Neoplasm)",
"SPACE 3D (6) + T2 HASTE No FS (10) + T1 VIBE Dynamic (8)",
PALE_LAVEN, PURPLE),
("Choledochal Cyst",
"SPACE 3D (6) — Todani classification; thick slab (4,5) for overview",
PALE_AMBER, GOLD),
("Acute Cholecystitis",
"T2 HASTE FS Axial (3) + DWI (11) — wall restricted diffusion; T1 VIBE (7)",
LIGHT_BLUE, TEAL),
("IgG4 Cholangiopathy / Autoimmune Pancreatitis",
"SPACE 3D (6) + T1 VIBE Dynamic (8); smooth long stricture + capsule-like rim",
PALE_GREEN, GREEN_DARK),
("Post-op Anatomy (Whipple, Roux-en-Y, etc.)",
"SPACE 3D (6) + T1 VIBE Dynamic (8); thick slab optional",
PALE_LAVEN, PURPLE),
]
col1_ind_x = tbl_x
col2_ind_x = tbl_x + tbl_w/2 + 3
ind_col_w = tbl_w/2 - 3
ind_box_h = 32
gap_ind = 4
current_y = ind_y - 20
for i, (cond, seqs, fill, stroke) in enumerate(ind_data):
col_x = col1_ind_x if i % 2 == 0 else col2_ind_x
if i % 2 == 0 and i > 0:
current_y -= ind_box_h + gap_ind
draw_rounded_rect(c, col_x, current_y - ind_box_h, ind_col_w, ind_box_h,
r=5, fill_color=fill, stroke_color=stroke, stroke_width=1.2)
c.setFillColor(stroke)
c.roundRect(col_x, current_y - ind_box_h, 5, ind_box_h, 2, fill=1, stroke=0)
c.setFont("Helvetica-Bold", 7.8)
c.setFillColor(stroke)
c.drawString(col_x + 9, current_y - 12, cond)
c.setFont("Helvetica", 7)
c.setFillColor(DARK_GREY)
# word-wrap sequences text
words = seqs.split()
lines_out = []
current_line = ""
for w in words:
test = (current_line + " " + w).strip()
if c.stringWidth(test, "Helvetica", 7) < ind_col_w - 16:
current_line = test
else:
lines_out.append(current_line)
current_line = w
if current_line:
lines_out.append(current_line)
ly = current_y - 23
for ln in lines_out[:2]:
c.drawString(col_x + 9, ly, ln)
ly -= 9
# ── SIGNAL INTENSITY LEGEND ───────────────────────────────────────────────
legend_y = 50
draw_rounded_rect(c, tbl_x, legend_y, tbl_w, 36, r=5,
fill_color=WHITE, stroke_color=NAVY, stroke_width=1.5)
c.setFillColor(NAVY)
c.setFont("Helvetica-Bold", 8)
c.drawString(tbl_x + 8, legend_y + 25, "SIGNAL INTENSITY CHEAT SHEET:")
items = [
("Bile / fluid", "BRIGHT (T2)", TEAL),
("Stones / air", "DARK (signal void)", RED_DARK),
("Vessels – HASTE", "DARK", DARK_GREY),
("Vessels – TrueFISP", "BRIGHT", GREEN_DARK),
("Fat – T1 pre-FS", "BRIGHT", GOLD),
("Restricted diffusion (DWI b800)", "BRIGHT / low ADC", PURPLE),
]
ix = tbl_x + 8
c.setFont("Helvetica", 7.5)
for label, sig, col in items:
c.setFillColor(col)
c.rect(ix, legend_y + 8, 8, 8, fill=1, stroke=0)
c.setFillColor(DARK_GREY)
c.drawString(ix + 10, legend_y + 9, f"{label}: {sig}")
ix += 88
# Footer
c.setFillColor(NAVY)
c.rect(0, 0, W, 22, fill=1, stroke=0)
c.setFont("Helvetica", 7)
c.setFillColor(colors.lightgrey)
c.drawString(18, 8, "MRI MRCP Protocol Reference | For educational use | Sources: mrimaster.com, JAJ Gastrohepatology 2024, Tranow/UCSF MRI Protocol")
c.setFillColor(GOLD)
c.drawRightString(W - 18, 8, "Page 2 / 2")
# ══════════════════════════════════════════════════════════════════════════════
# MAIN
# ══════════════════════════════════════════════════════════════════════════════
def main():
c = canvas.Canvas(OUTPUT, pagesize=A4)
c.setTitle("MRI MRCP Protocol Reference Guide")
c.setAuthor("Orris AI – Medical Reference")
c.setSubject("MRCP MRI Protocol Flowchart – Sequences, Planning & Clinical Indications")
page1(c)
c.showPage()
page2(c)
c.save()
print(f"PDF saved: {OUTPUT}")
if __name__ == "__main__":
main()
cd /home/daytona/workspace/mrcp-flowchart && python3 generate_mrcp_pdf.py 2>&1
ls -lh /home/daytona/workspace/mrcp-flowchart/MRCP_Protocol_Reference.pdf
MRCP Protocol Reference
PDF Document · PDF
Make a same pdf for pelvic studies of men and women the sequences used clinical indication planning with diagram
MRI pelvis protocol sequences planning male prostate female uterus ovary parameters 2024 mrimaster
https://mrimaster.com/plan-prostate
MRI prostate DWI DCE sequences planning parameters T2 axial oblique sagittal coronal STIR large FOV site:mrimaster.com
https://mrimaster.com/plan-gyne-pelvis
~/mrcp-flowchart/generate_pelvis_pdf.py
#!/usr/bin/env python3
"""
MRI Pelvis Protocol Reference Guide – Male & Female
4-page A4 PDF:
Page 1 : Male Pelvis (Prostate) – Flowchart
Page 2 : Female Pelvis (Gynaecological) – Flowchart
Page 3 : Parameters Tables (Male + Female)
Page 4 : Clinical Indications + Planning Diagrams + Signal Cheat Sheet
"""
from reportlab.lib.pagesizes import A4
from reportlab.lib import colors
from reportlab.lib.units import mm
from reportlab.pdfgen import canvas
import math
OUTPUT = "/home/daytona/workspace/mrcp-flowchart/Pelvis_MRI_Protocol_Reference.pdf"
W, H = A4 # 595.27 x 841.89 pts
# ─── Palette ─────────────────────────────────────────────────────────────────
NAVY = colors.HexColor("#0D2B55")
TEAL = colors.HexColor("#1A7A8A")
MALE_BLUE = colors.HexColor("#1565C0")
MALE_LIGHT = colors.HexColor("#DDEEFF")
FEMALE_ROSE = colors.HexColor("#AD1457")
FEMALE_LT = colors.HexColor("#FCE4EC")
GOLD = colors.HexColor("#E8A020")
AMBER_LT = colors.HexColor("#FEF3DC")
GREEN = colors.HexColor("#1B5E20")
GREEN_LT = colors.HexColor("#D8F0E4")
PURPLE = colors.HexColor("#4527A0")
PURPLE_LT = colors.HexColor("#EDE7F6")
RED = colors.HexColor("#B71C1C")
RED_LT = colors.HexColor("#FFEBEE")
GREY_LT = colors.HexColor("#F0F4F8")
GREY_MED = colors.HexColor("#666666")
GREY_DK = colors.HexColor("#333333")
WHITE = colors.white
ORANGE = colors.HexColor("#E65100")
ORANGE_LT = colors.HexColor("#FFF3E0")
# ─── Helpers ─────────────────────────────────────────────────────────────────
def rr(c, x, y, w, h, r=5, fill=WHITE, stroke=NAVY, sw=1):
c.setFillColor(fill); c.setStrokeColor(stroke); c.setLineWidth(sw)
c.roundRect(x, y, w, h, r, fill=1, stroke=1)
def header(c, title, subtitle, accent):
c.setFillColor(NAVY); c.rect(0, H-52, W, 52, fill=1, stroke=0)
c.setFillColor(accent); c.rect(0, H-57, W, 5, fill=1, stroke=0)
c.setFont("Helvetica-Bold", 16); c.setFillColor(WHITE)
c.drawCentredString(W/2, H-30, title)
c.setFont("Helvetica", 8.5); c.setFillColor(GOLD)
c.drawCentredString(W/2, H-43, subtitle)
def footer(c, page, total, note=""):
c.setFillColor(NAVY); c.rect(0,0,W,22,fill=1,stroke=0)
c.setFont("Helvetica",6.5); c.setFillColor(colors.lightgrey)
c.drawString(18,8, note or "MRI Pelvis Protocol Reference | Educational use only | Sources: mrimaster.com, 2024")
c.setFillColor(GOLD)
c.drawRightString(W-18,8,f"Page {page} / {total}")
def arrow_down(c, cx, y_from, y_to, col=NAVY):
c.setStrokeColor(col); c.setFillColor(col); c.setLineWidth(1.2)
c.line(cx, y_from, cx, y_to+5)
p = c.beginPath()
p.moveTo(cx, y_to); p.lineTo(cx-4, y_to+7); p.lineTo(cx+4, y_to+7); p.close()
c.drawPath(p, fill=1, stroke=0)
def section_badge(c, x, y, num, accent):
c.setFillColor(accent); c.circle(x, y, 9, fill=1, stroke=0)
c.setFont("Helvetica-Bold", 8); c.setFillColor(WHITE)
c.drawCentredString(x, y-3, str(num))
def seq_block(c, x, y, w, h, num, title, lines, fill, accent, font_size=7.4, title_size=8.2):
rr(c, x, y-h, w, h, r=5, fill=fill, stroke=accent, sw=1.4)
# left accent bar
c.setFillColor(accent)
c.roundRect(x, y-h, 6, h, 3, fill=1, stroke=0)
# number badge
if num:
c.setFillColor(accent); c.circle(x+18, y-11, 8.5, fill=1, stroke=0)
c.setFont("Helvetica-Bold", 8); c.setFillColor(WHITE)
c.drawCentredString(x+18, y-14, str(num))
# title
c.setFont("Helvetica-Bold", title_size); c.setFillColor(accent)
c.drawString(x+(28 if num else 10), y-14, title)
# body lines
c.setFont("Helvetica", font_size); c.setFillColor(GREY_DK)
ly = y-26
for ln in lines:
c.drawString(x+10, ly, ln); ly -= 9.8
return y-h # bottom y
def tip_box(c, y, tips, accent, bg, title="KEY PLANNING TIPS"):
h = 16 + len(tips)*10.5
rr(c, 18, y, W-36, h, r=6, fill=bg, stroke=accent, sw=1.5)
c.setFillColor(accent)
c.roundRect(18, y+h-16, W-36, 16, 5, fill=1, stroke=0)
c.setFont("Helvetica-Bold",8.5); c.setFillColor(NAVY)
c.drawCentredString(W/2, y+h-11, title)
c.setFont("Helvetica",7.8); c.setFillColor(GREY_DK)
ty = y+h-27
for t in tips:
c.drawString(24, ty, t); ty -= 10.5
# ─── ANATOMY DIAGRAM helpers ─────────────────────────────────────────────────
def draw_male_anatomy(c, cx, cy, scale=1.0):
"""Schematic sagittal male pelvis diagram."""
s = scale
# sacrum/spine background
c.setFillColor(colors.HexColor("#F5DEB3")); c.setStrokeColor(colors.HexColor("#8B7355")); c.setLineWidth(0.8)
c.ellipse(cx-8*s, cy-30*s, cx+8*s, cy+30*s, fill=1, stroke=1)
# bladder
c.setFillColor(colors.HexColor("#ADD8E6")); c.setStrokeColor(MALE_BLUE)
c.ellipse(cx-22*s, cy+8*s, cx+4*s, cy+30*s, fill=1, stroke=1)
c.setFont("Helvetica", 6*s if s > 0.7 else 5); c.setFillColor(MALE_BLUE)
c.drawCentredString(cx-9*s, cy+22*s, "Bladder")
# prostate
c.setFillColor(colors.HexColor("#FFA07A")); c.setStrokeColor(MALE_BLUE)
c.ellipse(cx-20*s, cy-8*s, cx+2*s, cy+8*s, fill=1, stroke=1)
c.setFont("Helvetica-Bold", 6.5*s if s > 0.7 else 5); c.setFillColor(RED)
c.drawCentredString(cx-9*s, cy+1*s, "Prostate")
# urethra line
c.setStrokeColor(GREY_MED); c.setLineWidth(1.2*s)
c.line(cx-9*s, cy-8*s, cx-9*s, cy-22*s)
c.setFont("Helvetica",5.5); c.setFillColor(GREY_MED)
c.drawCentredString(cx-9*s, cy-25*s, "Urethra")
# rectum
c.setFillColor(colors.HexColor("#DEB887")); c.setStrokeColor(colors.HexColor("#8B4513"))
c.ellipse(cx+4*s, cy-14*s, cx+22*s, cy+10*s, fill=1, stroke=1)
c.setFont("Helvetica",6*s if s > 0.7 else 5); c.setFillColor(GREY_DK)
c.drawCentredString(cx+13*s, cy-2*s, "Rectum")
# seminal vesicles
c.setFillColor(colors.HexColor("#98FB98")); c.setStrokeColor(GREEN)
c.ellipse(cx-4*s, cy+5*s, cx+10*s, cy+16*s, fill=1, stroke=1)
c.setFont("Helvetica",5); c.setFillColor(GREEN)
c.drawCentredString(cx+3*s, cy+12*s, "SV")
# zone labels
c.setFont("Helvetica",5.5); c.setFillColor(RED)
c.drawString(cx-19*s, cy-3*s, "PZ")
c.drawString(cx-14*s, cy+2*s, "TZ")
c.drawString(cx-9*s, cy+6*s, "CZ")
def draw_female_anatomy(c, cx, cy, scale=1.0):
"""Schematic sagittal female pelvis diagram."""
s = scale
# uterus body
c.setFillColor(colors.HexColor("#FFB6C1")); c.setStrokeColor(FEMALE_ROSE); c.setLineWidth(0.9)
# body of uterus (pear shape via polygon)
path = c.beginPath()
path.moveTo(cx, cy+28*s)
path.curveTo(cx-18*s, cy+26*s, cx-22*s, cy+10*s, cx-14*s, cy-4*s)
path.lineTo(cx-8*s, cy-10*s)
path.lineTo(cx+8*s, cy-10*s)
path.lineTo(cx+14*s, cy-4*s)
path.curveTo(cx+22*s, cy+10*s, cx+18*s, cy+26*s, cx, cy+28*s)
path.close()
c.drawPath(path, fill=1, stroke=1)
# endometrium
c.setFillColor(colors.HexColor("#FF69B4")); c.setStrokeColor(FEMALE_ROSE)
c.ellipse(cx-6*s, cy+2*s, cx+6*s, cy+22*s, fill=1, stroke=1)
c.setFont("Helvetica",5); c.setFillColor(WHITE)
c.drawCentredString(cx, cy+14*s, "Endo")
# junctional zone label
c.setFont("Helvetica-Bold",5); c.setFillColor(FEMALE_ROSE)
c.drawCentredString(cx, cy+26*s, "Uterus")
# cervix
c.setFillColor(colors.HexColor("#C71585")); c.setStrokeColor(FEMALE_ROSE)
c.ellipse(cx-8*s, cy-18*s, cx+8*s, cy-6*s, fill=1, stroke=1)
c.setFont("Helvetica",5.5); c.setFillColor(WHITE)
c.drawCentredString(cx, cy-12*s, "Cervix")
# vagina
c.setStrokeColor(FEMALE_ROSE); c.setLineWidth(1.5*s)
c.line(cx, cy-18*s, cx, cy-30*s)
c.setFont("Helvetica",5.5); c.setFillColor(FEMALE_ROSE)
c.drawCentredString(cx, cy-33*s, "Vagina")
# ovary left
c.setFillColor(colors.HexColor("#DDA0DD")); c.setStrokeColor(PURPLE)
c.ellipse(cx-28*s, cy+8*s, cx-16*s, cy+20*s, fill=1, stroke=1)
c.setFont("Helvetica",5); c.setFillColor(PURPLE)
c.drawCentredString(cx-22*s, cy+14*s, "Ovary")
# ovary right
c.ellipse(cx+16*s, cy+8*s, cx+28*s, cy+20*s, fill=1, stroke=1)
c.drawCentredString(cx+22*s, cy+14*s, "Ovary")
# bladder
c.setFillColor(colors.HexColor("#ADD8E6")); c.setStrokeColor(MALE_BLUE)
c.ellipse(cx-36*s, cy+8*s, cx-20*s, cy+26*s, fill=1, stroke=1)
c.setFont("Helvetica",5.5); c.setFillColor(MALE_BLUE)
c.drawCentredString(cx-28*s, cy+19*s, "Bladder")
# rectum
c.setFillColor(colors.HexColor("#DEB887")); c.setStrokeColor(colors.HexColor("#8B4513"))
c.ellipse(cx+18*s, cy-8*s, cx+36*s, cy+14*s, fill=1, stroke=1)
c.setFont("Helvetica",5.5); c.setFillColor(GREY_DK)
c.drawCentredString(cx+27*s, cy+4*s, "Rectum")
def draw_prostate_zones(c, cx, cy, r=28):
"""Simplified prostate zone diagram (axial view)."""
# whole gland
c.setFillColor(colors.HexColor("#FFE4C4")); c.setStrokeColor(MALE_BLUE); c.setLineWidth(1.5)
c.ellipse(cx-r, cy-int(r*0.7), cx+r, cy+int(r*0.7), fill=1, stroke=1)
# peripheral zone (outer ring)
c.setFillColor(colors.HexColor("#FFA07A")); c.setStrokeColor(MALE_BLUE); c.setLineWidth(1)
c.ellipse(cx-r+4, cy-int(r*0.7)+4, cx+r-4, cy+int(r*0.7)-4, fill=1, stroke=1)
# transition zone
c.setFillColor(colors.HexColor("#87CEEB")); c.setStrokeColor(MALE_BLUE)
c.ellipse(cx-int(r*0.45), cy-int(r*0.4), cx+int(r*0.45), cy+int(r*0.4), fill=1, stroke=1)
# central zone
c.setFillColor(colors.HexColor("#98FB98")); c.setStrokeColor(GREEN)
c.ellipse(cx-int(r*0.2), cy-int(r*0.2), cx+int(r*0.2), cy+int(r*0.2), fill=1, stroke=1)
# urethra
c.setFillColor(WHITE); c.setStrokeColor(GREY_MED); c.setLineWidth(0.8)
c.circle(cx, cy, 3, fill=1, stroke=1)
# labels
c.setFont("Helvetica",5); c.setFillColor(GREY_DK)
c.drawCentredString(cx, cy+int(r*0.6), "PZ")
c.drawCentredString(cx, cy+int(r*0.22), "TZ")
c.drawCentredString(cx, cy, "CZ")
c.setFont("Helvetica",5.5); c.setFillColor(NAVY)
c.drawCentredString(cx, cy-r-7, "Prostate Zones (Axial)")
def draw_planning_box(c, x, y, w, h, title, body_lines, fill, accent):
rr(c, x, y, w, h, r=5, fill=fill, stroke=accent, sw=1.2)
c.setFillColor(accent)
c.roundRect(x, y+h-14, w, 14, 4, fill=1, stroke=0)
c.setFont("Helvetica-Bold",7.5); c.setFillColor(WHITE)
c.drawCentredString(x+w/2, y+h-10, title)
c.setFont("Helvetica",6.8); c.setFillColor(GREY_DK)
ly = y+h-24
for ln in body_lines:
c.drawString(x+5, ly, ln); ly -= 9
# ══════════════════════════════════════════════════════════════════════════════
# PAGE 1 – MALE PELVIS (PROSTATE) FLOWCHART
# ══════════════════════════════════════════════════════════════════════════════
def page_male(c):
c.setFillColor(GREY_LT); c.rect(0,0,W,H,fill=1,stroke=0)
header(c, "MRI Male Pelvis – Prostate Protocol Flowchart",
"mpMRI (Multi-parametric MRI) | Sequences, Planning & Indications | July 2026", MALE_BLUE)
col1_x, col2_x = 16, W/2+4
cw = W/2 - 22
GAP = 7
bh = 60
# ── ANATOMY DIAGRAM (top right) ───────────────────────────────────────────
diag_x, diag_y = W-130, H-170
rr(c, diag_x, diag_y, 112, 105, r=8, fill=WHITE, stroke=MALE_BLUE, sw=1.2)
draw_male_anatomy(c, diag_x+56, diag_y+52, scale=1.05)
c.setFont("Helvetica-Bold",7); c.setFillColor(MALE_BLUE)
c.drawCentredString(diag_x+56, diag_y+6, "Male Pelvic Anatomy (Sagittal)")
# prostate zones inset
rr(c, diag_x, diag_y-80, 112, 72, r=8, fill=WHITE, stroke=MALE_BLUE, sw=1.2)
draw_prostate_zones(c, diag_x+56, diag_y-44, r=28)
# ── PATIENT PREP BOX ──────────────────────────────────────────────────────
top_y = H - 68
prep_h = 34
rr(c, col1_x, top_y-prep_h, W-268, prep_h, r=18, fill=MALE_BLUE, stroke=TEAL, sw=2)
c.setFont("Helvetica-Bold",8.5); c.setFillColor(WHITE)
c.drawCentredString(col1_x+(W-268)/2, top_y-14, "PATIENT PREP & POSITIONING")
c.setFont("Helvetica",7.2); c.setFillColor(GOLD)
c.drawCentredString(col1_x+(W-268)/2, top_y-26,
"Supine HFS | Body coil: nipple → 3″ below symphysis pubis | Laser: iliac crest | Fast 3 h | Empty bowel (buscopan IM before scan)")
# ── COLUMN 1 SEQUENCES ───────────────────────────────────────────────────
seqs_col1 = [
(1, "LOCALIZER – 3-Plane", MALE_LIGHT, MALE_BLUE,
["Axial + Coronal + Sagittal | BH < 25 s",
"Low-res T1-weighted fast scan",
"Use: Anatomy overview; plan all subsequent sequences"]),
(2, "T2 TSE Sagittal 3mm SFOV", MALE_LIGHT, MALE_BLUE,
["Plan on axial; block parallel to interpubic fibrocartilage & anal canal",
"TR/TE: 3500/110 ms | Flip: 140° | Matrix: 320×256 | FOV: 180-200 mm",
"Sat bands: top + front | Cover R acetabulum → L acetabulum",
"Use: Prostate zonal anatomy; base plan for axial oblique sequences"]),
(3, "T2 TSE Axial Large FOV 5mm (Whole Pelvis)", MALE_LIGHT, MALE_BLUE,
["Plan on sagittal; block vertical across pelvis",
"TR/TE: 5000/110 ms | Flip: 150° | Matrix: 384×320 | FOV: 350-400 mm",
"Cover ant abdominal wall → sacrum; parallel to hip joint line",
"Use: Lymph node assessment; local tumour spread; pelvic sidewall"]),
(4, "T1 TSE Axial Large FOV 6mm (Whole Pelvis)", MALE_LIGHT, MALE_BLUE,
["Plan same as T2 large FOV axial on sagittal",
"TR/TE: 500/15 ms | Flip: 130° | Matrix: 384×320 | FOV: 380-400 mm",
"Use: Bone metastases (T1 dark = replacement of marrow fat);",
" Haematoma; nodal assessment"]),
(5, "T2 STIR Coronal Large FOV 5mm", AMBER_LT, GOLD,
["Plan on sagittal; block parallel to lumbar spine",
"TR/TE/TI: 4500/110/150 ms | Flip: 130° | FOV: 380-400 mm",
"Cover para-aortic area from mid-abdomen to sacrum",
"Use: Para-aortic/pre-sacral lymph nodes; bone oedema; soft tissue oedema"]),
]
y_cur1 = top_y - prep_h
cx1 = col1_x + cw/2
for (num, title, fill, accent, lines) in seqs_col1:
bh_actual = 18 + len(lines)*10
arrow_down(c, cx1, y_cur1, y_cur1-GAP, accent)
y_cur1 -= GAP
y_cur1 = seq_block(c, col1_x, y_cur1, cw, bh_actual, num, title, lines, fill, accent)
# ── COLUMN 2 SEQUENCES ───────────────────────────────────────────────────
seqs_col2 = [
(6, "T2 TSE Axial Oblique 3mm SFOV ★ KEY", MALE_LIGHT, MALE_BLUE,
["Plan on sagittal; angle block PERPENDICULAR to prostate long axis",
"TR/TE: 3500/110 ms | Flip: 140° | Matrix: 320×256 | FOV: 180-230 mm",
"Check angle in coronal (parallel to interpubic fibrocartilage)",
"Cover prostate + seminal vesicles | Sat bands top + front",
"Use: Prostate zones TZ/PZ/CZ/SV; T-staging; neurovascular bundles"]),
(7, "T2 TSE Coronal Oblique 3mm SFOV", MALE_LIGHT, MALE_BLUE,
["Plan on sagittal; block PARALLEL to prostate long axis",
"TR/TE: 3500/110 ms | Flip: 140° | Matrix: 320×256 | FOV: 180-230 mm",
"Cover entire prostate from apex to base + seminal vesicles",
"Use: Extracapsular extension (ECE) at apex; SV invasion; rectal wall"]),
(8, "DWI / ZOOMit EPI 4-trace Axial Oblique 3mm SFOV", GREEN_LT, GREEN,
["Plan on T2 sagittal; same angle as T2 axial oblique SFOV",
"TR/TE: 5000/70 ms | b-values: 50, 500, 800, 1400 | FOV: 180 mm",
"ADC map auto-generated | ZOOMit (inner volume DWI) preferred",
"Use: Tumour detection PZ/TZ; restricted diffusion (low ADC) = suspicious;",
" PI-RADS v2.1 dominant sequence for PZ lesions"]),
(9, "T1 VIBE 3D Dynamic Axial (DCE) – 1 Pre + 8 Post", RED_LT, RED,
["Plan same as T2 axial oblique SFOV",
"TR/TE: 4/1.5 ms | Flip: 10° | Slice: 2 mm | FOV: 200-250 mm",
"Gd-DTPA 0.1 mmol/kg @ 3 mL/s | 9 dynamic phases (~7 min total)",
"Use: PI-RADS DCE (+) = early enhancement; tumour vascularity;",
" recurrent disease post-treatment; post-prostatectomy assessment"]),
(None, "T1 TSE FS Axial 3mm SFOV (Post-prostatectomy only)", RED_LT, RED,
["Plan same as T2 axial oblique | Only for post-prostatectomy patients",
"TR/TE: 600/15 ms | Flip: 130° | Fat sat | FOV: 200 mm",
"Use: Blood / haematoma in prostatectomy bed; anastomotic recurrence"]),
]
y_cur2 = top_y
cx2 = col2_x + cw/2
first = True
for (num, title, fill, accent, lines) in seqs_col2:
bh_actual = 18 + len(lines)*10
if not first:
arrow_down(c, cx2, y_cur2, y_cur2-GAP, accent)
y_cur2 -= GAP
y_cur2 = seq_block(c, col2_x, y_cur2, cw, bh_actual, num, title, lines, fill, accent)
first = False
# ── COLUMN HEADERS ────────────────────────────────────────────────────────
for cx_h, lbl in [(cx1,"COLUMN 1 – Localizer, Sagittal & Large FOV"),
(cx2,"COLUMN 2 – Small FOV, DWI & DCE (mpMRI Core)")]:
c.setFillColor(MALE_BLUE); c.roundRect(cx_h-110, top_y+2, 220, 14, 4, fill=1, stroke=0)
c.setFont("Helvetica-Bold",7.5); c.setFillColor(WHITE)
c.drawCentredString(cx_h, top_y+7, lbl)
# ── PI-RADS reminder strip ────────────────────────────────────────────────
pirads_y = max(y_cur1, y_cur2) - 14
rr(c, 16, pirads_y, W-32, 30, r=5, fill=MALE_LIGHT, stroke=MALE_BLUE, sw=1.2)
c.setFont("Helvetica-Bold",7.5); c.setFillColor(MALE_BLUE)
c.drawString(22, pirads_y+19, "PI-RADS v2.1 Dominant Sequences:")
c.setFont("Helvetica",7.2); c.setFillColor(GREY_DK)
c.drawString(22, pirads_y+9, "Peripheral Zone (PZ): DWI is dominant | Transition Zone (TZ): T2 axial oblique is dominant | DCE: ancillary (+/−)")
# ── TIPS ─────────────────────────────────────────────────────────────────
tips = [
"1. Angle T2 axial oblique PERPENDICULAR to the prostate long axis on sagittal T2 – this is the cardinal rule",
"2. Laser centre: iliac crest (not xiphoid as in MRCP/abdomen)",
"3. Buscopan (antiperistaltic) IM before scan reduces bowel motion artefacts significantly",
"4. Sat bands anterior + superior on all small FOV sequences to suppress bowel + bladder artefacts",
"5. ZOOMit DWI (inner volume): reduced geometric distortion vs standard EPI – preferred for prostate",
"6. Large FOV sequences: cover mid-abdomen → sacrum for lymph node staging (para-aortic, iliac, obturator)",
]
tip_box(c, 28, tips, MALE_BLUE, AMBER_LT)
footer(c, 1, 4)
# ══════════════════════════════════════════════════════════════════════════════
# PAGE 2 – FEMALE PELVIS (GYNAECOLOGICAL) FLOWCHART
# ══════════════════════════════════════════════════════════════════════════════
def page_female(c):
c.setFillColor(GREY_LT); c.rect(0,0,W,H,fill=1,stroke=0)
header(c, "MRI Female Pelvis – Gynaecological Protocol Flowchart",
"Uterus, Cervix, Ovaries & Adnexa | Endometrial / Cervical / Ovarian Protocols | July 2026", FEMALE_ROSE)
col1_x, col2_x = 16, W/2+4
cw = W/2 - 22
GAP = 7
# ── ANATOMY DIAGRAM ───────────────────────────────────────────────────────
diag_x, diag_y = W-130, H-175
rr(c, diag_x, diag_y, 112, 115, r=8, fill=WHITE, stroke=FEMALE_ROSE, sw=1.2)
draw_female_anatomy(c, diag_x+56, diag_y+58, scale=1.0)
c.setFont("Helvetica-Bold",7); c.setFillColor(FEMALE_ROSE)
c.drawCentredString(diag_x+56, diag_y+6, "Female Pelvic Anatomy (Axial)")
# uterine zones inset
rr(c, diag_x, diag_y-75, 112, 68, r=8, fill=WHITE, stroke=FEMALE_ROSE, sw=1.2)
ux, uy = diag_x+56, diag_y-42
# draw uterine layers
c.setFillColor(colors.HexColor("#FFB6C1")); c.setStrokeColor(FEMALE_ROSE); c.setLineWidth(1)
c.ellipse(ux-30, uy-22, ux+30, uy+22, fill=1, stroke=1) # outer myometrium
c.setFillColor(colors.HexColor("#8B0045")); c.setStrokeColor(FEMALE_ROSE)
c.ellipse(ux-22, uy-16, ux+22, uy+16, fill=1, stroke=1) # junctional zone
c.setFillColor(colors.HexColor("#FF69B4")); c.setStrokeColor(FEMALE_ROSE)
c.ellipse(ux-14, uy-10, ux+14, uy+10, fill=1, stroke=1) # endometrium
c.setFont("Helvetica",5); c.setFillColor(WHITE)
c.drawCentredString(ux, uy, "Endo")
c.setFillColor(WHITE); c.drawCentredString(ux, uy+18, "JZ")
c.setFillColor(GREY_DK); c.drawCentredString(ux, uy-29, "Outer Myo")
c.setFont("Helvetica-Bold",6.5); c.setFillColor(FEMALE_ROSE)
c.drawCentredString(ux, diag_y-8, "Uterine Layers (T2 Sagittal)")
# ── PREP ─────────────────────────────────────────────────────────────────
top_y = H - 68
prep_h = 34
rr(c, col1_x, top_y-prep_h, W-268, prep_h, r=18, fill=FEMALE_ROSE, stroke=TEAL, sw=2)
c.setFont("Helvetica-Bold",8.5); c.setFillColor(WHITE)
c.drawCentredString(col1_x+(W-268)/2, top_y-14, "PATIENT PREP & POSITIONING")
c.setFont("Helvetica",7.2); c.setFillColor(GOLD)
c.drawCentredString(col1_x+(W-268)/2, top_y-26,
"Supine HFS | Body coil: nipple → symphysis pubis | Laser: iliac crest | Fast 3 h | Buscopan IM | Moderate bladder filling | Vaginal gel (GYN cancer)")
# ── COLUMN 1 ──────────────────────────────────────────────────────────────
seqs_col1 = [
(1, "LOCALIZER – 3-Plane", FEMALE_LT, FEMALE_ROSE,
["Axial + Coronal + Sagittal | BH < 25 s | Low-res T1",
"Use: Anatomy localisation; plan all subsequent sequences"]),
(2, "T2 TSE Sagittal 3mm SFOV ★ FIRST KEY SEQUENCE", FEMALE_LT, FEMALE_ROSE,
["Plan on axial; block parallel to uterine long axis",
"TR/TE: 3500/110 ms | Flip: 140° | FOV: 270-300 mm | Phase: A>P",
"Cover entire uterus from fundus to cervix + vagina + bladder",
"Use: Uterine flexion/version; plan all oblique sequences;",
" endometrial thickness; fibroid mapping; junctional zone"]),
(3, "T2 TSE Axial Large FOV 6mm (Whole Pelvis)", FEMALE_LT, FEMALE_ROSE,
["Plan on sagittal; block vertical across pelvis",
"TR/TE: 5000/110 ms | Flip: 150° | FOV: 350-400 mm | Phase: R>L",
"Cover ant. wall → sacrum; parallel to hip joint line",
"Use: Ovaries/adnexa; bladder/rectum involvement; lymph nodes;",
" pelvic sidewall invasion; overall disease extent"]),
(4, "T1 TSE Axial Large FOV 6mm (Whole Pelvis)", FEMALE_LT, FEMALE_ROSE,
["Same planning as T2 large FOV axial",
"TR/TE: 500/15 ms | Flip: 130° | FOV: 380-400 mm | Phase: R>L",
"Use: Bone metastases (dark marrow); haemorrhagic lesions (bright);",
" endometrioma identification (T1 bright); lymphadenopathy"]),
(5, "T2 STIR Coronal Large FOV 5mm", AMBER_LT, GOLD,
["Plan on sagittal; block parallel to lumbar spine",
"TR/TE/TI: 4500/110/150 ms | Flip: 130° | FOV: 380-400 mm",
"Use: Para-aortic/iliac lymph nodes; bone oedema; peritoneal disease"]),
]
y_cur1 = top_y - prep_h
cx1 = col1_x + cw/2
for (num, title, fill, accent, lines) in seqs_col1:
bh_actual = 18 + len(lines)*10
arrow_down(c, cx1, y_cur1, y_cur1-GAP, accent)
y_cur1 -= GAP
y_cur1 = seq_block(c, col1_x, y_cur1, cw, bh_actual, num, title, lines, fill, accent)
# ── COLUMN 2 ──────────────────────────────────────────────────────────────
seqs_col2 = [
(6, "T2 TSE Axial Oblique 3mm SFOV ★ KEY", FEMALE_LT, FEMALE_ROSE,
["Plan on sagittal T2; angle block PERPENDICULAR to endometrium/uterus",
"TR/TE: 3500/110 ms | Flip: 140° | FOV: 180-230 mm | Phase: R>L",
"Check angle in coronal (perpendicular to endometrium)",
"Cover whole uterus + ovaries | Sat bands top + front",
"Use: Myometrial invasion depth (FIGO staging); parametrial invasion"]),
(7, "T2 TSE Coronal Oblique 3mm SFOV (Uterus)", FEMALE_LT, FEMALE_ROSE,
["Plan on sagittal T2; block PARALLEL to endometrium",
"TR/TE: 3500/110 ms | Flip: 140° | FOV: 180-230 mm | Phase: R>L",
"Use: Cervical stroma invasion; uterine cornual lesions; adnexal detail;",
" fibroid exact location (subserosal/intramural/submucosal)"]),
(8, "T1 TSE FS Axial 3mm SFOV", FEMALE_LT, FEMALE_ROSE,
["Plan on sagittal T2; angle perpendicular to uterus",
"TR/TE: 600/15 ms | Flip: 130° | Fat sat | FOV: 200-250 mm",
"Use: Endometriomas (T1 bright FS = haemorrhagic cyst = shading sign);",
" haemorrhagic lesion characterisation; pelvic adhesions"]),
(9, "DWI EPI Axial Oblique 3mm SFOV", GREEN_LT, GREEN,
["Plan same angle as T2 axial oblique SFOV",
"TR/TE: 5000/70 ms | b-values: 50, 800 (+ 1400 optional) | FOV: 250 mm",
"ADC map generated | Free-breathing or BH",
"Use: Endometrial CA (restricted diffusion); cervical CA; ovarian CA;",
" lymph node malignancy; treatment response assessment"]),
(10, "T1 VIBE Dixon 3D Sagittal Dynamic 1Pre+8Post (DCE)", RED_LT, RED,
["Plan on axial; block parallel to endometrium",
"TR/TE: 4-5/2-3 ms | Flip: 10° | Slice: 2 mm | FOV: 200-250 mm",
"9 dynamic phases | Gd-DTPA 0.1 mmol/kg @ 2 mL/s",
"Use: Endometrial CA myometrial invasion; cervical CA parametrial;",
" ovarian mass characterisation; fibroid enhancement (pre-embolisation)"]),
]
y_cur2 = top_y
cx2 = col2_x + cw/2
first = True
for (num, title, fill, accent, lines) in seqs_col2:
bh_actual = 18 + len(lines)*10
if not first:
arrow_down(c, cx2, y_cur2, y_cur2-GAP, accent)
y_cur2 -= GAP
y_cur2 = seq_block(c, col2_x, y_cur2, cw, bh_actual, num, title, lines, fill, accent)
first = False
# ── COLUMN HEADERS ────────────────────────────────────────────────────────
for cx_h, lbl in [(cx1,"COLUMN 1 – Localizer, Sagittal & Large FOV"),
(cx2,"COLUMN 2 – Small FOV Obliques, DWI & DCE")]:
c.setFillColor(FEMALE_ROSE); c.roundRect(cx_h-110, top_y+2, 220, 14, 4, fill=1, stroke=0)
c.setFont("Helvetica-Bold",7.5); c.setFillColor(WHITE)
c.drawCentredString(cx_h, top_y+7, lbl)
# ── FIGO reminder ────────────────────────────────────────────────────────
figo_y = max(y_cur1, y_cur2) - 14
rr(c, 16, figo_y, W-32, 30, r=5, fill=FEMALE_LT, stroke=FEMALE_ROSE, sw=1.2)
c.setFont("Helvetica-Bold",7.5); c.setFillColor(FEMALE_ROSE)
c.drawString(22, figo_y+19, "Endometrial CA FIGO Staging Key MRI Features:")
c.setFont("Helvetica",7.2); c.setFillColor(GREY_DK)
c.drawString(22, figo_y+9,
"Stage I: Limited to uterus (IA <50% myometrium, IB ≥50%) | Stage II: Cervical stroma | "
"Stage III: Adnexa/nodes/vagina | Stage IV: Bladder/rectum/distant")
# ── TIPS ─────────────────────────────────────────────────────────────────
tips = [
"1. T2 sagittal is FIRST – determines uterine version (anteverted/retroverted) and plans all oblique sequences",
"2. Axial oblique = PERPENDICULAR to endometrium; Coronal oblique = PARALLEL to endometrium (rule of 90°)",
"3. Phase direction R>L for axial sequences (suppresses anterior abdominal fat ghost artefact)",
"4. Phase direction H>F for sagittal sequences (avoids pulsation artefact from iliac vessels)",
"5. Vaginal gel: required for all GYN cancer staging (cervical, vaginal, endometrial) – fills vaginal canal",
"6. Buscopan (antiperistaltic) IM before scan – reduces bowel motion artefacts substantially",
]
tip_box(c, 28, tips, FEMALE_ROSE, FEMALE_LT)
footer(c, 2, 4)
# ══════════════════════════════════════════════════════════════════════════════
# PAGE 3 – PARAMETERS TABLES (Male + Female)
# ══════════════════════════════════════════════════════════════════════════════
def page_params(c):
c.setFillColor(GREY_LT); c.rect(0,0,W,H,fill=1,stroke=0)
header(c, "MRI Pelvis Protocol – Sequence Parameters Reference",
"Male (Prostate) & Female (Gynaecological) | All Sequences | July 2026", TEAL)
def params_table(c, x, y, title, accent, rows, col_widths, headers):
tbl_w = sum(col_widths)
hdr_h = 16
row_h = 13
# title banner
rr(c, x, y, tbl_w, 16, r=4, fill=accent, stroke=accent, sw=1)
c.setFont("Helvetica-Bold",8.5); c.setFillColor(WHITE)
c.drawCentredString(x+tbl_w/2, y+5, title)
y -= hdr_h
# header row
c.setFillColor(NAVY); c.setStrokeColor(NAVY)
c.rect(x, y-hdr_h, tbl_w, hdr_h, fill=1, stroke=0)
c.setFont("Helvetica-Bold",6.8); c.setFillColor(WHITE)
xp = x
for h, cw in zip(headers, col_widths):
c.drawCentredString(xp+cw/2, y-hdr_h+4, h); xp += cw
y -= hdr_h
alt = [colors.HexColor("#EAF4FB"), WHITE]
for ri, row in enumerate(rows):
bg = alt[ri%2]
c.setFillColor(bg); c.setStrokeColor(colors.HexColor("#CCDDEE"))
c.setLineWidth(0.3); c.rect(x, y-row_h, tbl_w, row_h, fill=1, stroke=1)
xp = x
for ci, (cell, cw) in enumerate(zip(row, col_widths)):
fn = "Helvetica-Bold" if ci<=1 else "Helvetica"
c.setFont(fn, 6.5); c.setFillColor(GREY_DK)
if ci<=1: c.drawString(xp+3, y-row_h+4, cell)
else: c.drawCentredString(xp+cw/2, y-row_h+4, cell)
xp += cw
y -= row_h
return y - 4
male_headers = ["#","Sequence","TR","TE","Flip","Slice","Matrix","FOV","Phase","Notes"]
male_cw = [14, 130, 38, 42, 30, 32, 46, 36, 32, 95]
male_rows = [
["1","3-Plane TrueFISP Localizer","4-6 ms","~2 ms","70°","5-8 mm","256×256","400","A>P","BH < 25 s"],
["2","T2 TSE Sagittal 3mm SFOV","3500","100-120","140°","3 mm","320×256","180-200","H>F","BH | Sat bands top+front"],
["3","T2 TSE Axial Large FOV 5mm","5000","110","150°","5 mm","384×320","350-400","R>L","BH | whole pelvis"],
["4","T1 TSE Axial Large FOV 6mm","500","15-25","130°","6 mm","384×320","380-400","R>L","BH | bone mets/nodes"],
["5","T2 STIR Coronal Large FOV 5mm","4500","110","130°","5 mm","384×384","380-400","R>L","TI 150 | BH | para-aortic"],
["6","T2 TSE Axial Oblique 3mm SFOV ★","3500","110","140°","3 mm","320×256","180-230","R>L","BH | ⊥ prostate long axis"],
["7","T2 TSE Coronal Oblique 3mm SFOV","3500","110","140°","3 mm","320×256","180-230","R>L","BH | ∥ prostate long axis"],
["8","DWI ZOOMit EPI 4-trace Axial Obl","5000","70","90°","3 mm","160×160","180","R>L","b=50/500/800/1400 | ADC"],
["9","T1 VIBE 3D Dynamic Axial (DCE)","4-5","1.5","10°","2 mm","256×256","200-250","R>L","1pre+8post | Gd 0.1mmol/kg"],
["OPT","T1 TSE FS Axial SFOV (post-op)","600","15","130°","3 mm","320×256","200","R>L","Post-prostatectomy only | FS"],
]
female_headers = ["#","Sequence","TR","TE","Flip","Slice","Matrix","FOV","Phase","Notes"]
female_cw = [14, 130, 38, 42, 30, 32, 46, 36, 32, 95]
female_rows = [
["1","3-Plane TrueFISP Localizer","4-6 ms","~2 ms","70°","5-8 mm","256×256","400","A>P","BH < 25 s"],
["2","T2 TSE Sagittal 3mm SFOV ★","3500","100-120","140°","3 mm","320×320","270-300","A>P","BH | ∥ uterine long axis"],
["3","T2 TSE Axial Large FOV 6mm","5000","110","150°","6 mm","384×384","350-400","R>L","BH | whole pelvis"],
["4","T1 TSE Axial Large FOV 6mm","400-600","15-25","130°","6 mm","384×384","350-400","R>L","BH | haem/nodes/bone mets"],
["5","T2 STIR Coronal Large FOV 5mm","4500","110","160°","5 mm","384×320","380-400","R>L","TI 150 | BH | para-aortic"],
["6","T2 TSE Axial Oblique 3mm SFOV ★","3500","100-120","130-150°","3 mm","320×256","180-230","R>L","BH | ⊥ endometrium"],
["7","T2 TSE Coronal Oblique 3mm SFOV","3500","100-120","130-150°","3 mm","320×256","180-230","R>L","BH | ∥ endometrium"],
["8","T1 TSE Fat Sat Axial 3mm SFOV","600","15","130°","3 mm","320×256","200-250","R>L","BH | endometrioma detection"],
["9","DWI EPI Axial Oblique 3mm SFOV","5000","70","90°","3 mm","192×192","250","R>L","b=50/800(+1400) | ADC map"],
["10","T1 VIBE Dixon 3D Sagittal Dynamic","4-5","2-3","10°","2 mm","256×256","200-250","R>L","1pre+8post | Gd | Dixon"],
]
y = H - 70
y = params_table(c, 12, y, "MALE PELVIS (PROSTATE) – SEQUENCE PARAMETERS", MALE_BLUE,
male_rows, male_cw, male_headers)
y -= 10
y = params_table(c, 12, y, "FEMALE PELVIS (GYNAECOLOGICAL) – SEQUENCE PARAMETERS", FEMALE_ROSE,
female_rows, female_cw, female_headers)
# ── NOTES ────────────────────────────────────────────────────────────────
y -= 6
rr(c, 12, y-42, W-24, 42, r=5, fill=AMBER_LT, stroke=GOLD, sw=1.2)
c.setFillColor(GOLD); c.roundRect(12, y-14, W-24, 14, 4, fill=1, stroke=0)
c.setFont("Helvetica-Bold",8); c.setFillColor(NAVY)
c.drawCentredString(W/2, y-10, "ABBREVIATION & NOTES KEY")
c.setFont("Helvetica",7.2); c.setFillColor(GREY_DK)
notes = [
"BH = Breath-hold | FS = Fat Saturated | SFOV = Small Field of View | ★ = Key/Core sequence | ⊥ = Perpendicular | ∥ = Parallel",
"STIR TI 150 ms = Short-tau Inversion Recovery | ZOOMit = Inner volume DWI (reduced geometric distortion) | ADC = Apparent Diffusion Coefficient",
"DCE = Dynamic Contrast-Enhanced | Dixon = Fat/water separation technique | mpMRI = Multi-parametric MRI | Gd = Gadolinium contrast agent",
]
ny = y - 24
for n in notes:
c.drawCentredString(W/2, ny, n); ny -= 10
footer(c, 3, 4)
# ══════════════════════════════════════════════════════════════════════════════
# PAGE 4 – CLINICAL INDICATIONS + PLANNING DIAGRAMS + SIGNAL CHEAT SHEET
# ══════════════════════════════════════════════════════════════════════════════
def page_indications(c):
c.setFillColor(GREY_LT); c.rect(0,0,W,H,fill=1,stroke=0)
header(c, "MRI Pelvis – Clinical Indications & Planning Diagrams",
"Male & Female | Sequence Priorities by Pathology | July 2026", TEAL)
y = H - 68
# ─── MALE INDICATIONS ────────────────────────────────────────────────────
c.setFillColor(MALE_BLUE); c.roundRect(12, y-16, (W-28)/2, 16, 4, fill=1, stroke=0)
c.setFont("Helvetica-Bold",9); c.setFillColor(WHITE)
c.drawCentredString(12+(W-28)/4, y-11, "MALE PELVIS – CLINICAL INDICATIONS")
male_inds = [
("Prostate Cancer (PCa) – Staging",
"T2 Axial Obl SFOV (6) + DWI (8) + DCE T1 VIBE (9)\nPI-RADS ≥3 lesion = suspect | Low ADC + early enhancement",
MALE_LIGHT, MALE_BLUE),
("Rising PSA / Pre-biopsy Targeting",
"mpMRI: T2 Ax Obl (6) + DWI (8) + DCE (9)\nLocate index lesion in PZ/TZ; guide targeted biopsy",
MALE_LIGHT, MALE_BLUE),
("Extracapsular Extension (ECE)",
"T2 Axial Obl (6) + T2 Coronal Obl (7)\nIrregular capsule; tumour angulation > 1 cm; NVB involvement",
MALE_LIGHT, MALE_BLUE),
("Seminal Vesicle Invasion (SVI)",
"T2 Axial Obl (6) + T2 Coronal Obl (7) + DWI (8)\nLow signal in SV on T2; restricted diffusion",
MALE_LIGHT, MALE_BLUE),
("Post-Prostatectomy / Recurrence",
"T1 VIBE Dynamic (9) + T1 FS Axial (OPT) + DWI (8)\nSoft-tissue nodule at anastomosis with enhancement",
RED_LT, RED),
("Benign Prostatic Hyperplasia (BPH)",
"T2 Sagittal (2) + T2 Axial Obl (6)\nTZ nodules; elevated PI-RADS TZ; urethral compression",
MALE_LIGHT, MALE_BLUE),
("Bladder Cancer (Staging)",
"T2 SFOV all planes (6,7) + DWI (8) + T1 VIBE DCE (9)\nT-stage: depth of muscle invasion; perivesical fat",
AMBER_LT, GOLD),
("Lymph Node Staging",
"T2 Large FOV (3) + STIR Coronal (5) + DWI (8)\nSize >10 mm short axis; restricted diffusion; irregular border",
AMBER_LT, GOLD),
]
female_inds = [
("Endometrial Cancer – FIGO Staging",
"T2 Axial Obl (6) + T2 Sag (2) + DWI (9) + DCE (10)\nMyometrial invasion depth; cervical stroma; junctional zone",
FEMALE_LT, FEMALE_ROSE),
("Cervical Cancer – Staging",
"T2 Sag (2) + T2 Axial Obl (6) + DWI (9) + DCE (10)\nParametrial invasion; vaginal/bladder/rectal involvement",
FEMALE_LT, FEMALE_ROSE),
("Ovarian Cancer / Adnexal Mass",
"T2 Large FOV (3) + T1 FS (8) + DWI (9) + DCE (10)\nSolid components; peritoneal deposits; bilateral lesions",
FEMALE_LT, FEMALE_ROSE),
("Endometriosis",
"T1 FS Axial (8) [shading sign] + T2 Axial Obl (6)\nT1 bright FS = haemorrhagic endometrioma; junctional zone thickening",
PURPLE_LT, PURPLE),
("Uterine Fibroids (pre-embolisation)",
"T2 Sag (2) + T2 Axial Obl (6) + T1 VIBE DCE (10)\nSubmucosal/intramural/subserosal classification; feeding vessels",
FEMALE_LT, FEMALE_ROSE),
("Congenital Uterine Anomalies",
"T2 Coronal Obl (7) + T2 Sagittal (2)\nArcuate/septate/bicornuate/didelphys classification",
FEMALE_LT, FEMALE_ROSE),
("Pelvic Inflammatory Disease / Abscess",
"T2 Large FOV (3) + DWI (9) + T1 VIBE DCE (10)\nRestricted diffusion = pus; rim enhancement; free fluid",
ORANGE_LT, ORANGE),
("Placenta Previa / Accreta (pregnancy)",
"T2 Sagittal (2) + T2 Axial Large FOV (3) [NO Gd]\nLoss of decidual interface; placental tissue into myometrium",
ORANGE_LT, ORANGE),
]
# Two halves
half_w = (W-28)/2
ind_h = 30
ind_gap = 3
# Male column
ym = y - 18
for i, (title, body, fill, accent) in enumerate(male_inds):
rr(c, 12, ym-ind_h, half_w-4, ind_h, r=4, fill=fill, stroke=accent, sw=1.2)
c.setFillColor(accent)
c.roundRect(12, ym-ind_h, 5, ind_h, 2, fill=1, stroke=0)
c.setFont("Helvetica-Bold",7.5); c.setFillColor(accent)
c.drawString(20, ym-12, title)
c.setFont("Helvetica",6.8); c.setFillColor(GREY_DK)
for li, ln in enumerate(body.split("\n")):
c.drawString(20, ym-22-li*9, ln)
ym -= ind_h + ind_gap
# Female column
yf = y - 18
fx = 12 + half_w + 4
for i, (title, body, fill, accent) in enumerate(female_inds):
rr(c, fx, yf-ind_h, half_w-4, ind_h, r=4, fill=fill, stroke=accent, sw=1.2)
c.setFillColor(accent)
c.roundRect(fx, yf-ind_h, 5, ind_h, 2, fill=1, stroke=0)
c.setFont("Helvetica-Bold",7.5); c.setFillColor(accent)
c.drawString(fx+8, yf-12, title)
c.setFont("Helvetica",6.8); c.setFillColor(GREY_DK)
for li, ln in enumerate(body.split("\n")):
c.drawString(fx+8, yf-22-li*9, ln)
yf -= ind_h + ind_gap
# Female header
c.setFillColor(FEMALE_ROSE); c.roundRect(12+half_w+4, y-16, half_w-4, 16, 4, fill=1, stroke=0)
c.setFont("Helvetica-Bold",9); c.setFillColor(WHITE)
c.drawCentredString(12+half_w+4+half_w/2-2, y-11, "FEMALE PELVIS – CLINICAL INDICATIONS")
# ─── PLANNING DIAGRAMS ────────────────────────────────────────────────────
bottom_y = min(ym, yf) - 8
diag_h = bottom_y - 32
diag_section_y = bottom_y
# Section header
rr(c, 12, diag_section_y-16, W-24, 16, r=4, fill=NAVY, stroke=NAVY)
c.setFont("Helvetica-Bold",8.5); c.setFillColor(WHITE)
c.drawCentredString(W/2, diag_section_y-11, "PLANNING DIAGRAMS – ANGULATION RULES")
dh = diag_section_y - 18
box_w = (W-36)/3
boxes = [
(12, "Male T2 Axial Oblique\n(⊥ to Prostate Long Axis)",
["Plan on SAGITTAL view","Angle block ⊥ to prostate","long axis & anal canal","Cover prostate + SV"], MALE_BLUE, MALE_LIGHT),
(12+box_w+6, "Female T2 Sagittal\n(∥ to Uterine Long Axis)",
["Plan on AXIAL view","Block parallel to uterus","(fundus → cervix)","Phase direction: A>P"], FEMALE_ROSE, FEMALE_LT),
(12+2*box_w+12,"Female T2 Axial Oblique\n(⊥ to Endometrium)",
["Plan on SAGITTAL T2","Block ⊥ to endometrium","Check on CORONAL view","Phase direction: R>L"], FEMALE_ROSE, FEMALE_LT),
]
bw_d = box_w
bh_d = max(dh - 28, 40)
for bx, btitle, blines, accent, fill in boxes:
rr(c, bx, 28, bw_d, bh_d, r=6, fill=fill, stroke=accent, sw=1.2)
c.setFillColor(accent); c.roundRect(bx, 28+bh_d-14, bw_d, 14, 5, fill=1, stroke=0)
c.setFont("Helvetica-Bold",7); c.setFillColor(WHITE)
for li, tl in enumerate(btitle.split("\n")):
c.drawCentredString(bx+bw_d/2, 28+bh_d-9-li*9, tl)
c.setFont("Helvetica",6.8); c.setFillColor(GREY_DK)
ly2 = 28+bh_d-26
for bl in blines:
c.drawCentredString(bx+bw_d/2, ly2, bl); ly2 -= 9
# Draw small schematic in each box
mid_x = bx + bw_d/2
mid_y = 28 + bh_d/2 - 8
if "Prostate" in btitle:
draw_prostate_zones(c, mid_x, mid_y, r=22)
# angle indicator
c.setStrokeColor(RED); c.setDash(3,2); c.setLineWidth(1.2)
c.line(mid_x-20, mid_y+8, mid_x+20, mid_y+8)
c.setDash()
c.setStrokeColor(MALE_BLUE); c.setLineWidth(1.5)
c.line(mid_x-15, mid_y-22, mid_x+15, mid_y+22)
elif "Sagittal" in btitle:
# simplified uterus sagittal outline
c.setFillColor(colors.HexColor("#FFB6C1")); c.setStrokeColor(FEMALE_ROSE); c.setLineWidth(1.2)
c.ellipse(mid_x-12, mid_y-18, mid_x+12, mid_y+18, fill=1, stroke=1)
c.setFillColor(colors.HexColor("#FF69B4")); c.setStrokeColor(FEMALE_ROSE)
c.ellipse(mid_x-5, mid_y-12, mid_x+5, mid_y+12, fill=1, stroke=1)
# parallel planning line
c.setStrokeColor(MALE_BLUE); c.setDash(3,2); c.setLineWidth(1.2)
c.line(mid_x-22, mid_y, mid_x+22, mid_y)
c.setDash()
else:
# simplified uterus axial
c.setFillColor(colors.HexColor("#FFB6C1")); c.setStrokeColor(FEMALE_ROSE); c.setLineWidth(1.2)
c.ellipse(mid_x-18, mid_y-12, mid_x+18, mid_y+12, fill=1, stroke=1)
c.setFillColor(colors.HexColor("#FF69B4"))
c.ellipse(mid_x-8, mid_y-6, mid_x+8, mid_y+6, fill=1, stroke=1)
# perpendicular planning line
c.setStrokeColor(MALE_BLUE); c.setDash(3,2); c.setLineWidth(1.2)
c.line(mid_x, mid_y-24, mid_x, mid_y+24)
c.setDash()
# ─── SIGNAL CHEAT SHEET ──────────────────────────────────────────────────
sig_y = 28
rr(c, 12, sig_y-sig_y+22, W-24, 0, r=0, fill=WHITE, stroke=WHITE) # padding
footer(c, 4, 4, "MRI Pelvis Protocol Reference | Educational use | Sources: mrimaster.com, PI-RADS v2.1, FIGO 2023, JAJ Gastrohepatology 2024")
# ══════════════════════════════════════════════════════════════════════════════
# MAIN
# ══════════════════════════════════════════════════════════════════════════════
def main():
c = canvas.Canvas(OUTPUT, pagesize=A4)
c.setTitle("MRI Pelvis Protocol Reference Guide – Male & Female")
c.setAuthor("Orris AI – Medical Reference")
c.setSubject("Pelvic MRI Protocol – Sequences, Planning, Parameters & Clinical Indications")
page_male(c); c.showPage()
page_female(c); c.showPage()
page_params(c); c.showPage()
page_indications(c); c.showPage()
c.save()
print(f"PDF saved: {OUTPUT}")
if __name__ == "__main__":
main()
cd /home/daytona/workspace/mrcp-flowchart && python3 generate_pelvis_pdf.py 2>&1
ls -lh /home/daytona/workspace/mrcp-flowchart/Pelvis_MRI_Protocol_Reference.pdf
Pelvis MRI Protocol Reference
PDF Document · PDF