Skip to main content

Overview

BeamFinder outputs all detections to a CSV file at output/detections.csv. Each row represents one detected drone bounding box. The CSV is generated in detect.py:30-48 during the prediction loop.

CSV Schema

The output CSV has 7 columns written at detect.py:32:
image,x_center,y_center,width,height,confidence,class

Field Descriptions

image
string
Source image filename (basename only, no path).Extracted from Path(r.path).name at detect.py:40.Example: test_001.jpg
x_center
float
Bounding box center X coordinate in pixels (absolute, not normalized).Extracted from box.xywh[0][0] and rounded to 2 decimal places at detect.py:44.Range: 0 to image widthExample: 512.34
y_center
float
Bounding box center Y coordinate in pixels (absolute, not normalized).Extracted from box.xywh[0][1] and rounded to 2 decimal places at detect.py:44.Range: 0 to image heightExample: 384.67
width
float
Bounding box width in pixels.Extracted from box.xywh[0][2] and rounded to 2 decimal places at detect.py:44.Example: 128.45
height
float
Bounding box height in pixels.Extracted from box.xywh[0][3] and rounded to 2 decimal places at detect.py:44.Example: 96.23
confidence
float
Detection confidence score (0-1).Extracted from box.conf.item() and rounded to 4 decimal places at detect.py:46.Only detections with confidence >= CONF (default 0.4) are included.Range: 0.0 to 1.0Example: 0.8734
class
string
Detected object class name.Mapped from class ID using r.names[int(box.cls.item())] at detect.py:47.For BeamFinder, this is always "drone" since there’s only one class.Example: drone

Example Output

Sample CSV Content

image,x_center,y_center,width,height,confidence,class
test_001.jpg,512.34,384.67,128.45,96.23,0.8734,drone
test_001.jpg,1024.12,720.89,156.78,112.34,0.7621,drone
test_002.jpg,640.00,480.00,140.56,98.12,0.9123,drone
test_003.jpg,800.45,600.23,132.67,105.89,0.6854,drone

Multiple Detections Per Image

If an image contains multiple drones, each detection is written as a separate row with the same image value. Example from test_001.jpg above:
  • Detection 1: confidence 0.8734 at (512.34, 384.67)
  • Detection 2: confidence 0.7621 at (1024.12, 720.89)

Coordinate System

Absolute Pixel Coordinates

All coordinates are in absolute pixels, not normalized [0,1] values.
  • Origin (0, 0) is at the top-left corner of the image
  • X increases rightward
  • Y increases downward

Converting to Corner Format

The CSV uses center-width-height format (xywh). To convert to corner coordinates (xyxy):
x1 = x_center - width / 2   # left
y1 = y_center - height / 2  # top
x2 = x_center + width / 2   # right
y2 = y_center + height / 2  # bottom
Example:
# From CSV: 512.34, 384.67, 128.45, 96.23
x1 = 512.34 - 128.45/2 = 448.115
y1 = 384.67 - 96.23/2 = 336.555
x2 = 512.34 + 128.45/2 = 576.565
y2 = 384.67 + 96.23/2 = 432.785

Converting to Normalized Coordinates

To normalize coordinates for input to other systems:
x_center_norm = x_center / image_width
y_center_norm = y_center / image_height
width_norm = width / image_width
height_norm = height / image_height

Filtering & Thresholds

Confidence Threshold

The CONF parameter in detect.py:12 controls which detections are saved:
CONF = 0.4  # Only save detections with confidence >= 0.4
Adjusting the threshold:
Increase CONF to reduce false positives:
CONF = 0.7  # More conservative
  • Fewer false alarms
  • May miss some true drones
  • Recommended for production deployment

Post-Processing Filters

You can apply additional filters after detection:
import pandas as pd

df = pd.read_csv("output/detections.csv")

# Filter by confidence
df_high_conf = df[df['confidence'] >= 0.8]

# Filter by bounding box size (remove tiny detections)
df_filtered = df[(df['width'] >= 50) & (df['height'] >= 50)]

# Count detections per image
detection_counts = df.groupby('image').size()

Output Statistics

The detection script prints a summary when complete (detect.py:50):
1,247 detections saved to output/detections.csv
This total includes all detections across all processed images that meet the confidence threshold.

Annotated Images

In addition to the CSV, BeamFinder saves annotated images with bounding boxes drawn at output/annotated/. These images are generated by the save=True parameter in model.predict() at detect.py:36.

Annotation Style

  • Green boxes for detections above confidence threshold
  • Confidence score displayed on each box
  • Class label (“drone”) displayed on each box
Annotated images are for visualization only. All downstream processing should use the CSV file for accurate coordinates.

Integration Example

Reading Detection Results

import pandas as pd
import matplotlib.pyplot as plt
import matplotlib.patches as patches
from PIL import Image

# Load detections
df = pd.read_csv("output/detections.csv")

# Filter for specific image
image_name = "test_001.jpg"
detections = df[df['image'] == image_name]

# Load and display image
img = Image.open(f"data/images/test/{image_name}")
fig, ax = plt.subplots()
ax.imshow(img)

# Draw bounding boxes
for _, det in detections.iterrows():
    x1 = det['x_center'] - det['width'] / 2
    y1 = det['y_center'] - det['height'] / 2
    
    rect = patches.Rectangle(
        (x1, y1), det['width'], det['height'],
        linewidth=2, edgecolor='green', facecolor='none'
    )
    ax.add_patch(rect)
    
    # Add confidence label
    ax.text(x1, y1 - 5, f"{det['confidence']:.3f}",
            color='green', fontsize=10, weight='bold')

plt.show()

Converting for THz Beam Steering

For THz beam steering applications, you may need to convert pixel coordinates to angular positions:
import numpy as np

def pixel_to_angle(x_center, y_center, image_width, image_height, fov_horizontal, fov_vertical):
    """
    Convert pixel coordinates to azimuth/elevation angles.
    
    Args:
        x_center, y_center: Detection center in pixels
        image_width, image_height: Image dimensions
        fov_horizontal, fov_vertical: Camera field of view in degrees
    
    Returns:
        azimuth, elevation: Angles in degrees from image center
    """
    # Normalize to [-0.5, 0.5]
    x_norm = (x_center / image_width) - 0.5
    y_norm = (y_center / image_height) - 0.5
    
    # Convert to angles
    azimuth = x_norm * fov_horizontal
    elevation = -y_norm * fov_vertical  # Negative because y increases downward
    
    return azimuth, elevation

# Example usage
for _, det in detections.iterrows():
    az, el = pixel_to_angle(
        det['x_center'], det['y_center'],
        1920, 1080,  # Image dimensions
        60, 40       # Camera FOV (degrees)
    )
    print(f"Drone at azimuth={az:.2f}°, elevation={el:.2f}°")

Build docs developers (and LLMs) love