When you deploy an object detector in a real environment, you rarely want it to watch the entire frame. A camera pointed at a building entrance doesn’t need to detect people walking past on the distant street. A factory camera monitoring a conveyor belt doesn’t need to track movement in the maintenance corridor behind it.
The solution is to define detection zones — polygons that tell your system where detections should count. This article walks through the complete workflow: drawing zones in RegionKit, exporting polygon coordinates, and using OpenCV to apply them in Python.
The basic pattern
The workflow has three steps:
- Load a still frame from your camera
- Draw zone polygons on that frame using RegionKit
- In your inference loop, filter detections to those that fall inside a zone polygon
OpenCV provides two key functions for this:
cv2.fillPoly— fills a polygon on a mask image (for creating binary zone masks)cv2.pointPolygonTest— tests whether a point is inside, outside, or on the boundary of a polygon
Drawing zones in RegionKit
Export a still frame from your camera feed (most CV pipelines have a way to save individual frames). Open RegionKit, drag the frame onto the canvas, and use the Polygon tool (P) to draw your zones.
For each zone:
- Press
Pto activate the polygon tool - Click each vertex of the zone boundary
- Double-click or press
Enterto close the polygon - Open the Properties panel and set a label (e.g.
"entrance","conveyor_section_a")
Use layers to separate zone types — Detection zones on one layer, Exclusion zones on another. This makes the JSON easier to filter in code.
When finished, click Export → Native JSON and save the file.
Loading zone polygons in Python
import json
import numpy as np
with open('scene.json') as f:
scene = json.load(f)
# Extract all visible polygon annotations
zones = {}
for ann in scene['annotations']:
if ann['type'] != 'polygon':
continue
if not ann.get('visibility', True):
continue
label = ann.get('label', 'unlabelled')
pts = ann['data']['points'] # flat [x0,y0, x1,y1, ...]
coords = np.array(list(zip(pts[::2], pts[1::2])), dtype=np.int32)
zones[label] = coords
print(f"Loaded {len(zones)} zone(s): {list(zones.keys())}")
Creating zone masks with cv2.fillPoly
For pixel-level zone checking (useful for segmentation or dense prediction), create a binary mask:
import cv2
frame_h, frame_w = 480, 640 # your frame dimensions
# Build one mask per zone
zone_masks = {}
for label, pts in zones.items():
mask = np.zeros((frame_h, frame_w), dtype=np.uint8)
cv2.fillPoly(mask, [pts], 255)
zone_masks[label] = mask
To check if a pixel (x, y) is inside the zone:
if zone_masks['entrance'][y, x] > 0:
print("Point is inside the entrance zone")
Filtering detections with cv2.pointPolygonTest
For bounding box detections, test whether the detection centroid falls inside a zone:
def is_in_zone(cx, cy, zone_polygon):
"""Returns True if point (cx, cy) is inside the zone polygon."""
result = cv2.pointPolygonTest(zone_polygon, (float(cx), float(cy)), False)
return result >= 0 # 0 = on boundary, >0 = inside, <0 = outside
# In your detection loop:
for det in detections:
x1, y1, x2, y2 = det['bbox']
cx = (x1 + x2) / 2
cy = (y1 + y2) / 2
for zone_label, zone_pts in zones.items():
if is_in_zone(cx, cy, zone_pts):
print(f"Detection in zone: {zone_label}")
Handling exclusion zones
If you have exclusion zones (areas to ignore), apply them as a second filter:
for ann in scene['annotations']:
if ann.get('label', '').startswith('exclusion'):
pts = ann['data']['points']
exclusion_zones.append(np.array(list(zip(pts[::2], pts[1::2])), dtype=np.int32))
def is_excluded(cx, cy):
return any(
cv2.pointPolygonTest(zone, (float(cx), float(cy)), False) >= 0
for zone in exclusion_zones
)
Scaling zones to different resolutions
Zone coordinates in RegionKit are in image pixels relative to the loaded image. If your inference frame is a different resolution, scale the coordinates before use:
scale_x = inference_w / scene_w
scale_y = inference_h / scene_h
scaled_pts = (zone_pts * [scale_x, scale_y]).astype(np.int32)
The scene image dimensions are available in the RegionKit export under imageWidth and imageHeight — or you can read them from the original image using cv2.imread.
Complete example
import json
import cv2
import numpy as np
# Load zones
with open('scene.json') as f:
scene = json.load(f)
zones = {}
for ann in scene['annotations']:
if ann['type'] == 'polygon' and ann.get('visibility', True):
pts = ann['data']['points']
zones[ann.get('label', 'zone')] = np.array(
list(zip(pts[::2], pts[1::2])), dtype=np.int32
)
# Open camera
cap = cv2.VideoCapture(0)
while True:
ret, frame = cap.read()
if not ret:
break
# Draw zone overlays
for label, pts in zones.items():
cv2.polylines(frame, [pts], isClosed=True, color=(99, 102, 241), thickness=2)
cv2.putText(frame, label, pts[0], cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255,255,255), 1)
cv2.imshow('Zones', frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
cap.release()
cv2.destroyAllWindows()
Draw your zones in RegionKit, export the JSON, and drop it into this script. No manual coordinate entry needed.
Related: How to Define Regions of Interest for Computer Vision · COCO Format Guide