【目标跟踪】奇葩需求如何处理(二)

news2024/9/25 17:13:07

文章目录

  • 一、前言
  • 二、奇葩需求
    • 2.1、井盖
    • 2.2、管线
  • 三、后记

一、前言

  1. 工作中往往出现些奇葩需求。
  2. 上一篇介绍了一些奇葩需求奇葩需求如何处理(一) ,今天给大家分享一些更奇葩的需求。

二、奇葩需求

2.1、井盖

昨天突然接到一个需求,识别井盖且判断是否有井盖或无井盖。而且时间紧急,比赛突然加的需求,只给一天时间。一天时间用深度学习方法大概率是来不及了,采集数据标注数据训练模型都要花时间。

下面是现场用手机拍的图片,给可以看看。图片中一个有井盖、一个无井盖

图片名称

1、首先要判断前方井盖位置。2、其次要判断是否真的存在井盖。

传统的方法,那无疑是分割了,分割然后判断圆形,最后统计分布,寻找能区分的特征量,能够有简单区分的值是最好的。

然后花了 20min 得出一个检测圆的代码。

python 代码

import cv2
import numpy as np


def cover_detect(image):
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)  # 转换为灰度图像
    roi_x, roi_y, roi_w, roi_h = 0, height // 2, width - 1, height // 2 - 1
    roi = gray[roi_y:roi_y + roi_h, roi_x:roi_x + roi_w]
    blurred = cv2.GaussianBlur(roi, (5, 5), 0)  # 应用高斯滤波去噪
    edges = cv2.Canny(blurred, 100, 200)  # 应用Canny边缘检测
    cv2.imshow("show", edges)
    cv2.waitKey(100)

    # 应用霍夫圆变换检测圆形物体
    circles = cv2.HoughCircles(edges, cv2.HOUGH_GRADIENT, 2, 1000, param1=50, param2=35, minRadius=80, maxRadius=100)
    # 确保检测到了圆
    if circles is not None:
        # 将坐标和半径转换为整数
        circles = np.uint16(np.around(circles))
        # 遍历检测到的每个圆
        for i in circles[0, :]:
            # 在原图上绘制圆形轮廓和圆心
            cv2.circle(image, (i[0], i[1] + roi_y), i[2], (0, 255, 0), 2)
            cv2.circle(image, (i[0], i[1] + roi_y), 2, (0, 0, 255), 3)

            x, y, r = i
            mask = np.zeros_like(roi)
            cv2.circle(mask, (x, y), r, (255, 255, 255), -1)
            cv2.putText(image, "Well Cover", (i[0] - 50, i[1] - 50 + roi_y), cv2.FONT_HERSHEY_SIMPLEX, 0.75,
                        (0, 255, 0), 2)
    return image

c++ 代码

#include <opencv2/opencv.hpp>  
#include <iostream>  
  
using namespace std;  
using namespace cv;  

Mat cover_detect(Mat image) {  
    Mat gray;  
    cvtColor(image, gray, COLOR_BGR2GRAY); // 转换为灰度图像  
  
    int height = image.rows;  
    int width = image.cols;  
    int roi_x = 0;  
    int roi_y = height / 2;  
    int roi_w = width;  
    int roi_h = height / 2; // 注意:这里应该是height / 2而不是height / 2 - 1,除非你有特别的理由要减去1  
  
    Mat roi(gray, Rect(roi_x, roi_y, roi_w, roi_h));  
    GaussianBlur(roi, roi, Size(5, 5), 0); // 应用高斯滤波去噪  
  
    Mat edges;  
    Canny(roi, edges, 100, 200); // 应用Canny边缘检测,注意:这里应该是Canny而不是Canny  
  
    // 显示边缘检测结果(如果需要)  
    // namedWindow("show", WINDOW_AUTOSIZE);  
    // imshow("show", edges);  
    // waitKey(100);  
  
    // 应用霍夫圆变换检测圆形物体  
    vector<Vec3f> circles;  
    HoughCircles(edges, circles, HOUGH_GRADIENT, 1, 100, 50, 35, 80, 100); // 注意:这里是HOUGH_GRADIENT而不是HOUGH_GRADIENT  
  
    // 遍历检测到的每个圆  
    for (size_t i = 0; i < circles.size(); i++) {  
        Vec3f c = circles[i];  
        Point center(cvRound(c[0]), cvRound(c[1]) + roi_y);  
        int radius = cvRound(c[2]);  
  
        // 在原图上绘制圆形轮廓和圆心  
        circle(image, center, radius, Scalar(0, 255, 0), 2);  
        circle(image, center, 2, Scalar(0, 0, 255), 3);  
  
        // 在图像上添加文本  
        putText(image, "Well Cover", Point(center.x - 50, center.y - 50), FONT_HERSHEY_SIMPLEX, 0.75, Scalar(0, 255, 0), 2);  
    }  
  
    return image;  
}  
  
int main() {  
    // 读取图像  
    Mat image = imread("path_to_your_image.jpg");  
    if (image.empty()) {  
        cerr << "Error: Could not open or find the image!" << endl;  
        return -1;  
    }  
  
    // 调用函数并显示结果  
    Mat result = cover_detect(image);  
    namedWindow("Result", WINDOW_AUTOSIZE);  
    imshow("Result", result);  
    waitKey(0);  
  
    return 0;  
}

上述代码注释很清晰,大概思路也很明了。

  1. 转换为灰度图像
  2. 取一定区域进行操作
  3. 高斯滤波去噪
  4. Canny 边缘检测
  5. HoughCircles 霍夫曼圆找圆
  6. 画图

在找到圆中可以添加一些过滤条件,过滤一些误检的圆。这里可以根据具体需求操作,比如分割特征、形状、纹理、颜色等方式。

分割效果图

图片名称

结果图

图片名称

看看效果还不错,第二步我们要区分是否真的有井盖。第一个想到的是利用灰度分布,毕竟受光照影响小。

灰度分布结果

图片名称

看到这里其实结果就显而易见了。找出相应的特征计算。最终通过计算结果,270帧图片检测结果,共400左右个井盖,分类正确率高达99%

2.2、管线

识别管线、跟踪+定位、发送消息给规控。

在这里插入图片描述

如图中绳子、管子等。

  1. 深度学习分割出绳子如 segformer 模型,后处理找出像素包络框,
  2. 计算最小矩形框,跟踪,赋值id。
  3. 发送凸包以及相应的距离信息。

流程图

在这里插入图片描述
(一)最小矩形框
由于检测分割管线,输入的是管线像素的包络点。点的输入可能会大于2000,单纯对点的跟踪耗时长且不稳定。 首先对输入的点求最小矩形框,用最小矩形框去跟踪与航迹管理(分配id)。

红色框为检测后的最小矩形框
在这里插入图片描述

cv::Rect_<float> Tracking::GetMinBox(std::vector<cv::Point> points)
{
    cv::Point_<float> minPoint, maxPoint;
    minPoint.x = minPoint.y = FLT_MAX;
    maxPoint.x = maxPoint.y = -FLT_MAX;
    for (cv::Point point:points) {
        minPoint.x = std::min(minPoint.x, float(point.x));
        maxPoint.x = std::max(maxPoint.x, float(point.x));
        minPoint.y = std::min(minPoint.y, float(point.y));
        maxPoint.y = std::max(maxPoint.y, float(point.y));
    }
    cv::Rect_<float> res = cv::Rect_<float>(minPoint, maxPoint);
    return res;
}

(二)凸包计算

box可以跟踪,但是最终输出给下游的应该是世界坐标系的点。而点应该是包络框的形式,则需要计算凸包减少点的传递,避免增加无效的计算。(你要是一次性传上千个点,你看规控的人打不打你[坏笑.jpg])。

在这里插入图片描述
蓝色框是跟踪框包络点的最小凸包。获得了凸包的像素点,直接输出像素点的世界坐标,最终得到的包络框输出给规控。

计算凸包可以利用 opencv 中 cv::convexHull 函数,输入所有点像素,得出凸包点像素。根据凸包点像素发送俯视图下的位置就可。

#include <opencv2/opencv.hpp>  
#include <vector>  
  
