Fight and Violence Detection

Property Value
Category Object Detection (Violence / Fight / Safety)
Base Model Fight Detection YOLOv8 (community, Ultralytics YOLOv8-nano)
Source Framework PyTorch (Ultralytics)
Supported Precisions FP32, FP16, INT8 (mixed-precision)
Inference Engine OpenVINO
Hardware CPU, GPU, NPU
Detected Class(es) violence, non_violence

Overview

Fight and Violence Detection is a Metro Analytics use case that flags physically aggressive activity -- such as fighting, brawling, and sparring -- in images and video streams and raises an on-screen alert whenever violence is present. It is built on a community YOLOv8 fight/violence detector, exported to OpenVINO IR and optionally quantized to INT8 for efficient inference on Intel hardware.

The model is a single-stage detector trained on two classes, violence and non_violence. Rather than relying on pose estimation or hand-tuned kinematic features, it directly localizes violent activity in each frame. Both the OpenVINO and DLStreamer samples draw a box around any violence detection and overlay a VIOLENCE DETECTED banner across the top of the frame, so operators get an immediate, unambiguous alert.

Note on weights: This use case uses the Yolo_nano_weights.pt file, which Hugging Face's scanner reports as safe. The repository's larger yolo_small_weights.pt is flagged as unsafe (pickle) and is intentionally not used.

Typical Metro deployments include:

  • Platform and Concourse Safety -- flag altercations on platforms, stairs, and concourses for rapid operator response.
  • Ticket Hall and Gateline Monitoring -- detect fights and physical confrontations around fare gates and queues.
  • Depot and Facility Security -- monitor restricted areas and back-of-house spaces for violent incidents.
  • Automated Incident Escalation -- trigger alerts and video capture the moment violence is confirmed.

Prerequisites

Create and activate a Python virtual environment before running the scripts:

python3 -m venv .venv --system-site-packages
source .venv/bin/activate

Note: The --system-site-packages flag is required so the virtual environment can access the system-installed OpenVINO and DLStreamer Python packages.


Getting Started

Download and Quantize Model

Run the provided script to download the fight/violence model, export it to OpenVINO IR, and optionally quantize:

chmod +x export_and_quantize.sh
./export_and_quantize.sh

This exports the model in FP16 precision.

Optional: Select a Different Precision

./export_and_quantize.sh FP32   # full-precision
./export_and_quantize.sh INT8   # quantized

The script performs the following steps:

  1. Installs dependencies (openvino, ultralytics; adds nncf for INT8).
  2. Downloads the community YOLOv8-nano fight/violence weights (Yolo_nano_weights.pt).
  3. Downloads a Pexels-licensed sample sparring video, transcoding it to test_video.mp4.
  4. Exports the PyTorch weights to OpenVINO IR.
  5. (INT8 only) Quantizes the model using NNCF post-training quantization.

Output files:

  • fight_detection_openvino_model/ -- FP32 or FP16 OpenVINO IR model directory.
  • fight_detection_int8.xml / .bin -- INT8 quantized model (only when INT8 is selected).
  • test_video.mp4 -- transcoded sample clip.

Precision / Device Compatibility

Precision CPU GPU NPU
FP32 Yes Yes No
FP16 Yes Yes Yes
INT8 Yes Yes Yes

OpenVINO Sample

The sample below runs the YOLOv8 fight/violence detector on the sample video. For each frame it decodes the detector output, applies non-maximum suppression, draws a box around any violence detection, and overlays a VIOLENCE DETECTED banner across the top of the frame. The annotated result is written to output_openvino.mp4. Change the DEVICE string to run on CPU, GPU, or NPU.

import cv2
import numpy as np
import openvino as ov

# Change DEVICE to "GPU" or "NPU" to run on integrated GPU or NPU.
DEVICE = "CPU"
INPUT_SIZE = 640
MODEL = "fight_detection_openvino_model/fight_detection.xml"

CLASS_NAMES = {0: "non_violence", 1: "violence"}
VIOLENCE_ID = 1
CONF_THRESHOLD = 0.25
NMS_IOU = 0.45

core = ov.Core()
compiled = core.compile_model(core.read_model(MODEL), DEVICE)
output_port = compiled.output(0)

cap = cv2.VideoCapture("test_video.mp4")
fps = cap.get(cv2.CAP_PROP_FPS) or 25.0
width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
sx, sy = width / INPUT_SIZE, height / INPUT_SIZE
writer = cv2.VideoWriter(
    "output_openvino.mp4", cv2.VideoWriter_fourcc(*"mp4v"), fps, (width, height)
)

