[计算机视觉]100行python实现摄像机偏移、抖动告警
背景
在实际项目中,利用深度学习在检测道路车辆并分析车辆行为时,需要按照事先规定的方法绘制检测区(包含道路方向、车道区域等)。由于各种原因(人为、天气),获取视频数据的摄像角度容易偏移原来设定的位置,造成检测区域和实际画面不匹配,系统容易产生误检误报等错误数据。因此需要在摄像机位置偏移第一时间告诉系统检测模块停止工作,直到摄像机归位后再进行检测。摄像机角度偏移告警属于‘视频诊断’中的一类,本文利用提取图片特征点实现摄像机偏移告警,demo全部python代码不足200行。
前面有几篇博客文字太少,发不了首页:
这里是[AI+计算机视觉]的所有文章,需要的朋友可以点一波关注或者收藏一下。
图像特征点
对于任何一张二维图片,从像素级别上看,都存在一些我们肉眼看不到的比较独特的像素单元(可以理解为像素块),就像我们每个人的脸都会与众不同一样,我们称这些具有特点的像素区域为“图像特征点”。已经有非常成熟的算法来提取图片的特征点:
(1)Harris:用于检测角点;
(2)SIFT:用于检测斑点;
(3)SURF:用于检测斑点;
(4)FAST:用于检测角点;
(5)BRIEF:用于检测斑点;
(6)ORB:表示带方向的FAST算法与具有旋转不变性的BRIEF算法;
详细算法原理上网搜一下(我也不是很清楚:)),OpenCV中包含以上几种算法实现。
角点:
图像中涉及到拐角的区域,比如物体有轮廓,图像中的物体有边缘区分。
斑点:
一块有特别规律的像素区域。
方向、尺寸不变性:
指特征点不会受图片尺寸、旋转而改变,比如同一张图,你缩小一倍旋转90度后,特征点还是一样的。
图像匹配
提取两张图片的特征点,然后将这些特征点进行匹配关联。如果匹配程度满足某一阈值,则认为这两张图满足匹配条件。注意,对于同一个物体,拍摄角度不同,亮度不同都应该满足匹配条件。
可以看到,对于同一个场景的不同拍摄角度的两张图片,能找到匹配到的特征点,但是误差非常大。我们设置一个阈值,满足该条件才认为两个点匹配:
误差少很多了,匹配到的特征点也非常正确。
换一组摄像机的照片,前一张和后一张在拍摄时,摄像机角度往左下角有偏移,所以对应匹配到的特征点往右上方移动了:
我们可以看到,虽然拍摄角度不同,但是由于场景类似,仍然能匹配到特征点(为了减少绘图方便看清楚,阈值设置非常严格,如果放宽一点还能看到更多匹配到的点),而且这些匹配到的点几乎都正确。对于两张完全不同的场景照片,匹配到的特征点非常少或者为零(具体看设置的阈值)
场景不同,匹配到的特征点只有视频上的文字。
角度偏移告警
如果摄像机位置不变,前后拍摄两张照片,那么这两张照片匹配到的特征点的二维物理坐标应该是一样的(可能有轻微偏移,两张照片尺寸一致)。那么我们可以根据摄像机前后两帧(或间隔时间内取得的两帧)的匹配点物理位置是否有偏移,设置一个偏移阈值,大于该阈值时则认为偏移,否则认为没偏移(或轻微偏移),当然,如果两帧匹配到的特征点非常少(低于一个阈值),那么我们认为这俩帧完全不一样了(场景不一样了),这时候摄像机完全偏移了原来的角度。
注意点:
1)阈值非常重要;
2)前后帧匹配时,要去掉类似摄像机自动加上去的“视频位置”、“当前时间”等等区域,因为这些区域很多时候能够匹配到特征点,并且物理位置坐标不会发生变化,造成误差;
3)在计算特征点物理位置偏移量时,取所有特征点物理位置偏移的平均值。
最终效果
间隔时间取视频中的帧,进行特征点对比。根据前面的思路分为4个等级:“无偏移”、“轻度偏移(抖动)”、“严重偏移”、“完全偏移”。
源代码
最重要的是代码,很简单,直接贴上来即可。加起来不到160行。测试很多场景,效果都不错。
'''
视频帧匹配脚本
'''
import numpy as np
import cv2 #至少10个点匹配
MIN_MATCH_COUNT = 10
#完全匹配偏移 d<4
BEST_DISTANCE = 4
#微量偏移 4<d<10
GOOD_DISTANCE = 10 # 特征点提取方法,内置很多种
algorithms_all = {
"SIFT": cv2.xfeatures2d.SIFT_create(),
"SURF": cv2.xfeatures2d.SURF_create(8000),
"ORB": cv2.ORB_create()
} '''
# 图像匹配
# 0完全不匹配 1场景匹配 2角度轻微偏移 3完全匹配
'''
def match2frames(image1, image2):
img1 = cv2.cvtColor(image1, cv2.COLOR_BGR2GRAY)
img2 = cv2.cvtColor(image2, cv2.COLOR_BGR2GRAY) size1 = img1.shape
size2 = img2.shape img1 = cv2.resize(img1, (int(size1[1]*0.3), int(size1[0]*0.3)), cv2.INTER_LINEAR)
img2 = cv2.resize(img2, (int(size2[1]*0.3), int(size2[0]*0.3)), cv2.INTER_LINEAR) sift = algorithms_all["SIFT"] kp1, des1 = sift.detectAndCompute(img1, None)
kp2, des2 = sift.detectAndCompute(img2, None) FLANN_INDEX_KDTREE = 0
index_params = dict(algorithm = FLANN_INDEX_KDTREE, trees = 5)
search_params = dict(checks = 50) flann = cv2.FlannBasedMatcher(index_params, search_params) matches = flann.knnMatch(des1, des2, k=2) # 过滤
good = []
for m,n in matches:
if m.distance < 0.7*n.distance:
good.append(m) if len(good) <= MIN_MATCH_COUNT:
return 0 # 完全不匹配
else:
distance_sum = 0 # 特征点2d物理坐标偏移总和
for m in good:
distance_sum += get_distance(kp1[m.queryIdx].pt, kp2[m.trainIdx].pt)
distance = distance_sum / len(good) #单个特征点2D物理位置平均偏移量 if distance < BEST_DISTANCE:
return 3 #完全匹配
elif distance < GOOD_DISTANCE and distance >= BEST_DISTANCE:
return 2 #部分偏移
else:
return 1 #场景匹配 '''
计算2D物理距离
'''
def get_distance(p1, p2):
x1,y1 = p1
x2,y2 = p2
return np.sqrt((x1-x2)**2 + (y1-y2)**2) if __name__ == "__main__":
pass
测试
'''
摄像机角度偏移告警
'''
import cv2
import do_match
import numpy as np
from PIL import Image, ImageDraw, ImageFont '''
告警信息
'''
def putText(frame, text):
cv2_im = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
pil_im = Image.fromarray(cv2_im) draw = ImageDraw.Draw(pil_im)
font = ImageFont.truetype("fonts/msyh.ttc", 30, encoding="utf-8")
draw.text((50, 50), text, (0, 255, 255), font=font) cv2_text_im = cv2.cvtColor(np.array(pil_im), cv2.COLOR_RGB2BGR) return cv2_text_im texts = ["完全偏移","严重偏移", "轻微偏移", "无偏移"] cap = cv2.VideoCapture('videos/test4_new.mp4') if (cap.isOpened()== False):
print("Error opening video stream or file") first_frame = True
pre_frame = 0 index = 0 while(cap.isOpened()):
ret, frame = cap.read()
if ret == True:
if first_frame:
pre_frame = frame
first_frame = False
continue index += 1
if index % 24 == 0:
result = do_match.match2frames(pre_frame, frame)
print("检测结果===>", texts[result]) if result > 1: # 缓存最近无偏移的帧
pre_frame = frame size = frame.shape if size[1] > 720: # 缩小显示
frame = cv2.resize(frame, (int(size[1]*0.5), int(size[0]*0.5)), cv2.INTER_LINEAR) text_frame = putText(frame, texts[result]) cv2.imshow('Frame', text_frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
else:
break cap.release()
cv2.destroyAllWindows()
[计算机视觉]100行python实现摄像机偏移、抖动告警的更多相关文章
- 100行Python代码实现一款高精度免费OCR工具
近期Github开源了一款基于Python开发.名为 Textshot 的截图工具,刚开源不到半个月已经500+Star. 很多人学习python,不知道从何学起.很多人学习python,掌握了基本语 ...
- 哪吒票房超复联4,100行python代码抓取豆瓣短评,看看网友怎么说
<哪吒之魔童降世>这部国产动画巅峰之作,上映快一个月时间,票房口碑双丰收. 迄今已有超一亿人次观看,票房达到42.39亿元,超过复联4,跻身中国票房纪录第三名,仅次于<战狼2> ...
- 看完100篇Python技术精华文章,平均涨薪30%!
一个以技术为立身根基的教育机构做出来的微信号,干货程度会有多高? 马哥Linux运维公众号运营五年,从一开始的定位就是给技术人分享加薪干货的地方.这五年里,公众号运营最重的任务就是做内容.内容并不好做 ...
- 一个 11 行 Python 代码实现的神经网络
一个 11 行 Python 代码实现的神经网络 2015/12/02 · 实践项目 · 15 评论· 神经网络 分享到:18 本文由 伯乐在线 - 耶鲁怕冷 翻译,Namco 校稿.未经许可,禁止转 ...
- 200行Python代码实现2048
200行Python代码实现2048 一.实验说明 1. 环境登录 无需密码自动登录,系统用户名shiyanlou 2. 环境介绍 本实验环境采用带桌面的Ubuntu Linux环境,实验中会用到桌面 ...
- 【Python精华】100个Python练手小程序
100个Python练手小程序,学习python的很好的资料,覆盖了python中的每一部分,可以边学习边练习,更容易掌握python. [程序1] 题目:有1.2.3.4个数字,能组成多少个互不相同 ...
- 10行Python代码实现人脸定位
10行python机器学习全卷机网,实现100+张人脸同时定位! 发表评论 1,049 游览 A+ 所属分类:未分类 收 藏 今天介绍一个快速定位人脸的深度学习算法MTCNN,全称是:Multi-t ...
- 150+行Python代码实现带界面的数独游戏
150行代码实现图形化数独游戏 Github地址,欢迎各位大佬们fork.star啥的,感谢: 今天闲着没事干,以前做过html+js版的数独,这次做个python版本的,界面由pygame完成,数独 ...
- 200行PYTHON代码实现贪吃蛇
200行Python代码实现贪吃蛇 话不多说,最后会给出全部的代码,也可以从这里Fork,正文开始: 目前实现的功能列表: 贪吃蛇的控制,通过上下左右方向键: 触碰到边缘.墙壁.自身则游戏结束: 接触 ...
随机推荐
- SSM-MyBatis-05:Mybatis中别名,sql片段和模糊查询加getMapper
------------吾亦无他,唯手熟尔,谦卑若愚,好学若饥------------- 简单概述一下本讲 别名,sql片段简单写一下,模糊查询多写一点 一.别名 <typeAliases> ...
- Centos7搭建hadoop完全分布式
虽然说是完全分布式,但三个节点也都是在一台机器上.拿来练手也只能这样咯,将就下.效果是一样滴.这个我自己都忘了步骤,一起来回顾下吧. 必备知识: Linux基本命令 vim基本命令 准备软件: VMw ...
- 在webpack里使用jquery.mCustomScrollbar插件
malihu-custom-scrollbar-plugin是一个依赖jquery的自定义网页滚动条样式插件 网站:http://manos.malihu.gr/jquery-custom-conte ...
- 第四天 Java语言基础
一.函数的概念 1)什么函数 函数就是定义在类中的具有特定功能的一段独立小程序,并能被多次使用. 2)问题引入 在昨天讲述使用循环嵌套画出矩形.但有问题,每次要画矩形都要写很多重复性的代码,能不能将这 ...
- 从一个例子了解window.onload、$(function(){})、$(window).load(function(){})的加载顺序
最近遇到一个轮播需求: 1. ajax请求服务器,返回json,判断json数据里每一项中isFix属性是0还是1,0表示不轮播,1表示需要轮播. 2. 当isFix属性为0的时候,表示该图片不轮播, ...
- C++ 文件流的方式操作文件(一个简单的写入,读取)
新手学习c++哈,归纳的写了一下以 C++ 的方式写入读取文件,读文件到控制台,并加了行号.博客记录主要为了备忘.当然 ^_^ 喜欢同学的话可以拿走代码直接用.转帖注明出处哈.欢迎讨论,我一直认为:知 ...
- NOIP2013华容道 大爆搜
预处理出每个点周围四个点互相到达的最短路,再在整个图上跑SPFA,要记录路径 #include<cstdio> #include<cstring> #include<io ...
- JS基础:常用API
全局属性 typeof() 检测数据类型 parseInt() 从字符串解析一个整数 parseFloat() 从字符串解析一个小数 Number() 强制类型转换为数字 String() 强制类型转 ...
- .Net Remoting 调用远程对象
根据需求,我们的系统必须以C/S方式构建,而且是三层架构,这样一来,就出现了服务器端和客户端通信的问题. 为了解决双方的通信问题,还要考虑效率.性能等方面,经过分析.试验,我们根据效率.移植.开发难易 ...
- python-----HTMLTestRunner报告生成注意点!
简单的测试加HTMLTestRunner使用的具体方式如下: