基与OpenCV实现单目相机实时追踪对物体的测量算法(Android版)

article/2025/6/16 9:07:01
本人原创转载请注明出处

前言

本算法是基于摄像头到测量物体的已知距离和摄像头像素焦距的前提下测量物体的宽高,并根据参照物的实际尺寸测量其他物体。

效果图

准备环境

1.首先硬件你要有一个摄像头,最好是USB即插即用免驱的

2.我使用的是12寸的安卓平板,阁下可用任意一台手机或者平板,Android系统在4.4以上版本就行

3.引入我们需要的OpenCV ,这里使用的是无依赖管理包4.11.0版本

 implementation 'org.opencv:opencv:4.11.0'

第一步 新建项目,引入依赖包

这里默认阁下已经把项目新建完成

在项目的build.gradle下引入OpenCV4.11.0 并同步sync

第二步 新建一个USBCamera2的类继承自JavaCamera2View

主要是为了后续更改摄像头参数,如果阁下只是使用画面预览的功能可以不用继承,直接使用JavaCamera2View在Xml布局中

USBCamera2类的实现

public class USBCamera2 extends JavaCamera2View {public USBCamera2(Context context, int cameraId) {super(context, cameraId);}public USBCamera2(Context context, AttributeSet attrs) {super(context, attrs);}public void openCamera(){}public void setCameraWH(int w,int h){setMaxFrameSize(w,h);}@Overrideprotected void deliverAndDrawFrame(CvCameraViewFrame frame) {super.deliverAndDrawFrame(frame);}
}

第三步 在你的MainActivity的布局在layout布局中使用

    <com.example.opencv_demo.USBCamera2android:id="@+id/usbcamera2"android:layout_width="match_parent"android:layout_height="match_parent"opencv:camera_id="any"opencv:show_fps="true"></com.example.opencv_demo.USBCamera2>

注意:引入属性时需在跟节点加入自定义属性,来自OpenCV

xmlns:opencv="http://schemas.android.com/apk/res-auto"

camera_id="any" 指任意摄像头,阁下如果有多个摄像头,可指定摄像头id,一般使用手机运行项目的话id=0为前置摄像头,1为后置摄像头

show_fps="true" 是否显示帧率以及预览的画面大小

第四步 在MainActivity的中预览画面

首先预览前需要在AndroidMainfest加入权限

<uses-feature android:name="android.hardware.camera" android:required="false"/><uses-feature android:name="android.hardware.camera.autofocus" android:required="false"/><uses-feature android:name="android.hardware.camera.front" android:required="false"/><uses-feature android:name="android.hardware.camera.front.autofocus" android:required="false"/>

在你的application节点下开启硬件加入

android:hardwareAccelerated="true"

ok,现在回到MainActivity

首先MainActivity 继承CameraActivity 回检测请求所需要的权限,再次我没有进行权限请求因为我使用的设备默认获取了Root权限,阁下如果没获取Root权限可正常申请摄像头权限

然后设置USBCamera2可见

class MainActivity : CameraActivity() {lateinit var cv: CameraBinding;override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)cv=CameraBinding.inflate(layoutInflater);setContentView(cv.root)cv.usbcamera2.visibility = CameraBridgeViewBase.VISIBLE

下面检测是否初始化成功

if (OpenCVLoader.initLocal()){//后续操作
}else{Log.e("MainActivity","初始化失败")
}

第五步 在MainActivity的生命周期中监听

override fun onResume() {super.onResume()cv.usbcamera2.enableView()}override fun onPause() {super.onPause()cv.usbcamera2.disableView()}override fun onDestroy() {super.onDestroy()cv.usbcamera2.disableView()
}

重载获取摄像头列表的方法

 override fun getCameraViewList(): List<CameraBridgeViewBase?>? {return Collections.singletonList(cv.usbcamera2)}

第六步 完成以上步骤后就可以预览画面了

添加监听,在onCreate中