int main() {  
    std::vector<cv::Point2f> points = {  
        {0, 0}, {10, 0}, {10, 10}, {5, 4}, {0, 10}  
    };  
  
    std::vector<int> hullIndices;  
    cv::convexHull(points, hullIndices, false, false);  
  
    std::vector<cv::Point2f> hullPoints(hullIndices.size());  
    for (size_t i = 0; i < hullIndices.size(); ++i) {  
        hullPoints[i] = points[hullIndices[i]];  
    }  
  
    // Draw the points and the convex hull  
    cv::Mat img(200, 200, CV_8UC3, cv::Scalar(255, 255, 255));  
    for (const auto& pt : points) {  
        cv::circle(img, pt, 2, cv::Scalar(0, 0, 255), -1);  
    }  
    for (size_t i = 0; i < hullPoints.size(); ++i) {  
        const auto& pt1 = hullPoints[i];  
        const auto& pt2 = hullPoints[(i + 1) % hullPoints.size()];  
        cv::line(img, pt1, pt2, cv::Scalar(0, 255, 0), 2);  
    }  
  
    cv::imshow("Convex Hull", img);  
    cv::waitKey(0);  
  
    return 0;  
}

三、后记

除了上面这些需求,博主还遇到更加奇葩的,今天就到这,下次分享更奇葩的。欢迎大家交流

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

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

相关文章

JAVA_会话

会话技术 1.会话: 一次会话包含多次请求和响应 2.功能: 在一次会话的范围内的多次请求&#xff0c;共享数据 3.方式: 3.1.客户端会话技术 Cookie(甜点) 1.概念: 客户端会话技术,将数据保存到客户端 2.快速入门: 1.创建Cookie对象,绑定数据new Cookie(String name,String v…

msvcp140.dll是什么文件?msvcp140.dll丢失如何解决(最新教程)

在玩电脑的时候&#xff0c;经常会碰到一些烦人的东西&#xff0c;比如那个“msvcp140.dll丢失”啥啥啥的。这个东西一出现&#xff0c;整个人都不好了&#xff0c;完全影响了我们愉快电脑生活的节奏。为啥会出现msvcp140.dll丢失的这种情况&#xff0c;怎么解决&#xff0c;还…

精读《架构设计之 DCI》

本期精读文章是&#xff1a;The DCI Architecture 1 引言 随着前端 ES6 ES7 的一路前行&#xff0c; 我们大前端借鉴和引进了各种其他编程语言中的概念、特性、模式; 我们可以使用函数式 Functional 编程设计&#xff0c;可以使用面向对象 OOP 的设计&#xff0c;可以使用面向…

【C++从练气到飞升】04---拷贝构造函数

&#x1f388;个人主页&#xff1a;库库的里昂 ✨收录专栏&#xff1a;C从练气到飞升 &#x1f389;鸟欲高飞先振翅&#xff0c;人求上进先读书。 目录 ⛳️推荐 一、拷贝构造函数的引入 1. 以日期类为例:进行的值拷贝是不会发生错误的 2. 以栈类为例:进行的值拷贝会发现发…

C语言基础(十六)通过指针来输入和获取结构体的变量值

老样子&#xff0c;先看代码 #include <stdio.h> #include <string.h>#define NLEN 30 struct namect{char fname[NLEN];char lname[NLEN];int letters; };void getinfo(struct namect *); void makeinfo(struct namect *ptr); void showinfo(const struct namec…

Kubernetes的Namespace使用

在 Kubernetes 中&#xff0c;命名空间提供了一种用于隔离单个集群中的资源组的机制。资源名称在命名空间内必须是唯一的&#xff0c;但不能跨命名空间。基于命名空间的作用域仅适用于命名空间物体 &#xff08;例如部署、服务等&#xff09;而不是集群范围的对象&#xff08;例…

牛客周赛 Round 37VP(DEF)

D.思维题&#xff1a; 若按照顺序发现很难入手&#xff0c;于是我们不妨先小紫&#xff0c;再让小红反悔即可 假设为cabababbabazbc&#xff0c;如果直接小紫&#xff0c;那么它一定以a开头&#xff0c;于是小红可以先把首尾的a去掉&#xff0c;即czbc,此时可以得到bc,于是小红…

19---时钟电路设计

视频链接 时钟硬件电路设计01_哔哩哔哩_bilibili 时钟电路设计 晶振是数字电路的心脏&#xff0c;数字电路需要一个稳定的工作时钟信号&#xff0c;时钟电路至关重要&#xff01; 1、晶振概述 晶振一般指晶体振荡器。晶体振荡器是指从一块石英晶体上按一定方位角切下薄片&…

基于stable diffusion的IP海报生成

【AIGC】只要10秒&#xff0c;AI生成IP海报&#xff0c;解放双手&#xff01;&#xff01;&#xff01;在AIGC市场发展的趋势下&#xff0c;如何帮助设计工作者解放双手。本文将从图像生成方向切入&#xff0c;帮助大家体系化的学习Stable diffusion的使用&#xff0c;完成自有…

