斜光测距的原理及python实现

1.前言

最近做了一个基于opencv的斜光测距的小项目,东西不多,但是很有意思,值得拿出来学一学。项目里面需要比较精确的定位功能,将前人matlab代码移植到python上,并且做了一些优化,简化逻辑(毕竟我是专业的程序员),也用了tkinter界面包装了一下,最后通过pyinstaller打包成程序给同事使用。

2.原理

在这里插入图片描述

通过使用不同的亮点位置和对应的高度进行多元线性回归建模,再对新的亮点位置进行高度预测。

在这里插入图片描述

如图分别是14,14.5,15,15.5对应的四张光点位置图。

3.获取亮点位置

def get_box(image):
    # 将图像转换为灰度图像
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    # 应用高斯模糊来减少噪声
    blurred = cv2.GaussianBlur(gray, (5, 5), 0)
    max_val = np.max(blurred)
    _, binary = cv2.threshold(blurred, max_val/2, 255, cv2.THRESH_BINARY)
    # 形态学开运算去除噪声
    kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5))
    opened = cv2.morphologyEx(binary, cv2.MORPH_OPEN, kernel)
    # 找到轮廓
    contours, _ = cv2.findContours(opened, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    # 如果找到轮廓,计算质心
    if contours:
        largest_contour = max(contours, key=cv2.contourArea)
        M = cv2.moments(largest_contour)
        if M["m00"] != 0:
            cx = int(M["m10"] / M["m00"])
            cy = int(M["m01"] / M["m00"])
        else:
            cx, cy = 0, 0
        centroid = (cx, cy)
        # 计算边界框
        x, y, w, h = cv2.boundingRect(largest_contour)
        p=10
        bbox = (x-p, y-p, w+2*p, h+2*p)
        # 在图像上绘制质心和边界框
        output_image = image.copy()
        cv2.circle(output_image, centroid, 5, (0, 255, 0), -1)
        x,y,w,h=bbox
        cv2.rectangle(output_image, (x, y), (x + w, y + h), (0, 255, 0), 2)
        print(f"亮点的中心位置: {centroid},亮点的边界框: {bbox}")
        return centroid,bbox,output_image
    else:
        return None

4.建模

不想再安装其它的python包了,就基于numpy写的LineRegression。

class LinearRegression:
    def __init__(self):
        self.theta = None

    def fit(self, X, y):
        """
        训练线性回归模型

        参数:
        X:自变量数据,形状为 (m, n),其中 m 是样本数量,n 是特征数量
        y:因变量数据,形状为 (m, 1)
        """
        # 在 X 前面加一列1,以便于计算截距项
        X_b = np.c_[np.ones((X.shape[0], 1)), X]

        # 使用正规方程求解回归系数
        self.theta = np.linalg.inv(X_b.T @ X_b) @ X_b.T @ y

    def predict(self, X):
        """
        对新样本进行预测

        参数:
        X:自变量数据,形状为 (m, n),其中 m 是样本数量,n 是特征数量

        返回值:
        y_pred:预测的因变量数据,形状为 (m, 1)
        """
        if self.theta is None:
            raise ValueError("模型未经过训练,请先调用 fit 方法")

        # 在 X 前面加一列1,以便于计算截距项
        X_b = np.c_[np.ones((X.shape[0], 1)), X]

        # 使用训练得到的回归系数进行预测
        y_pred = X_b @ self.theta

        return y_pred

建模效果
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

5.全部代码

项目地址:https://gitee.com/zhang_jie_sc/auto-focus

import re
import cv2
import numpy as np
import os

from matplotlib import pyplot as plt


def get_box(image):
    # 将图像转换为灰度图像
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    # 应用高斯模糊来减少噪声
    blurred = cv2.GaussianBlur(gray, (5, 5), 0)
    max_val = np.max(blurred)
    _, binary = cv2.threshold(blurred, max_val/2, 255, cv2.THRESH_BINARY)
    # 形态学开运算去除噪声
    kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5))
    opened = cv2.morphologyEx(binary, cv2.MORPH_OPEN, kernel)
    # 找到轮廓
    contours, _ = cv2.findContours(opened, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    # 如果找到轮廓,计算质心
    if contours:
        largest_contour = max(contours, key=cv2.contourArea)
        M = cv2.moments(largest_contour)
        if M["m00"] != 0:
            cx = int(M["m10"] / M["m00"])
            cy = int(M["m01"] / M["m00"])
        else:
            cx, cy = 0, 0
        centroid = (cx, cy)
        # 计算边界框
        x, y, w, h = cv2.boundingRect(largest_contour)
        p=10
        bbox = (x-p, y-p, w+2*p, h+2*p)
        # 在图像上绘制质心和边界框
        output_image = image.copy()
        cv2.circle(output_image, centroid, 5, (0, 255, 0), -1)
        x,y,w,h=bbox
        cv2.rectangle(output_image, (x, y), (x + w, y + h), (0, 255, 0), 2)
        print(f"亮点的中心位置: {centroid},亮点的边界框: {bbox}")
        return centroid,bbox,output_image
    else:
        return None

def get_files(dir):
    img_path_list = [f for f in os.listdir(dir) if
                     f.startswith('Point') and f.endswith('.jpg')]  # 获取该文件夹中所有jpg格式的图像
    val_list=[]
    for p in img_path_list:
        # 使用正则表达式匹配_后.前的0或0.5
        match = re.search(r'_(\d+(\.\d+)?)\.', p)
        if match:
            val=match.group(1)
            val_list.append(float(val))
        else:
            raise ValueError('{0}文件名错误,无法提取位置i学那些'.format(p))
    return img_path_list,val_list

def merge_intersecting_boxes(boxes):
    merged_boxes = []

    # 计算包含所有框的大框
    x_min = min(box[0] for box in boxes)
    y_min = min(box[1] for box in boxes)
    x_max = max(box[0] + box[2] for box in boxes)
    y_max = max(box[1] + box[3] for box in boxes)
    big_box = (x_min, y_min, x_max - x_min, y_max - y_min)

    # 返回大框和空的合并框列表
    return big_box, merged_boxes

def r2_score(y_true,y_pred):
    # 计算相关系数
    corr = np.corrcoef(y_true, y_pred)[0, 1]
    # 计算 R 方值
    r2 = corr ** 2
    return r2

def plot_image_and_r2_zzz(image, x, y,r2,theta):
    # 将 BGR 格式转换为 RGB 格式
    image = cv2.cvtColor(image.copy(), cv2.COLOR_BGR2RGB)
    # 创建一个图形和两个子图
    fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(10, 5), gridspec_kw={'top': 0.85})
    # 设置窗口标题方式二
    fig.canvas.manager.window.title("建模结果")
    # 在第一个子图中显示图片
    ax1.imshow(image)
    ax1.axis('off')
    ax1.set_title('Box')

    # 在第二个子图中显示拟合直线
    ax2.plot(x, y, 'o', label='Data')
    ax2.plot(x, x, label='Fitted Line')
    # 将每个数字转换为字符串,保留五位小数
    theta_str = "(k1={:.4f}, k2={:.4f}, b={:.4f})".format(*theta)
    ax2.set_title('Fitted Line (theta={}, r2={:.5f})'.format(theta_str,r2))
    # 添加轴标签
    ax2.set_xlabel('y_true')
    ax2.set_ylabel('y_pred')
    ax2.legend()
    # 显示图形
    plt.tight_layout()
    plt.show()

