首页 > 其他分享 >动态物体追踪

动态物体追踪

时间:2023-05-03 22:45:16浏览次数:55  
标签:__ socket sock 物体 cv2 ret import 动态 追踪

动态物体追踪

闲话

我个人是比较喜欢捣鼓一些程序设计,算法之类的。但毕竟是人工智能专业的,电子类大赛也必须去打,起初是导师让我来打这个比赛,后面发现还是很有挑战,很有意思的。一开始我对全国大学生电子设计大赛真的一点不懂,之前也没了解过。后来听导师说,我们团队做的都是些控制类题,这我还算能接受。如果纯硬件,让我画电路,那我水平真的不太行。

我拿到的是2021年国赛题——基于互联网的摄像测量系统。要求(1)是做两个独立摄像头出来,我直接用树莓派连摄像头,这也很简单。之前我也给树莓派装了系统,并且在树莓派里面装了OpenCV,后面肯定会用到机器视觉。要求(2)就是我这篇要写到的,两个摄像节点要实时检测摆动的激光笔,然后把激光笔的轮廓检测到并框出。

我一开始觉得,这也太没难度了吧,我随便一个二值化处理,然后对比度框一下。可是我看题目看到后面的时候我发现事情没那么简单!说明(4)中有一句话:拍摄背景为一般实验室场景,背景物体静止即可,不得要求额外处理;如果实验室背景很嘈杂的话,我的激光笔不是比较难检测,我用二值化处理了一下,果然并没有把背景和激光笔区分出来,我后续一顿操作都没有好的结果,虽然能把笔检测出来,但是如果有和笔相似的像素也会被误判。

然后我尝试用内置函数轮廓检测直接去用drawCoutours()画出来轮廓,效果不太行,还是那句话,背景太杂了,检测的轮廓太多了,检测的结果不是单一的激光笔。如果背景是纯白的话,那确实没什么难度!可惜如果背景嘈杂的话,检测一个物体还要精准的框出来确实不是那么容易。

之后我尝试读取视频的第一帧,把笔的样子保存下来,然后去匹配笔,实现单目标匹配。效果虽然还行,但是不确定太大了,如果是在比赛,匹配的结果不是那么好,直接原地退役。毕竟后面要算激光笔的摆长和角度。在检测上面不能有误差。

后面我找到一种比较好的方法,就是两帧画面作差。因为激光笔是不断摆动的,背景都是静止的,那差出来的像素就是激光笔的像素,然后我去把它用findCountours()框起来!这个效果在本地实现起来是很好的。然后我就去码通信部分了,我就用socket写了个客户端和服务端。然后发现大问题了!因为用的是帧差法,如果通信有一点网络波动两帧差的比较多,那么框出来的框就特别的“大“。如果出现这种情况,那误差……如果在本地的话我觉得这种方法真的是最优了!不管再多嘈杂的环境都没什么影响,就算放一万支一模一样的激光笔,我也能检测出动的那一支。只是有点可惜通信的话,不能采用这个方法。

后面我问了下学长,学长说背景肯定要是白色的呀,我当时的心情……我辛苦那么久想去处理背景的问题,没想到比赛的时候背景肯定要是全白!

那我还是回归老方法,直接去用内置函数去检测轮廓,不过有几个小细节。框的时候,如果有面积太小或者太大的噪点,直接给它Pass掉。还有就是检测的轮廓如果长宽比不是像笔那样的比例,也Pass掉。这样误差几乎降为0。

匹配法(效果差)

代码给我删掉了,不贴出来了

帧差法(本机效果好)

import cv2
def fitter_img(frame):
    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) # 转成灰度图像
    blur = cv2.GaussianBlur(gray, (5, 5), 0) # 高斯模糊
    t, thresh = cv2.threshold(blur, 20, 255, cv2.THRESH_BINARY) # 二值化
    dilated = cv2.dilate(thresh, None, iterations=3) # 膨胀
    return dilated

