Hi

Hey! How can I help you today?

Tell me every sequence used in mri mrcp with detailed planning with pictures and the uses of the sequences used

Reading File
Finding Sources
Searching the Web

MRI MRCP sequences planning detailed protocol 2024

Searching PubMed

MRCP MRI protocol sequences biliary pancreatic

Reading Web Page

https://mrimaster.com/plan-mrcp

Loading Image
Loading Image
Finding Sources
Searching the Web

MRCP MRI sequences TrueFISP HASTE SPACE T1 VIBE planning images diagram site:mrimaster.com

Reading Web Page

https://mrimaster.com/plan-mrcp

Loading Image
Loading Image
Loading Image
Searching the Web

MRCP MRI protocol sequences planning diagram biliary pancreatic duct T2 HASTE SPACE TrueFISP

Loading Image
Loading Image
I now have comprehensive information and one planning image. Let me compile the complete, detailed answer.

MRI MRCP - Complete Sequences, Detailed Planning & Clinical Uses

Anatomy Overview

MRCP (Magnetic Resonance Cholangiopancreatography) is a non-invasive MRI technique that uses heavily T2-weighted sequences to visualize fluid-filled structures - the biliary tree, gallbladder, and pancreatic duct - without ionizing radiation or contrast injection.
The image below shows the 3-plane localizer planning overlaid on biliary/hepatic anatomy:
MRCP 3-plane localizer planning

Patient Positioning & Setup

  • Position: Supine, head-first into the magnet
  • Coils: Spine coil beneath the patient + phased-array body coil placed over the upper abdomen (from nipple level down to iliac crest), strapped tightly to minimize respiratory motion artifacts
  • Laser centering: Over the xiphoid process of the sternum
  • Respiratory belt/bellows: Applied for monitoring and triggering gated sequences
  • Patient preparation: Fasting for 4-6 hours (reduces bowel fluid and gallbladder motility), negative oral contrast (e.g., pineapple juice) to suppress gastric signal if needed

Complete MRCP Sequence Protocol


SEQUENCE 1 - Three-Plane TrueFISP (True FISP / bSSFP) Localizer

Acquisition: Breath-hold, all three planes simultaneously Acquisition time: Under 25 seconds

Planning

Plan axially, coronally, and sagittally across the upper abdomen. This is a fast steady-state free precession localizer used to locate the liver, biliary system, and pancreas before planning all subsequent sequences.

Parameters

TRTEFlipNEXSliceMatrixFOVPhase
~4-6 ms~2 ms70-80°15-8 mm256x256400A>P

Use

  • Provides an overview of abdominal anatomy
  • Used as the base reference image for planning all subsequent sequences
  • Fluid (bile, CSF, ascites) appears bright; vessels appear bright (different from HASTE where vessels are dark)
  • Identifies position of the liver dome, CBD, and gallbladder

SEQUENCE 2 - T2 TrueFISP / TRUFI Fat Sat Coronal (4 mm, Breath-hold)

Acquisition: Breath-hold (~20 seconds)

Planning

Plan the coronal slices on the axial localizer. Position the block horizontally across the liver so coverage extends from the anterior abdominal wall to the posterior erector spinae muscles. On the sagittal view, ensure coverage from the dome of the diaphragm down to the C-loop of the duodenum.

Parameters

TRTEFlipNEXSliceMatrixFOVPhaseFat Sat
4-5 ms2 ms70°14 mm320x320400R>LYes

Use

  • Survey sequence of the biliary system in coronal plane
  • Fluid, bile, and ductal structures appear very bright
  • Background parenchyma is suppressed with fat saturation
  • Detects gallstones, biliary dilation, and large ductal pathology
  • Used as a planning reference for all subsequent sequences (especially gated sequences)

SEQUENCE 3 - T2 HASTE Fat Sat Axial (4-5 mm, Breath-hold) (Core sequence)

Full name: Half-Fourier Acquisition Single-shot Turbo Spin Echo Siemens: HASTE | GE: SSFSE | Philips: UFSE
Acquisition: Multiple breath-holds; ~15-20 seconds per station

Planning

Plan the axial slices on the coronal TrueFISP image. Position the block across the liver, covering the biliary system from the diaphragm down to the C-loop of the duodenum. Add saturation bands above and below the axial block to suppress arterial pulsation and breathing artifacts. Check coverage in all three planes.