class LinearRegression:
    def __init__(self):
        self.theta = None

    def fit(self, X, y):
        """
        训练线性回归模型

        参数:
        X:自变量数据,形状为 (m, n),其中 m 是样本数量,n 是特征数量
        y:因变量数据,形状为 (m, 1)
        """
        # 在 X 前面加一列1,以便于计算截距项
        X_b = np.c_[np.ones((X.shape[0], 1)), X]

        # 使用正规方程求解回归系数
        self.theta = np.linalg.inv(X_b.T @ X_b) @ X_b.T @ y

    def predict(self, X):
        """
        对新样本进行预测

        参数:
        X:自变量数据,形状为 (m, n),其中 m 是样本数量,n 是特征数量

        返回值:
        y_pred:预测的因变量数据,形状为 (m, 1)
        """
        if self.theta is None:
            raise ValueError("模型未经过训练,请先调用 fit 方法")

        # 在 X 前面加一列1,以便于计算截距项
        X_b = np.c_[np.ones((X.shape[0], 1)), X]

        # 使用训练得到的回归系数进行预测
        y_pred = X_b @ self.theta

        return y_pred

if __name__=='__main__':
    file_dir="./20240531_113524"
    img_path_list, locs = get_files(file_dir)
    coors = []
    boxs = []
    for i, image_name in enumerate(img_path_list):  # 逐一读取图像
        item = cv2.imread(os.path.join(file_dir, image_name))
        cneter, box, _ = get_box(item)
        coors.append(list(cneter))
        boxs.append(box)
    merge_box, _ = merge_intersecting_boxes(boxs)
    # 使用线性回归拟合数据
    matx = np.array(coors)
    arr_x = matx[:, 0]
    reg = LinearRegression()
    reg.fit(matx, locs)
    y_true = np.array(locs)
    y_pred = reg.predict(matx)
    r2 = r2_score(y_true, y_pred)
    # 输出 R^2 值
    draw_img = cv2.imread(os.path.join(file_dir, img_path_list[0]), cv2.IMREAD_COLOR)
    x, y, w, h = merge_box
    cv2.rectangle(draw_img, (x, y), (x + w, y + h), (0, 255, 0), 2)
    plot_image_and_r2_zzz(draw_img, y_true, y_pred, r2, reg.theta)

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

