Python暴力画面检测

 人参与 | 时间:2026-10-17 07:09:32

去年夏天,暴力我在一个短视频审核团队做技术顾问。画面他们的检测审核员每天要看上千条视频,其中大概3%到5%会被标记为“疑似暴力”。暴力问题是画面,这3%里有一大半是检测误报——比如有人切西瓜、打拳击游戏、暴力或者电影片段。画面审核员看多了会麻木,检测真正需要拦截的暴力内容反而可能被漏掉。我当时就想,画面能不能用Python写个简单的检测暴力画面检测脚本,先把明显不是暴力的过滤掉?

说干就干。暴力画面在视觉上有个很粗暴的画面特征:大量红色像素、快速剧烈的检测画面变化、以及人体肢体的非常规角度。当然,这不是绝对标准,但用来做第一层粗筛足够了。我用OpenCV读取视频帧,先算每帧的红色通道占比。比如一帧里红色像素超过30%,就标记为“可疑帧”。这个阈值是我试出来的——切西瓜的红色占比通常在15%到20%,而真实的暴力场景往往超过35%。

光看红色不够。有人穿红衣服打架怎么办?所以还得加上帧间差分。暴力画面往往伴随剧烈运动,相邻两帧的像素差值会突然飙升。我用cv2.absdiff()计算前后帧的差异,再取均值。正常对话场景的差分均值大概在5到10之间,而打斗场景能冲到40以上。把红色占比和差分均值两个指标一组合,准确率就上来了。代码其实很短:

import cv2 import numpy as np cap = cv2.VideoCapture('test.mp4') ret, prev = cap.read() prev_gray = cv2.cvtColor(prev, cv2.COLOR_BGR2GRAY) while True: ret, frame = cap.read() if not ret: break gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) diff = cv2.absdiff(prev_gray, gray) motion = np.mean(diff) hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV) mask1 = cv2.inRange(hsv, (0, 70, 50), (10, 255, 255)) mask2 = cv2.inRange(hsv, (170, 70, 50), (180, 255, 255)) red_ratio = np.sum(mask1 | mask2) / frame.size * 3 if motion > 35 and red_ratio > 0.25: print(f"可疑帧: 运动={ motion:.1f}, 红色占比={ red_ratio:.2f}") prev_gray = gray 

跑了几段测试视频,效果比预想的好。一段拳击比赛集锦,脚本标出了70%的帧为可疑,而一段风景纪录片只标了2%。但问题也很明显:恐怖片里的红色灯光、消防演习、甚至番茄酱广告都会触发警报。有个审核员跟我说,他拿这个脚本去筛《杀死比尔》的片段,结果全片都是“可疑帧”——这倒也没错,但说明阈值调得太敏感了。

后来我加了个简单的后处理:连续可疑帧超过15帧才最终判定,并且要求画面里检测到人脸或人体轮廓。用cv2.HOGDescriptor做人体检测,虽然慢了点,但误报率降到了原来的三分之一。不过说实话,Python做实时暴力检测还是吃力,一秒钟只能处理七八帧。真要上生产环境,得用C++重写或者上GPU。

有意思的是,这个脚本最后没用在审核系统里,反而被一个做直播监控的客户拿去改成了“异常动作提醒”。他们把阈值调低,专门抓主播突然挥拳或者摔东西的瞬间。有个游戏主播打《拳皇》时摔键盘,系统立刻弹窗提示“检测到剧烈动作”,把他自己都逗笑了。技术这东西,落地的时候总会跑偏,但跑偏的方向往往比原计划更有意思。

如果你也想试试,建议从短视频片段入手,别一上来就处理整部电影。另外,别迷信红色阈值——不同摄像头白平衡差异很大,最好先跑一段正常视频统计一下基线。暴力检测没有银弹,Python脚本只能帮你把最明显的筛出来,剩下的还得靠人眼。但至少,审核员不用再盯着切西瓜的视频发呆了。

顶: 8踩: 4921