基于单目相机的2D测量(工件尺寸和物体尺寸)

news2025/3/1 15:06:37

目录

1.简介

2.基于单目相机的2D测量

2.1 想法:

2.2 代码思路

2.2 主函数部分


1.简介

基于单目相机的2D测量技术在许多领域中具有重要的背景和意义。

  1. 工业制造:在工业制造过程中,精确测量是确保产品质量和一致性的关键。基于单目相机的2D测量技术可以用于检测和测量零件尺寸、位置、形状等参数,进而实现自动化生产和质量控制。通过实时监测并反馈测量结果,可以快速发现和纠正生产中的偏差,提高产品的一致性和合格率。

  2. 计算机视觉:单目相机作为计算机视觉的传感器之一,能够捕捉并记录场景中的图像信息。基于单目相机的2D测量技术可以通过对图像进行处理和分析来提取目标物体的特征和参数。这种技术在目标检测、物体跟踪、姿态估计等计算机视觉任务中起着至关重要的作用。

  3. 地理测绘和导航:基于单目相机的2D测量技术可以应用于地理测绘和导航领域。通过获取地面或航空图像,并利用图像处理和计算机视觉算法,可以实现地表特征的提取、地形建模、数字地图的生成等工作。这对于城市规划、农业管理、导航系统等方面具有重要的应用价值。

  4. 医学影像:在医学领域,基于单目相机的2D测量技术可以用于医学影像的分析和测量。通过对医学图像进行处理和分析,可以提取器官、病灶的形状、大小、位置等信息,辅助医生进行诊断和治疗决策。这种技术在影像学、放射学、眼科等医学专业中得到广泛应用。

综上所述,基于单目相机的2D测量技术在工业制造、计算机视觉、地理测绘和导航、医学影像等领域都有着重要的背景和意义。它可以提高生产效率、产品质量,推动科学研究和医学进步,为各个领域带来更多的机遇和挑战。

2.基于单目相机的2D测量

2.1 想法:

因为是静态测量,所以核心测量算法使用仿射变换。

首先拿到参照物区域,进行真实距离和像素距离的尺寸换算

在参照物区域找到物体轮廓,进行多边形拟合,对拟合到的物体进行测量

2.2 代码思路

代码思路的简要描述:

  1. 导入所需的库,包括cv2和自定义的utlis

  2. 初始化一些变量,如是否使用网络摄像机 (webcam)、图像路径 (path)、捕获对象 (cap)、图像缩放比例 (scale)、纸张宽度和高度 (wPhP) 以及上一帧时间 (pTime)。

  3. 进入主循环。在每次循环中,如果使用网络摄像机,则读取图像帧;否则,从指定路径读取图像。

  4. 对图像进行轮廓检测,获取所有检测到的轮廓 (conts) 和最大轮廓 (biggest)。

  5. 使用最大轮廓对图像进行透视变换 (warpImg),得到纸张的鸟瞰图 (imgWarp)。

  6. 对纸张鸟瞰图进行轮廓检测,获取所有检测到的轮廓 (conts2)。

  7. 遍历每个轮廓对象,绘制轮廓线和箭头,并计算测量结果 (纸张宽度 nW 和高度 nH)。

  8. 在图像上绘制测量结果和帧率信息。

  9. 显示原始图像和处理后的图像。

  10. 等待按键,继续下一次循环。

以上是代码的简要思路,具体实现和功能可以参考代码中的注释。

关于复现,你可以准备一张A4纸,一张小卡片,或者边缘信息明显的其他物件,按照图片中方式摆放即可。

像素与真实距离换算为

3像素=1mm

检测帧率可达30帧,误差在2%(误差取决相机分辨率),缺点是无法检测轮廓不清晰的物件以及复杂物体。

下一篇介绍如何测量复杂形状的物体

2.2 主函数部分

import cv2
import utlis
import time

###################################
webcam = True  # 网络摄像机一开始为FALSE
path = '1.jpg'
cap = cv2.VideoCapture(0)  # 使用捕获和cv定义相机-点点视频捕获,,我们将定义id,因此在这种情况下为0
cap.set(10, 160)  # 设置参数,宽度,高度,亮度,为他们中的每一根写入间隙点集,具有不同的亮度id,有十个,所以将其保持为160,然后在宽度和高度上,宽度为3,1920
cap.set(3, 1920)
cap.set(4, 1080)
scale = 3
wP = 270 * scale
hP = 370 * scale
###################################
pTime = 0
while True:
    if webcam:
        success, img = cap.read()
    else:
        img = cv2.imread(path)

    imgContours, conts = utlis.getContours(img, minArea=50000, filter=4)
    if len(conts) != 0:
        biggest = conts[0][2]
        # print(biggest)
        imgWarp = utlis.warpImg(img, biggest, wP, hP)
        imgContours2, conts2 = utlis.getContours(imgWarp,
                                                 minArea=2000, filter=4,
                                                 cThr=[50, 50], draw=False)
        if len(conts) != 0:
            for obj in conts2:
                cv2.polylines(imgContours2, [obj[2]], True, (0, 255, 0), 2)
                nPoints = utlis.reorder(obj[2])
                nW = round((utlis.findDis(nPoints[0][0] // scale, nPoints[1][0] // scale) / 10), 1)
                nH = round((utlis.findDis(nPoints[0][0] // scale, nPoints[2][0] // scale) / 10), 1)
                cv2.arrowedLine(imgContours2, (nPoints[0][0][0], nPoints[0][0][1]),
                                (nPoints[1][0][0], nPoints[1][0][1]),
                                (255, 0, 255), 3, 8, 0, 0.05)
                cv2.arrowedLine(imgContours2, (nPoints[0][0][0], nPoints[0][0][1]),
                                (nPoints[2][0][0], nPoints[2][0][1]),
                                (255, 0, 255), 3, 8, 0, 0.05)
                x, y, w, h = obj[3]
                cv2.putText(imgContours2, '{}cm'.format(nW), (x + 30, y - 10), cv2.FONT_HERSHEY_COMPLEX_SMALL, 1.5,
                            (255, 0, 255), 2)
                cv2.putText(imgContours2, '{}cm'.format(nH), (x - 70, y + h // 2), cv2.FONT_HERSHEY_COMPLEX_SMALL, 1.5,
                            (255, 0, 255), 2)
                cTime = time.time()

                fps = 1 / (cTime - pTime)
                pTime = cTime
                cv2.putText(imgContours2, str(int(fps)), (10, 70), cv2.FONT_HERSHEY_PLAIN, 3,
                            (255, 0, 255), 3)
                # 图像预处理及边缘检测
        cv2.imshow('A4', imgContours2)

    img = cv2.resize(img, (0, 0), None, 0.5, 0.5)
    cv2.imshow('Original', img)
    cv2.waitKey(1)

utlis模块代码

import cv2
import numpy as np
import math
import time
def getContours(img, cThr=[100, 100], showCanny=False, minArea=5000, filter=0, draw=False):
    # 将输入图像转换为灰度图像
    imgGray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    # 对灰度图像进行高斯模糊
    imgBlur = cv2.GaussianBlur(imgGray, (5, 5), 1)
    # 使用Canny边缘检测算法得到边缘图像
    imgCanny = cv2.Canny(imgBlur, cThr[0], cThr[1])
    # 对Canny边缘图像进行膨胀操作
    kernel = np.ones((5, 5))
    imgDial = cv2.dilate(imgCanny, kernel, iterations=3)
    # 对膨胀后的图像进行腐蚀操作
    imgThre = cv2.erode(imgDial, kernel, iterations=1)
    # gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    # blurred = cv2.GaussianBlur(gray, (5, 5), 0)
    # kernal = np.ones((5, 5), np.uint8)
    # blurred = cv2.erode(blurred, kernal)  # 腐蚀
    # blurred = cv2.erode(blurred, kernal)
    # edges = cv2.Canny(blurred, 50, 150)

    # 寻找图像中的轮廓
    contours, hierarchy = cv2.findContours(imgThre, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    finalContours = []
    for i in contours:
        area = cv2.contourArea(i)
        # 仅保留面积大于minArea的轮廓
        if area > minArea:
            peri = cv2.arcLength(i, True)
            approx = cv2.approxPolyDP(i, 0.02 * peri, True)
            bbox = cv2.boundingRect(approx)
            if filter > 0:
                # 如果指定了过滤条件,仅保留满足条件的轮廓
                if len(approx) == filter:
                    finalContours.append([len(approx), area, approx, bbox, i])
            else:
                finalContours.append([len(approx), area, approx, bbox, i])
    # 根据面积对轮廓进行排序
    finalContours = sorted(finalContours, key=lambda x: x[1], reverse=True)
    if draw:
        # 将保留的轮廓在原始图像上绘制出来
        for con in finalContours:
            cv2.drawContours(img, con[4], -1, (0, 0, 255), 3)
    return img, finalContours


def dectshow(org_img, boxs):
    img = org_img.copy()
    for box in boxs:
        cv2.rectangle(img, (int(box[0]), int(box[1])), (int(box[2]), int(box[3])), (0, 255, 0), 2)

        x1 = box[0]
        y1 = box[1]
        x2 = box[2]
        y2 = box[3]

        cv2.circle(img, (x1, y1), 5, (0, 0, 255), -1)  # 红色圆点
        cv2.circle(img, (x2, y1), 5, (0, 255, 0), -1)  # 绿色圆点
        cv2.circle(img, (x2, y2), 5, (255, 0, 0), -1)  # 蓝色圆点

        point1 =  [x1, y1]
        point2 =  [x2, y1]
        point3 =  [x2, y2]

        width = math.sqrt((point2[0] - point1[0]) ** 2 + (point2[1] - point1[1]) ** 2)
        height = math.sqrt((point3[0] - point2[0]) ** 2 + (point3[1] - point2[1]) ** 2)
        width = width/3
        height = height/3

        # cv2.imshow('1', img)
        # 计算两点之间的距离

        print("宽 is:", width, "m")
        print("长 is:", height, "m")


        cv2.putText(img, f'W: {str(width)[:4] }mm H: {str(height)[:4]}mm', (int(box[0]), int(box[1]) - 40), cv2.FONT_HERSHEY_SIMPLEX, 0.5,
                    (0, 255, 0), 2)
        cv2.imshow('dec_img', img)

    return img

# 对角点排序函数
def reorder(myPoints):
    myPointsNew = np.zeros_like(myPoints)
    myPoints = myPoints.reshape((4, 2))
    add = myPoints.sum(1)
    myPointsNew[0] = myPoints[np.argmin(add)]
    myPointsNew[3] = myPoints[np.argmax(add)]
    diff = np.diff(myPoints, axis=1)
    myPointsNew[1] = myPoints[np.argmin(diff)]
    myPointsNew[2] = myPoints[np.argmax(diff)]
    return myPointsNew


# 透视变换函数
def warpImg(img, points, w, h, pad=20):
    points = reorder(points)
    pts1 = np.float32(points)
    pts2 = np.float32([[0, 0], [w, 0], [0, h], [w, h]])
    matrix = cv2.getPerspectiveTransform(pts1, pts2)
    imgWarp = cv2.warpPerspective(img, matrix, (w, h))
    imgWarp = imgWarp[pad:imgWarp.shape[0] - pad, pad:imgWarp.shape[1] - pad]
    return imgWarp


# 计算两点之间的距离
def findDis(pts1, pts2):
    return ((pts2[0] - pts1[0]) ** 2 + (pts2[1] - pts1[1]) ** 2) ** 0.5

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

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

相关文章

uniapp编译到小程序Component is not found in path “components/energy/illumination“

Component is not found in path "components/energy/illumination" 直接清除缓存重新编译

如何保护我们的网络安全

保护网络安全是至关重要的,尤其是在今天的数字化时代。以下是一些保护网络安全的基本步骤: 1、使用强密码:使用包含字母、数字和特殊字符的复杂密码。不要在多个网站上重复使用相同的密码。定期更改密码。 2、启用双因素认证 (2FA)&#xff…

JavaScript反爬虫技巧详细攻略

在互联网时代,网站采取了各种手段来防止被爬虫抓取数据,其中最常见的就是JavaScript反爬虫技巧。本文将揭示一些常用的JavaScript反爬虫技巧,并提供一些实际操作建议,帮助您保护自己的爬虫免受检测和封禁。 1、为什么网站使用Java…

CentOS 搭建 OpenVPN 服务

CentOS 搭建 OpenVPN 服务 概述OpenVPN 应用场景OpenVPN 主机准备OpenVPN 服务端搭建部署安装配置证书软件创建证书安装openvpn并写入服务端配置文件启动并检查端口 OpenVPN客户端配置(linux端)配置openvpn测试连接 OpenVPN客户端搭建部署(wi…

AMD Zen5、Zen6架构细节首次曝光:原生32核心!直奔2nm工艺

AMD将在明年推出Zen5架构的锐龙8000系列、霄龙9005/8005系列,更下一代的Zen6架构也已经崭露头角,据说可以支持到史无前例的16通道内存。 现在,MLID曝光了一份AMD架构路线图,列出了Zen5、Zen6的不少细节,尤其是…

Element Plus阻止 el-dropdown、el-switch等冒泡事件

最近做vue3项目&#xff0c;使用Element Plus,又遇到坑了&#xff01; 问题点&#xff1a;组件中遇到事件冒泡问题了&#xff0c;el-checkbox 中 change事件要求阻止冒泡&#xff0c;如下代码中要求点击checkbox时不调用li标签的show方法 <li click"show()">…

UE4和C++ 开发-C++与UMG的交互2(C++获取UMG的属性)

1、...C获取UMG的属性 1.1、第一种方法&#xff1a;通过名称获取控件。 void UMyUserWidget::NativeConstruct() {Super::NativeConstruct();//通过名字&#xff0c;获取蓝图控件中的按钮引用。CtnClic Cast<UButton>(GetWidgetFromName(TEXT("Button_44"))…

什么是实验室超声波乳化?超声波乳化的工作原理?

乳液是什么&#xff1f;它是两种或多种不相混合的液体的共同体。那么&#xff0c;这些互不相容的液体是如何混合在一起的呢&#xff1f;这就要归功于超声波的神奇力量。超声波乳化棒&#xff0c;就像一个无形的魔法师&#xff0c;将高强度的超声波能量耦合到液体中&#xff0c;…

【API篇】一、执行环境API

文章目录 0、认识1、创建执行环境2、执行模式3、触发程序执行4、关于executeAsync方法 0、认识 DataStream API是Flink的核心层API。一个Flink程序&#xff0c;其实就是对数据源DataStream的各种转换。具体来说&#xff0c;代码基本上都由以下几部分构成&#xff1a; 后面章节…

早安心语|不委屈不将就,让生活充满仪式感

1、让自己的生活多一种可能&#xff0c;给自己的未来多一份惊喜&#xff0c;人生所有的机会和惊喜&#xff0c;都是在你全力以赴的道路上遇到的。 2、推开自己喜欢的人叫成长&#xff0c;留住自己喜欢的人叫本事&#xff0c;总有人嫌你不够好&#xff0c;也有人觉得你哪都好&am…

【RKNN】YOLO V5中pytorch2onnx,pytorch和onnx模型输出不一致,精度降低

在yolo v5训练的模型&#xff0c;转onnx&#xff0c;再转rknn后&#xff0c;测试发现&#xff1a; rknn模型&#xff0c;量化与非量化&#xff0c;相较于pytorch模型&#xff0c;测试精度都有降低onnx模型&#xff0c;相较于pytorch模型&#xff0c;测试精度也有降低&#xff…

The Foundry Nuke 15视频后期合成和特效制作Mac软件

Nuke 15 是一款专业的合成软件&#xff0c;主要用于电影、电视和广告制作中的后期合成和特效制作。 Nuke 15 提供了强大的合成工具和功能&#xff0c;可以对多个图像、视频和3D元素进行无缝融合和合成。它支持多通道图像处理&#xff0c;能够处理高动态范围&#xff08;HDR&…

算法通关村第18关【青铜】| 回溯

回溯算法是一种解决组合优化问题和搜索问题的算法。它通过尝试各种可能的选择来找到问题的解决方案。回溯算法通常用于问题的解空间非常大&#xff0c;而传统的穷举法会导致计算时间爆炸的情况。回溯算法可以帮助限制搜索空间&#xff0c;以提高效率。 回溯算法的核心思想是在…

ARM作业2

.设置按键中断&#xff0c;按键1按下&#xff0c;LED亮&#xff0c;再按一次&#xff0c;灭 按键2按下&#xff0c;蜂鸣器响。再按一次&#xff0c;不响 按键3按下&#xff0c;风扇转&#xff0c;再按一次&#xff0c;风扇停 头文件key_it.h #ifndef __KEY_IT_H__ #define …

没有炫光的台灯有哪些?2023五款优秀护眼台灯

很多家长有时候会说孩子觉得家里的台灯灯光刺眼&#xff0c;看书看久了就不舒服。这不仅要看光线亮度是否柔和&#xff0c;还要考虑台灯是不是有做遮光式设计。没有遮光式设计的台灯&#xff0c;光源外露&#xff0c;灯光会直射孩子头部&#xff0c;孩子视线较低&#xff0c;很…

【小米技术分享】面试题:什么是乐观锁?你是如何设计一个乐观锁

大家好&#xff0c;我是小米。今天我们来聊一下面试中常见的一个问题&#xff1a;“什么是乐观锁&#xff1f;你是如何设计一个乐观锁&#xff1f;”作为一位热爱技术的程序员&#xff0c;对于这个问题&#xff0c;我有着自己独特的理解和实践经验。接下来&#xff0c;我将以通…

flink1.15 savepoint 超时报错 java.util.concurrent.TimeoutException

savepoint命令 flink savepoint e04813d4e7480c526912eb4d32bba510 hdfs://flink/flink/migration/savepoint56650 -Dyarn.application.id=application_1683808492336_1222报错内容 org.apache.flink.util.FlinkException: Triggering a savepoint for the job e04813d4e7480…

通讯网关软件023——利用CommGate X2HTTP实现HTTP访问Modbus TCP

本文介绍利用CommGate X2HTTP实现HTTP访问Modbus TCP。CommGate X2HTTP是宁波科安网信开发的网关软件&#xff0c;软件可以登录到网信智汇(http://wangxinzhihui.com)下载。 【案例】如下图所示&#xff0c;SCADA系统上位机、PLC、设备具备Modbus RTU通讯接口&#xff0c;现在…

【计算机网络】——前言计算机网络发展的历程概述

主页点击直达&#xff1a;个人主页 我的小仓库&#xff1a;代码仓库 C语言偷着笑&#xff1a;C语言专栏 数据结构挨打小记&#xff1a;初阶数据结构专栏 Linux被操作记&#xff1a;Linux专栏 LeetCode刷题掉发记&#xff1a;LeetCode刷题 算法&#xff1a;算法专栏 C头…

Python编程必备:掌握列表遍历的6种神级技巧!

更多资料获取 &#x1f4da; 个人网站&#xff1a;涛哥聊Python 遍历列表是Python中最常见的任务之一&#xff0c;因为列表是一种非常常用的数据结构&#xff0c;它用于存储一组项目。 在编程中&#xff0c;经常需要对这些项目进行操作&#xff0c;例如查找特定元素&#xff…