uView 2.0:uni-app生态的利剑出鞘,引领UI框架新纪元

引言 随着移动互联网的快速发展,跨平台应用开发成为了开发者们关注的焦点。uni-app,一个基于Vue.js的跨平台应用开发框架,因其高效、易用的特性而广受欢迎。在uni-app的生态系统中,UI框架的选择对于开发者而言至关重要。今天&…

AI 编程探索- iOS动态标签控件

需求分析: 标签根据文字长度,自适应标签居中显示扩展 超过内容显示范围,需要换行显示,且保持居中显示 AI实现过程 提问: 回答: import UIKit import SnapKitclass DynamicLabelsContainerView: UIView…

docker 多网卡指定网卡出网

前言 宿主机中有多个网卡 ens160 192.168.4.23/20 内网通信用 ens192 10.31.116.128/24 出公网访问-1 ens193 10.31.116.128/24 出公网访问-2 现在需要不同容器中不同出网访问,举例 容器1 192.168.0.1/20 网段走宿主机 ens160网卡,否则全部走ens192 网…

CAS自旋解析

CAS全称CompareAndSwap(比较并交换),是cpu的指令,调用时不涉及上下文的切换。Java中属于乐观锁的一种,具体流程如下图: 具体的实现使用的是Unsafe类去调用native修饰的compareAndSwap方法,4个字段分别是对象实例&#…

Shell编程实战

脚本编程步骤 脚本编程一般分为以下几个步骤: 需求分析:根据系统管理的需求,分析脚本要实现的功能、功能实现的层次、实现的命令与语句等; 命令测试:将要用到的命令逐个进行测试,以决定使用的选项、要设置的变量等: 脚本编程:将测试好的命令写入到脚本文…

庆祝东兴市金顺心贸易有限公司代理越南三原竹系列产品五周年

🎉庆祝金顺心贸易代理越南三原竹系列产品五周年!这五年,我们共同成长,每一份产品都承载着越南的美味与匠心。感恩有你们,未来的路,我们继续携手前行,品味更多美好!🥢&…

电子名片小程序源码系统 前后端分离 带完整的安装代码包以及搭建教程

系统概述 电子名片小程序源码系统是一款基于前后端分离架构的综合性平台,旨在为用户提供一个集销售名片和企业商城于一体的解决方案。该系统采用先进的技术手段,实现了个性化名片设计、便捷的销售功能、企业商城模块等一系列实用功能。同时,…

vue-cli 搭建项目

创建 router 目录 在一个.js文件中添加 打开外部命令 打开外部命令后,在指令栏输入npm i vue-router3.5.3 ,等待下载 下载完成后 在 main.js 中配置路由 输入这些后,基本的配置就实现了 最后进行测试,验证是否配置 或者打开外部命…

