Fundoscopy images in ophthalmology which is imp for neet pg





| Grade | Features |
|---|---|
| Mild NPDR | Microaneurysms only |
| Moderate NPDR | MA + dot/blot haemorrhages + hard exudates + cotton-wool spots |
| Severe NPDR | "4-2-1 rule": haemorrhages in all 4 quadrants OR venous beading in 2+ quadrants OR IRMA in 1+ quadrant |
| PDR | Neovascularization (NVD/NVE), vitreous haemorrhage, tractional RD |

| Grade | Features |
|---|---|
| 1 | Mild generalised arteriolar narrowing (A/V ratio <2/3), increased light reflex (silver wiring early) |
| 2 | Definite A/V nipping (AV nicking/arteriovenous crossing changes) |
| 3 | Flame haemorrhages + cotton-wool spots + hard exudates (± AV nicking) |
| 4 | Grade 3 + papilloedema (disc swelling) |
| Type | Fundus |
|---|---|
| Dry AMD | Drusen (soft > hard), geographic atrophy of RPE |
| Wet AMD | Subretinal/intraretinal fluid, choroidal neovascular membrane, haemorrhage, disciform scar |
| Finding on Fundoscopy | Diagnosis |
|---|---|
| Cherry-red spot | CRAO, Tay-Sachs, Niemann-Pick, Gaucher |
| Blood-and-thunder / stormy sunset | CRVO |
| Bone spicule pigmentation + waxy disc pallor + narrow arteries | Retinitis pigmentosa |
| Disc swelling (bilateral) + raised ICP | Papilledema |
| NVD/NVE + dot blot haemorrhages | Proliferative DR |
| AV nipping + silver wiring + macular star | Hypertensive retinopathy |
| Bull's-eye maculopathy | Chloroquine toxicity, Stargardt, cone dystrophy |
| Roth spots (white-centred haemorrhages) | SBE (infective endocarditis), leukaemia, severe anaemia |
| Hollenhorst plaques (bright yellow at bifurcations) | Carotid artery emboli |
| Disc cupping (C:D > 0.6), notching | Glaucoma |
| Tortuous dilated vessels + exudates (children) | Coats disease |
| Sea-fan neovascularization (periphery) | Sickle cell retinopathy |
All images in a pdf format
ls /home/daytona/workspace/ 2>/dev/null && echo "---" && pip show reportlab 2>/d…
mkdir -p /home/daytona/workspace/fundoscopy-neetpg
~/fundoscopy-neetpg/generate_pdf.py
import requests
import os
from io import BytesIO
from PIL import Image
from reportlab.lib.pagesizes import A4
from reportlab.lib import colors
from reportlab.lib.units import cm, mm
from reportlab.platypus import (
SimpleDocTemplate, Paragraph, Spacer, Image as RLImage,
Table, TableStyle, PageBreak, HRFlowable, KeepTogether
)
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.enums import TA_CENTER, TA_LEFT, TA_JUSTIFY
from reportlab.platypus.flowables import HRFlowable
OUTPUT_DIR = "/home/daytona/workspace/fundoscopy-neetpg"
IMG_DIR = os.path.join(OUTPUT_DIR, "images")
os.makedirs(IMG_DIR, exist_ok=True)
# -------------------------------------------------------------------
# Image URLs from Kanski's Clinical Ophthalmology (via Orris CDN)
# -------------------------------------------------------------------
IMAGES = {
"papilledema_stages": {
"url": "https://cdn.orris.care/cdss_images/b7c105bd882750d932aad3c38800c4bc90f57f440eca07e2a56649f43e01b90a.png",
"caption": "Fig 1: Papilloedema - All 4 Stages\n(A) Early: mild disc hyperaemia, cup preserved (B) Acute established: severe hyperaemia, obliterated cup, venous engorgement, flame haemorrhages, Paton lines\n(C) Chronic: disc elevation, optociliary shunts (D) Atrophic: grey-white disc, secondary optic atrophy"
},
"impending_crvo": {
"url": "https://cdn.orris.care/cdss_images/50960b6921c9458506bd772f40097bbae991b9dbcdaac264f3b48efb6866ba74.png",
"caption": "Fig 2: Impending CRVO\nMild retinal venous dilatation and tortuosity. Few dot/blot haemorrhages."
},
"nonisch_crvo_b": {
"url": "https://cdn.orris.care/cdss_images/e947b7c9f04955a98a3e06527954d3a5c737a96ee028ac774439c45f677759a4.png",
"caption": "Fig 3: Non-ischaemic CRVO\nDot and blot haemorrhages in all four quadrants ('blood and thunder' fundus). Venous dilatation and tortuosity."
},
"nonisch_crvo_stages": {
"url": "https://cdn.orris.care/cdss_images/20c1431b8ce34d43bed6abd41c6745ab3c90b74042a8678817c9a499bdc98f5c.png",
"caption": "Fig 4: Non-ischaemic CRVO - Stages\n(A) Acute: tortuosity + dilatation, dot/blot/flame haemorrhages all quadrants (B) Extensive flame haemorrhages\n(C) Resolving haemorrhages (D) FA: good capillary perfusion (non-ischaemic)"
},
"crao": {
"url": "https://cdn.orris.care/cdss_images/caced8335dd2df2e0c8faf5e05b18fc1e6a05ac9b6b402a8419f688c6799df5c.png",
"caption": "Fig 5: Central Retinal Artery Occlusion (CRAO)\nClassic 'cherry-red spot' at the fovea with surrounding milky-white (ground-glass) retinal oedema.\nThe fovea appears red due to preserved underlying choroidal circulation."
},
"adv_diabetic": {
"url": "https://cdn.orris.care/cdss_images/f304bf0b9cf94cd32bf4ec1ecc1fc27bdfd09d9e0d5e8c4a4bd4a22b69844b92.png",
"caption": "Fig 6: Advanced Diabetic Eye Disease / Proliferative DR\nNeovascularization on disc (NVD), fibrovascular proliferative tissue, preretinal/vitreous haemorrhage."
},
"diabetic_papillopathy": {
"url": "https://cdn.orris.care/cdss_images/11a8f25d7e75446489f9684c072bac29d216a5e9f80547c7f1d10eff8033e7c8.png",
"caption": "Fig 7: Diabetic Papillopathy\nHyperaemic disc swelling with surface telangiectasia (arrow). Usually resolves spontaneously."
},
}
# -------------------------------------------------------------------
# Download images
# -------------------------------------------------------------------
print("Downloading images...")
img_paths = {}
for key, info in IMAGES.items():
path = os.path.join(IMG_DIR, f"{key}.png")
if not os.path.exists(path):
try:
resp = requests.get(info["url"], timeout=30)
resp.raise_for_status()
with open(path, "wb") as f:
f.write(resp.content)
print(f" Downloaded: {key}")
except Exception as e:
print(f" FAILED {key}: {e}")
continue
else:
print(f" Cached: {key}")
img_paths[key] = path
# -------------------------------------------------------------------
# Styles
# -------------------------------------------------------------------
styles = getSampleStyleSheet()
TITLE_STYLE = ParagraphStyle(
"Title", parent=styles["Title"],
fontSize=22, leading=28, textColor=colors.HexColor("#1a237e"),
spaceAfter=6, spaceBefore=0, alignment=TA_CENTER
)
SUBTITLE_STYLE = ParagraphStyle(
"Subtitle", parent=styles["Normal"],
fontSize=12, leading=16, textColor=colors.HexColor("#283593"),
spaceAfter=4, alignment=TA_CENTER
)
SECTION_STYLE = ParagraphStyle(
"Section", parent=styles["Heading1"],
fontSize=14, leading=18, textColor=colors.white,
backColor=colors.HexColor("#1565c0"),
spaceBefore=14, spaceAfter=6,
leftIndent=-6, rightIndent=-6,
borderPad=5
)
SUBSECTION_STYLE = ParagraphStyle(
"Subsection", parent=styles["Heading2"],
fontSize=11, leading=14, textColor=colors.HexColor("#1565c0"),
spaceBefore=8, spaceAfter=4,
borderPad=2
)
CAPTION_STYLE = ParagraphStyle(
"Caption", parent=styles["Normal"],
fontSize=8.5, leading=12, textColor=colors.HexColor("#424242"),
alignment=TA_CENTER, spaceAfter=4, spaceBefore=4,
fontName="Helvetica-Oblique"
)
BODY_STYLE = ParagraphStyle(
"Body", parent=styles["Normal"],
fontSize=9.5, leading=14, textColor=colors.HexColor("#212121"),
spaceAfter=4, spaceBefore=2, alignment=TA_JUSTIFY
)
BULLET_STYLE = ParagraphStyle(
"Bullet", parent=styles["Normal"],
fontSize=9.5, leading=13, textColor=colors.HexColor("#212121"),
spaceAfter=2, spaceBefore=1,
leftIndent=14, bulletIndent=4
)
KEY_STYLE = ParagraphStyle(
"Key", parent=styles["Normal"],
fontSize=9, leading=13, textColor=colors.HexColor("#b71c1c"),
spaceAfter=2, spaceBefore=2,
leftIndent=14, bulletIndent=4, fontName="Helvetica-Bold"
)
TABLE_HEADER_STYLE = ParagraphStyle(
"TableHeader", parent=styles["Normal"],
fontSize=9, fontName="Helvetica-Bold",
textColor=colors.white, alignment=TA_CENTER
)
TABLE_CELL_STYLE = ParagraphStyle(
"TableCell", parent=styles["Normal"],
fontSize=8.5, leading=12,
textColor=colors.HexColor("#212121"), alignment=TA_LEFT
)
PAGE_W, PAGE_H = A4
MARGIN = 1.8 * cm
CONTENT_W = PAGE_W - 2 * MARGIN
def section_header(text):
return Paragraph(f" {text}", SECTION_STYLE)
def subsection(text):
return Paragraph(text, SUBSECTION_STYLE)
def body(text):
return Paragraph(text, BODY_STYLE)
def bullet(text):
return Paragraph(f"• {text}", BULLET_STYLE)
def key_point(text):
return Paragraph(f"★ {text}", KEY_STYLE)
def add_image(key, width_frac=0.85):
path = img_paths.get(key)
if not path or not os.path.exists(path):
return [body(f"[Image unavailable: {key}]")]
try:
with Image.open(path) as im:
iw, ih = im.size
aspect = ih / iw
w = CONTENT_W * width_frac
h = w * aspect
# Cap height to avoid going off page
max_h = 16 * cm
if h > max_h:
h = max_h
w = h / aspect
rl_img = RLImage(path, width=w, height=h)
rl_img.hAlign = "CENTER"
caption = IMAGES[key]["caption"]
cap_para = Paragraph(caption.replace("\n", "<br/>"), CAPTION_STYLE)
return [rl_img, cap_para]
except Exception as e:
return [body(f"[Image error: {e}]")]
def make_table(header_row, data_rows, col_widths=None):
header = [Paragraph(cell, TABLE_HEADER_STYLE) for cell in header_row]
rows = [header]
for row in data_rows:
rows.append([Paragraph(str(cell), TABLE_CELL_STYLE) for cell in row])
if col_widths is None:
col_widths = [CONTENT_W / len(header_row)] * len(header_row)
t = Table(rows, colWidths=col_widths, repeatRows=1)
t.setStyle(TableStyle([
("BACKGROUND", (0, 0), (-1, 0), colors.HexColor("#1565c0")),
("ROWBACKGROUNDS", (0, 1), (-1, -1), [colors.HexColor("#e8eaf6"), colors.white]),
("GRID", (0, 0), (-1, -1), 0.4, colors.HexColor("#90a4ae")),
("VALIGN", (0, 0), (-1, -1), "TOP"),
("LEFTPADDING", (0, 0), (-1, -1), 5),
("RIGHTPADDING", (0, 0), (-1, -1), 5),
("TOPPADDING", (0, 0), (-1, -1), 4),
("BOTTOMPADDING", (0, 0), (-1, -1), 4),
]))
return t
# -------------------------------------------------------------------
# Build document
# -------------------------------------------------------------------
def build():
out_path = os.path.join(OUTPUT_DIR, "Fundoscopy_NEET_PG.pdf")
doc = SimpleDocTemplate(
out_path,
pagesize=A4,
leftMargin=MARGIN, rightMargin=MARGIN,
topMargin=MARGIN, bottomMargin=MARGIN,
title="Fundoscopy Images - NEET PG Ophthalmology",
author="Orris Medical Library (Kanski's Clinical Ophthalmology 10e)"
)
story = []
# ---- COVER ----
story.append(Spacer(1, 1.5 * cm))
story.append(Paragraph("FUNDOSCOPY IMAGES", TITLE_STYLE))
story.append(Paragraph("High-Yield Guide for NEET PG Ophthalmology", SUBTITLE_STYLE))
story.append(Spacer(1, 0.3 * cm))
story.append(HRFlowable(width="100%", thickness=2, color=colors.HexColor("#1565c0")))
story.append(Spacer(1, 0.2 * cm))
story.append(Paragraph(
"Source: Kanski's Clinical Ophthalmology: A Systematic Approach, 10th Edition | "
"The Wills Eye Manual, 8th Edition",
CAPTION_STYLE
))
story.append(Spacer(1, 0.6 * cm))
# ---- TABLE OF CONTENTS box ----
toc_data = [
["#", "Topic"],
["1", "Papilloedema - Stages & Features"],
["2", "Central Retinal Vein Occlusion (CRVO)"],
["3", "Central Retinal Artery Occlusion (CRAO) - Cherry Red Spot"],
["4", "Diabetic Retinopathy - Classification & Fundus"],
["5", "Retinitis Pigmentosa"],
["6", "Hypertensive Retinopathy - KWB Grading"],
["7", "Glaucomatous Optic Disc"],
["8", "Age-Related Macular Degeneration (AMD)"],
["9", "Quick Reference Mnemonic Table"],
]
story.append(subsection("Contents"))
story.append(make_table(toc_data[0], toc_data[1:], col_widths=[1.2*cm, CONTENT_W - 1.2*cm]))
story.append(PageBreak())
# ================================================================
# SECTION 1 - PAPILLOEDEMA
# ================================================================
story.append(section_header("1. PAPILLOEDEMA (Swollen Disc due to Raised ICP)"))
story.append(Spacer(1, 0.3*cm))
story += add_image("papilledema_stages", width_frac=0.9)
story.append(Spacer(1, 0.3*cm))
story.append(subsection("Four Stages - Classic NEET PG Classification"))
stage_data = [
["Stage", "VA", "Disc", "Haemorrhages / CWS", "Cup"],
["Early", "Normal", "Mild hyperaemia, blurred margins, indistinct peripapillary nerve fibres", "None", "Preserved"],
["Acute Established", "Normal or reduced", "Severe hyperaemia, moderate elevation, indistinct margins", "Peripapillary flame haemorrhages + CWS", "Obliterated"],
["Chronic", "Variable, fields constrict", "Elevation without haemorrhages, optociliary shunts, drusen-like deposits", "None", "Absent"],
["Atrophic", "Severely impaired", "Grey-white, slightly elevated, few vessels, indistinct margins", "None", "Absent"],
]
story.append(make_table(
stage_data[0], stage_data[1:],
col_widths=[2.0*cm, 2.2*cm, 5.5*cm, 3.5*cm, 2.2*cm]
))
story.append(Spacer(1, 0.3*cm))
story.append(subsection("Key NEET Points"))
for pt in [
"Nearly always <b>bilateral</b> (may be asymmetrical)",
"Absent SVP (spontaneous venous pulsation) is significant; present SVP means ICP normal at that moment",
"<b>Paton lines</b> = circumferential retinal folds, especially temporal",
"<b>Macular fan</b> in younger patients (vesicles converging on fovea - not to be confused with macular star)",
"<b>Enlarged blind spot</b> on visual fields",
"ALL patients must have urgent neuroradiological investigation",
"<b>IIH (Idiopathic Intracranial Hypertension)</b> = obese young women; headache >90%, pulsatile tinnitus; treat with acetazolamide, weight loss, LP",
"Causes of disc elevation: papilloedema, accelerated hypertension, optic neuritis, disc drusen (pseudopapilloedema)",
"Normal CSF pressure = 10–18 cmH₂O on LP (patient lying flat)",
]:
story.append(bullet(pt))
story.append(PageBreak())
# ================================================================
# SECTION 2 - CRVO
# ================================================================
story.append(section_header("2. CENTRAL RETINAL VEIN OCCLUSION (CRVO)"))
story.append(Spacer(1, 0.3*cm))
story.append(subsection("Impending CRVO"))
story += add_image("impending_crvo", width_frac=0.7)
story.append(Spacer(1, 0.3*cm))
story.append(subsection("Non-ischaemic CRVO - 'Blood and Thunder' Fundus"))
story += add_image("nonisch_crvo_b", width_frac=0.7)
story.append(Spacer(1, 0.3*cm))
story.append(subsection("CRVO Stages Comparison"))
story += add_image("nonisch_crvo_stages", width_frac=0.9)
story.append(Spacer(1, 0.3*cm))
story.append(subsection("Non-ischaemic vs Ischaemic CRVO"))
crvo_data = [
["Feature", "Non-ischaemic", "Ischaemic"],
["VA", "Moderately reduced (6/12–6/60)", "Severely reduced (CF or worse)"],
["RAPD", "Absent or mild", "Present"],
["Haemorrhages", "Mild-moderate in all 4 quadrants", "Massive in all 4 quadrants"],
["Cotton-wool spots", "Few", "Many"],
["Rubeosis iridis", "Rare", "~50% (at 2–4 months = '100-day glaucoma')"],
["Capillary non-perfusion (FA)", "<10 disc areas", ">10 disc areas"],
["Risk of NVG", "Low", "High"],
["Prognosis", "~50% return to near-normal", "Very poor"],
]
story.append(make_table(
crvo_data[0], crvo_data[1:],
col_widths=[3.5*cm, 5.5*cm, 5.5*cm]
))
story.append(Spacer(1, 0.3*cm))
for pt in [
"<b>'Stormy sunset' / 'blood and thunder'</b> = haemorrhages in ALL 4 quadrants",
"Second most common retinal vascular disease after DR",
"Risk factors: age >65, hypertension, hyperlipidaemia, glaucoma, hyperviscosity",
"AV sharing a common adventitial sheath at lamina cribrosa → atherosclerosis of artery compresses vein",
"VEGF mediates macular oedema → treat with intravitreal anti-VEGF",
"<b>Papillophlebitis</b> = CRVO variant in young adults; good prognosis",
]:
story.append(bullet(pt))
story.append(PageBreak())
# ================================================================
# SECTION 3 - CRAO
# ================================================================
story.append(section_header("3. CENTRAL RETINAL ARTERY OCCLUSION (CRAO) - Cherry Red Spot"))
story.append(Spacer(1, 0.3*cm))
story += add_image("crao", width_frac=0.8)
story.append(Spacer(1, 0.3*cm))
story.append(subsection("Key Features"))
crao_tbl = [
["Feature", "Finding"],
["Classic sign", "Cherry-red spot at fovea/macula"],
["Mechanism of cherry-red spot", "Fovea has thin retina → underlying choroid visible; surrounding retina whitened by oedema"],
["Retinal appearance", "Milky-white (ground-glass) oedema due to retinal ischaemia"],
["Arteries", "Attenuated; 'boxcarring' of blood column"],
["VA", "Sudden, severe, painless monocular loss"],
["Pupil", "RAPD present"],
["Window for treatment", "~6 hours"],
["Prognosis", "2/3 end with VA <6/120; only 1/5 achieve 6/12 or better"],
]
story.append(make_table(crao_tbl[0], crao_tbl[1:], col_widths=[5.0*cm, CONTENT_W - 5.0*cm]))
story.append(Spacer(1, 0.3*cm))
story.append(subsection("Embolus Types"))
emb_tbl = [
["Type", "Appearance", "Source"],
["Hollenhorst plaque (cholesterol)", "Sparkling/bright yellow, usually at bifurcations", "Carotid artery disease"],
["Platelet-fibrin", "Dull grey, elongated", "Carotid disease / cardiac"],
["Calcium", "Dull white, near/on disc", "Cardiac (valvular) disease"],
["Talc / cornstarch", "Small yellow-white glistening particles in macular arterioles", "IV drug use"],
]
story.append(make_table(emb_tbl[0], emb_tbl[1:], col_widths=[3.5*cm, 5.0*cm, CONTENT_W-8.5*cm]))
story.append(Spacer(1, 0.2*cm))
for pt in [
"If <b>cilioretinal artery is patent</b> → central/papillomacular vision preserved (cilioretinal artery arises from choroidal circulation)",
"Other causes of cherry-red spot: <b>Tay-Sachs</b>, <b>Niemann-Pick</b>, Gaucher, GM1 gangliosidosis (metabolic storage disorders)",
"Acute treatment (within 6 hrs): ocular massage, AC paracentesis, topical IOP-lowering agents, carbogen inhalation",
]:
story.append(bullet(pt))
story.append(PageBreak())
# ================================================================
# SECTION 4 - DIABETIC RETINOPATHY
# ================================================================
story.append(section_header("4. DIABETIC RETINOPATHY"))
story.append(Spacer(1, 0.3*cm))
story += add_image("adv_diabetic", width_frac=0.75)
story.append(Spacer(1, 0.3*cm))
story += add_image("diabetic_papillopathy", width_frac=0.55)
story.append(Spacer(1, 0.3*cm))
story.append(subsection("ETDRS Classification (International Clinical Classification)"))
dr_tbl = [
["Grade", "Fundoscopic Features"],
["No DR", "No abnormalities"],
["Mild NPDR", "Microaneurysms only"],
["Moderate NPDR", "More than mild NPDR but less than severe NPDR: microaneurysms + dot/blot haemorrhages + hard exudates + soft exudates (CWS)"],
["Severe NPDR (4-2-1 rule)", "Any ONE of: haemorrhages in ALL 4 quadrants / venous beading in 2+ quadrants / IRMA in 1+ quadrant. No PDR."],
["Very Severe NPDR", "Two or more of the above 4-2-1 criteria"],
["PDR", "Neovascularization: NVD (on disc) or NVE (elsewhere), ± vitreous/preretinal haemorrhage"],
["High-risk PDR", "NVD ≥ 1/4 disc area OR NVD + vitreous haemorrhage OR NVE + vitreous haemorrhage"],
]
story.append(make_table(dr_tbl[0], dr_tbl[1:], col_widths=[3.5*cm, CONTENT_W-3.5*cm]))
story.append(Spacer(1, 0.3*cm))
story.append(subsection("Clinically Significant Macular Oedema (CSMO) - ETDRS Definition"))
for pt in [
"Retinal thickening ≤500 µm from foveal centre",
"Hard exudates ≤500 µm from foveal centre with adjacent retinal thickening",
"Disc-area (≥1 DA) retinal thickening ≤1 disc diameter from foveal centre",
]:
story.append(bullet(pt))
story.append(Spacer(1, 0.2*cm))
story.append(subsection("Key NEET Points"))
for pt in [
"<b>Microaneurysms</b> = earliest fundoscopic change (focal outpouching of capillary wall due to pericyte loss)",
"<b>Hard exudates</b> = yellow, well-defined, lipid deposits; circinate pattern around leaking microaneurysms",
"<b>Cotton-wool spots (soft exudates)</b> = fluffy, ill-defined, nerve fibre layer infarcts",
"<b>IRMA</b> = Intraretinal Microvascular Abnormalities (shunt vessels, sign of ischaemia)",
"<b>NVD</b> = new vessels on/within 1 disc diameter of disc; <b>NVE</b> = elsewhere",
"Venous beading = sausage-like dilatation of veins = marker of severe ischaemia",
"Diabetic macular oedema = most common cause of visual loss in DR",
"Treatment: anti-VEGF (ranibizumab, bevacizumab, aflibercept) first line for CSMO; focal/grid laser; PRP for high-risk PDR",
"Duration of diabetes = most important predictor; poor control, hypertension, nephropathy increase risk",
"<b>Diabetic papillopathy</b> = hyperaemic disc swelling + telangiectasia in young diabetics; usually resolves spontaneously",
]:
story.append(bullet(pt))
story.append(PageBreak())
# ================================================================
# SECTION 5 - RETINITIS PIGMENTOSA
# ================================================================
story.append(section_header("5. RETINITIS PIGMENTOSA (RP)"))
story.append(Spacer(1, 0.3*cm))
story.append(body(
"RP is the most common hereditary retinal degeneration (prevalence 1:3000–5000). "
"It is a clinically and genetically diverse group of inherited diffuse retinal degenerations predominantly affecting rod photoreceptors initially."
))
story.append(Spacer(1, 0.3*cm))
story.append(subsection("Classic Fundoscopic Triad"))
triad_tbl = [
["Finding", "Description", "NEET Significance"],
["Bone-spicule pigmentation", "Mid-peripheral, perivascular, intraretinal pigment clumping resembling bone spicules", "Pathognomonic appearance"],
["Arteriolar attenuation", "Markedly narrow retinal arteries", "Sign of widespread photoreceptor loss"],
["'Waxy' disc pallor", "Yellowish-white optic disc pallor", "Distinguishes from other optic atrophies"],
]
story.append(make_table(triad_tbl[0], triad_tbl[1:], col_widths=[3.2*cm, 6.0*cm, CONTENT_W-9.2*cm]))
story.append(Spacer(1, 0.3*cm))
story.append(subsection("Inheritance & Severity"))
inh_tbl = [
["Pattern", "Gene", "Severity", "Notes"],
["Autosomal Dominant (AD)", "Rhodopsin (most common)", "Best prognosis", "Most cases due to rhodopsin mutations"],
["Autosomal Recessive (AR)", "Multiple", "Intermediate", "Sporadic cases often AR"],
["X-linked", "RPGR (90%)", "Most severe", "Central vision <6/60 by 5th decade; female carriers show 'tapetal reflex'"],
]
story.append(make_table(inh_tbl[0], inh_tbl[1:], col_widths=[3.0*cm, 3.5*cm, 3.0*cm, CONTENT_W-9.5*cm]))
story.append(Spacer(1, 0.3*cm))
story.append(subsection("Syndromic Associations (High-yield for NEET)"))
syn_tbl = [
["Syndrome", "Associated Feature(s)"],
["Usher syndrome", "Sensorineural deafness + RP (most common cause of deaf-blind)"],
["Laurence-Moon-Bardet-Biedl (LMBB)", "Obesity + polydactyly + hypogonadism + cognitive impairment + RP"],
["Kearns-Sayre syndrome", "Mitochondrial: CPEO + heart block + RP + cerebellar ataxia"],
["Refsum disease", "Elevated phytanic acid; treat with phytanic acid-free diet"],
["Abetalipoproteinaemia (Bassen-Kornzweig)", "Fat malabsorption + spinocerebellar ataxia + RP; treat with Vit A & E"],
["Cockayne syndrome", "Dwarfism + progeria + RP"],
]
story.append(make_table(syn_tbl[0], syn_tbl[1:], col_widths=[4.5*cm, CONTENT_W-4.5*cm]))
story.append(Spacer(1, 0.3*cm))
for pt in [
"<b>Earliest symptom:</b> nyctalopia (night blindness) and dark adaptation difficulties",
"Visual field: ring scotoma → progresses to tunnel vision",
"Full-field ERG: reduced/extinguished rod response early (most sensitive test)",
"FAF: abnormal perimacular ring of hyper-autofluorescence",
"Complication: posterior subcapsular cataract (most common); open-angle glaucoma (3%)",
"Gene therapy: <b>Luxturna (voretigene neparvovec)</b> approved for RPE65 mutations",
"RP sine pigmento = paucity of pigment (every RP patient is initially 'sine pigmento')",
]:
story.append(bullet(pt))
story.append(PageBreak())
# ================================================================
# SECTION 6 - HYPERTENSIVE RETINOPATHY
# ================================================================
story.append(section_header("6. HYPERTENSIVE RETINOPATHY"))
story.append(Spacer(1, 0.3*cm))
story.append(subsection("Keith-Wagener-Barker (KWB) Grading"))
kwb_tbl = [
["Grade", "Arterial Changes", "Other Changes", "Clinical Correlation"],
["Grade 1", "Mild generalised arteriolar narrowing (A/V ratio <2/3), increased light reflex ('silver wiring')", "—", "Mild hypertension"],
["Grade 2", "Definite AV nipping (Gunn sign), 'copper wiring', Salus sign (vein deflection at crossing)", "—", "Moderate hypertension"],
["Grade 3", "Arteriolar changes as above + narrower vessels", "Flame haemorrhages, cotton-wool spots, hard exudates (macular star)", "Severe hypertension"],
["Grade 4", "As Grade 3", "Papilloedema (disc swelling)", "Accelerated/malignant hypertension"],
]
story.append(make_table(
kwb_tbl[0], kwb_tbl[1:],
col_widths=[1.8*cm, 4.5*cm, 4.5*cm, 3.6*cm]
))
story.append(Spacer(1, 0.3*cm))
story.append(subsection("Key Fundoscopic Signs"))
htn_tbl = [
["Sign", "Description"],
["Normal A/V ratio", "2:3 (arteries:veins)"],
["Silver wiring", "Increased arteriolar light reflex; arteries appear silver (early)"],
["Copper wiring", "Less reflective than silver; indicates more severe arteriolosclerosis"],
["Gunn's sign", "AV nipping at arteriovenous crossing (Grade 2)"],
["Salus sign", "Deflection/bowing of vein at arterial crossing"],
["Bonnet sign", "Right-angle deviation of vein at crossing"],
["Macular star", "Hard exudates arranged in star pattern around fovea (Grade 3-4)"],
["Elschnig spots", "White RPE infarcts over choroidal lobules (choroidopathy in accelerated HTN)"],
["Siegrist streaks", "Linear hyperpigmentation along choroidal vessels"],
["Flame haemorrhages", "Nerve fibre layer haemorrhages (splinter-shaped)"],
]
story.append(make_table(htn_tbl[0], htn_tbl[1:], col_widths=[4.0*cm, CONTENT_W-4.0*cm]))
story.append(Spacer(1, 0.2*cm))
for pt in [
"<b>Grade 3 and 4 = accelerated hypertension</b> requiring urgent BP control",
"Macular star = exudates in Henle fibre layer radiating from fovea; classic in accelerated HTN and optic neuritis",
"Elschnig spots + Siegrist streaks = hypertensive choroidopathy (indicates very severe/malignant HTN)",
"Disc changes in HTN: papilloedema (Grade 4) from raised ICP or direct effect of severe HTN",
]:
story.append(bullet(pt))
story.append(PageBreak())
# ================================================================
# SECTION 7 - GLAUCOMA DISC
# ================================================================
story.append(section_header("7. GLAUCOMATOUS OPTIC DISC"))
story.append(Spacer(1, 0.3*cm))
story.append(body(
"Glaucoma causes progressive loss of retinal ganglion cell axons, leading to characteristic "
"structural changes at the optic disc. The cup-to-disc (C:D) ratio is the key parameter."
))
story.append(Spacer(1, 0.2*cm))
story.append(subsection("Key Disc Signs in Glaucoma"))
glauc_tbl = [
["Sign", "Description / Significance"],
["Increased C:D ratio", ">0.6 suspicious; >0.8 highly suspicious. Asymmetry >0.2 between eyes is significant."],
["Vertical cup elongation", "Vertical C:D > horizontal C:D; cup elongated vertically due to superior/inferior RNFL loss"],
["Neuroretinal rim (NRR) thinning", "ISNT rule: Normal thickness order = Inferior > Superior > Nasal > Temporal. Glaucoma reverses this."],
["Notching", "Focal thinning/loss of NRR; inferior pole most common, then superior"],
["Drance haemorrhage", "Flame-shaped disc margin haemorrhage; strongly predictive of glaucoma progression"],
["Bayoneting sign", "Double angulation of vessels at disc margin due to deep cupping"],
["Nasal displacement of vessels", "Vessels pushed nasally as cup enlarges temporally"],
["Baring of circumlinear vessel", "A vessel previously within the cup is now at the rim due to cup enlargement"],
["Peripapillary atrophy (PPA)", "Beta zone (direct RPE/Bruch membrane loss) more significant than alpha zone"],
["RNFL defects", "Wedge-shaped arcuate defects on fundus photography; precede visual field loss"],
]
story.append(make_table(glauc_tbl[0], glauc_tbl[1:], col_widths=[4.5*cm, CONTENT_W-4.5*cm]))
story.append(Spacer(1, 0.2*cm))
for pt in [
"<b>ISNT rule:</b> Inferior > Superior > Nasal > Temporal (normal NRR thickness order) — violation = glaucoma",
"Normal C:D ratio up to 0.4–0.5; >0.6 requires investigation",
"Asymmetry of C:D ratio >0.2 between two eyes = significant even if each eye is within normal range",
"<b>Normal tension glaucoma (NTG):</b> IOP within normal range (≤21 mmHg) but glaucomatous disc and field changes; notching more common",
"Disc haemorrhage = highest risk factor for NTG progression",
]:
story.append(bullet(pt))
story.append(PageBreak())
# ================================================================
# SECTION 8 - AMD
# ================================================================
story.append(section_header("8. AGE-RELATED MACULAR DEGENERATION (AMD)"))
story.append(Spacer(1, 0.3*cm))
amd_tbl = [
["Feature", "Dry (Atrophic) AMD", "Wet (Neovascular / Exudative) AMD"],
["Prevalence", "~85–90% of AMD", "~10–15% but responsible for 80% of severe vision loss"],
["Drusen", "Soft drusen (large, confluent) = high-risk; hard drusen = low-risk", "Often present; may precede CNV"],
["Key finding", "Geographic atrophy of RPE", "Choroidal neovascular membrane (CNVM): subretinal grey-green membrane, subretinal fluid, blood"],
["Symptoms", "Slow central visual loss over years", "Rapid painless central visual loss; metamorphopsia (distortion)"],
["Amsler grid", "May show scotoma", "Metamorphopsia (wavy/distorted lines)"],
["Late stage", "Geographic atrophy (RPE + outer retina loss)", "Disciform scar (fibrous scar replacing CNV)"],
["Treatment", "Antioxidants + zinc (AREDS2): Vit C, Vit E, beta-carotene (not smokers), lutein, zeaxanthin, zinc", "Anti-VEGF intravitreal: ranibizumab, bevacizumab, aflibercept, brolucizumab, faricimab"],
]
story.append(make_table(
amd_tbl[0], amd_tbl[1:],
col_widths=[3.5*cm, 6.0*cm, 6.0*cm]
))
story.append(Spacer(1, 0.3*cm))
for pt in [
"<b>Hard drusen</b> (hyaline, small, well-defined) = low risk; <b>soft drusen</b> (large, confluent, ill-defined) = HIGH risk for CNV",
"Drusen composition: lipid, vitronectin, complement factors, amyloid → local inflammation",
"Amsler grid tests central 10° of visual field (20/20 square); must be used with near correction",
"Sub-RPE blood/fluid = occult CNV; sub-retinal blood/fluid = classic CNV",
"Polypoidal choroidal vasculopathy (PCV) = variant of wet AMD; more common in Asian/African populations",
"AREDS2 formula reduces risk of progression from intermediate to advanced AMD by 25–30%",
]:
story.append(bullet(pt))
story.append(PageBreak())
# ================================================================
# SECTION 9 - QUICK REFERENCE TABLE
# ================================================================
story.append(section_header("9. QUICK REFERENCE MNEMONIC TABLE"))
story.append(Spacer(1, 0.3*cm))
ref_tbl = [
["Fundoscopic Finding", "Classic Diagnosis / Disease"],
["Cherry-red spot", "CRAO (vascular) | Tay-Sachs, Niemann-Pick, Gaucher (metabolic storage)"],
["'Blood and thunder' (haemorrhages all 4 quadrants)", "CRVO"],
["Bone-spicule pigmentation + waxy disc pallor + narrow arteries", "Retinitis Pigmentosa"],
["Bilateral disc swelling (papilloedema) + raised ICP", "Space-occupying lesion, IIH, cerebral venous sinus thrombosis"],
["NVD/NVE + dot-blot haemorrhages ± vitreous haemorrhage", "Proliferative Diabetic Retinopathy"],
["AV nipping + silver/copper wiring + macular star", "Hypertensive Retinopathy (Grade 2–4)"],
["Bull's-eye maculopathy", "Chloroquine/hydroxychloroquine toxicity | Stargardt disease | Cone dystrophy"],
["Roth spots (white-centred haemorrhages)", "Infective endocarditis (SBE) | Leukaemia | Severe anaemia | HIV retinopathy"],
["Hollenhorst plaques (sparkling yellow at bifurcations)", "Carotid artery atherosclerotic emboli"],
["Disc cupping (C:D >0.6) + NRR notching", "Primary Open-Angle Glaucoma"],
["Tortuous dilated vessels + massive exudates (young boy)", "Coats disease"],
["Sea-fan neovascularization (periphery)", "Sickle cell retinopathy (SC or SS Hb)"],
["Salmon-patch haemorrhages (periphery)", "Sickle cell retinopathy"],
["Cotton-wool spots alone", "Hypertension, diabetes, HIV, collagen vascular disease, CRVO/CRAO"],
["Optic disc drusen (pseudopapilloedema)", "Buried disc drusen (autosomal dominant; check USS/autofluorescence)"],
["Peripapillary RNFL wedge defects", "Glaucoma (precede visual field defects)"],
["Angioid streaks", "Pseudoxanthoma elasticum (most common) | Paget's disease | Sickle cell | Ehlers-Danlos"],
["Elschnig spots + Siegrist streaks", "Hypertensive choroidopathy (malignant HTN)"],
["Disc pallor (optic atrophy)", "Post-CRAO, optic neuritis, glaucoma, compressive optic neuropathy, Leber's"],
["Myelinated nerve fibres (white fluffy patches from disc)", "Normal variant; associated with myopia and amblyopia"],
]
story.append(make_table(
ref_tbl[0], ref_tbl[1:],
col_widths=[5.5*cm, CONTENT_W-5.5*cm]
))
story.append(Spacer(1, 0.4*cm))
# ---- FOOTER ----
story.append(HRFlowable(width="100%", thickness=1, color=colors.HexColor("#1565c0")))
story.append(Spacer(1, 0.2*cm))
story.append(Paragraph(
"Sources: Kanski's Clinical Ophthalmology: A Systematic Approach, 10th Ed. (ISBN 9780443110993) | "
"The Wills Eye Manual, 8th Ed. (ISBN 9781975160753) | Prepared for NEET PG Examination",
CAPTION_STYLE
))
doc.build(story)
print(f"\nPDF saved: {out_path}")
return out_path
if __name__ == "__main__":
build()
cd /home/daytona/workspace/fundoscopy-neetpg && python generate_pdf.py 2>&1
ls -lh /home/daytona/workspace/fundoscopy-neetpg/Fundoscopy_NEET_PG.pdf
Fundoscopy NEET PG
PDF Document · PDF
| Section | Content |
|---|---|
| 1 | Papilloedema - 4-stage fundus image (Early/Established/Chronic/Atrophic) + full staging table |
| 2 | CRVO - Impending, non-ischaemic, ischaemic comparison with fundus images + comparison table |
| 3 | CRAO - Cherry-red spot fundus image + embolus types table |
| 4 | Diabetic Retinopathy - Advanced DR + papillopathy images + full ETDRS classification + CSMO criteria |
| 5 | Retinitis Pigmentosa - Classic triad, inheritance table, syndromic associations (Usher, LMBB, Kearns-Sayre, Refsum, etc.) |
| 6 | Hypertensive Retinopathy - KWB grading table + all fundoscopic signs (Gunn, Salus, Bonnet, Elschnig, Siegrist) |
| 7 | Glaucomatous Optic Disc - ISNT rule, C:D ratio, all disc signs |
| 8 | AMD - Dry vs Wet comparison table + AREDS2 + anti-VEGF drugs |
| 9 | Quick Reference Mnemonic Table - 20+ classic fundus findings mapped to diagnoses |