File size: 4,227 Bytes
585244b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
import numpy as np

FEATURE_NAMES = [
    "w_frac", "h_frac", "area_frac", "log_aspect", "channel", "step_frac", "q",
    "res_mean", "res_std", "res_abs_mean", "res_rms", "res_min", "res_max",
    "res_p05", "res_p25", "res_p50", "res_p75", "res_p95",
    "pos_frac", "neg_frac", "zeroish_frac",
    "uniq_frac", "mean_abs_dx", "mean_abs_dy", "grad_energy", "lap_abs_mean",
    "target_mean", "target_std", "canvas_mean", "canvas_std",
    "before_mse", "before_mae",
]
FEATURE_DIM = len(FEATURE_NAMES)

# Features requiring a sort (percentiles / unique) -> excluded from the cheap path.
SORT_FEATURES = {"res_p05", "res_p25", "res_p50", "res_p75", "res_p95", "uniq_frac"}


def extract_features(target, canvas, box, step, q, image_w, image_h, patch_count, n_channels):
    c, x, y, bw, bh = box
    t = target[y:y + bh, x:x + bw, c].astype(np.float64)
    cv = canvas[y:y + bh, x:x + bw, c].astype(np.float64)
    res = t - cv
    before = t - np.clip(cv, 0, 255)
    flat = res.ravel()
    n = max(1, flat.size)
    absflat = np.abs(flat)
    p05, p25, p50, p75, p95 = np.percentile(flat, [5, 25, 50, 75, 95])
    dx = float(np.abs(np.diff(res, axis=1)).mean()) if bw > 1 else 0.0
    dy = float(np.abs(np.diff(res, axis=0)).mean()) if bh > 1 else 0.0
    lapx = float(np.abs(res[:, 2:] - 2 * res[:, 1:-1] + res[:, :-2]).mean()) if bw > 2 else 0.0
    lapy = float(np.abs(res[2:] - 2 * res[1:-1] + res[:-2]).mean()) if bh > 2 else 0.0
    uniq = np.unique(np.rint(flat / 2.0)).size / n
    feats = np.array([
        bw / image_w, bh / image_h, (bw * bh) / (image_w * image_h),
        np.log((bw + 1e-6) / (bh + 1e-6)),
        c / max(1, n_channels - 1), step / max(1, patch_count), q,
        float(flat.mean()), float(flat.std()), float(absflat.mean()),
        float(np.sqrt((flat ** 2).mean())), float(flat.min()), float(flat.max()),
        p05, p25, p50, p75, p95,
        float((flat > 0).mean()), float((flat < 0).mean()), float((absflat < 2).mean()),
        uniq, dx, dy, dx + dy, lapx + lapy,
        float(t.mean()), float(t.std()), float(cv.mean()), float(cv.std()),
        float((before ** 2).mean()), float(np.abs(before).mean()),
    ], dtype=np.float32)
    return feats


def extract_cheap(names, target, canvas, box, step, q, image_w, image_h, patch_count, n_channels):
    """Compute only the requested non-sort features (no percentile/unique)."""
    c, x, y, bw, bh = box
    t = target[y:y + bh, x:x + bw, c].astype(np.float64)
    cv = canvas[y:y + bh, x:x + bw, c].astype(np.float64)
    res = t - cv
    flat = res.ravel()
    absflat = np.abs(flat)
    d = {
        "w_frac": bw / image_w, "h_frac": bh / image_h, "area_frac": (bw * bh) / (image_w * image_h),
        "log_aspect": np.log((bw + 1e-6) / (bh + 1e-6)), "channel": c / max(1, n_channels - 1),
        "step_frac": step / max(1, patch_count), "q": q,
        "res_mean": flat.mean(), "res_std": flat.std(), "res_abs_mean": absflat.mean(),
        "res_rms": np.sqrt((flat ** 2).mean()), "res_min": flat.min(), "res_max": flat.max(),
        "pos_frac": (flat > 0).mean(), "neg_frac": (flat < 0).mean(), "zeroish_frac": (absflat < 2).mean(),
        "target_mean": t.mean(), "target_std": t.std(), "canvas_mean": cv.mean(), "canvas_std": cv.std(),
    }
    if {"mean_abs_dx", "grad_energy"} & set(names):
        d["mean_abs_dx"] = float(np.abs(np.diff(res, axis=1)).mean()) if bw > 1 else 0.0
    if {"mean_abs_dy", "grad_energy"} & set(names):
        d["mean_abs_dy"] = float(np.abs(np.diff(res, axis=0)).mean()) if bh > 1 else 0.0
    if "grad_energy" in names:
        d["grad_energy"] = d["mean_abs_dx"] + d["mean_abs_dy"]
    if "lap_abs_mean" in names:
        lx = float(np.abs(res[:, 2:] - 2 * res[:, 1:-1] + res[:, :-2]).mean()) if bw > 2 else 0.0
        ly = float(np.abs(res[2:] - 2 * res[1:-1] + res[:-2]).mean()) if bh > 2 else 0.0
        d["lap_abs_mean"] = lx + ly
    before = t - np.clip(cv, 0, 255)
    if "before_mse" in names:
        d["before_mse"] = float((before ** 2).mean())
    if "before_mae" in names:
        d["before_mae"] = float(np.abs(before).mean())
    return np.array([d[nm] for nm in names], dtype=np.float32)