Parameters

TRTEFlipNEXSliceMatrixFOVPhaseOversample
2500-300090-110 ms130°14-5 mm256x256350R>L50%

Use

  • Primary diagnostic sequence for the biliary system - the most important axial acquisition
  • Fluid (bile) = high signal (bright), stones = signal void (dark) within bright bile
  • Detects choledocholithiasis, biliary strictures, ductal dilation
  • Blood vessels appear dark (key difference from TrueFISP)
  • Assesses wall thickness of gallbladder and ducts
  • Plans positioning for all gated/3D sequences because the diaphragm is in its natural position during exhalation
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.

SEQUENCE 4 - T2 HASTE Thick Slab Coronal Oblique - LAO (40 mm, Breath-hold) (Core MRCP sequence)

Acquisition: Single breath-hold per slice (~15 seconds)

Planning

Plan on the axial HASTE sequence. Center the block over the common bile duct (CBD). Rotate the block 20-30 degrees clockwise (Left Anterior Oblique - LAO) to include both the CBD and the gallbladder in the same thick slab. Verify angulation on the sagittal plane - align horizontally across the bile duct. Use phase oversampling to prevent wrap-around artifacts.

Parameters

TRTEFlipNEXSlice ThicknessMatrixFOVPhaseOversample
4000-5000500 ms150°140 mm320x320350-450R>L50%

Use

  • Classic MRCP "projectional" image - resembles an ERCP image
  • Displays the gallbladder + CBD in continuity
  • Detects gallstones, choledocholithiasis, cystic duct anatomy
  • Single thick slab collapses the entire fluid column into one image using MIP-like projection

SEQUENCE 5 - T2 HASTE Thick Slab Coronal Oblique - RAO (40 mm, Breath-hold)

Acquisition: Single breath-hold per slice

Planning

Same as LAO but rotate the block 20-30 degrees counterclockwise (Right Anterior Oblique - RAO) to include both the CBD and the pancreatic duct (duct of Wirsung) - without worrying about including the gallbladder. This angle better profiles the pancreatic duct.

Parameters

Same as LAO thick slab above (TR 4000-5000, TE 500, 40 mm slab thickness)

Use

  • Best single view of the CBD + pancreatic duct junction (ampulla of Vater)
  • Detects pancreatic duct dilation, strictures, stones, IPMN (intraductal papillary mucinous neoplasm)
  • Essential for suspected chronic pancreatitis, pancreatic cancer

SEQUENCE 6 - T2 HASTE Thick Slab Radial (40 mm, Breath-hold) (Optional)

Acquisition: Multiple breath-holds; each slice at a different rotation angle (e.g., 0°, 30°, 60°, 90°, 120°, 150°)

Planning

Plan radial thick slab slices on the axial HASTE sequence. Center the block over the CBD. Each radial slice rotates around the CBD as the central pivot point. Verify in sagittal and coronal planes. Each acquisition requires a separate breath-hold.

Parameters

Same as other thick slab parameters (TR 4000-5000, TE 500, 40 mm)

Use

  • Provides multiple viewing angles of the biliary/pancreatic system in one acquisition set
  • Helps characterize ductal anatomy from all directions - especially useful when ducts are tortuous
  • Particularly helpful when standard LAO/RAO views are ambiguous
  • Can be used to plan the 3D SPACE sequence

SEQUENCE 7 - T2 SPACE 3D Coronal Respiratory-Gated (1 mm isotropic) (Gold standard MRCP sequence)

Full name: Sampling Perfection with Application optimized Contrasts using different flip angle Evolution Siemens: SPACE | GE: CUBE | Philips: VISTA | Also called T2 TSE 3D
Acquisition: Respiratory-gated (free-breathing), ~5-10 minutes

Planning

Plan the coronal 3D block on the axial HASTE scan (not the free-breathing localizer). Position the block across the CBD and pancreatic duct. Ensure coverage includes the entire:
  • Common bile duct (down to the ampulla)
  • Pancreatic duct
  • Gallbladder
  • Intrahepatic ducts
Navigator placement: Place the respiratory navigator box in the middle of the right dome of the diaphragm - half the box over the right lobe of the liver (segment 8), half over the lungs. Plan in the non-breath-hold localizer since the liver is in a lower position during inhalation. Instruct the patient to breathe gently and regularly - very shallow or erratic breathing reduces navigator efficiency.

