DariusGiannoli commited on
Commit
f5f0736
·
1 Parent(s): a2b92f9

feat: add 4 new RCE feature modules — Laplacian, Gradient Orientation, Gabor, LBP

Browse files
pages/3_Feature_Lab.py CHANGED
@@ -32,11 +32,14 @@ with col_rce:
32
  st.header("🧬 RCE: Modular Physics Engine")
33
  st.subheader("Select Active Modules")
34
 
35
- # Dynamically build checkboxes from the registry
36
- m_cols = st.columns(len(REGISTRY))
37
  active = {}
38
- for i, (key, meta) in enumerate(REGISTRY.items()):
39
- active[key] = m_cols[i].checkbox(meta["label"], value=True)
 
 
 
 
40
 
41
  # Build vector + collect visualizations by calling registry functions
42
  final_vector = []
@@ -47,12 +50,14 @@ with col_rce:
47
  final_vector.extend(vec)
48
  viz_images.append((meta["viz_title"], viz))
49
 
50
- # Visualizations
51
  st.divider()
52
  if viz_images:
53
- v_cols = st.columns(len(viz_images))
54
- for idx, (title, img) in enumerate(viz_images):
55
- v_cols[idx].image(img, caption=title, use_container_width=True)
 
 
56
  else:
57
  st.warning("No modules selected — vector is empty.")
58
 
 
32
  st.header("🧬 RCE: Modular Physics Engine")
33
  st.subheader("Select Active Modules")
34
 
35
+ # Dynamically build checkboxes from the registry (rows of 4)
 
36
  active = {}
37
+ items = list(REGISTRY.items())
38
+ for row_start in range(0, len(items), 4):
39
+ row_items = items[row_start:row_start + 4]
40
+ m_cols = st.columns(4)
41
+ for col, (key, meta) in zip(m_cols, row_items):
42
+ active[key] = col.checkbox(meta["label"], value=(key in ("intensity", "sobel", "spectral")))
43
 
44
  # Build vector + collect visualizations by calling registry functions
45
  final_vector = []
 
50
  final_vector.extend(vec)
51
  viz_images.append((meta["viz_title"], viz))
52
 
53
+ # Visualizations (rows of 3)
54
  st.divider()
55
  if viz_images:
56
+ for row_start in range(0, len(viz_images), 3):
57
+ row = viz_images[row_start:row_start + 3]
58
+ v_cols = st.columns(3)
59
+ for col, (title, img) in zip(v_cols, row):
60
+ col.image(img, caption=title, use_container_width=True)
61
  else:
62
  st.warning("No modules selected — vector is empty.")
63
 
src/detectors/rce/features.py CHANGED
@@ -53,6 +53,69 @@ def compute_spectral(gray: np.ndarray):
53
  return hist.astype(np.float32), viz
54
 
55
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
56
  # ---------------------------------------------------------------------------
57
  # Registry — defines the order and display labels seen by the UI
58
  # Add new modules here; the Feature Lab page iterates this dict.
@@ -73,10 +136,24 @@ REGISTRY: dict = {
73
  "fn": compute_spectral,
74
  "viz_title": "Frequency Domain (FFT)",
75
  },
76
- # --- ADD NEW MODULES BELOW ---
77
- # "lbp": {
78
- # "label": "LBP (Local Texture)",
79
- # "fn": compute_lbp,
80
- # "viz_title": "LBP Pattern",
81
- # },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
82
  }
 
53
  return hist.astype(np.float32), viz
54
 
55
 
