OpenCV 实现对形似宝马标的黄黑四象限标定位
文章目录
- 功能
- 背景
- 代码
- 效果
功能
实现对形似宝马标的黄黑四象限光学识别标定位
背景
大学同学遇到了这个场景,琢磨了下,以备不时之需。
代码
所用opencv版本:4.12
numpy==2.2.4
scikit_learn==1.6.1
import time
import cv2
import numpy as np
import math
from sklearn.cluster import KMeansdef calculate_tilt_angle(a, b): # 确保 a >= bif a < b:a, b = b, a# 计算倾斜角度(弧度)theta_rad = math.acos(b / a)# 转为角度theta_deg = math.degrees(theta_rad)return theta_degdef compute_intersection(line1, line2):"""计算两条直线的交点"""(x1, y1), (x2, y2) = line1(x3, y3), (x4, y4) = line2# 计算分母den = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4)if den == 0: # 直线平行return None# 计算交点坐标t = ((x1 - x3) * (y3 - y4) - (y1 - y3) * (x3 - x4)) / denu = -((x1 - x2) * (y1 - y3) - (y1 - y2) * (x1 - x3)) / den# 判断交点是否在线段内if 0 <= t <= 1 and 0 <= u <= 1:x = x1 + t * (x2 - x1)y = y1 + t * (y2 - y1)return (int(x), int(y))else:return Nonedef calculate_angle(line1, line2):"""计算两条线段之间的夹角(度数)"""# 提取线段端点坐标(x1, y1), (x2, y2) = line1(x3, y3), (x4, y4) = line2# 计算方向向量vec1 = (x2 - x1, y2 - y1)vec2 = (x4 - x3, y4 - y3)# 计算向量模长mod1 = np.sqrt(vec1[0]**2 + vec1[1]**2)mod2 = np.sqrt(vec2[0]**2 + vec2[1]**2)if mod1 == 0 or mod2 == 0:return None # 无效向量(线段长度为0)# 计算点积和夹角余弦dot_product = vec1[0] * vec2[0] + vec1[1] * vec2[1]cos_theta = dot_product / (mod1 * mod2)cos_theta = np.clip(cos_theta, -1.0, 1.0) # 处理浮点误差# 计算角度(0°~180°)angle = np.degrees(np.arccos(cos_theta))return angleif __name__ == '__main__':use_camera_flag = 1fps_list = []prev_time = 0if use_camera_flag:cap = cv2.VideoCapture(2) # 自己修改为摄像头对应IDwhile True:if use_camera_flag:current_time = time.time()ret, image = cap.read()else:# 读取图像image = cv2.imread("label.jpeg")# image = cv2.imread("label0.png")# image = cv2.imread("label1.png")# image = cv2.imread("label2.jpeg")hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)# 定义黄色和黑色的HSV阈值范围(需根据实际图像调整)lower_yellow = np.array([20, 100, 100])upper_yellow = np.array([40, 255, 255])lower_black_h = np.array([35,10,0])upper_black_h = np.array([120,230,255])lower_black_l = np.array([0,0,0])upper_black_l = np.array([120,120,60])lower_white = np.array([0,0,125])upper_white = np.array([180,50,255])# 创建黄色和黑色的掩模mask_yellow = cv2.inRange(hsv, lower_yellow, upper_yellow)mask_black = cv2.bitwise_or(cv2.inRange(hsv, lower_black_h, upper_black_h), cv2.inRange(hsv, lower_black_l, upper_black_l))mask_white = cv2.inRange(hsv, lower_white, upper_white)# 合并黑黄区域的掩模并减去白色部分的掩模mask_combined = cv2.bitwise_or(mask_yellow, mask_black) & ~mask_white# 形态学操作(去噪+连接区域)kernel = np.ones((5,5), np.uint8)mask_processed = cv2.morphologyEx(mask_combined, cv2.MORPH_CLOSE, kernel)mask_yellow = cv2.morphologyEx(mask_yellow, cv2.MORPH_CLOSE, kernel)mask_black = cv2.morphologyEx(mask_black, cv2.MORPH_CLOSE, kernel)# 查找轮廓contours, _ = cv2.findContours(mask_processed, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)image_height, image_width = image.shape[:2]# 筛选最大轮廓(假设标记是图像中最显著的物体)if contours:for cnt in contours:if len(cnt) >= 5: # 至少需要5个点才能拟合椭圆ellipse = cv2.fitEllipse(cnt)(center, (width, height), angle) = ellipse# 过滤条件:长宽均大于阈值if width > image_width / 50 and height > image_height / 50:# 创建空白掩膜(与原图同尺寸)ellipse_mask = np.zeros_like(image[:, :, 0], dtype=np.uint8)# 绘制填充的椭圆(白色)cv2.ellipse(ellipse_mask, ellipse, 255, -1)# --- 计算椭圆区域内的黄色和黑色占比 ---# 提取椭圆区域内的黄色部分yellow_in_ellipse = cv2.bitwise_and(mask_yellow, mask_yellow, mask=ellipse_mask)yellow_area = cv2.countNonZero(yellow_in_ellipse)# 提取椭圆区域内的黑色部分black_in_ellipse = cv2.bitwise_and(mask_black, mask_black, mask=ellipse_mask)black_area = cv2.countNonZero(black_in_ellipse)# 计算椭圆总面积ellipse_area = cv2.countNonZero(ellipse_mask)# 计算比例(避免除零错误)if ellipse_area > 0:yellow_ratio = yellow_area / ellipse_areablack_ratio = black_area / ellipse_area# 过滤条件:黄黑区域各占至少30%if yellow_ratio >= 0.3 and black_ratio >= 0.3:# 使用椭圆掩膜提取原图label_in_image = cv2.bitwise_and(image, image, mask=ellipse_mask)gray = cv2.cvtColor(label_in_image, cv2.COLOR_BGR2GRAY) edges = cv2.Canny(gray, threshold1=50, threshold2=150)# 直线检测与交点计算lines = cv2.HoughLinesP(edges, rho=1, # 距离分辨率(像素单位)theta=np.pi/180, # 角度分辨率(弧度单位)threshold=50, # 检测阈值(累加器投票数阈值)minLineLength=((width + height) / 2) / 4, # 最小线段长度(短于此长度的线段被丢弃)maxLineGap=10 # 允许线段间的最大间隔(小于此间隔的线段合并))# 延长线段extended_lines = []if lines is not None:for line in lines:x1, y1, x2, y2 = line[0]# 延长线段,例如延长比例t=1.0t=1.0dx = x2 - x1dy = y2 - y1new_x1 = int(x1 - t * dx)new_y1 = int(y1 - t * dy)new_x2 = int(x2 + t * dx)new_y2 = int(y2 + t * dy)extended_lines.append([(new_x1, new_y1), (new_x2, new_y2)])# 计算交点intersections = []if extended_lines:for i in range(len(extended_lines)):for j in range(i+1, len(extended_lines)):line1 = extended_lines[i]line2 = extended_lines[j]# 计算夹角并过滤5°~175°以外的结果angle = calculate_angle(line1, line2)delta_angle = 85if angle is None or not (90 - delta_angle <= angle <= 90 + delta_angle):continuept = compute_intersection(line1, line2)if pt:# 检查交点是否在椭圆内# 椭圆参数来自ellipse变量# ellipse的格式是((h, k), (a, b), theta)(h, k), (a, b), theta_deg = ellipsetheta = np.deg2rad(theta_deg)x, y = pt# 转换到椭圆的标准坐标系x_trans = x - hy_trans = y - k# 旋转x_rot = x_trans * np.cos(theta) + y_trans * np.sin(theta)y_rot = -x_trans * np.sin(theta) + y_trans * np.cos(theta)# 判断是否在椭圆内if (x_rot**2)/(a**2) + (y_rot**2)/(b**2) <= 1:intersections.append(pt)# 聚类确定中心if intersections:X = np.array(intersections)kmeans = KMeans(n_clusters=1).fit(X)center_x, center_y = kmeans.cluster_centers_[0].astype(int)(h, k), (a, b), theta_deg = ellipse# (center_x, center_y) 即为目标点坐标cv2.circle(image, (center_x, center_y), int(((width + height) / 2) / 20 + 0.5), (0, 0, 255), int(((width + height) / 2) / 50 + 0.5))cv2.ellipse(image, ((float(center_x), float(center_y)), (a, b), theta_deg), (0, 255, 0), int(((width + height) / 2) / 50 + 0.5)) print("angle: {}, {}".format(calculate_tilt_angle(a, b), (a, b)))if use_camera_flag:fps = 1 / (current_time - prev_time)fps_list.append(fps)if len(fps_list) > 5:fps_list.pop(0)avg_fps = sum(fps_list) / len(fps_list)# 将帧率文本绘制到左上角cv2.putText(image,f"FPS: {fps:.2f}", # 显示两位小数(10, 30), # 左上角坐标 (x, y)cv2.FONT_HERSHEY_SIMPLEX, # 字体1, # 字体大小(255, 255, 255), # 颜色 (BGR格式,白色)2, # 字体厚度)# 显示结果cv2.namedWindow("Result", cv2.WINDOW_NORMAL)cv2.resizeWindow("Result", 500, 500)cv2.imshow("Result", image)if not use_camera_flag:cv2.waitKey(0)cv2.destroyAllWindows()cv2.imwrite("output.jpg", image)breakelse:prev_time = current_timeif cv2.waitKey(1) == ord('q'):cap.release()breakcv2.destroyAllWindows()
效果
部分图片取自c++识别象限标 —— 灯火~


