Hey everyone, hope you guys are doing great. I would like to know how this graph and quartile results are calculated in the Roboflow Analytics section. I am trying to reproduce these (with Codex CLI) but couldn’t obtain the exact numbers.
I attached the code below:
def _increment_heatmap(grid: List[List[int]], x: float, y: float) -> None:
"""Increment one heatmap cell from normalized [0, 1] coordinates."""
if not grid or not grid[0]:
return
if x < 0 or y < 0:
return
h = len(grid)
w = len(grid[0])
gx = min(w - 1, max(0, int(x * w)))
gy = min(h - 1, max(0, int(y * h)))
grid[gy][gx] += 1
def _increment_heatmap_box(
grid: List[List[int]],
x1: float,
y1: float,
x2: float,
y2: float,
) -> None:
"""Increment grid cells whose centers lie inside a normalized [0, 1] rectangle."""
if not grid or not grid[0]:
return
h = len(grid)
w = len(grid[0])
nx1 = max(0.0, min(1.0, min(x1, x2)))
ny1 = max(0.0, min(1.0, min(y1, y2)))
nx2 = max(0.0, min(1.0, max(x1, x2)))
ny2 = max(0.0, min(1.0, max(y1, y2)))
if nx2 <= nx1 or ny2 <= ny1:
return
gx0 = min(w - 1, max(0, int(math.floor(nx1 * w))))
gy0 = min(h - 1, max(0, int(math.floor(ny1 * h))))
gx1 = min(w - 1, max(0, int(math.ceil(nx2 * w) - 1)))
gy1 = min(h - 1, max(0, int(math.ceil(ny2 * h) - 1)))
hit = False
for gy in range(gy0, gy1 + 1):
cy = (gy + 0.5) / float(h)
if cy < ny1 or cy > ny2:
continue
row = grid[gy]
for gx in range(gx0, gx1 + 1):
cx = (gx + 0.5) / float(w)
if nx1 <= cx <= nx2:
row[gx] += 1
hit = True
# Keep tiny/very thin boxes visible at this grid resolution.
if not hit:
cx = (nx1 + nx2) * 0.5
cy = (ny1 + ny2) * 0.5
_increment_heatmap(grid, cx, cy)
def _point_in_polygon(x: float, y: float, polygon: List[Tuple[float, float]]) -> bool:
"""Ray-casting point-in-polygon test in normalized coordinates."""
n = len(polygon)
if n < 3:
return False
inside = False
j = n - 1
for i in range(n):
xi, yi = polygon[i]
xj, yj = polygon[j]
if (yi > y) != (yj > y):
denom = yj - yi
if abs(denom) < 1e-12:
denom = 1e-12
x_cross = (xj - xi) * (y - yi) / denom + xi
if x < x_cross:
inside = not inside
j = i
return inside
def _increment_heatmap_polygon(
grid: List[List[int]],
polygon: List[Tuple[float, float]],
) -> None:
"""Increment grid cells whose centers fall inside a normalized polygon."""
if not grid or not grid[0] or len(polygon) < 3:
return
h = len(grid)
w = len(grid[0])
xs = [p[0] for p in polygon]
ys = [p[1] for p in polygon]
min_x = max(0.0, min(1.0, min(xs)))
min_y = max(0.0, min(1.0, min(ys)))
max_x = max(0.0, min(1.0, max(xs)))
max_y = max(0.0, min(1.0, max(ys)))
if max_x <= min_x or max_y <= min_y:
return
gx0 = min(w - 1, max(0, int(math.floor(min_x * w))))
gy0 = min(h - 1, max(0, int(math.floor(min_y * h))))
gx1 = min(w - 1, max(0, int(math.ceil(max_x * w) - 1)))
gy1 = min(h - 1, max(0, int(math.ceil(max_y * h) - 1)))
hit = False
for gy in range(gy0, gy1 + 1):
cy = (gy + 0.5) / float(h)
row = grid[gy]
for gx in range(gx0, gx1 + 1):
cx = (gx + 0.5) / float(w)
if _point_in_polygon(cx, cy, polygon):
row[gx] += 1
hit = True
# Keep very small polygons visible at this grid resolution.
if not hit:
cx = sum(xs) / float(len(xs))
cy = sum(ys) / float(len(ys))
_increment_heatmap(grid, cx, cy)
def _point_in_polygon(x: float, y: float, polygon: List[Tuple[float, float]]) -> bool:
"""Ray-casting point-in-polygon test in normalized coordinates."""
n = len(polygon)
if n < 3:
return False
inside = False
j = n - 1
for i in range(n):
xi, yi = polygon[i]
xj, yj = polygon[j]
if (yi > y) != (yj > y):
denom = yj - yi
if abs(denom) < 1e-12:
denom = 1e-12
x_cross = (xj - xi) * (y - yi) / denom + xi
if x < x_cross:
inside = not inside
j = i
return inside
def _increment_heatmap_polygon(
grid: List[List[int]],
polygon: List[Tuple[float, float]],
) -> None:
"""Increment grid cells whose centers fall inside a normalized polygon."""
if not grid or not grid[0] or len(polygon) < 3:
return
h = len(grid)
w = len(grid[0])
# Find bounding box of polygon
xs = [p[0] for p in polygon]
ys = [p[1] for p in polygon]
min_x = max(0.0, min(1.0, min(xs)))
min_y = max(0.0, min(1.0, min(ys)))
max_x = max(0.0, min(1.0, max(xs)))
max_y = max(0.0, min(1.0, max(ys)))
if max_x <= min_x or max_y <= min_y:
return
# Calculate grid cells within bounding box
gx0 = min(w - 1, max(0, int(math.floor(min_x * w))))
gy0 = min(h - 1, max(0, int(math.floor(min_y * h))))
gx1 = min(w - 1, max(0, int(math.ceil(max_x * w) - 1)))
gy1 = min(h - 1, max(0, int(math.ceil(max_y * h) - 1)))
# Test each cell's center against polygon
hit = False
for gy in range(gy0, gy1 + 1):
cy = (gy + 0.5) / float(h)
row = grid[gy]
for gx in range(gx0, gx1 + 1):
cx = (gx + 0.5) / float(w)
if _point_in_polygon(cx, cy, polygon):
row[gx] += 1
hit = True
# Fallback for very small polygons: use centroid
if not hit:
cx = sum(xs) / float(len(xs))
cy = sum(ys) / float(len(ys))
_increment_heatmap(grid, cx, cy)
# then in the main analyses block I use
HEATMAP_LARGE_REGION_AREA_RATIO = 0.50
box_area_ratio = (bw * bh) / image_area
if box_area_ratio > HEATMAP_LARGE_REGION_AREA_RATIO:
# Use center point only (avoid flooding entire grid)
cx = ((bx1 + bx2) * 0.5) / width_f
cy = ((by1 + by2) * 0.5) / height_f
_increment_heatmap(heat_grid, cx, cy)
else:
# Use full cell-center-in-shape logic
_increment_heatmap_box(heat_grid, bx1/width_f, by1/height_f, bx2/width_f, by2/height_f)
# these are for polygons
HEATMAP_POLYGON_BLUR_SIGMA_RATIO = 0.115
HEATMAP_POLYGON_GAMMA = 2.3
HEATMAP_POLYGON_LOW_CUTOFF = 0.04
HEATMAP_POLYGON_DENSITY_SCALE = 0.2555
def _postprocess_polygon_only_heatmap(
grid: List[List[int]],
grid_size: int,
box_hits: int,
polygon_hits: int,
) -> List[List[int]]:
"""
Improve polygon-only heatmap comparability by smoothing and contrast remapping.
Leaves mixed/box datasets untouched.
"""
if polygon_hits <= 0 or box_hits > 0:
return grid
# Gaussian blur
sigma = max(0.0, float(grid_size) * HEATMAP_POLYGON_BLUR_SIGMA_RATIO)
arr = _gaussian_blur_grid(arr, sigma=sigma)
# Gamma correction (2.3) for contrast
arr = np.power(arr, HEATMAP_POLYGON_GAMMA)
# Low cutoff filtering: zero out values below 0.04
if HEATMAP_POLYGON_LOW_CUTOFF > 0.0:
arr = (arr - HEATMAP_POLYGON_LOW_CUTOFF) / (1.0 - HEATMAP_POLYGON_LOW_CUTOFF)
arr = np.clip(arr, 0.0, 1.0)
# Rescale to target density
target_max = max(1, int(round(float(polygon_hits) * HEATMAP_POLYGON_DENSITY_SCALE)))
arr = np.rint(arr * float(target_max))
arr = np.clip(arr, 0.0, float(target_max))
return arr.astype(int).tolist()
def _percentile(values: List[int], p: float) -> float:
"""Linear percentile interpolation for p in [0, 1]."""
if not values:
return 0.0
ordered = sorted(values)
idx = (len(ordered) - 1) * p
lo = int(math.floor(idx))
hi = int(math.ceil(idx))
if lo == hi:
return float(ordered[lo])
frac = idx - lo
return float(ordered[lo] * (1.0 - frac) + ordered[hi] * frac)
flat_heat = [v for row in heat_grid for v in row]
heat_stats = {
"min": int(min(flat_heat) if flat_heat else 0),
"q1": int(round(_percentile(flat_heat, 0.25))), # 25th percentile
"median": int(round(_percentile(flat_heat, 0.50))), # 50th percentile
"q3": int(round(_percentile(flat_heat, 0.75))), # 75th percentile
"max": int(max(flat_heat) if flat_heat else 0),
}