Parameters

TRTEFlipNEXSliceMatrixFOVPhaseOversampleTrigger
2000-3000200 ms12°11 mm320x320350R>L50%YES (gated)

Use

  • Highest resolution 3D dataset of the biliary and pancreatic systems
  • Isotropic 1 mm voxels allow multiplanar reconstructions (MPR) in any plane without loss of resolution
  • Maximum Intensity Projection (MIP) reconstructions generate true ERCP-like images
  • Detects subtle ductal changes in primary sclerosing cholangitis (PSC), biliary strictures, small stones (down to ~2 mm), IPMN, choledochal cysts
  • Far superior to 2D thick slab for small lesions and complex ductal anatomy
  • Can reconstruct curved planar reconstructions (CPR) along the duct course

SEQUENCE 8 - T1 VIBE Fat Sat Axial (3-4 mm, Breath-hold) - Pre and Post-Contrast

Full name: Volumetric Interpolated Breath-hold Examination Siemens: VIBE | GE: LAVA | Philips: THRIVE
Acquisition: Breath-hold (~20 seconds); performed pre-contrast and dynamically post-Gd injection (arterial, portal venous, delayed phases)

Planning

Plan axial slices on the coronal TrueFISP image. Position the block from the dome of the liver to the inferior pole of the kidneys (covering the entire liver and pancreas). Use fat saturation (FS). Add in-phase and out-of-phase acquisitions for Dixon fat/water separation if available.

Parameters

TRTEFlipNEXSliceMatrixFOVPhaseFat Sat
5-6 ms2.5 ms12°13-4 mm256x256350A>PYes (SPAIR)

Use

  • Pre-contrast T1 VIBE: Detects T1-bright lesions - hemorrhage, proteinaceous bile, melanoma metastases, fatty liver, hemochromatosis
  • Dynamic contrast-enhanced T1 VIBE: Characterizes focal liver lesions (HCC, metastases, hemangioma, cholangiocarcinoma), pancreatic lesions (PDAC, NET), biliary wall enhancement
  • Assesses pancreatic parenchymal enhancement (loss in chronic pancreatitis, early enhancement in neuroendocrine tumors)
  • Detects biliary wall thickening and enhancement (cholangitis, IgG4 disease)
  • Essential for staging pancreatic and hepatobiliary malignancies

SEQUENCE 9 - T1 In-Phase / Out-of-Phase (Dixon) Axial (3-4 mm, Breath-hold)

Acquisition: Single breath-hold dual-echo acquisition

Planning

Same as T1 VIBE axial - plan on coronal TrueFISP. Cover from liver dome to inferior liver margin and pancreas.

Parameters

TRTE (in-phase)TE (out-of-phase)FlipNEXSlice
150-200 ms4.76 ms2.38 ms70°15-6 mm

Use

  • Detects hepatic steatosis (fatty liver): signal drops significantly on out-of-phase images
  • Identifies adrenal adenoma vs. metastasis
  • Characterizes focal fatty lesions and lipid-containing tumors
  • Detects iron deposition (signal drop on both phases due to susceptibility)

SEQUENCE 10 - T2 HASTE / TSE Axial Without Fat Saturation (4-5 mm, Breath-hold)

Acquisition: Multiple breath-holds

Planning

Same axial planning as the fat-sat HASTE (plan on coronal TrueFISP), but without fat suppression.

Use

  • Characterizes cystic vs. solid lesions in the liver and pancreas
  • Assesses liver parenchyma (cirrhosis, fibrosis, iron overload)
  • Detects fluid collections, ascites, pleural effusions
  • Evaluates gallbladder wall and pericholecystic fluid
  • Distinguishes hemangiomas (very bright on T2) from malignant lesions

SEQUENCE 11 - DWI (Diffusion-Weighted Imaging) Axial (5-6 mm, Free-breathing or Breath-hold)

b values typically used: b=50, b=400, b=800 (higher b values reduce T2 shine-through)

Planning

Plan axially on the coronal TrueFISP, same coverage as T2 HASTE axial (liver dome to C-loop of duodenum). ADC maps are automatically generated from multiple b-value acquisitions.

Parameters

