refactor(praca/generate_images): fix ruff violations in generate_q24_diagrams.py

This commit is contained in:
Krzysztof kuhy Rudnicki 2026-03-14 17:15:35 +01:00
parent 54fb3ec0e8
commit bd45d60c85

View File

@ -4,16 +4,27 @@
Monochrome, A4-printable PNGs (300 DPI). Monochrome, A4-printable PNGs (300 DPI).
""" """
from __future__ import annotations
import logging
from pathlib import Path
from typing import TYPE_CHECKING
import matplotlib as mpl import matplotlib as mpl
mpl.use("Agg") mpl.use("Agg")
from pathlib import Path
import matplotlib.patches as mpatches import matplotlib.patches as mpatches
from matplotlib.patches import FancyBboxPatch from matplotlib.patches import FancyBboxPatch
import matplotlib.pyplot as plt import matplotlib.pyplot as plt
import numpy as np import numpy as np
if TYPE_CHECKING:
from matplotlib.axes import Axes
from matplotlib.figure import Figure
_logger = logging.getLogger(__name__)
rng = np.random.default_rng(42) rng = np.random.default_rng(42)
DPI = 300 DPI = 300
@ -32,23 +43,30 @@ GRAY3 = "#B8B8B8"
GRAY4 = "#F5F5F5" GRAY4 = "#F5F5F5"
GRAY5 = "#C0C0C0" GRAY5 = "#C0C0C0"
_PIXEL_BRIGHT_THRESH = 127
_GRADIENT_BRIGHT_THRESH = 100
_DATA_BRIGHT_THRESH = 5
_II_BRIGHT_THRESH = 25
_DOTS_STAGE_IDX = 2
def draw_box( def draw_box(
ax, ax: Axes,
x, x: float,
y, y: float,
w, w: float,
h, h: float,
text, text: str,
fill="white", *,
lw=1.2, fill: str = "white",
fontsize=FS, lw: float = 1.2,
fontweight="normal", fontsize: float = FS,
ha="center", fontweight: str = "normal",
va="center", ha: str = "center",
rounded=True, va: str = "center",
edgecolor=LN, rounded: bool = True,
linestyle="-", edgecolor: str = LN,
linestyle: str = "-",
) -> None: ) -> None:
"""Draw box.""" """Draw box."""
if rounded: if rounded:
@ -85,7 +103,17 @@ def draw_box(
) )
def draw_arrow(ax, x1, y1, x2, y2, lw=1.2, style="->", color=LN) -> None: def draw_arrow(
ax: Axes,
x1: float,
y1: float,
x2: float,
y2: float,
*,
lw: float = 1.2,
style: str = "->",
color: str = LN,
) -> None:
"""Draw arrow.""" """Draw arrow."""
ax.annotate( ax.annotate(
"", "",
@ -95,26 +123,27 @@ def draw_arrow(ax, x1, y1, x2, y2, lw=1.2, style="->", color=LN) -> None:
) )
def save_fig(fig, name) -> None: def save_fig(fig: Figure, name: str) -> None:
"""Save fig.""" """Save fig."""
path = str(Path(OUTPUT_DIR) / name) path = str(Path(OUTPUT_DIR) / name)
fig.savefig(path, dpi=DPI, bbox_inches="tight", facecolor=BG, pad_inches=0.15) fig.savefig(path, dpi=DPI, bbox_inches="tight", facecolor=BG, pad_inches=0.15)
plt.close(fig) plt.close(fig)
print(f" Saved: {path}") _logger.info(" Saved: %s", path)
def draw_table( def draw_table(
ax, ax: Axes,
headers, headers: list[str],
rows, rows: list[list[str]],
x0, x0: float,
y0, y0: float,
col_widths, col_widths: list[float],
row_h=0.4, *,
header_fill=GRAY2, row_h: float = 0.4,
row_fills=None, header_fill: str = GRAY2,
fontsize=FS, row_fills: list[str] | None = None,
header_fontsize=None, fontsize: float = FS,
header_fontsize: float | None = None,
) -> None: ) -> None:
"""Draw table.""" """Draw table."""
if header_fontsize is None: if header_fontsize is None:
@ -307,7 +336,7 @@ def draw_hog_gradient_steps() -> None:
va="center", va="center",
fontsize=FS_LABEL, fontsize=FS_LABEL,
fontweight="bold", fontweight="bold",
color="white" if patch[i, j] > 127 else "black", color="white" if patch[i, j] > _PIXEL_BRIGHT_THRESH else "black",
) )
ax.set_title("① Fragment obrazu\n(jasność pikseli)", fontsize=FS, fontweight="bold") ax.set_title("① Fragment obrazu\n(jasność pikseli)", fontsize=FS, fontweight="bold")
ax.set_xticks([]) ax.set_xticks([])
@ -327,7 +356,7 @@ def draw_hog_gradient_steps() -> None:
va="center", va="center",
fontsize=FS_LABEL, fontsize=FS_LABEL,
fontweight="bold", fontweight="bold",
color="white" if gx[i, j] > 100 else "black", color="white" if gx[i, j] > _GRADIENT_BRIGHT_THRESH else "black",
) )
ax.set_title("② Gradient Gx\n(krawędź pionowa!)", fontsize=FS, fontweight="bold") ax.set_title("② Gradient Gx\n(krawędź pionowa!)", fontsize=FS, fontweight="bold")
ax.set_xticks([]) ax.set_xticks([])
@ -476,7 +505,7 @@ def draw_viola_jones_cascade() -> None:
) )
# Dots between stage 3 and stage 25 # Dots between stage 3 and stage 25
if i == 2: if i == _DOTS_STAGE_IDX:
ax.text( ax.text(
x_pos + 2.0, 3.1, "· · ·", ha="center", fontsize=12, fontweight="bold" x_pos + 2.0, 3.1, "· · ·", ha="center", fontsize=12, fontweight="bold"
) )
@ -592,26 +621,29 @@ def draw_haar_features() -> None:
ax.axis("off") ax.axis("off")
ax.set_title("Linia (3 prostokąty)\n(nos vs policzki)", fontsize=FS) ax.set_title("Linia (3 prostokąty)\n(nos vs policzki)", fontsize=FS)
# Feature 4: Application on face (schematic) _draw_haar_face_panel(axes[3])
ax = axes[3]
# Draw face outline (oval) fig.tight_layout()
face = mpatches.Ellipse((1.2, 1.2), 2.0, 2.4, facecolor=GRAY4, edgecolor=LN, lw=1.5) save_fig(fig, "q24_haar_features.png")
def _draw_haar_face_panel(ax: Axes) -> None:
"""Draw Haar feature application on face schematic."""
face = mpatches.Ellipse(
(1.2, 1.2), 2.0, 2.4, facecolor=GRAY4, edgecolor=LN, lw=1.5,
)
ax.add_patch(face) ax.add_patch(face)
# Eyes (dark)
ax.add_patch( ax.add_patch(
mpatches.Ellipse((0.7, 1.6), 0.4, 0.2, facecolor=GRAY3, edgecolor=LN, lw=1) mpatches.Ellipse((0.7, 1.6), 0.4, 0.2, facecolor=GRAY3, edgecolor=LN, lw=1)
) )
ax.add_patch( ax.add_patch(
mpatches.Ellipse((1.7, 1.6), 0.4, 0.2, facecolor=GRAY3, edgecolor=LN, lw=1) mpatches.Ellipse((1.7, 1.6), 0.4, 0.2, facecolor=GRAY3, edgecolor=LN, lw=1)
) )
# Nose (light)
ax.plot([1.2, 1.1, 1.3], [1.3, 0.9, 0.9], color=LN, lw=1) ax.plot([1.2, 1.1, 1.3], [1.3, 0.9, 0.9], color=LN, lw=1)
# Mouth
ax.plot([0.8, 1.0, 1.2, 1.4, 1.6], [0.55, 0.5, 0.55, 0.5, 0.55], color=LN, lw=1) ax.plot([0.8, 1.0, 1.2, 1.4, 1.6], [0.55, 0.5, 0.55, 0.5, 0.55], color=LN, lw=1)
# Haar feature overlay on eyes
ax.add_patch( ax.add_patch(
mpatches.Rectangle( mpatches.Rectangle(
(0.3, 1.4), 1.8, 0.4, facecolor="none", edgecolor=LN, lw=2, linestyle="--" (0.3, 1.4), 1.8, 0.4, facecolor="none", edgecolor=LN, lw=2, linestyle="--",
) )
) )
ax.annotate( ax.annotate(
@ -628,9 +660,6 @@ def draw_haar_features() -> None:
ax.axis("off") ax.axis("off")
ax.set_title("Zastosowanie na twarzy", fontsize=FS) ax.set_title("Zastosowanie na twarzy", fontsize=FS)
fig.tight_layout()
save_fig(fig, "q24_haar_features.png")
# ============================================================ # ============================================================
# 5. Integral Image # 5. Integral Image
@ -659,7 +688,7 @@ def draw_integral_image() -> None:
va="center", va="center",
fontsize=12, fontsize=12,
fontweight="bold", fontweight="bold",
color="white" if data[i, j] > 5 else "black", color="white" if data[i, j] > _DATA_BRIGHT_THRESH else "black",
) )
ax.set_title("① Obraz oryginalny", fontsize=FS, fontweight="bold") ax.set_title("① Obraz oryginalny", fontsize=FS, fontweight="bold")
ax.set_xticks([]) ax.set_xticks([])
@ -679,7 +708,7 @@ def draw_integral_image() -> None:
va="center", va="center",
fontsize=12, fontsize=12,
fontweight="bold", fontweight="bold",
color="white" if ii[i, j] > 25 else "black", color="white" if ii[i, j] > _II_BRIGHT_THRESH else "black",
) )
ax.set_title("② Integral Image\n(sumy kumulatywne)", fontsize=FS, fontweight="bold") ax.set_title("② Integral Image\n(sumy kumulatywne)", fontsize=FS, fontweight="bold")
ax.set_xticks([]) ax.set_xticks([])
@ -859,6 +888,75 @@ def draw_rcnn_evolution() -> None:
save_fig(fig, "q24_rcnn_evolution.png") save_fig(fig, "q24_rcnn_evolution.png")
def _draw_yolo_cell_prediction(ax: Axes) -> None:
"""Draw YOLO cell prediction vector panel."""
ax.axis("off")
ax.set_xlim(0, 6)
ax.set_ylim(-1, 5)
labels = [
"x", "y", "w", "h", "conf",
"x", "y", "w", "h", "conf",
"P(c₁)", "...", "P(c₂₀)",
]
colors_vec = [GRAY4] * 5 + [GRAY2] * 5 + [GRAY1] * 3
bw = 0.42
for i, (label, col) in enumerate(
zip(labels, colors_vec, strict=False),
):
x_pos = 0.3 + i * bw
ax.add_patch(
mpatches.Rectangle(
(x_pos, 2.5), bw - 0.02, 0.6,
facecolor=col, edgecolor=LN, lw=0.8,
)
)
ax.text(
x_pos + bw / 2,
2.8,
label,
ha="center",
va="center",
fontsize=5,
fontweight="bold",
)
ax.annotate(
"", xy=(0.3, 2.4), xytext=(2.4, 2.4),
arrowprops={"arrowstyle": "-", "lw": 1},
)
ax.text(1.35, 2.15, "bbox 1 (5 wartości)", ha="center", fontsize=FS_SMALL)
ax.annotate(
"", xy=(2.4, 2.4), xytext=(4.5, 2.4),
arrowprops={"arrowstyle": "-", "lw": 1},
)
ax.text(3.45, 2.15, "bbox 2 (5 wartości)", ha="center", fontsize=FS_SMALL)
ax.annotate(
"", xy=(4.5, 2.4), xytext=(5.8, 2.4),
arrowprops={"arrowstyle": "-", "lw": 1},
)
ax.text(5.15, 2.15, "20 klas", ha="center", fontsize=FS_SMALL)
ax.text(
3.0,
3.5,
"Każda komórka → 30 wartości\n= 2x(x,y,w,h,conf) + 20 klas",
ha="center",
fontsize=FS,
fontweight="bold",
bbox={"boxstyle": "round,pad=0.3", "facecolor": GRAY4, "edgecolor": GRAY3},
)
ax.set_title(
"② Predykcja jednej komórki\n(S=7, B=2, C=20)",
fontsize=FS,
fontweight="bold",
)
# ============================================================ # ============================================================
# 7. YOLO Grid # 7. YOLO Grid
# ============================================================ # ============================================================
@ -874,14 +972,17 @@ def draw_yolo_grid() -> None:
# Grid on image # Grid on image
ax = axes[0] ax = axes[0]
S = 7 grid_size = 7
ax.set_xlim(0, S) ax.set_xlim(0, grid_size)
ax.set_ylim(0, S) ax.set_ylim(0, grid_size)
for i in range(S + 1): for i in range(grid_size + 1):
ax.axhline(y=i, color=LN, lw=0.5, alpha=0.5) ax.axhline(y=i, color=LN, lw=0.5, alpha=0.5)
ax.axvline(x=i, color=LN, lw=0.5, alpha=0.5) ax.axvline(x=i, color=LN, lw=0.5, alpha=0.5)
ax.add_patch( ax.add_patch(
mpatches.Rectangle((0, 0), S, S, facecolor=GRAY4, edgecolor=LN, lw=1.5) mpatches.Rectangle(
(0, 0), grid_size, grid_size,
facecolor=GRAY4, edgecolor=LN, lw=1.5,
)
) )
# Highlight one cell # Highlight one cell
ax.add_patch(mpatches.Rectangle((3, 3), 1, 1, facecolor=GRAY2, edgecolor=LN, lw=2)) ax.add_patch(mpatches.Rectangle((3, 3), 1, 1, facecolor=GRAY2, edgecolor=LN, lw=2))
@ -907,77 +1008,7 @@ def draw_yolo_grid() -> None:
ax.set_xticks([]) ax.set_xticks([])
ax.set_yticks([]) ax.set_yticks([])
# Cell prediction _draw_yolo_cell_prediction(axes[1])
ax = axes[1]
ax.axis("off")
ax.set_xlim(0, 6)
ax.set_ylim(-1, 5)
# Draw prediction vector
labels = [
"x",
"y",
"w",
"h",
"conf",
"x",
"y",
"w",
"h",
"conf",
"P(c₁)",
"...",
"P(c₂₀)",
]
colors_vec = [GRAY4] * 5 + [GRAY2] * 5 + [GRAY1] * 3
bw = 0.42
for i, (l, c) in enumerate(zip(labels, colors_vec, strict=False)):
x_pos = 0.3 + i * bw
ax.add_patch(
mpatches.Rectangle(
(x_pos, 2.5), bw - 0.02, 0.6, facecolor=c, edgecolor=LN, lw=0.8
)
)
ax.text(
x_pos + bw / 2,
2.8,
l,
ha="center",
va="center",
fontsize=5,
fontweight="bold",
)
# Brackets for grouping
ax.annotate(
"", xy=(0.3, 2.4), xytext=(2.4, 2.4), arrowprops={"arrowstyle": "-", "lw": 1}
)
ax.text(1.35, 2.15, "bbox 1 (5 wartości)", ha="center", fontsize=FS_SMALL)
ax.annotate(
"", xy=(2.4, 2.4), xytext=(4.5, 2.4), arrowprops={"arrowstyle": "-", "lw": 1}
)
ax.text(3.45, 2.15, "bbox 2 (5 wartości)", ha="center", fontsize=FS_SMALL)
ax.annotate(
"", xy=(4.5, 2.4), xytext=(5.8, 2.4), arrowprops={"arrowstyle": "-", "lw": 1}
)
ax.text(5.15, 2.15, "20 klas", ha="center", fontsize=FS_SMALL)
ax.text(
3.0,
3.5,
"Każda komórka → 30 wartości\n= 2x(x,y,w,h,conf) + 20 klas",
ha="center",
fontsize=FS,
fontweight="bold",
bbox={"boxstyle": "round,pad=0.3", "facecolor": GRAY4, "edgecolor": GRAY3},
)
ax.set_title(
"② Predykcja jednej komórki\n(S=7, B=2, C=20)", fontsize=FS, fontweight="bold"
)
# Speed comparison # Speed comparison
ax = axes[2] ax = axes[2]
@ -1429,7 +1460,6 @@ def draw_svm_hyperplane() -> None:
pad=12, pad=12,
) )
# Class +1 (top-right)
x_pos = rng.standard_normal(15) * 0.5 + 3 x_pos = rng.standard_normal(15) * 0.5 + 3
y_pos = rng.standard_normal(15) * 0.5 + 3 y_pos = rng.standard_normal(15) * 0.5 + 3
ax.scatter( ax.scatter(
@ -1444,7 +1474,6 @@ def draw_svm_hyperplane() -> None:
zorder=3, zorder=3,
) )
# Class -1 (bottom-left)
x_neg = rng.standard_normal(15) * 0.5 + 1 x_neg = rng.standard_normal(15) * 0.5 + 1
y_neg = rng.standard_normal(15) * 0.5 + 1 y_neg = rng.standard_normal(15) * 0.5 + 1
ax.scatter( ax.scatter(
@ -1610,7 +1639,7 @@ def draw_roi_pooling() -> None:
va="center", va="center",
fontsize=10, fontsize=10,
fontweight="bold", fontweight="bold",
color="white" if roi_data[i, j] > 5 else "black", color="white" if roi_data[i, j] > _DATA_BRIGHT_THRESH else "black",
) )
ax.set_title("② ROI podzielony\nna siatkę 2x2", fontsize=FS, fontweight="bold") ax.set_title("② ROI podzielony\nna siatkę 2x2", fontsize=FS, fontweight="bold")
ax.set_xticks([]) ax.set_xticks([])
@ -1633,7 +1662,7 @@ def draw_roi_pooling() -> None:
va="center", va="center",
fontsize=14, fontsize=14,
fontweight="bold", fontweight="bold",
color="white" if out[i, j] > 5 else "black", color="white" if out[i, j] > _DATA_BRIGHT_THRESH else "black",
) )
ax.set_title( ax.set_title(
"③ Po ROI Pool 2x2\n(max z każdej komórki)", fontsize=FS, fontweight="bold" "③ Po ROI Pool 2x2\n(max z każdej komórki)", fontsize=FS, fontweight="bold"
@ -1866,7 +1895,6 @@ def draw_fpn() -> None:
pad=12, pad=12,
) )
# Bottom-up (backbone)
levels = [ levels = [
(0, 0, 2.0, 2.0, "C2\n56x56", "duże\ndetale"), (0, 0, 2.0, 2.0, "C2\n56x56", "duże\ndetale"),
(0, 2.2, 1.5, 1.5, "C3\n28x28", ""), (0, 2.2, 1.5, 1.5, "C3\n28x28", ""),
@ -1993,7 +2021,6 @@ def draw_anchor_boxes() -> None:
# 9 anchors: 3 sizes x 3 ratios # 9 anchors: 3 sizes x 3 ratios
anchors = [ anchors = [
# (w, h, style, label)
(0.8, 0.8, "-", "1:1 small"), (0.8, 0.8, "-", "1:1 small"),
(1.6, 1.6, "-", "1:1 medium"), (1.6, 1.6, "-", "1:1 medium"),
(2.4, 2.4, "-", "1:1 large"), (2.4, 2.4, "-", "1:1 large"),
@ -2247,7 +2274,7 @@ def draw_cnn_architecture() -> None:
# MAIN # MAIN
# ============================================================ # ============================================================
if __name__ == "__main__": if __name__ == "__main__":
print("Generating PYTANIE 24 diagrams...") _logger.info("Generating PYTANIE 24 diagrams...")
draw_hog_svm_pipeline() draw_hog_svm_pipeline()
draw_hog_gradient_steps() draw_hog_gradient_steps()
draw_viola_jones_cascade() draw_viola_jones_cascade()
@ -2267,4 +2294,4 @@ if __name__ == "__main__":
draw_anchor_boxes() draw_anchor_boxes()
draw_detection_tasks() draw_detection_tasks()
draw_cnn_architecture() draw_cnn_architecture()
print("\nAll PYTANIE 24 diagrams generated!") _logger.info("All PYTANIE 24 diagrams generated!")