56
+ # ---------------------------------------------------------------------------
57
+ # Module 3 — Laplacian (2nd-order, curvature / blobs)
58
+ # ---------------------------------------------------------------------------
59
+ def compute_laplacian(gray: np.ndarray):
60
+ """10-bin histogram of absolute Laplacian response (blob / corner energy)."""
61
+ lap = cv2.Laplacian(gray, cv2.CV_64F, ksize=3)
62
+ mag = np.abs(lap)
63
+ hist, _ = np.histogram(mag, bins=10, range=(0, 255))
64
+ viz = (mag / (mag.max() + 1e-5)).astype(np.float32)
65
+ return hist.astype(np.float32), viz
66
+
67
+
68
+ # ---------------------------------------------------------------------------
69
+ # Module 4 — Gradient Orientation (1st-order direction)
70
+ # ---------------------------------------------------------------------------
71
+ def compute_grad_orient(gray: np.ndarray):
72
+ """10-bin histogram of gradient orientations (0-360°), weighted by magnitude."""
73
+ sx = cv2.Sobel(gray, cv2.CV_64F, 1, 0, ksize=3)
74
+ sy = cv2.Sobel(gray, cv2.CV_64F, 0, 1, ksize=3)
75
+ mag = np.sqrt(sx ** 2 + sy ** 2)
76
+ angle = np.degrees(np.arctan2(sy, sx)) % 360
77
+ hist, _ = np.histogram(angle, bins=10, range=(0, 360), weights=mag)
78
+ viz = (angle / 360.0).astype(np.float32)
79
+ return hist.astype(np.float32), viz
80
+
81
+
82
+ # ---------------------------------------------------------------------------
83
+ # Module 5 — Gabor (oriented texture / frequency)
84
+ # ---------------------------------------------------------------------------
85
+ def compute_gabor(gray: np.ndarray):
86
+ """10-bin histogram of mean Gabor filter responses across 4 orientations."""
87
+ ksize = 15
88
+ sigma, lambd, gamma, psi = 4.0, 10.0, 0.5, 0.0
89
+ responses = np.zeros_like(gray, dtype=np.float64)
90
+ for theta in [0, np.pi / 4, np.pi / 2, 3 * np.pi / 4]:
91
+ kernel = cv2.getGaborKernel(
92
+ (ksize, ksize), sigma, theta, lambd, gamma, psi, ktype=cv2.CV_64F
93
+ )
94
+ responses += np.abs(cv2.filter2D(gray, cv2.CV_64F, kernel))
95
+ responses /= 4.0
96
+ hist, _ = np.histogram(responses, bins=10, range=(0, responses.max() + 1e-5))
97
+ viz = (responses / (responses.max() + 1e-5)).astype(np.float32)
98
+ return hist.astype(np.float32), viz
99
+
100
+
101
+ # ---------------------------------------------------------------------------
102
+ # Module 6 — LBP (Local Binary Pattern, texture micro-structure)
103
+ # ---------------------------------------------------------------------------
104
+ def compute_lbp(gray: np.ndarray):
105
+ """10-bin histogram of simplified 8-neighbour LBP codes."""
106
+ padded = cv2.copyMakeBorder(gray, 1, 1, 1, 1, cv2.BORDER_REFLECT)
107
+ h, w = gray.shape
108
+ lbp = np.zeros((h, w), dtype=np.uint8)
109
+ offsets = [(-1, -1), (-1, 0), (-1, 1),
110
+ (0, 1), (1, 1), (1, 0), (1, -1), (0, -1)]
111
+ for bit, (dy, dx) in enumerate(offsets):
112
+ neighbour = padded[1 + dy: 1 + dy + h, 1 + dx: 1 + dx + w]
113
+ lbp |= ((neighbour >= gray).astype(np.uint8) << bit)
114
+ hist = cv2.calcHist([lbp], [0], None, [10], [0, 256]).flatten().astype(np.float32)
115
+ viz = lbp.astype(np.float32) / 255.0
116
+ return hist, viz
117
+
118
+
119
  # ---------------------------------------------------------------------------
120
  # Registry — defines the order and display labels seen by the UI
121
  # Add new modules here; the Feature Lab page iterates this dict.
 
136
  "fn": compute_spectral,
137
  "viz_title": "Frequency Domain (FFT)",
138
  },
139
+ "laplacian": {
140
+ "label": "2-Order (Laplacian)",
141
+ "fn": compute_laplacian,
142
+ "viz_title": "Curvature / Blobs (Laplacian)",
143
+ },
144
+ "grad_orient": {
145
+ "label": "Gradient Orient.",
146
+ "fn": compute_grad_orient,
147
+ "viz_title": "Edge Directions",
148
+ },
149
+ "gabor": {
150
+ "label": "Gabor (Texture)",
151
+ "fn": compute_gabor,
152
+ "viz_title": "Oriented Texture (Gabor)",
153
+ },
154
+ "lbp": {
155
+ "label": "LBP (Local Texture)",
156
+ "fn": compute_lbp,
157
+ "viz_title": "Local Binary Pattern",
158
+ },
159
  }