cap = cv2.VideoCapture(0)
ret, frame1 = cap.read()
ret, frame2 = cap.read()
while cap.isOpened():
    diff = cv2.absdiff(frame1,frame2)
    mask = fitter_img(diff)
    # 寻找轮廓
    contours, her = cv2.findContours(mask, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
    for i in contours:
        x, y, w, h = cv2.boundingRect(i)
        if cv2.contourArea(i) < 200:
            continue
        cv2.rectangle(frame1, (x, y), (x + w, y + h), (0, 0, 255), 2)
    cv2.imshow("diff", diff)
    cv2.imshow("frame1", frame1)
    # cv2.imshow("mask", mask)
    # 前面一帧转成后面一帧
    frame1 = frame2
    ret, frame2 = cap.read()
    if cv2.waitKey(1) & 0xFF == ord("q"):
        break
cap.release()
cv2.destroyAllWindows()

直接检测轮廓

这段代码我直接写在socket的服务端的,我直接贴出来。

import socket
import time
import cv2
import numpy

def ReceiveVideo():
    # IP地址'0.0.0.0'为等待客户端连接
    address = ('0.0.0.0', 8002)
    # 建立socket对象,参数意义见https://blog.csdn.net/rebelqsp/article/details/22109925
    # socket.AF_INET:服务器之间网络通信
    # socket.SOCK_STREAM:流式socket , for TCP
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    # 将套接字绑定到地址, 在AF_INET下,以元组(host,port)的形式表示地址.
    s.bind(address)
    # 开始监听TCP传入连接。参数指定在拒绝连接之前,操作系统可以挂起的最大连接数量。该值至少为1,大部分应用程序设为5就可以了。
    s.listen(1)

    def recvall(sock, count):
        buf = b''  # buf是一个byte类型
        while count:
            # 接受TCP套接字的数据。数据以字符串形式返回,count指定要接收的最大数据量.
            newbuf = sock.recv(count)
            if not newbuf: return None
            buf += newbuf
            count -= len(newbuf)
        return buf

    # 接受TCP连接并返回(conn,address),其中conn是新的套接字对象,可以用来接收和发送数据。addr是连接客户端的地址。
    # 没有连接则等待有连接
    conn, addr = s.accept()
    print('connect from:' + str(addr))
    while 1:
        start = time.time()  # 用于计算帧率信息
        length = recvall(conn, 16)  # 获得图片文件的长度,16代表获取长度
        stringData = recvall(conn, int(length))  # 根据获得的文件长度,获取图片文件
        data = numpy.frombuffer(stringData, numpy.uint8)  # 将获取到的字符流数据转换成1维数组
        decimg = cv2.imdecode(data, cv2.IMREAD_COLOR)  # 将数组解码成图像
        # cv2.imshow('SERVER', decimg)  # 显示图像
        img = decimg

        # 进行下一步处理
        gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
        t, binary = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
        coutours, h = cv2.findContours(binary, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
        for i in coutours:
            # 排除掉面积过大或过小的区域
            if cv2.contourArea(i) < 1000 or cv2.contourArea(i) > 5000:
                continue
            x, y, w, h = cv2.boundingRect(i)
            # 排除不正确的面积比
            if h < 2 * w or h > 8 * w: continue

            cv2.rectangle(img, (x, y), (x + w, y + h), (0, 0, 255), 2)
        cv2.imshow("img", img)

        # 将帧率信息回传,主要目的是测试可以双向通信
        end = time.time()
        seconds = end - start
        fps = 1 / seconds;
        conn.send(bytes(str(int(fps)), encoding='utf-8'))
        k = cv2.waitKey(30) & 0xff
        if k == ord('q'):
            break
    s.close()
    cv2.destroyAllWindows()


if __name__ == '__main__':
    ReceiveVideo()

客户端代码

上面有服务端的代码,怎么能少了客户端的呢?

import socket
import cv2
import numpy
import time
import sys


def SendVideo():
    # 建立sock连接
    # address要连接的服务器IP地址和端口号
    address = ('192.168.31.110', 8002)
    try:
        # 建立socket对象,参数意义见https://blog.csdn.net/rebelqsp/article/details/22109925
        # socket.AF_INET:服务器之间网络通信
        # socket.SOCK_STREAM:流式socket , for TCP
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        # 开启连接
        sock.connect(address)
    except socket.error as msg:
        print(msg)
        sys.exit(1)

    # 建立图像读取对象
    capture = cv2.VideoCapture(0)
    # 读取一帧图像,读取成功:ret=1 frame=读取到的一帧图像;读取失败:ret=0
    ret, frame = capture.read()
    # 压缩参数,后面cv2.imencode将会用到,对于jpeg来说,15代表图像质量,越高代表图像质量越好为 0-100,默认95
    encode_param = [int(cv2.IMWRITE_JPEG_QUALITY), 15]

    while ret:
        # 停止0.1S 防止发送过快服务的处理不过来,如果服务端的处理很多,那么应该加大这个值
        time.sleep(0.01)
        # cv2.imencode将图片格式转换(编码)成流数据,赋值到内存缓存中;主要用于图像数据格式的压缩,方便网络传输
        # '.jpg'表示将图片按照jpg格式编码。
        result, imgencode = cv2.imencode('.jpg', frame, encode_param)
        # 建立矩阵
        data = numpy.array(imgencode)
        # 将numpy矩阵转换成字符形式,以便在网络中传输
        stringData = data.tostring()

        # 先发送要发送的数据的长度
        # ljust() 方法返回一个原字符串左对齐,并使用空格填充至指定长度的新字符串
        sock.send(str.encode(str(len(stringData)).ljust(16)));
        # 发送数据
        sock.send(stringData);
        # 读取服务器返回值
        receive = sock.recv(1024)
        if len(receive): print(str(receive, encoding='utf-8'))
        # 读取下一帧图片
        ret, frame = capture.read()
        if cv2.waitKey(10) == 27:
            break
    sock.close()

if __name__ == '__main__':
    SendVideo()

总结

其实我当时想这个背景问题,想了很久,用了10几种方法吧,但是其他的方法简直太不科学了!还是老老实实用库函数吧!不过我真的是喜欢帧差法,效果真的很棒!可惜通信太拉了!

效果图

标签:__,socket,sock,物体,cv2,ret,import,动态,追踪
From: https://www.cnblogs.com/itduan/p/17369822.html

相关文章

  • 使用Python实现基于图像的物体识别技术
    摘要:物体识别是计算机视觉技术中的一个核心问题,其应用涵盖了许多领域。本文将介绍如何使用Python编写一个简单的基于图像的物体识别程序,并且通过实际案例展示其应用。关键词:Python,物体识别,计算机视觉,OpenCV一、需求分析在使用Python实现物体识别技术之前,我们需要了解应用的具体需求......
  • [动态规划-背包问题入门] 原理,运用,实战
    背包问题--动态规划经典类型动态规划是将问题细分为有限个小问题并通过递推或递归来求得最终值。具象化来说,就是对某一问题的答案,我们转化为dp[n],而对于0<=i<n,dp[i][j]的值会根据前后上下的相关值来变化(i.e.dp[i-1][j]或dp[i][j-1])。注意这时算法强调的不是【容量】,而是......
  • mybatis控制动态SQL拼接标签之if test标签
    if标签通常用于WHERE语句、UPDATE语句、INSERT语句中,通过判断参数值来决定是否使用某个查询条件、判断是否更新某一个字段、判断是否插入某个字段的值。mybatis是一个天才设计,面向对象未必就是真理,相对于JPA等框架,具有更大的自由度和灵活度。简单示例selecthost_nameashos......
  • mybatis 动态sql语句拼接
    mybatis常用占位符的方式传递参数,比较安全,可以防止sql注入。有些时候,特殊逻辑需要java封装好一些特殊的查询条件,然后和mybatis一起封装成特殊sql进行执行。{param}:替换参数${sql}:拼接sql语句。遇到一些模板类的功能,例如用户选择查询条件,或者查询语句,就需要动态sql技术。这个......
  • 【动态规划】No 309. 最佳买卖股票时机含冷冻期
    【动态规划】309.最佳买卖股票时机含冷冻期给定一个整数数组prices,其中第prices[i]表示第i天的股票价格。设计一个算法计算出最大利润。在满足以下约束条件下,你可以尽可能地完成更多的交易(多次买卖一支股票):卖出股票后,你无法在第二天买入股票(即冷冻期为1天)。注意:......
  • 庆军之xamarin.forms 动态页面构造及查询补充
    为什么会有补充了,因为数据是能绑定了。但是又出现一个问题。如果换了条件,点击查询,会闪退。开始我还以为是占大量的内存的问题。在真实机,虚拟机上面都会。找了两天,突然想起来双向绑定可能存在问题。于是把ItemSource=DataTable.Rows改成了 ObservableCollection<DataRow>body......
  • Chemistry Experiment Codeforces Round 247 (Div. 2) 线段树动态开点,二分
    第一次写的时候还不会线段树的动态开点,写了一个是线段树但是是\(O(N^2)\)的写法,现在用动态开点武装了自己,会了正解\(O(qlogn^2)\)。首先建立一个权值线段树,但这里的权值很大,通过动态开点去建树来节省空间,对于两种操作:操作1,常见的动态开点的单点修改操作2,二分答案,然后在线段树......
  • NGINX 加载动态模块
    NGINX1.9.11开始增加加载动态模块支持,从此不再需要替换nginx文件即可增加第三方扩展。目前官方只有几个模块支持动态加载,第三方模块需要升级支持才可编译成模块。#./configure--help|grepdynamic--with-http_xslt_module=dynamicenabledynamicngx_http_xslt_modul......
  • nacos1.4读取properties配置文件中的数组对象,实现动态更新
     方法一:不可自动更新配置,有待检查。packagecom.javaweb.admin.config;importcom.alibaba.nacos.api.config.ConfigType;importcom.alibaba.nacos.api.config.annotation.NacosConfigurationProperties;importlombok.Data;importorg.springframework.context.annotat......
  • echarts数据可视化-动态柱状图
    效果如下:此处用的echarts柱状图为:AxisAlignwithTick本文的要讨论的内容:1、柱状图样式修改2、多数据的缩放展示柱状图样式修改//数据constcity=reactive([{value:335,name:'长沙'},{value:310,name:'武汉'},{value:274,name:'岳阳'}......