springcloud第4季 分布式事务seata作用服务搭建1

一 seata作用 1.1 seata简介 1.seata是一款解决分布式事务的解决方案,致力于在微服务架构下提供高性能和简单易用的分布式事务服务。 1.2 seata的术语 一个中心:全局事务id,xid,在调用服务链路的上下文中进行传播。TC(Transa…

iPhone怎么恢复删除的数据?几款顶级iPhone数据恢复软件

从iOS设备恢复数据。 对于任何数据恢复软件来说,从iOS设备恢复数据都是一项复杂的任务,因为Apple已将众多数据保护技术集成到现代iPhone和iPad中。其中包括硬件加密和文件级加密。iOS 上已删除的数据只能通过取证文件工件搜索来找到,例如分析…

最新扣子(Coze)实战案例:图像流工具之空间风格化,完全免费教程

🧙‍♂️ 大家好,我是斜杠君,手把手教你搭建扣子AI应用。 📜 本教程是《AI应用开发系列教程之扣子(Coze)实战教程》,完全免费学习。 👀 关注斜杠君,可获取完整版教程。👍&#x1f3f…

EHS,制造业安全绿色生产的隐形守护神

当我们提到EHS,可能很多人会稍感陌生,毕竟它不是一个日常生活中经常提及的词汇。但实际上,EHS在我们的生活和工作中扮演着极其重要的角色,尤其对制造业而言更是可持续发展经营管理的重中之重。 一、EHS是什么意思? E…

vue项目内网部署流程

由于第一次部署,也是第一次自己用 Nginx , 百度了很久,没有看到想看的步骤,所以作此文以记录,也是给像我一样的人一个大概方向。 注:windows系统 1、首先要弄好jar包的运行环境。 安装jdk 详细安装过程引用 jdk的完整…

码农:如何快速融入团队

问题: 码农如何快速融入团队? 记住一个标准:能干事、能抗事。 总结一个字: 靠谱。 适用范围:新手码农、老司机码农、测试、DBA、运维、产品经理、项目经理、架构师、技术专家、。。。。适用于任何行业的打工者。 下面要…

伙伴活动推荐丨当 RTC 遇上 AI ,大模型创新应用星城论道

近年来,音视频技术已成为推动在线新经济和企业数字化转型的重要力量。作为中部互联网产业高地,湖南长沙亦将音视频产业视为战略性新兴产业重点布局。 2024年7月6日,声网联合 CSDN 在湖南长沙举办声网城市沙龙,以“当 RTC 遇上 AI…

VMware Workstation环境下,DHCP服务的安装配置,用ubuntu来测试

需求说明: 某企业信息中心计划使用IP地址17216.11.0用于虚拟网络测试,注册域名为xyz.net.cn.并将172.16.11.2作为主域名的服务器(DNS服务器)的IP地址,将172.16.11.3分配给虚拟网络测试的DHCP服务器,将172.16.11.4分配给虚拟网络测试的web服务器,将172.16.11.5分配给FTP服务器…

Matlab/simulink三段式电流保护

电流1段仿真波形如下所示 电流2段仿真波形如下所示 电流3段仿真波形如下所示

SSL证书在网站访问中的核心作用及快速申请指南

在当今的互联网时代,数据安全与用户隐私保护成为了网站运营不可或缺的一部分。SSL证书作为一种重要的网络安全协议,它在网站访问中扮演着至关重要的角色,主要体现在以下几个方面: 一、加密通信内容:SSL证书通过建立安…

系统进程与计划任务

目录 系统进程 ps命令 top命令 pgrep命令 pstree命令 jobs命令 计划任务 一次性计划任务at 周期性计划任务crontab 系统进程 我们系统在打开的一瞬间就会加载很多进程,那么我们该如何查看这些进程和管理这些进程呢? ps命令 常用的参数 -a&am…

Linux创建目录——mkdir命令,du命令,touch用法,创建tree拓扑图

1. mkdir 命令 格式 mkdir - 参数 路径 / 目录名 参数 -p :快速创建多级目录(递归目录) -v :显示创建目录的详细过程 例: [rootserver ~] # mkdir t1 [rootserver ~] # mkdir t2 t3 t4 [rootserver ~] # mk…