TRTEb-valuesNEXSliceMatrixFOV
3000-500060-80 ms50, 400, 8004-65-6 mm128x128350

Use

  • Detects cholangiocarcinoma, gallbladder carcinoma, liver metastases, HCC (restricted diffusion = high b-value signal + low ADC)
  • Characterizes pancreatic ductal adenocarcinoma (PDAC) - restricted diffusion
  • Differentiates benign vs. malignant biliary strictures
  • Detects acute cholecystitis (wall shows restricted diffusion)
  • Identifies liver abscesses vs. simple cysts
  • Tumor staging and treatment response assessment

Summary Table: All MRCP Sequences at a Glance

#SequencePlaneTechniqueBreathingKey Use
1TrueFISP 3-plane3-planebSSFPBreath-holdLocalizer, anatomy overview
2T2 TrueFISP FS CoronalCoronalbSSFP + FSBreath-holdSurvey biliary anatomy; planning reference
3T2 HASTE FS AxialAxialHASTE + FSBreath-holdMain diagnostic axial; stones, dilation, strictures
4T2 HASTE Thick Slab LAOCoronal obliqueHASTEBreath-holdGallbladder + CBD projection
5T2 HASTE Thick Slab RAOCoronal obliqueHASTEBreath-holdCBD + pancreatic duct projection
6T2 HASTE Radial Thick SlabRadialHASTEBreath-hold (each)Multiple-angle biliary projections
7T2 SPACE 3D 1mm CoronalCoronal3D TSERespiratory gatedGold standard - high-res MPR/MIP reconstructions
8T1 VIBE FS Axial ± GdAxial3D GRE + FSBreath-holdFocal lesion characterization, enhancement
9T1 In/Out-of-PhaseAxialGRE dual-echoBreath-holdSteatosis, iron, lipid-containing lesions
10T2 HASTE Axial (no FS)AxialHASTEBreath-holdParenchyma, cystic lesion characterization
11DWIAxialEPIFree-breathingMalignancy detection, restricted diffusion

Clinical Indications for MRCP

ConditionKey Sequences
Choledocholithiasis (CBD stones)HASTE FS axial + SPACE 3D + thick slab
Primary Sclerosing Cholangitis (PSC)SPACE 3D (multifocal strictures + "beading")
CholangiocarcinomaSPACE 3D + DWI + T1 VIBE dynamic
Pancreatic cancer (PDAC)T1 VIBE dynamic + DWI + SPACE 3D
Chronic pancreatitisThick slab + SPACE 3D (duct dilation, stones)
IPMNSPACE 3D + T2 HASTE
Choledochal cystSPACE 3D (Todani classification)
Acute cholecystitisT2 HASTE FS + DWI
Post-surgical anatomy (post-Whipple, Roux-en-Y)SPACE 3D + T1 VIBE
IgG4-related cholangiopathySPACE 3D + T1 VIBE dynamic

Key Tips to Remember

  1. Always plan gated/3D sequences on the breath-hold HASTE coronal (not the initial TrueFISP localizer) - the liver is in a different position during inhalation vs. exhalation
  2. Navigator placement: Half over liver (segment 8), half over lung at the dome of the right hemidiaphragm
  3. Phase oversampling 50% on all sequences to prevent aliasing/wrap artifacts
  4. Vessels in HASTE = dark; Vessels in TrueFISP = bright - this distinction is critical for differentiating ducts from vessels
  5. 40 mm thick slab = projectional ERCP-like image; 1 mm 3D SPACE = high-resolution dataset for MPR
  6. Saturation bands above and below the axial block in T2 HASTE FS axial reduce arterial pulsation artifacts
For a visual walkthrough of the full scanning protocol, this mrimaster video is excellent:

Create a visual MRCP protocol flowchart as a PDF reference guide

Reading File
Running Command

mkdir -p /home/daytona/workspace/mrcp-flowchart && ls /home/daytona/workspace/mr…

Writing File

~/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()
Running Command

cd /home/daytona/workspace/mrcp-flowchart && python3 generate_mrcp_pdf.py 2>&1

Running Command

ls -lh /home/daytona/workspace/mrcp-flowchart/MRCP_Protocol_Reference.pdf

Your MRCP Protocol Reference Guide PDF is ready. Here's what's inside:

MRCP Protocol Reference

PDF Document · PDF