cv.usbcamera2.setCvCameraViewListener(object : CvCameraViewListener2 {override fun onCameraViewStarted(width: Int, height: Int) {}override fun onCameraViewStopped() {}override fun onCameraFrame(inputFrame: CameraBridgeViewBase.CvCameraViewFrame?): Mat? {val srcMat = inputFrame!!.rgba()var gray = Mat()val blurred = Mat()val edges = Mat()//灰度转换Imgproc.cvtColor(srcMat, gray, Imgproc.COLOR_BGR2GRAY)//高斯模糊Imgproc.GaussianBlur(gray, blurred, Size(5.0, 5.0), 0.0)//canny边缘检测Imgproc.Canny(blurred, edges, 20.0, 30.0)var contours  = mutableListOf<MatOfPoint>()val hierarchy = Mat()Imgproc.findContours(edges,contours,hierarchy, Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_SIMPLE )
//                  // Step 3: 筛选最大轮廓contours.maxByOrNull  { Imgproc.contourArea(it)  }?.let { maxContour ->val rect = Imgproc.boundingRect(maxContour)var pixelPerMetric = 20.0 / 6.5// Step 4: 计算实际尺寸val actualWidth = rect.width  / pixelPerMetricval actualHeight = rect.height  / pixelPerMetric// Step 5: 绘制结果Imgproc.rectangle(srcMat,  rect, Scalar(0.0, 255.0, 0.0), 2)val text = "%.1fmm x %.1fmm".format(actualWidth, actualHeight)var outLine=30.0 //边框外线延长var radius=15 //圆圈半径var thickness=2 //边框厚度
//                        Imgproc.circle(srcMat, Point(rect.x.toDouble(),  rect.y.toDouble()), 5, Scalar(255.0, 0.0, 0.0), 2)//中心圆圈Imgproc.circle(srcMat, Point(rect.x.toDouble()+rect.width/2,  rect.y.toDouble()+rect.height/2), radius, Scalar(255.0, 0.0, 0.0), thickness)//竖线drawDashedLine(srcMat, Point(rect.x.toDouble()+rect.width/2,  rect.y.toDouble()-outLine), Point(rect.x.toDouble()+rect.width/2,  rect.y.toDouble()+rect.height+outLine), Scalar(255.0, 0.0, 0.0), thickness,4,7)
//                        Imgproc.line(srcMat, Point(rect.x.toDouble()+rect.width/2,  rect.y.toDouble()-outLine), Point(rect.x.toDouble()+rect.width/2,  rect.y.toDouble()+rect.height+outLine), Scalar(255.0, 0.0, 0.0), thickness,2,2)//横线drawDashedLine(srcMat, Point(rect.x.toDouble()-outLine,  rect.y.toDouble()+rect.height/2), Point(rect.x.toDouble()+rect.width+outLine,  rect.y.toDouble()+rect.height/2), Scalar(255.0, 0.0, 0.0), thickness,4,7)
//                        Imgproc.line(srcMat, Point(rect.x.toDouble()-outLine,  rect.y.toDouble()+rect.height/2), Point(rect.x.toDouble()+rect.width+outLine,  rect.y.toDouble()+rect.height/2), Scalar(255.0, 0.0, 0.0), thickness)// Step 6: 绘制文本
//                        Imgproc.putText(srcMat, text, Point(100.0, 100.0), 0, 1.0, Scalar(255.0, 255.0, 255.0, 255.0), 2)Imgproc.putText(srcMat, text, Point(rect.x.toDouble()+20,  rect.y.toDouble()  - 30),Imgproc.FONT_HERSHEY_SIMPLEX, 0.8, Scalar(255.0, 0.0, 0.0), 2)}gray.release()blurred.release()hierarchy.release()return srcMat}})

在onCameraFrame 监听回调方法中获得mat对象

然后根据上面的代码以此进行灰度转换、高斯模糊、canny边缘查找、筛选轮廓

最后根据比例进行参照物和像素计算

最后绘制结果

其次是绘制中心圆,横竖虚线,这些步骤可去除,根据需要

添加虚线的方法如下

// 虚线绘制(以直线为例)fun drawDashedLine(img: Mat,pt1: Point,pt2: Point,color: Scalar,thickness: Int,dashLength: Int,gapLength: Int) {val dx = pt2.x - pt1.xval dy = pt2.y - pt1.yval distance = sqrt(dx * dx + dy * dy)val unitX = dx / distanceval unitY = dy / distancevar drawn = 0.0var draw = truewhile (drawn < distance) {if (draw) {val start = Point(pt1.x + unitX * drawn, pt1.y + unitY * drawn)val remain = min(dashLength.toDouble(), distance - drawn)val end = Point(start.x + unitX * remain, start.y + unitY * remain)Imgproc.line(img, start, end, color, thickness)drawn += dashLength.toDouble()} else {drawn += gapLength.toDouble()}draw = !draw}}

最后总结

步骤不多,精度与摄像头和物体距离有关,需要全部源码的可评论留言

本人原创转载请注明出处


http://www.hkcw.cn/article/ZKxGBQkLta.shtml

相关文章

【计算机视觉】单目深度估计模型-Depth Anything-V2

概述 本篇将简单介绍Depth Anything V2单目深度估计模型&#xff0c;该模型旨在解决现有的深度估计模型在处理复杂场景、透明或反射物体时的性能限制。与前一代模型相比&#xff0c;V2版本通过采用合成图像训练、增加教师模型容量&#xff0c;并利用大规模伪标签现实数据进行学…

常见离线语音识别模块功能说明——天问ASR;机芯智能;海凌科;轻语音

本文章从开发方式及功能等方面上进行说明&#xff0c;便于开发者进行选择与开发。 本文章依据截至2024年8月1日各网站资料进行整理编写。 总结&#xff1a; 开发方式上&#xff1a; 天文ASR 有着最灵活的开发方式&#xff0c;可完全按照程序员意愿进行开发&#xff0c;同时有…

Whisper.cpp + GPU 加速全攻略:Mac M 芯片高效转录音频教程

内容预告 本文将手把手教你如何利用 GPU 加速&#xff0c;在 Mac M 芯片上使用 Whisper 进行音频转文字&#xff0c;大幅提升转录效率。 本教程涵盖&#xff1a; Whisper.cpp 简介&#xff1a;为什么它适用于本地语音转写&#xff1f;Mac M 芯片的 GPU 加速&#xff1a;如何…

ROS2 与机器人视觉入门教程(ROS2 OpenCV)

系列文章目录 目录 系列文章目录 前言 一、 1.1 先决条件 1.1.1 安装 0. 安装要求 1. 安装 ROS2&#xff1a; 2. cv_bridge 1.2 获取机器人视觉库简介&#xff1a; 1.2.1 克隆该资源库 1.2.2 下载附加数据 1.2.3 编译代码 1.3 测试代码 二、ROS2 中的图像发布者…

计算机视觉:扩散模型(Diffusion Models)在图像生成中的突破

计算机视觉:扩散模型(Diffusion Models)在图像生成中的突破 一、前言二、扩散模型基础概念​2.1 马尔可夫链(Markov Chain)​2.2 扩散过程(Diffusion Process)​2.3 逆扩散过程(Reverse Diffusion Process)​三、扩散模型在图像生成中的原理​3.1 训练阶段​3.2 推理阶…

深入了解视频质量诊断的关键技术要点

本文还有配套的精品资源&#xff0c;点击获取 简介&#xff1a;视频质量诊断是IT行业中的关键任务&#xff0c;它包括多个领域的专业知识&#xff0c;用于确保视频内容的高质量呈现。本文将详细解析与视频质量诊断相关的各个技术要点&#xff0c;包括视频编码技术、分辨率和…

高效降噪不求人!精选6款音频降噪免费软件助你一键消噪

你是否也常常被音频中的噪音困扰&#xff1f;环境杂音、设备电流声、甚至呼吸声&#xff0c;都可能让精心录制的内容大打折扣。 本文从百款工具中实测筛选出6款还不错的会议录音降噪处理工具。无论你是剪辑小白还是职业后期&#xff0c;3分钟快速匹配你的降噪刚需&#xff01;…

深度学习目标检测构建基于YOLOv8的人体动作识别系统,如何使系统能够通过GUI界面支持图片、视频和摄像头输入,来识别人体动作站立,行走,摔倒,弯腰,坐立进行检测

深度学习目标检测构建基于YOLOv8的人体动作识别系统&#xff0c;如何使系统能够通过GUI界面支持图片、视频和摄像头输入&#xff0c;来识别人体动作站立&#xff0c;行走&#xff0c;摔倒&#xff0c;弯腰&#xff0c;坐立进行检测 文章目录 1. 环境配置2. 数据准备与模型训练…

猫粮哪个牌子质量好性价比高?安全猫粮选购推荐

从开始养猫到现在也算是有10年的铲屎年龄了&#xff0c;先后经历了膨化粮、烘焙猫粮以及猫罐头的喂养时期。结果这么多年过去了&#xff0c;现在喂的最多的还是烘焙猫粮&#xff0c;低温工艺留住大部分营养&#xff0c;不会让猫咪黑下巴&#xff0c;适口性也更好&#xff0c;性…

重塑在线软件开发新纪元:集成高效安全特性,深度解析与评估会员与促销管理系统的系统架构设计

案例 阅读以下关于软件架构设计与评估的叙述&#xff0c;回答问题1和问题2。 【题目】 某电子商务公司拟升级其会员与促销管理系统&#xff0c;向用户提供个性化服务&#xff0c;提高用户的粘性。在项目立项之初&#xff0c;公司领导层一致认为本次升级的主要目标是提升会员管…

【Linux探索学习】第三十弹——线程互斥与同步(上):深入理解线程保证安全的机制

Linux学习笔记&#xff1a; https://blog.csdn.net/2301_80220607/category_12805278.html?spm1001.2014.3001.5482 前言&#xff1a; 在上篇我们已经学习了关于线程的大部分知识&#xff0c;包括线程概念和线程控制等内容&#xff0c;今天我们来学习一下使用线程要做到的很…

飞牛的ipv6已动态解析到阿里云,访问显示不安全?教你绑定免费SSL证书

前言 最近有很多小伙伴陆续通过DDNS-GO做好了飞牛的ipv6动态解析了&#xff0c;如果还没有弄&#xff0c;又不知道文章在哪&#xff0c;可以点击下方这篇教程&#xff1a; 飞牛NAS有IPV6&#xff0c;想用DDNS-GO动态解析到域名&#xff1f;这简单了&#xff01; 很多搞定了的…

银河麒麟桌面操作系统V10 SP1:取消安装应用的安全授权认证

银河麒麟桌面操作系统V10 SP1&#xff1a;取消安装应用的安全授权认证 &#x1f496;The Begin&#x1f496;点点关注&#xff0c;收藏不迷路&#x1f496; 使用银河麒麟V10 SP1安装应用时&#xff0c;若频繁遇到安全授权认证提示&#xff0c;可按以下步骤设置&#xff1a; 打开…

最新Spring Security实战教程(十二)CORS安全配置 - 跨域请求的安全边界设定

&#x1f337; 古之立大事者&#xff0c;不惟有超世之才&#xff0c;亦必有坚忍不拔之志 &#x1f390; 个人CSND主页——Micro麦可乐的博客 &#x1f425;《Docker实操教程》专栏以最新的Centos版本为基础进行Docker实操教程&#xff0c;入门到实战 &#x1f33a;《RabbitMQ》…

解决Windows安全中心显示空白页面

1、电脑重装系统后&#xff0c;发现原本一些软件打不开了&#xff0c;电脑莫名认为有病毒&#xff0c;自动删除插件。附图。 2、第一反应是电脑防火墙的原因&#xff0c;默认威胁防护识别到了病毒软件&#xff0c;自动删除。在开始屏幕搜Windows安全中心&#xff0c;打开之后发…

Python全流程开发实战:基于IMAP协议安全下载个人Gmail邮箱内所有PDF附件

文章目录 一、需求分析与安全前置&#xff1a;为什么需要专用工具&#xff1f;1.1 痛点场景1.2 技术方案选择 二、准备工作&#xff1a;Gmail账号安全配置与环境搭建2.1 开启两步验证&#xff08;必做&#xff01;&#xff09;2.2 创建应用专用密码&#xff08;替代普通密码&am…

【网络安全论文】筑牢局域网安全防线:策略、技术与实战分析

【网络安全论文】筑牢局域网安全防线:策略、技术与实战分析 简述一、引言1.1 研究背景1.2 研究目的与意义1.3 国内外研究现状1.4 研究方法与创新点二、局域网网络安全基础理论2.1 局域网概述2.1.1 局域网的定义与特点2.1.2 局域网的常见拓扑结构2.2 网络安全基本概念2.2.1 网络…

评论员点评郑钦文无缘四强 体能优势未转化

北京时间6月3日,在2025年法网女单1/4决赛中,郑钦文以0-2的成绩不敌萨巴伦卡,遗憾未能晋级四强。赛后,资深评论员许旸对比赛进行了点评。许旸表示,这场比赛让人感到不甘心,郑钦文未能将体能优势转化为战斗力。特别是在第二盘,不仅体能没有得到充分利用,技术上的发挥也非…

【吾爱】逆向实战crackme160破解记录(一)

前言 最近想拿吾爱上的crackme程序练练手&#xff0c;发现论坛上已经有pk8900总结好的160个crackme&#xff0c;非常方便&#xff0c;而且有很多厉害的前辈已经写好经验贴和方法了&#xff0c;我这里只是做一下自己练习的记录&#xff0c;欢迎讨论学习&#xff0c;感谢吾爱论坛…

媒体:硬碰硬才是郑钦文的底色 移山之路无悔前行

面对高山,有人绕路,有人架桥,郑钦文选择移山。第八次和萨巴伦卡交手,郑钦文用硬碰硬的方式展示自己强悍的底色。虽然再次失败,依然无怨无悔。这才是郑钦文的网球为人所爱的原因。她的正手轰球已经让高山颤抖。如果郑钦文在第一盘4比2领先时拿下那两次网前机会球,比赛结果…