具有一定的抗倾斜能力

相关文章:
OpenCV 实现对形似宝马标的黄黑四象限标定位
文章目录 功能背景代码效果 功能 实现对形似宝马标的黄黑四象限光学识别标定位 背景 大学同学遇到了这个场景,琢磨了下,以备不时之需。 代码 所用opencv版本:4.12 numpy2.2.4 scikit_learn1.6.1import time import cv2 import numpy as…...
2025 年 4 月补丁星期二预测:微软将推出更多 AI 安全功能
微软正在继续构建其 AI 网络安全战略,并于本月宣布在 Microsoft Security Copilot 中引入新代理。 他们引入了用于网络钓鱼分类的代理、用于数据丢失预防和内部风险管理的警报分类、条件访问优化、漏洞修复和威胁情报简报。 这些代理的目标是不断从这些不同学科中…...
从吉卜力漫画到艺术创造:GPT-4o多种风格绘图Prompt大全
在3月底,GPT-4o掀起了一阵吉卜力绘图浪潮,大家纷纷输入一张图片,让4o模型进行风格化迁移,其中吉卜力风格的漫画在社交媒体上最为火热。在大家争议4o的训练数据是否侵权和4o背后的技术原理的时候,我们先来玩一玩&#x…...
resttemplate设置params
如何使用RestTemplate设置请求参数 RestTemplate设置请求参数的方式根据请求类型(GET/POST)和参数形式(路径参数、查询参数、JSON请求体)有所不同,以下是具体实现方法: 一、GET请求参数设置 路径参数 使用…...
16.1Linux自带的LED灯驱动实验(知识)_csdn
前面我们都是自己编写 LED 灯驱动,其实像 LED 灯这样非常基础的设备驱动, Linux 内核已经集成了。 Linux 内核的 LED 灯驱动采用 platform 框架,因此我们只需要按照要求在设备树文件中添加相应的 LED 节点即可,本章我们就来学习如…...
【vLLM】使用 vLLM 对自定义实现模型进行高速推理
推荐超级课程: 本地离线DeepSeek AI方案部署实战教程【完全版】Docker快速入门到精通Kubernetes入门到大师通关课AWS云服务快速入门实战目录 介绍什么是 vLLM?处理 vLLM 中的多模态模型实现独特的视频生成模型转换为 vLLM 模型的策略准备输入标记序列如何添加多个多模式输入如…...
SQL Server 数据库实验报告
1.1 实验题目:索引和数据完整性的使用 1.2 实验目的: (1)掌握SQL Server的资源管理器界面应用; (2)掌握索引的使用; (3)掌握数据完整性的…...
在响应式网页的开发中使用固定布局、流式布局、弹性布局哪种更好
一、首先看下固定布局与流体布局的区别 (一)固定布局 固定布局的网页有一个固定宽度的容器,内部组件宽度可以是固定像素值或百分比。其容器元素不会移动,无论访客屏幕分辨率如何,看到的网页宽度都相同。现代网页设计…...
代码随想录算法训练营第三十八天 | 322.零钱兑换 279.完全平方数 139.单词拆分
322. 零钱兑换 题目链接:322. 零钱兑换 - 力扣(LeetCode) 文章讲解:代码随想录 视频讲解:动态规划之完全背包,装满背包最少的物品件数是多少?| LeetCode:322.零钱兑换_哔哩哔哩_b…...
linux提取 Suid提权入门 Sudo提权入门
前言 suid基本使用 Suid 是什么命令? suid 是管理员用户(root)可以对命令文件进行赋权 让其在低权限用户下下也可以保持root权限的执行能力 我现在是管理员我 使用网站用户查找信息的时候总是被阻拦没权限 查找的内容不完整 这个使用我…...
Talib库安装教程
1. 打开 https://github.com/cgohlke/talib-build 2. 点击 Releases 3. 选择对应版本下载(本人电脑win-amd64,python版本3.12) 4. 安装该库(进入该文件路径) pip install ta_lib-0.6.3-cp312-cp312-win_amd64.whl 5…...
TDengine 3.3.6.0 版本中非常实用的 Cols 函数
简介 在刚刚发布的 TDengine 3.3.6.0 版本 中,新增了一个非常实用的 函数COLS ,此函数用于获取选择函数所在行列信息,主要应用在生成报表数据,每行需要出现多个选择函数结果,如统计每天最大及最小电压,并报…...
LeetCode 249 解法揭秘:如何把“abc”和“bcd”分到一组?
文章目录 摘要描述痛点分析 & 实际应用场景Swift 题解答案可运行 Demo 代码题解代码分析差值是怎么来的?为什么加 26 再 %26? 示例测试及结果时间复杂度分析空间复杂度分析总结 摘要 你有没有遇到过这种情况:有一堆字符串,看…...
Python数据可视化-第4章-图表样式的美化
环境 开发工具 VSCode库的版本 numpy1.26.4 matplotlib3.10.1 ipympl0.9.7教材 本书为《Python数据可视化》一书的配套内容,本章为第4章 图表样式的美化 本章主要介绍了图表样式的美化,包括图表样式概述、使用颜色、选择线型、添加数据标记、设置字体…...
ROS Master多设备连接
Bash Shell Shell是位于用户与操作系统内核之间的桥梁,当用户在终端敲入命令后,这些输入首先会进入内核中的tty子系统,TTY子系统负责捕获并处理终端的输入输出流,确保数据正确无误的在终端和系统内核之中。Shell在此过程不仅仅是…...
系统思考:思考的快与慢
在做重大决策之前,什么原因一定要补充碳水化合物?人类的大脑其实有两套运作模式:系统1:自动驾驶模式,依赖直觉,反应快但易出错;系统2:手动驾驶模式,理性严谨,…...
音视频入门基础:RTP专题(21)——使用Wireshark分析海康网络摄像机RTSP的RTP流
一、引言 使用vlc等播放器可以播放海康网络摄像机的RTSP流: 网络摄像机的RTSP流中,RTSP主要用于控制媒体流的传输,如播放、暂停、停止等操作。RTSP本身并不用于转送媒体流数据,而是会通过PLAY方法使用RTP来传输实际的音视频数据。…...
浅谈StarRocks 常见问题解析
StarRocks数据库作为高性能分布式分析数据库,其常见问题及解决方案涵盖环境部署、数据操作、系统稳定性、安全管控及生态集成五大核心领域,需确保Linux系统环境、依赖库及环境变量配置严格符合官方要求以避免节点启动失败,数据导入需遵循格式…...
ASP.NET Core Web API 参数传递方式
文章目录 前言一、参数传递方式路由参数(Route Parameters)查询字符串参数(Query String Parameters)请求体参数(Request Body)表单数据(Form Data)请求头参数(Header Pa…...
04.游戏开发-unity编辑器详细-工具栏、菜单栏、工作识图详解
04.游戏开发,unity编辑器详细-工具栏、菜单栏、工作识图详解 提示:帮帮志会陆续更新非常多的IT技术知识,希望分享的内容对您有用。本章分享的是Python基础语法。前后每一小节的内容是存在的有:学习and理解的关联性,希…...
基于STM32与应变片的协作机械臂力反馈控制系统设计与实现----2.2 机械臂控制系统硬件架构设计
2.2 机械臂控制系统硬件架构设计 一、总体架构拓扑 1.1 典型三级硬件架构 #mermaid-svg-MWmxD3zX6bu4iFCv {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-MWmxD3zX6bu4iFCv .error-icon{fill:#552222;}#mermaid-s…...
【java】在 Java 中,获取一个类的`Class`对象有多种方式
在 Java 中,获取一个类的Class对象有多种方式。Class对象代表了 Java 中的一个类或接口的运行时类信息,可以用于反射操作。以下是获取Class对象的几种常见方法: 1.使用.class属性 每个类都有一个.class属性,可以直接获取该类的Cl…...
QGIS中第三方POI坐标偏移的快速校正-百度POI
1.百度POI: name,lng,lat,address 龙记黄焖鸡米饭(共享区店),121.908315,30.886636,南汇新城镇沪城环路699弄117号(A1区110室) 好福记黄焖鸡(御桥路店),121.571409,31.162292,沪南路2419弄26号1层B间 御品黄焖鸡米饭(安亭店),121.160322,31.305977,安亭镇新源路792号…...
将电脑控制手机编写为MCP server
文章目录 电脑控制手机后,截屏代码复习MCP server构建修改MCP的config文件测试效果困惑电脑控制手机后,截屏代码复习 def capture_window(hwnd: int, filename: str = None) -> dict:""&...
Pycharm 启动时候一直扫描索引/更新索引 Update index/Scanning files to index
多个项目共用一个虚拟环境,有助于加快PyCharm 启动吗 chatgpt 4o认为很有帮助,gemini 2.5pro认为没鸟用,我更认可gemini的观点。不知道他们谁在一本正经胡说八道。 -------- 打开pycharm的时候,下方的进度条一直显示在扫描文件…...
Vanna:用检索增强生成(RAG)技术革新自然语言转SQL
引言:为什么我们需要更智能的SQL生成? 在数据驱动的业务环境中,SQL 仍然是数据分析的核心工具。然而,编写正确的 SQL 查询需要专业知识,而大型语言模型(LLM)直接生成的 SQL 往往存在**幻觉&…...
Unity:标签(tags)
为什么需要Tags? 在游戏开发中,游戏对象(GameObject)数量可能非常多,比如玩家、敌人、子弹等。开发者需要一种简单的方法来区分这些对象,并根据它们的类型执行不同的逻辑。 核心需求: 分类和管…...
如何创建一个自行设计的nginx的Docker Image
目录 前奏问题描述问题解决第一步:设置构建环境第二步:构建BoringSSL第三步:下载并构建Nginx第四步:创建最终镜像 整体的Dockerfile 前奏 你是否曾经想过,亲手打造一个属于自己的Nginx Docker镜像呢? 今天…...
CKPT文件是什么?
检查点(Checkpoint,简称ckpt)是一种用于记录系统状态或数据变化的技术,广泛应用于数据库管理、机器学习模型训练、并行计算以及网络安全等领域。以下将详细介绍不同领域中ckpt检查点的定义、功能和应用场景。 数据库中的ckpt检查点…...
zk基础—5.Curator的使用与剖析二
大纲 1.基于Curator进行基本的zk数据操作 2.基于Curator实现集群元数据管理 3.基于Curator实现HA主备自动切换 4.基于Curator实现Leader选举 5.基于Curator实现分布式Barrier 6.基于Curator实现分布式计数器 7.基于Curator实现zk的节点和子节点监听机制 8.基于Curator创…...
