实时检测Aruco标签坐标opencv-python之添加卡尔曼滤波

news2024/11/15 11:43:46

在实时检测Aruco标签坐标用于定位的时候发现,追踪效果不是很好,于是在检测过程中添加了卡尔曼滤波,在aruco检测算法检测不到aruco标签的时候,调用卡尔曼滤波算法(KalmanFilter),补偿丢失的定位的坐标信息。

步骤1:标定相机,获得“标定文件.yaml”,前面的文章有详细介绍;相机+棋盘格opencv-python标定生成yaml文件_大胡子大叔的博客-CSDN博客

步骤2:调用摄像头/使用本地的视频

        调用摄像头写法

        video_cap = cv2.VideoCapture(0,cv2.CAP_DSHOW)

        调用本地视频写法

        video_cap = cv2.VideoCapture('./aruco.mp4'        

步骤3:进行实时检测

 检测效果见下图

        

 直接上代码

import cv2
import numpy as np
import time
import cv2.aruco as aruco
import yaml
file_path = ("./标定文件.yaml")
###加载文件路径###

with open(file_path, "r") as file:
    parameter = yaml.load(file.read(), Loader=yaml.Loader)
    mtx = parameter['camera_matrix']
    dist = parameter['dist_coeff']
    camera_u = parameter['camera_u']
    camera_v = parameter['camera_v']
    mtx = np.array(mtx)
    dist = np.array(dist)
font = cv2.FONT_HERSHEY_SIMPLEX #font for displaying text (below)
#卡尔曼滤波的算法,参考链接https://blog.csdn.net/Miaosh999/article/details/106934655/
#本文修改了min_hsv_bound,max_hsv_bound里面的数值,还将cv2.rectangle里面的数值强制转化为int
#这个检测可以和未加卡尔曼滤波的做比对,即tracker5里面
# hsv阈值,便于进行轮廓判断及轨迹绘制,需要根据运动目标的颜色自己进行调整
min_hsv_bound = (25, 52, 72)
max_hsv_bound = (100, 255, 255)
#状态向量
stateSize = 6
#观测向量
measSize = 4
coutrSize = 0
kf = cv2.KalmanFilter(stateSize,measSize,coutrSize)
state = np.zeros(stateSize, np.float32)#[x,y,v_x,v_y,w,h],簇心位置,速度,高宽
meas = np.zeros(measSize, np.float32)#[z_x,z_y,z_w,z_h]
procNoise = np.zeros(stateSize, np.float32)

#状态转移矩阵
cv2.setIdentity(kf.transitionMatrix)#生成单位矩阵
kf.measurementMatrix = np.zeros((measSize,stateSize),np.float32)
kf.measurementMatrix[0,0]=1.0
kf.measurementMatrix[1,1]=1.0
kf.measurementMatrix[2,4]=1.0
kf.measurementMatrix[3,5]=1.0


cv2.setIdentity(kf.processNoiseCov)
kf.processNoiseCov[0,0] = 1e-2
kf.processNoiseCov[1,1] = 1e-2
kf.processNoiseCov[2,2] = 5.0
kf.processNoiseCov[3,3] = 5.0
kf.processNoiseCov[4,4] = 1e-2
kf.processNoiseCov[5,5] = 1e-2

#测量噪声
cv2.setIdentity(kf.measurementNoiseCov)
#video_cap = cv2.VideoCapture(0,cv2.CAP_DSHOW)
#video_cap = cv2.VideoCapture('./zhenAruco.mp4')
video_cap = cv2.VideoCapture('./aruco.mp4')
# 视频输出
fps = video_cap.get(cv2.CAP_PROP_FPS) #获得视频帧率,即每秒多少帧
size = (int(video_cap.get(cv2.CAP_PROP_FRAME_WIDTH)),int(video_cap.get(cv2.CAP_PROP_FRAME_HEIGHT)))
#保存检测结果,将预测框和中心点的路径保存到视频中
videoWriter = cv2.VideoWriter('./new_aruco.mp4' ,cv2.VideoWriter_fourcc('m', 'p', '4', 'v'), fps, size)
ticks = 0
i=0
found = False
notFoundCount = 0
prePointCen = [] #存储中心点位置
meaPointCen = []
while(True):
    ret, frame = video_cap.read()
    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
    aruco_dict = aruco.getPredefinedDictionary(aruco.DICT_4X4_1000)
    parameters = aruco.DetectorParameters_create()
    corners, ids, rejectedImgPoints = aruco.detectMarkers(gray,
                                                          aruco_dict,
                                                          parameters=parameters)
    if ret is False:
        break
    cv2.imshow('frame',frame)
    cv2.waitKey(1)
    precTick = ticks
    ticks = float(cv2.getTickCount())
    res = frame.copy()
    # dT = float(1/fps)
    dT = float((ticks - precTick)/cv2.getTickFrequency())
    if(found):
        #预测得到的位置
        kf.transitionMatrix[0,2] = dT
        kf.transitionMatrix[1,3] = dT

        state = kf.predict()
        width = state[4]
        height = state[5]
        x_left = state[0] - width/2 #左上角横坐标
        y_left = state[1] - height/2  #左上角纵坐标
        x_right = state[0] + width/2
        y_right = state[1] + height/2

        center_x = state[0]
        center_y = state[1]
        prePointCen.append((int(center_x),int(center_y)))
        cv2.circle(res, (int(center_x),int(center_y)),2,(255,0,0),-1)
        cv2.rectangle(res,(int(x_left),int(y_left)),(int(x_right),int(y_right)),(255,0,0),2)

    #根据颜色二值化得到的位置
    if ids is not None:
        rvec, tvec, _ = aruco.estimatePoseSingleMarkers(corners, 0.05, mtx, dist)
        (rvec - tvec).any()
        for i in range(rvec.shape[0]):
            cv2.drawFrameAxes(frame, mtx, dist, rvec[i, :, :], tvec[i, :, :], 0.03)
            aruco.drawDetectedMarkers(frame, corners)
        # 显示ID,rvec,tvec, 旋转向量和平移向量
        cv2.putText(frame, "Id: " + str(ids), (0, 40), font, 0.5, (0, 0, 255), 1, cv2.LINE_AA)
        cv2.putText(frame, "rvec: " + str(rvec[i, :, :]), (0, 60), font, 0.5, (0, 255, 0), 2, cv2.LINE_AA)
        cv2.putText(frame, "tvec: " + str(tvec[i, :, :]), (0, 80), font, 0.5, (0, 0, 255), 1, cv2.LINE_AA)
    #检测轮廓,只检测最外围轮廓,保存物体边界上所有连续的轮廓点到contours向量内
    balls = []
    ballsBox = []
    for i in range(len(corners)):
        x, y, w, h = cv2.boundingRect(np.array(corners[i]))

        ratio = float(w/h)
        if(ratio > 1.0):
            ratio = 1.0 / ratio
        if(ratio > 0.75 and w*h>=400):
            balls.append(corners[i])
            ballsBox.append([x, y, w, h])

    print( "found:", len(ballsBox))
    print("\n")

    for i in range(len(balls)):
        # 绘制轮廓
        #cv2.drawContours(res, balls, i, (20,150,20),1)
        cv2.rectangle(res,(ballsBox[i][0],ballsBox[i][1]),(ballsBox[i][0]+ballsBox[i][2],ballsBox[i][1]+ballsBox[i][3]),(0,255,0),2) #二值化得到边界

        center_x = ballsBox[i][0] + ballsBox[i][2] / 2
        center_y = ballsBox[i][1] + ballsBox[i][3] / 2

        meaPointCen.append((int(center_x),int(center_y)))
        cv2.circle(res,(int(center_x),int(center_y)), 2, (20,150,20) ,-1)

        name = "(" + str(center_x) + "," + str(center_y) + ")"
        cv2.putText(res, name, (int(center_x) + 3, int(center_y) - 3), cv2.FONT_HERSHEY_COMPLEX, 0.5, (20,150,20), 2)
    n = len(prePointCen)
    for i in range(1, n):
        print(i)
        if prePointCen[i-1] is None or prePointCen[i] is None:
            continue
         #  注释掉的这块是为了绘制能够随时间先后慢慢消失的追踪轨迹,但是有一些小错误
        # 计算所画小线段的粗细
        # thickness = int(np.sqrt(64 / float(n - i + 1))*2.5)
        # print(thickness)
        # 画出小线段
        # cv2.line(res, prePointCen[i-1], prePointCen[i], (0, 0, 255), thickness)
        cv2.line(res, prePointCen[i-1], prePointCen[i], (0,0,255), 1, 4)
    if(len(balls) == 0):
        notFoundCount += 1
        print("notFoundCount",notFoundCount)
        print("\n")

        if notFoundCount >= 100:
            found = False

    else:
        #测量得到的物体位置
        notFoundCount = 0
        meas[0] = ballsBox[0][0] + ballsBox[0][2] / 2
        meas[1] = ballsBox[0][1] + ballsBox[0][3] / 2
        meas[2] = float(ballsBox[0][2])
        meas[3] = float(ballsBox[0][3])

        #第一次检测
        if not found:
            for i in range(len(kf.errorCovPre)):
                kf.errorCovPre[i,i] = 1
            state[0] = meas[0]
            state[1] = meas[1]
            state[2] = 0
            state[3] = 0
            state[4] = meas[2]
            state[5] = meas[3]

            kf.statePost = state
            found = True

        else:
            kf.correct(meas) #Kalman修正

            print('rr',res.shape)
            print("Measure matrix:", meas)
            cv2.imshow("Tracking", res)

            cv2.waitKey(1)
    videoWriter.write(res)

 全部的代码以及用到的视频文件,和标定文件已经上传至资源,需要的朋友自行下载

https://download.csdn.net/download/sunnyrainflower/87936967

/*------------------------------------------------------------
// 作    者:    大胡子大叔
// 版权声明:  未经同意请勿转载
-------------------------------------------------------------*/

如果有帮助到你,请给个赞,谢谢!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/671479.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Rabbitmq学习

文章目录 前言RabbitMQ 1 同步调用和异步调用2 常见的MQ对比3 安装RabbitMQ4 RabbitMQ学习4.1 helloworld学习 5 Spring AMQP5.1 AMQP的入门案例(使用rabbittemplate进行消息发送和接受)5.2 RabbitMQ的workquene5.3 发布订阅模型(exchange(广播fanout 路由direct 话题topic))5.…

RK3588 修改USB/Sata/TF挂载点

文章目录 概要整体架构流程技术名词解释技术细节小结APP概要 rk3588 android12 平台的挂载点是:/storage/设备卷名(即uuid) 对上层开发不太友好,因此需要固定某个挂载点提供上层app调用。 修改后的路径效果如下: 整体架构流程 从概要图示中可知:对每个挂载点创建软连接来…

大牛分享,提高工程性能的7个简单技巧

软件性能和弹性(恢复能力)是用户体验的关键组成部分,但随着软件行业采用DevOps,它开始在性能和弹性方面出现不足。在软件完全失败之前,性能问题经常被忽略。 但是,我们都知道性能不会突然降低。随着软件通…

【二叉树part02】| 102.二叉树的层序遍历、226.翻转二叉树、101.对称二叉树

目录 ✿LeetCode102.二叉树的层序遍历❀ ✿LeetCode226.翻转二叉树❀ ✿LeetCode101.对称二叉树❀ ✿LeetCode102.二叉树的层序遍历❀ 链接:102.二叉树的层序遍历 给你二叉树的根节点 root ,返回其节点值的 层序遍历 。 (即逐层地&#xff…

Python入门(二十七)测试(二)

测试(二) 1.测试类2.各种断言方法3.一个要测试的类4.测试AnonymousSurvey类5.方法setUp() 1.测试类 前面我们编写了针对单个函数的测试,下面来编写针对类的测试。很多程序中都会用到类,因此证明我们的类能够正确工作大有裨益。如…

AI绘画——了解AI绘画爆火原因与工具,并生成几个端午绘画小作品

作者简介:一名云计算网络运维人员、每天分享网络与运维的技术与干货。 座右铭:低头赶路,敬事如仪 个人主页:网络豆的主页​​​​​ 目录 前言 一.AI绘画 1.AI绘画爆火原因 2.AI绘画背后原理 二.AI绘画工具介绍 1.midjour…

IDC武连峰:应用数字安全免疫力理念,促进企业在数字时代韧性发展

作者:IDC中国副总裁、首席分析师 武连峰 数字安全免疫力是一个企业针对各种数字安全威胁时的防御机制,与人体免疫力相似,企业数字安全免疫力包含两类:企业安全文化意识与合规是先天性数字安全免疫力,今天任何一个企业…

vue使用swiper三行轮播问题

1、轮播图设置属性slidesPerColumn:3实现不了,解决方案如下: this.scheduleData是后台请求的数据,通过3个一组分组转换为this.scheduleListThreede 的数据! 2、逻辑处理如下: computed: { scheduleListThree: functi…

做测试不会 SQL?超详细的 SQL 查询语法教程来啦!

前言 作为一名测试工程师,工作中在对测试结果进行数据比对的时候,或多或少要和数据库打交道的,要和数据库打交道,那么一些常用的sql查询语法必须要掌握。最近有部分做测试小伙伴表示sql查询不太会,问我有没有sql查询语…

list赋值方法add()...和set()简介

目录 一、方法展示 二、add() 方法介绍 2.1.add(E element) 2.1.1源码 2.1.2.实例截图 2.1.3.Null引起的题外话 2.2.add(int index, E element) 2.2.1.源码 2.2.2.示例截图 2.2.3. add()引起IndexOutOfBoundsException简介 三、addAll()方法…

CSS查缺补漏之《媒体查询@media与BFC》

示例代码如下&#xff1a; <div class"box">世界你好</div> .box {width: 200px;height: 200px;background-color: yellow;color: white;font-size: 24px;text-align: center;line-height: 200px; } 媒体类型 媒体类型允许指定文件将如何在不同媒体上…

OpenCV——《图像平滑》结果输出对比《形态学操作》

1.图像平滑 图像平滑是一项简单且使用频率很高的图像处理方法&#xff0c;可以用来压制、弱化或消除图像中的细节、突变、边缘和噪声&#xff0c;最常见的是用来减少图像上的噪声。 常用的滤波器主要为&#xff1a; 均值滤波器&#xff1a;并不能完全消除噪声&#xff0c;只能…

【服务器】搭建hMailServer 服务实现远程发送邮件

typora-copy-images-to: upload hMailServer 是一个邮件服务器,通过它我们可以搭建自己的邮件服务,通过cpolar内网映射工具即可实现远程发送邮件,不需要使用公网服务器,不需要域名,而且邮件账号名称可以自定义. 下面以windows 10系统为环境,介绍使用方法: 1. 安装hMailServe…

小鹏G9高压平台800V电驱动实拍

近日&#xff0c;小鹏汽车董事长何小鹏在其个人社交账号上透露&#xff0c;小鹏G9正按原定计划按部就班推进节奏&#xff0c;将于8月启动预订&#xff0c;9月正式迎来上市&#xff0c;上市后很快就会启动用户交付。 图片来源&#xff1a;何小鹏官方微博 需要样件请联&#xff1…

Oracle DMP文件导入

dmp文件可以在Navicat中的 把dmp放入其中。然后用数据泵导入。遗憾的是报错 [ERR] ORA-39001: invalid argument value [ERR] ORA-39000: bad dump file specification [ERR] ORA-39143: dump file "/xxx.DMP" may be an original export dump file 改为imp工具&…

干货分享 | TSMaster标定模块自动化控制应用指南

本文目录&#xff1a; 一、TSMaster标定模块自动化控制的基础原理 1.1 TSMaster的标定系统变量 1.2 内部TSMaster调用C脚本实现标定模块的自动化控制 1.3 外部调用COM组件实现自动化标定 二、标定自动化控制场景与TSMaster实例 2.1 C脚本实现控制标定模块的启动与关闭的设…

Goby 漏洞发布|网神SecGate 3600防火墙 sys_export_conf_local_save 文件读取漏洞

漏洞名称&#xff1a;网神SecGate 3600防火墙 sys_export_conf_local_save 文件读取漏洞 English Name&#xff1a;Weaver OA PluginViewServlet Authentication Bypass Vulnerability CVSS core: 8.0 影响资产数&#xff1a;738 漏洞描述&#xff1a; 网神SecGate 3600防…

同一 tomcat 不同项目 session 共享实现

说明 这里仅讨论 同一个tomcat&#xff0c;部署了两个工程&#xff08;两个war包&#xff09;。不涉及不同tomcat,不涉及集群 背景 tomcat中的工程A包含用户登录、退出、权限控制等功能&#xff1b;工程B包含业务功能接口。工程A将用户登录信息加密响应给前端&#xff0c;前…

基于Java校园快递代取系统设计实现(源码+lw+部署文档+讲解等)

博主介绍&#xff1a; ✌全网粉丝30W,csdn特邀作者、博客专家、CSDN新星计划导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战 ✌ &#x1f345; 文末获取源码联系 &#x1f345; &#x1f447;&#x1f3fb; 精…

NLP(五十五)LangChain入门

LangChain简介 背景 由于ChatGPT的发行&#xff0c;大模型&#xff08;Large Language Model, LLM&#xff09;已经变得非常流行了。也许你可能没有足够的资金和计算资源从头开始训练大模型&#xff0c;但你仍然可以使用大模型来做一些比较酷的事情&#xff0c;比如&#xff1…