frame_idx = 0
violence_frames = 0
while True:
    ok, frame = cap.read()
    if not ok:
        break
    frame_idx += 1

    blob = cv2.resize(frame, (INPUT_SIZE, INPUT_SIZE))
    blob = cv2.cvtColor(blob, cv2.COLOR_BGR2RGB).astype(np.float32) / 255.0
    blob = blob.transpose(2, 0, 1)[np.newaxis, ...]  # NCHW

    # YOLOv8 output is [1, 4 + num_classes, 8400]; transpose to [8400, 6].
    pred = compiled([blob])[output_port][0].T
    boxes_xywh = pred[:, :4]
    scores = pred[:, 4:]
    class_ids = np.argmax(scores, axis=1)
    confs = scores[np.arange(len(scores)), class_ids]
    keep = confs > CONF_THRESHOLD
    boxes_xywh, class_ids, confs = boxes_xywh[keep], class_ids[keep], confs[keep]

    rects = []
    for cx, cy, w, h in boxes_xywh:
        rects.append([int((cx - w / 2) * sx), int((cy - h / 2) * sy),
                      int(w * sx), int(h * sy)])

    detected = False
    if rects:
        for i in np.array(cv2.dnn.NMSBoxes(
                rects, confs.tolist(), CONF_THRESHOLD, NMS_IOU)).flatten():
            if int(class_ids[i]) != VIOLENCE_ID:
                continue
            detected = True
            x, y, w, h = rects[i]
            cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 0, 255), 2)
            cv2.putText(frame, f"violence {confs[i]:.2f}", (x, max(0, y - 6)),
                        cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 0, 255), 2)

    if detected:
        violence_frames += 1
        cv2.rectangle(frame, (0, 0), (width, 40), (0, 0, 200), -1)
        cv2.putText(frame, "VIOLENCE DETECTED", (12, 28),
                    cv2.FONT_HERSHEY_SIMPLEX, 0.9, (255, 255, 255), 2)

    if frame_idx % 30 == 0:
        print(f"frame {frame_idx}: {'VIOLENCE DETECTED' if detected else 'normal'}",
              flush=True)

    writer.write(frame)

cap.release()
writer.release()
if violence_frames:
    print(f"VIOLENCE DETECTED in {violence_frames}/{frame_idx} frames")
print("Saved: output_openvino.mp4")

Device targets:

  • "CPU" -- default, works on all Intel platforms.
  • "GPU" -- Intel integrated or discrete GPU.
  • "NPU" -- Intel NPU (validate with benchmark_app -d NPU).

Try It on a Sample Video

The export_and_quantize.sh script downloads and transcodes test_video.mp4 automatically. Re-run the OpenVINO sample above. The script reads test_video.mp4, prints a periodic status to the console, and writes the annotated video to output_openvino.mp4.

Expected console output (representative):

frame 30: VIOLENCE DETECTED
frame 60: VIOLENCE DETECTED
frame 90: normal

Expected Output

OpenVINO expected output showing a VIOLENCE DETECTED banner and a box around fighting people

DLStreamer Sample

The pipeline below runs the FP16 fight/violence detector on the sample video via gvadetect. DLStreamer parses the Ultralytics YOLOv8 model directly from the exported model directory (its metadata.yaml provides the class names), so no model-proc file is required. Frames are pulled through an appsink; for each frame a callback reads the detection metadata via GstAnalytics, draws a box around any violence detection, and overlays a VIOLENCE DETECTED banner across the top of the frame. The annotated result is written to output_dlstreamer.mp4.

Notes on running this sample:

  • Use the FP16 IR (fight_detection_openvino_model/fight_detection.xml).

  • Frames are converted to BGR for the appsink and all overlays are drawn with OpenCV, so no additional GStreamer overlay plugin is required.

  • Export PYTHONPATH so the DLStreamer Python module is importable:

    source /opt/intel/openvino_2026/setupvars.sh
    source /opt/intel/dlstreamer/scripts/setup_dls_env.sh
    export PYTHONPATH=/opt/intel/dlstreamer/python:\
    /opt/intel/dlstreamer/gstreamer/lib/python3/dist-packages:${PYTHONPATH:-}
    
import gi

gi.require_version("Gst", "1.0")
gi.require_version("GstAnalytics", "1.0")
from gi.repository import Gst, GLib, GstAnalytics

Gst.init([])

# Import cv2 after Gst.init to avoid a GStreamer re-initialization conflict.
import cv2
import numpy as np

MODEL = "fight_detection_openvino_model/fight_detection.xml"
INPUT_VIDEO = "test_video.mp4"
VIOLENCE_LABEL = "violence"

# For CPU: change device=GPU to device=CPU.
# For NPU: change device=GPU to device=NPU (batch-size=1, nireq=4 recommended).
pipeline_str = (
    f"filesrc location={INPUT_VIDEO} ! decodebin3 ! "
    f"videoconvert ! "
    f"gvadetect model={MODEL} device=GPU threshold=0.25 ! queue ! "
    f"videoconvert ! video/x-raw,format=BGR ! "
    f"appsink name=sink emit-signals=true sync=false max-buffers=4 drop=false"
)
pipeline = Gst.parse_launch(pipeline_str)
appsink = pipeline.get_by_name("sink")

state = {"writer": None, "frame": 0, "violence": 0}


def on_sample(sink):
    sample = sink.emit("pull-sample")
    if sample is None:
        return Gst.FlowReturn.OK

    buf = sample.get_buffer()
    caps = sample.get_caps().get_structure(0)
    width = caps.get_value("width")
    height = caps.get_value("height")

    # Read gvadetect metadata: collect violence bounding boxes.
    boxes = []
    rmeta = GstAnalytics.buffer_get_analytics_relation_meta(buf)
    if rmeta is not None:
        idx = 1
        while True:
            found, od = rmeta.get_od_mtd(idx)
            if not found:
                break
            label = GLib.quark_to_string(od.get_obj_type())
            _, x, y, w, h, conf = od.get_location()
            if label == VIOLENCE_LABEL:
                boxes.append((int(x), int(y), int(w), int(h), conf))
            idx += 1

    ok, mapinfo = buf.map(Gst.MapFlags.READ)
    if not ok:
        return Gst.FlowReturn.OK
    frame = np.frombuffer(mapinfo.data, np.uint8).reshape(height, width, 3).copy()
    buf.unmap(mapinfo)

    detected = len(boxes) > 0
    for x, y, w, h, conf in boxes:
        cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 0, 255), 2)
        cv2.putText(frame, f"violence {conf:.2f}", (x, max(0, y - 6)),
                    cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 0, 255), 2)

    if detected:
        state["violence"] += 1
        cv2.rectangle(frame, (0, 0), (width, 40), (0, 0, 200), -1)
        cv2.putText(frame, "VIOLENCE DETECTED", (12, 28),
                    cv2.FONT_HERSHEY_SIMPLEX, 0.9, (255, 255, 255), 2)

    if state["writer"] is None:
        ok_fps, fn, fd = caps.get_fraction("framerate")
        fps = fn / fd if ok_fps and fd > 0 else 25.0
        state["writer"] = cv2.VideoWriter(
            "output_dlstreamer.mp4",
            cv2.VideoWriter_fourcc(*"mp4v"), fps, (width, height),
        )
    state["writer"].write(frame)

    state["frame"] += 1
    if state["frame"] % 30 == 0:
        print(f"frame {state['frame']}: "
              f"{'VIOLENCE DETECTED' if detected else 'normal'}", flush=True)
    return Gst.FlowReturn.OK


appsink.connect("new-sample", on_sample)

pipeline.set_state(Gst.State.PLAYING)
bus = pipeline.get_bus()
bus.timed_pop_filtered(
    Gst.CLOCK_TIME_NONE,
    Gst.MessageType.EOS | Gst.MessageType.ERROR,
)
pipeline.set_state(Gst.State.NULL)

if state["writer"] is not None:
    state["writer"].release()
if state["violence"]:
    print(f"VIOLENCE DETECTED in {state['violence']}/{state['frame']} frames")
print("Saved: output_dlstreamer.mp4")

Try It on a Sample Video

The export_and_quantize.sh script downloads and transcodes test_video.mp4 automatically. Run the DLStreamer sample above. The callback prints a periodic status and writes the annotated video.

Expected console output (representative):

frame 30: VIOLENCE DETECTED
frame 60: VIOLENCE DETECTED
frame 90: normal

The annotated video is saved to output_dlstreamer.mp4 with the alert banner and violence boxes drawn by OpenCV.

Expected Output

DLStreamer expected output showing a VIOLENCE DETECTED banner and a box around fighting people

Device targets:

  • device=GPU -- default in the sample code.
  • device=CPU -- change device=GPU to device=CPU.
  • device=NPU -- change device=GPU to device=NPU; use batch-size=1 and nireq=4 for best NPU utilization.

License

Licensed under the MIT License. See LICENSE for details.

References

Downloads last month

-

Downloads are not tracked for this model. How to track
Inference Providers NEW
This model isn't deployed by any Inference Provider. 🙋 Ask for provider support

Collection including Intel/fight-and-violence-detection