{"version":2,"relationshipURLBase":"https://codingai-lec03.pages.dev/course/notebook-blocks/8d1df38317244d5097b2ef92388bf925.html","relationshipResolution":"Resolve relative relationship URLs against relationshipURLBase; complete records and absolute URLs are in blocks.","title":"可视化与实际输出 · 实现代码","url":"https://codingai-lec03.pages.dev/course/notebook-blocks/8d1df38317244d5097b2ef92388bf925.html","markdown":"https://codingai-lec03.pages.dev/course/notebook-blocks/8d1df38317244d5097b2ef92388bf925.md","pages":[{"id":"reading-opening","title":"可视化与实际输出 · 实现代码","unit":"可视化与实际输出 · 实现代码","unitId":"notebook-blocks/8d1df38317244d5097b2ef92388bf925","chapter":"course","number":1}],"aliases":[{"oldId":"b-cf82701486488ac2b283d9a8","targetId":"b-cf82701486488ac2b283d9a8","page":"reading-opening","operation":"move","beforeSHA256":"9027342eb8430f11699811edb871aeb2e37b28e9f874a2aa48778e6bac2818b7","afterSHA256":"9027342eb8430f11699811edb871aeb2e37b28e9f874a2aa48778e6bac2818b7","reason":"Same source body in authored semantic page"},{"oldId":"b-21585f6ab1b8298d5f54c7b4","targetId":"b-21585f6ab1b8298d5f54c7b4","page":"reading-opening","operation":"move","beforeSHA256":"b5d143e25e47f575610d1937fcb1b1b3f6168a7351cfe203f8f9f48c309f376d","afterSHA256":"b5d143e25e47f575610d1937fcb1b1b3f6168a7351cfe203f8f9f48c309f376d","reason":"Same source body in authored semantic page"},{"oldId":"b-9dc579e1ca389afa7332e19e","targetId":"b-9dc579e1ca389afa7332e19e","page":"reading-opening","operation":"move","beforeSHA256":"dc06c1e728a4b5740ba634b145bc9a1da6323031626734cb4555db5a744df806","afterSHA256":"dc06c1e728a4b5740ba634b145bc9a1da6323031626734cb4555db5a744df806","reason":"Same source body in authored semantic page"},{"oldId":"b-6a27487a31393748a70e5abb","targetId":"b-6a27487a31393748a70e5abb","page":"reading-opening","operation":"move","beforeSHA256":"4e0de6d032a3765c60907de32f28cd988b659299663e842d319bdb0426243d78","afterSHA256":"4e0de6d032a3765c60907de32f28cd988b659299663e842d319bdb0426243d78","reason":"Same source body in authored semantic page"},{"oldId":"b-98bea3d4cce59e64199cce19","targetId":"b-98bea3d4cce59e64199cce19","page":"reading-opening","operation":"move","beforeSHA256":"c18440913a5b9ceb5beb27a2efb527a7e2ba4dc882038771e5f12bc96458119e","afterSHA256":"c18440913a5b9ceb5beb27a2efb527a7e2ba4dc882038771e5f12bc96458119e","reason":"Same source body in authored semantic page"}],"blocks":[{"id":"b-cf82701486488ac2b283d9a8","type":"p","scope":"document","url":"https://codingai-lec03.pages.dev/course/notebook-blocks/8d1df38317244d5097b2ef92388bf925.html#b-cf82701486488ac2b283d9a8","content":"[LAB 09 · 检测几何：框、匹配、NMS 与 AP](/course/notebooks/90d213b4e1e04c7e889ee6fc042b2358.html)","sha256":"9027342eb8430f11699811edb871aeb2e37b28e9f874a2aa48778e6bac2818b7","chapter":{"id":"course","title":"可视化与实际输出 · 实现代码"},"unit":{"id":"notebook-blocks/8d1df38317244d5097b2ef92388bf925","title":"可视化与实际输出 · 实现代码"},"page":{"id":"reading-opening","title":"可视化与实际输出 · 实现代码","number":1},"sectionTitle":"可视化与实际输出 · 实现代码","currentQuestion":"可视化与实际输出 · 实现代码","sourceSlides":[],"knowledgeBlock":null,"formulas":[],"previousBlock":null,"nextBlock":"b-21585f6ab1b8298d5f54c7b4","prerequisites":[],"relatedBlocks":[]},{"id":"b-21585f6ab1b8298d5f54c7b4","type":"h1","scope":"document","url":"https://codingai-lec03.pages.dev/course/notebook-blocks/8d1df38317244d5097b2ef92388bf925.html#b-21585f6ab1b8298d5f54c7b4","content":"# 可视化与实际输出 · 实现代码","sha256":"b5d143e25e47f575610d1937fcb1b1b3f6168a7351cfe203f8f9f48c309f376d","chapter":{"id":"course","title":"可视化与实际输出 · 实现代码"},"unit":{"id":"notebook-blocks/8d1df38317244d5097b2ef92388bf925","title":"可视化与实际输出 · 实现代码"},"page":{"id":"reading-opening","title":"可视化与实际输出 · 实现代码","number":1},"sectionTitle":"可视化与实际输出 · 实现代码","currentQuestion":"可视化与实际输出 · 实现代码","sourceSlides":[],"knowledgeBlock":null,"formulas":[],"previousBlock":"b-cf82701486488ac2b283d9a8","nextBlock":"b-9dc579e1ca389afa7332e19e","prerequisites":[{"id":"b-cf82701486488ac2b283d9a8","url":"#b-cf82701486488ac2b283d9a8","role":"problem-context"}],"relatedBlocks":[]},{"id":"b-9dc579e1ca389afa7332e19e","type":"p","scope":"document","url":"https://codingai-lec03.pages.dev/course/notebook-blocks/8d1df38317244d5097b2ef92388bf925.html#b-9dc579e1ca389afa7332e19e","content":"并入相关学习路径","sha256":"dc06c1e728a4b5740ba634b145bc9a1da6323031626734cb4555db5a744df806","chapter":{"id":"course","title":"可视化与实际输出 · 实现代码"},"unit":{"id":"notebook-blocks/8d1df38317244d5097b2ef92388bf925","title":"可视化与实际输出 · 实现代码"},"page":{"id":"reading-opening","title":"可视化与实际输出 · 实现代码","number":1},"sectionTitle":"可视化与实际输出 · 实现代码","currentQuestion":"可视化与实际输出 · 实现代码","sourceSlides":[],"knowledgeBlock":null,"formulas":[],"previousBlock":"b-21585f6ab1b8298d5f54c7b4","nextBlock":"b-6a27487a31393748a70e5abb","prerequisites":[{"id":"b-21585f6ab1b8298d5f54c7b4","url":"#b-21585f6ab1b8298d5f54c7b4","role":"section-title"},{"id":"b-cf82701486488ac2b283d9a8","url":"#b-cf82701486488ac2b283d9a8","role":"problem-context"}],"relatedBlocks":[]},{"id":"b-6a27487a31393748a70e5abb","type":"p","scope":"document","url":"https://codingai-lec03.pages.dev/course/notebook-blocks/8d1df38317244d5097b2ef92388bf925.html#b-6a27487a31393748a70e5abb","content":"固定框匹配、PR/AP、NMS误删相邻对象反例","sha256":"4e0de6d032a3765c60907de32f28cd988b659299663e842d319bdb0426243d78","chapter":{"id":"course","title":"可视化与实际输出 · 实现代码"},"unit":{"id":"notebook-blocks/8d1df38317244d5097b2ef92388bf925","title":"可视化与实际输出 · 实现代码"},"page":{"id":"reading-opening","title":"可视化与实际输出 · 实现代码","number":1},"sectionTitle":"可视化与实际输出 · 实现代码","currentQuestion":"可视化与实际输出 · 实现代码","sourceSlides":[],"knowledgeBlock":null,"formulas":[],"previousBlock":"b-9dc579e1ca389afa7332e19e","nextBlock":"b-98bea3d4cce59e64199cce19","prerequisites":[{"id":"b-21585f6ab1b8298d5f54c7b4","url":"#b-21585f6ab1b8298d5f54c7b4","role":"section-title"},{"id":"b-cf82701486488ac2b283d9a8","url":"#b-cf82701486488ac2b283d9a8","role":"problem-context"}],"relatedBlocks":[]},{"id":"b-98bea3d4cce59e64199cce19","type":"pre","scope":"document","url":"https://codingai-lec03.pages.dev/course/notebook-blocks/8d1df38317244d5097b2ef92388bf925.html#b-98bea3d4cce59e64199cce19","content":"```\ndef svg_document(width, height, fragments):\n    return (f'<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"{width}\" height=\"{height}\" '\n            f'viewBox=\"0 0 {width} {height}\"><rect width=\"100%\" height=\"100%\" fill=\"#f7f8fa\"/>'\n            '<style>text{font-family:Arial,sans-serif;fill:#162235} .title{font-size:22px;font-weight:bold}'\n            '.label{font-size:15px}.small{font-size:13px}.axis{stroke:#738095;stroke-width:1}</style>'\n            + \"\".join(fragments) + \"</svg>\")\n\n\ndef svg_text(x, y, value, cls=\"label\", extra=\"\"):\n    return f'<text x=\"{x}\" y=\"{y}\" class=\"{cls}\" {extra}>{escape(str(value))}</text>'\n\n\ngeometry = [svg_text(32, 36, \"Two GT boxes / four scored predictions\", \"title\"),\n            svg_text(32, 62, \"Continuous xyxy coordinates; dashed = GT; solid = prediction\", \"label\")]\nfor value in range(9):\n    x, y = 80 + value * 55, 100 + value * 55\n    geometry.append(f'<path d=\"M {x} 100 V 540 M 80 {y} H 520\" stroke=\"#e0e4e9\"/>')\n    geometry += [svg_text(x - 4, 566, value, \"small\"), svg_text(55, y + 5, value, \"small\")]\ngeometry += [svg_text(293, 592, \"x coordinate\"), svg_text(20, 327, \"y\", \"label\")]\nfor i, b in enumerate(GT):\n    x1, y1, x2, y2 = b\n    geometry.append(f'<rect x=\"{80 + x1 * 55}\" y=\"{100 + y1 * 55}\" width=\"{(x2 - x1) * 55}\" '\n                    f'height=\"{(y2 - y1) * 55}\" fill=\"none\" stroke=\"#111827\" stroke-width=\"4\" stroke-dasharray=\"9 5\"/>')\n    geometry.append(svg_text(83 + x1 * 55, 88, f\"GT{i+1}\"))\ncolors = [\"#156a9c\", \"#d16a20\", \"#188164\", \"#845ec2\"]\nfor i, b in enumerate(PRED):\n    x1, y1, x2, y2 = b\n    geometry.append(f'<rect x=\"{80 + x1 * 55}\" y=\"{100 + y1 * 55}\" width=\"{(x2 - x1) * 55}\" '\n                    f'height=\"{(y2 - y1) * 55}\" fill=\"none\" stroke=\"{colors[i]}\" stroke-width=\"2\"/>')\n    geometry.append(f'<line x1=\"585\" x2=\"615\" y1=\"{128+i*50}\" y2=\"{128+i*50}\" stroke=\"{colors[i]}\" stroke-width=\"4\"/>')\n    geometry.append(svg_text(627, 134 + i * 50, f\"P{i+1} | score {SCORES[i]:.2f}\"))\n    geometry.append(svg_text(627, 153 + i * 50, f\"IoU(GT1,GT2) = {iou_matrix[i,0]:.3f}, {iou_matrix[i,1]:.3f}\", \"small\"))\ngeometry += [svg_text(585, 372, \"AP50: TP, FP, TP, FP\"),\n             svg_text(585, 399, \"P2 overlaps GT1, already used.\"),\n             svg_text(585, 444, \"AP75: TP, FP, FP, FP\"),\n             svg_text(585, 471, \"P3 IoU = 2/3 < 0.75.\"),\n             svg_text(585, 527, \"The axes include the distant FP.\", \"small\")]\n(OUT / \"geometry.svg\").write_text(svg_document(955, 615, geometry), encoding=\"utf-8\")\n\npr = [svg_text(28, 36, \"All-points AP integrates the precision envelope\", \"title\"),\n      svg_text(28, 62, \"Original predictions, before NMS | solid: envelope; dots: raw ranked precision\", \"label\")]\nfor panel, key in enumerate([\"AP50\", \"AP75\"]):\n    result = baseline[key]\n    left, top, width, height = 80 + panel * 460, 108, 340, 290\n    for tick in [0, .25, .5, .75, 1]:\n        x, y = left + tick * width, top + (1 - tick) * height\n        pr += [f'<path d=\"M {x} {top} V {top+height} M {left} {y} H {left+width}\" stroke=\"#dbe1e7\"/>',\n               svg_text(x - 8, top + height + 26, f\"{tick:g}\", \"small\"),\n               svg_text(left - 35, y + 4, f\"{tick:g}\", \"small\")]\n    mrec, env = np.array(result[\"envelope_recall\"]), np.array(result[\"precision_envelope\"])\n    intervals = np.flatnonzero(np.diff(mrec) > 0)\n    for interval_i, j in enumerate(intervals):\n        x0, x1 = left + mrec[j] * width, left + mrec[j + 1] * width\n        y = top + (1 - env[j + 1]) * height\n        pr.append(f'<rect x=\"{x0}\" y=\"{y}\" width=\"{x1-x0}\" height=\"{top+height-y}\" fill=\"#cce4ec\"/>')\n        pr.append(f'<path d=\"M {x0} {y} H {x1}\" stroke=\"#136b87\" stroke-width=\"4\"/>')\n        if interval_i + 1 < len(intervals):\n            next_y = top + (1 - env[intervals[interval_i + 1] + 1]) * height\n            pr.append(f'<path d=\"M {x1} {y} V {next_y}\" stroke=\"#136b87\" stroke-width=\"3\"/>')\n    for i, (r, p) in enumerate(zip(result[\"recall\"], result[\"precision\"])):\n        x, y = left + r * width, top + (1 - p) * height\n        pr += [f'<circle cx=\"{x}\" cy=\"{y}\" r=\"5\" fill=\"#cc662c\"/>',\n               svg_text(x + (10 if x < left + width else -26), y + (18 if i in (0, 3) else -9), f\"P{i+1}\", \"small\")]\n    pr += [svg_text(left + 108, 94, f\"{key} = {result['ap']:.6f}\"),\n           svg_text(left + 130, 451, \"Recall\"), svg_text(left - 38, 85, \"Precision\", \"small\")]\npr += [svg_text(80, 489, \"AP50 = (1/2)*1 + (1/2)*(2/3) = 5/6\"),\n       svg_text(540, 489, \"AP75 = (1/2)*1 + (1/2)*0 = 1/2\"),\n       svg_text(80, 529, \"Repeated recall adds no area; the interval from 0.5 to 1 uses precision 2/3.\", \"small\")]\n(OUT / \"precision_recall.svg\").write_text(svg_document(970, 556, pr), encoding=\"utf-8\")\n\nnms = [svg_text(28, 36, \"The same NMS rule can remove a duplicate or a real object\", \"title\"),\n       svg_text(28, 63, \"Class-aware NMS: suppress same-class boxes only when IoU > 0.50\", \"label\")]\nfor panel, (title, boxes, mutual, before, after) in enumerate([\n        (\"A. Duplicate predictions for one object\", PRED[:2], 9/11, 5/6, 1.0),\n        (\"B. Two adjacent real objects\", NEIGHBOR_GT, .6, 1.0, .5)]):\n    left = 40 + panel * 485\n    nms.append(svg_text(left, 110, title))\n    for i, b in enumerate(boxes):\n        x1, y1, x2, y2 = b\n        nms.append(f'<rect x=\"{left+25+x1*105}\" y=\"{145+y1*85}\" width=\"{(x2-x1)*105}\" '\n                   f'height=\"{(y2-y1)*85}\" fill=\"none\" stroke=\"{colors[i]}\" stroke-width=\"4\" '\n                   + ('stroke-dasharray=\"8 5\"' if i == 1 else '') + '/>')\n    nms += [svg_text(left + 25, 345, f\"Mutual IoU = {mutual:.3f} > 0.50\"),\n            svg_text(left + 25, 373, \"Blue P1 retained; dashed orange P2 removed.\"),\n            svg_text(left + 25, 410, f\"AP50: {before:.6f} -> {after:.6f}\")]\n    if panel == 0:\n        nms += [svg_text(left + 25, 442, \"P2 was a duplicate FP; full-scene AP improves.\", \"small\"),\n                svg_text(left + 25, 465, \"P3 and P4 remain in the original scene.\", \"small\")]\n    else:\n        nms += [svg_text(left + 25, 442, \"P2 was a TP; recall falls from 1.0 to 0.5.\", \"small\"),\n                svg_text(left + 25, 465, \"Changing only NMS threshold to 0.70 keeps both.\", \"small\")]\n(OUT / \"nms_tradeoff.svg\").write_text(svg_document(1000, 505, nms), encoding=\"utf-8\")\n\n# Rich display is optional; SVG files are created above without IPython.\ntry:\n    from IPython.display import SVG, display\n    for figure in [\"geometry.svg\", \"precision_recall.svg\", \"nms_tradeoff.svg\"]:\n        display(SVG(filename=str(OUT / figure)))\nexcept ImportError:\n    print(\"SVG files saved; open them in a browser for viewing.\")\n```","sha256":"c18440913a5b9ceb5beb27a2efb527a7e2ba4dc882038771e5f12bc96458119e","chapter":{"id":"course","title":"可视化与实际输出 · 实现代码"},"unit":{"id":"notebook-blocks/8d1df38317244d5097b2ef92388bf925","title":"可视化与实际输出 · 实现代码"},"page":{"id":"reading-opening","title":"可视化与实际输出 · 实现代码","number":1},"sectionTitle":"可视化与实际输出 · 实现代码","currentQuestion":"可视化与实际输出 · 实现代码","sourceSlides":[],"knowledgeBlock":null,"formulas":[],"previousBlock":"b-6a27487a31393748a70e5abb","nextBlock":null,"prerequisites":[{"id":"b-21585f6ab1b8298d5f54c7b4","url":"#b-21585f6ab1b8298d5f54c7b4","role":"section-title"},{"id":"b-cf82701486488ac2b283d9a8","url":"#b-cf82701486488ac2b283d9a8","role":"problem-context"}],"relatedBlocks":[]}]}