sonar接入maven项目

1、介绍 sonar是一款静态代码质量分析工具&#xff0c;支持Java、Python、PHP、JavaScript、CSS等25种以上的语言&#xff0c;而且能够集成在IDE、Jenkins、Git等服务中&#xff0c;方便随时查看代码质量分析报告。他有如下特性 (1) 检查代码是否遵循编程标准&#xff1a;如命…

【回归预测】基于DBO-BP(蜣螂优化算法优化BP神经网络)的回归预测 多输入单输出【Matlab代码#68】

文章目录 【可更换其他算法&#xff0c;获取资源请见文章第6节&#xff1a;资源获取】1. BP神经网络2. 蜣螂优化算法3. DBO-BP神经网络模型的构建4. 部分代码展示5. 仿真结果展示6. 资源获取 【可更换其他算法&#xff0c;获取资源请见文章第6节&#xff1a;资源获取】 1. BP神…

[云] vmware: host: net: Net.CoaleseDefaultOn

https://communities.vmware.com/t5/Storage-Performance/Advanced-Networking-Performance-Options/ta-p/2792649 在vsphere client下的路径是&#xff1a; 选择使用的host -> 右键setting->configure-> system->advanced system setting->edit->Net.Coales…

第九节HarmonyOS 常用基础组件31-Toggle

1、描述 组件提供勾选框样式、状态栏样式以及开关样式。 2、子组件 仅当ToggleType为Button时可包含子组件。 3、接口 Toggle(options: { type: ToggleType , isOn?: boolean}) 4、参数 参数名 参数类型 必填 描述 type ToggleType 是 开关的样式。 isOn boole…

蓝桥杯 第3217题 简单的异或难题 C++ Java Python

题目 思路和解题方法 计算给定数组中子数组异或和的问题。它采用了前缀异或的方法来预处理数组&#xff0c;然后对于每个查询&#xff0c;通过异或操作计算子数组的异或和。 读取输入的数组&#xff0c;并计算每个位置的前缀异或和。对于每个查询&#xff0c;读取查询的左右边界…

一文读懂MES和ERP的区别

MES&#xff08;Manufacturing Execution System&#xff09;系统是制造执行系统&#xff0c;位于上层的计划管理系统与生产过程的直接工业控制系统之间&#xff0c;是面向车间层的管理信息系统&#xff0c;能够对整个车间制造过程进行优化&#xff0c;实时收集生产过程中的数据…

python 爬虫 地理空间DEM 制作中国地形

一.配置Python 爬虫 环境 from selenium import webdriver import time # from selenium.webdriver.common.action_chains import ActionChains from selenium.webdriver.common.by import Byfrom selenium.webdriver.common.keys import Keys # from selenium.webdriver.comm…

Java毕业设计-基于springboot开发的乐校园二手书交易管理系统-毕业论文+答辩PPT(附源代码+演示视频)

文章目录 前言一、毕设成果演示&#xff08;源代码在文末&#xff09;二、毕设摘要展示1、开发说明2、需求分析3、系统功能结构 三、系统实现展示1、系统功能模块2、管理员功能模块3、卖家用户功能模块4、用户功能模块 四、毕设内容和源代码获取总结 Java毕业设计-基于springbo…

docker 修改日志存储路径

docker 日志默认存放在 /var/lib/docker/ 下 docker info修改步骤&#xff1a; 1、停止docker服务 systemctl stop docker 2、新建配置文件 vi /etc/docker/daemon.json添加如下内容 {"data-root": "/data/docker" }3、然后把之前的数据全部复制到新目…

LabVIEW柴油机安保监控系统

LabVIEW柴油机安保监控系统 随着航运业的快速发展&#xff0c;确保船舶柴油机的安全稳定运行变得尤为重要。船舶柴油机故障不仅会导致重大的经济损失&#xff0c;还可能危及人员安全和环境。设计并开发了一套基于LabVIEW平台的柴油机安保监控系统&#xff0c;旨在通过实时监控…

C++ 子序列

目录 最长递增子序列 摆动序列 最长递增子序列的个数 最长数对链 最长定差子序列 最长的斐波那契子序列的长度 最长等差数列 等差数列划分 II - 子序列 最长递增子序列 300. 最长递增子序列 子数组是连续的&#xff0c;子序列可以不连续&#xff0c;那么就要去[0, i - 1]…