What's in the PDF (2 pages, A4):

Page 1 - Protocol Flowchart

  • Patient Prep & Positioning banner at the top (navy header with gold text)
  • Two-column flowchart with colour-coded, numbered sequence boxes:
    • Column 1: Localizer → TrueFISP Coronal → HASTE FS Axial → Thick Slab LAO → Thick Slab RAO → Optional Radial Slab
    • Column 2: T2 SPACE 3D (gold standard, highlighted green) → T1 VIBE Pre-Gd → T1 VIBE Dynamic Post-Gd → Dixon In/Out Phase → T2 HASTE No FS → DWI
  • Each box shows: sequence name, planning instructions, key parameters, and clinical use
  • Key Planning Tips amber box at the bottom (6 critical rules)

Page 2 - Parameters & Indications

  • Full parameters table - all 11 sequences with TR, TE, flip angle, slice thickness, matrix, FOV, phase direction, and breathing technique
  • Clinical Indications grid - 10 pathologies (PSC, stones, PDAC, CCA, IPMN, choledochal cyst, IgG4, etc.) with the priority sequences for each
  • Signal intensity cheat sheet - colour-coded legend for bile, stones, vessels, fat, and DWI

Make a same pdf for pelvic studies of men and women the sequences used clinical indication planning with diagram

Searching the Web

MRI pelvis protocol sequences planning male prostate female uterus ovary parameters 2024 mrimaster

Finding Sources
Reading Web Page

https://mrimaster.com/plan-prostate

Searching the Web

MRI prostate DWI DCE sequences planning parameters T2 axial oblique sagittal coronal STIR large FOV site:mrimaster.com

Reading Web Page

https://mrimaster.com/plan-gyne-pelvis

Writing File

~/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()
Running Command

cd /home/daytona/workspace/mrcp-flowchart && python3 generate_pelvis_pdf.py 2>&1

Running Command

ls -lh /home/daytona/workspace/mrcp-flowchart/Pelvis_MRI_Protocol_Reference.pdf

Your MRI Pelvis Protocol Reference Guide is ready:

Pelvis MRI Protocol Reference

PDF Document · PDF


What's inside (4 pages, A4):

Page 1 – Male Pelvis (Prostate) Flowchart

  • Patient prep & positioning banner (supine HFS, iliac crest laser, buscopan)
  • 2-column colour-coded flowchart with 10 numbered sequence boxes:
    • Localizer → T2 Sagittal SFOV → T2/T1 Large FOV → STIR Coronal → T2 Axial Oblique SFOV → T2 Coronal Oblique → DWI ZOOMit → T1 VIBE DCE → Post-op T1 FS (optional)
  • Each box: planning instructions + parameters + clinical use
  • Sagittal anatomy diagram (bladder, prostate, SV, rectum, urethra) + prostate zone diagram (PZ/TZ/CZ)
  • PI-RADS v2.1 reminder strip + 6 key planning tips

Page 2 – Female Pelvis (Gynaecological) Flowchart

  • Same format with rose/pink colour scheme
  • 10 sequences: Localizer → T2 Sagittal → T2/T1 Large FOV → STIR → T2 Axial Oblique → T2 Coronal Oblique → T1 FS → DWI → T1 VIBE Dixon Dynamic
  • Uterine anatomy diagram (uterus, cervix, ovaries, bladder, rectum) + uterine layers diagram (endometrium/junctional zone/myometrium)
  • FIGO endometrial staging reminder + 6 planning tips (including vaginal gel rule and phase direction rules)

Page 3 – Full Parameters Tables

  • Side-by-side tables for all male + female sequences: TR, TE, flip angle, slice thickness, matrix, FOV, phase direction, and breathing technique
  • Abbreviations key

Page 4 – Clinical Indications + Planning Diagrams

  • 8 male pathologies (PCa staging, PSA workup, ECE, SVI, post-prostatectomy, BPH, bladder CA, lymph nodes) with priority sequences for each
  • 8 female pathologies (endometrial CA, cervical CA, ovarian CA, endometriosis, fibroids, congenital anomalies, PID, placenta accreta) with priority sequences
  • 3 planning diagrams with schematic anatomical illustrations showing angulation rules for prostate axial oblique, uterine sagittal, and uterine axial oblique planning
This is a shared conversation. Sign in to Orris to start your own chat.