python opencv卡尺测量边缘距离

2023-11-18

opencv 卡尺法 测量边缘距离
参考来源 :https://github.com/crackwitz/metrology-demo



前言
一、测量方法
二、测量步骤
1.获取直线的像素
2.高斯滤波平滑曲线
3.计算跳变幅度值
4.计算距离值
5.显示和保存图片
总结
前言
halcon中有按照直线找边缘测量距离的工具,但是opencv中没有类似的工具可以直接实现该测量方式,参考网上的实现方式,可以实现。

测量的效果贴图


一、测量方法
根据测量线的两个端点,获取直线的像素,然后进行滤波过滤噪点,计算跳变幅度,获取最大最小值的位置,计算距离

二、测量步骤
1.获取直线的像素
将直线的通过仿射变换转成1D的图像,可以采用亚像素的算法,先获取仿射变换的矩阵

代码如下(示例):

def build_transform(p0, p1, stride=None, nsamples=None):
    "builds an affine transform with x+ along defined line"
    # use one of stride (in pixels) or nsamples (absolute value)
    (x0, y0) = p0
    (x1, y1) = p1
    dx = x1 - x0
    dy = y1 - y0
    length = np.hypot(dx, dy)
    if nsamples is not None:
        #stride = length / nsamples
        factor = 1 / nsamples
    else:
        if stride is None:
            stride = 1.0
        factor = stride / length
        nsamples = int(round(length / stride))
    # map: src <- dst (use WARP_INVERSE_MAP flag for warpAffine)
    H = np.eye(3, dtype=np.float64) # homography
    H[0:2, 0] = (dx, dy) # x unit vector
    H[0:2, 1] = (-dy, dx) # y unit vector is x rotated by 90 degrees
    x=H[0:2, 0:2]
    H[0:2, 0:2] *= factor
    H[0:2, 2] = (x0, y0) # translate onto starting point
    # take affine part of homography
    assert np.isclose(a=H[2], b=(0,0,1)).all() # we didn't touch those but let's better check
    A = H[0:2, :]
    return (nsamples, A)



然后再采用变换的方法获取图像的像素值

def sample_opencv(im, M, nsamples):
    # use transform to get samples
    # available: INTER_{NEAREST,LINEAR,AREA,CUBIC,LANCOS4)
    samples = cv.warpAffine(im, M=M, dsize=(nsamples, 1), flags=cv.WARP_INVERSE_MAP | cv.INTER_CUBIC )
    # flatten row vector
    samples.shape = (-1,)
    # INTER_CUBIC seems to break down beyond 1/32 sampling (discretizes).
    # there might be fixed point algorithms at work
    return samples



2.高斯滤波平滑曲线
samples = scipy.ndimage.gaussian_filter1d(samples, sigma=args.sigma / args.stride)
1
3.计算跳变幅度值
# off-by-half in position because for values [0,1,1,0] this returns [+1,0,-1]
gradient = np.diff(samples) / args.stride

4.计算距离值
    i_falling = np.argmin(gradient) # in samples
    i_rising = np.argmax(gradient) # in samples
    distance = np.abs(i_rising - i_falling) * args.stride # in pixels

完整代码:
 

#!/usr/bin/env python3

import sys
import argparse
import numpy as np
import cv2
import scipy.ndimage


### "business logic" ###################################################

def build_transform(p0, p1, stride=None, nsamples=None):
    "builds an affine transform with x+ along defined line"
    # use one of stride (in pixels) or nsamples (absolute value)

    (x0, y0) = p0
    (x1, y1) = p1

    dx = x1 - x0
    dy = y1 - y0

    length = np.hypot(dx, dy)

    if nsamples is not None:
        # stride = length / nsamples
        factor = 1 / nsamples

    else:
        if stride is None:
            stride = 1.0

        factor = stride / length
        nsamples = int(round(length / stride))

    # map: src <- dst (use WARP_INVERSE_MAP flag for warpAffine)
    H = np.eye(3, dtype=np.float64)  # homography

    H[0:2, 0] = (dx, dy)  # x unit vector
    H[0:2, 1] = (-dy, dx)  # y unit vector is x rotated by 90 degrees

    H[0:2, 0:2] *= factor

    H[0:2, 2] = (x0, y0)  # translate onto starting point

    # take affine part of homography
    assert np.isclose(a=H[2], b=(0, 0, 1)).all()  # we didn't touch those but let's better check
    A = H[0:2, :]

    return (nsamples, A)


def sample_opencv(im, M, nsamples):
    # use transform to get samples
    # available: INTER_{NEAREST,LINEAR,AREA,CUBIC,LANCOS4)
    samples = cv2.warpAffine(im, M=M, dsize=(nsamples, 1), flags=cv2.WARP_INVERSE_MAP | cv2.INTER_CUBIC)

    # flatten row vector
    samples.shape = (-1,)

    # INTER_CUBIC seems to break down beyond 1/32 sampling (discretizes).
    # there might be fixed point algorithms at work

    return samples


def sample_scipy(im, M, nsamples):
    # coordinates to this function are (i,j) = (y,x)
    # I could permute first and second rows+columns of M, or transpose input+output
    Mp = M.copy()
    Mp[(0, 1), :] = Mp[(1, 0), :]  # permute rows
    Mp[:, (0, 1)] = Mp[:, (1, 0)]  # permute columns

    samples = scipy.ndimage.interpolation.affine_transform(input=im, matrix=Mp, output_shape=(1, nsamples), order=2,
        # 1: linear (C0, f' is piecewise constant), 2: C1 (f' is piecewise linear), 3: C2... https://en.wikipedia.org/wiki/Smoothness
        mode='nearest'  # border handling
    )

    # flatten row vector
    samples.shape = (-1,)

    return samples


### command line parsing utility functions #############################

def parse_linestr(arg):
    pieces = arg.split(",")
    pieces = [float(el) for el in pieces]
    x0, y0, x1, y1 = pieces
    return ((x0, y0), (x1, y1))


def parse_bool(arg):
    if isinstance(arg, bool):
        return arg
    if arg.lower() in ('yes', 'true', 't', 'y', '1'):
        return True
    elif arg.lower() in ('no', 'false', 'f', 'n', '0'):
        return False
    else:
        raise argparse.ArgumentTypeError(f'Boolean value expected, got {arg!r} instead')


def parse_float(arg):
    import ast

    if '/' in arg:
        num, denom = arg.split('/', 1)
        num = ast.literal_eval(num)
        denom = ast.literal_eval(denom)
        result = num / denom

    else:
        result = ast.literal_eval(arg)

    return result


### main... ############################################################

if __name__ == '__main__':
    # command line argument parsing
    # change defaults here

    parser = argparse.ArgumentParser()
    parser.add_argument("--picture", dest="fname", metavar="PATH", type=str, default="dish-1.jpg", help="path to picture file")
    parser.add_argument("--invert", type=parse_bool, default=True, metavar="BOOL", help="invert picture (cosmetic; distance between gradient extrema is absolute)")
    parser.add_argument("--line", type=parse_linestr, default=((1320, 2500), (1320, 2100)), metavar="X0,Y0,X1,Y1", help="line to sample on")
    parser.add_argument("--stride", type=parse_float, default=1 / 4, metavar="PX", help="stride in pixels to sample along line, fractions supported")
    parser.add_argument("--method", type=lambda s: s.lower(), default="opencv", help="sampling methods: SciPy (slower, smoother, default), OpenCV (faster, less smooth)")
    parser.add_argument("--sigma", type=float, default=2.0, metavar="PX", help="sigma for gaussian lowpass on sampled signal, before gradient is calculated")
    parser.add_argument("--verbose", type=parse_bool, default=True, metavar="BOOL", help="chatty or not")
    parser.add_argument("--display", type=parse_bool, default=True, metavar="BOOL", help="draw some plots")
    parser.add_argument("--saveplot", type=str, default="plot.png", metavar="PATH", help="save a picture (use '--saveplot=' to disable)")
    args = parser.parse_args()

    ########## here be dragons ##########

    if args.stride > 1:
        print(f"WARNING: stride should be <= 1, is {args.stride}")

    stride_decimals = max(0, int(np.ceil(-np.log10(args.stride))))

    if args.verbose: print("loading picture...", end=" ", flush=True)
    im = cv2.imread(args.fname, cv2.IMREAD_GRAYSCALE)
    imh, imw = im.shape[:2]
    if args.invert:
        im = 255 - im  # invert
    im = im.astype(np.float32)  # * np.float32(1/255)
    if args.verbose: print("done")

    # build transform
    p0, p1 = args.line
    nsamples, M = build_transform(p0, p1, stride=args.stride)

    if args.verbose: print(f"taking {nsamples} samples along line {p0} -> {p1}...", end=" ", flush=True)

    # pick one
    if args.method == 'opencv':
        samples = sample_opencv(im, M, nsamples)  # does "normal" cubic (4x4 support points, continuous first derivative)
    elif args.method == 'scipy':
        samples = sample_scipy(im, M, nsamples)  # does some fancy "cubic" with continuous higher derivatives
    else:
        assert False, "method needs to be opencv or scipy"

    if args.verbose: print("sampling done")

    # smoothing to remove noise
    if args.sigma > 0:
        if args.verbose: print(f"lowpass filtering with sigma = {args.sigma} px...", end=" ", flush=True)
        samples = scipy.ndimage.gaussian_filter1d(samples, sigma=args.sigma / args.stride)
        if args.verbose: print("done")

    # off-by-half in position because for values [0,1,1,0] this returns [+1,0,-1]
    gradient = np.diff(samples) / args.stride

    i_falling = np.argmin(gradient)  # in samples
    i_rising = np.argmax(gradient)  # in samples

    distance = np.abs(i_rising - i_falling) * args.stride  # in pixels

    if args.verbose:
        print(f"distance: {distance:.{stride_decimals}f} pixels")
    else:
        print(distance)

    # this was the result. algorithm is done.
    # now follows displaying code

    if args.display:
        gradient *= 255 / np.abs(gradient).max()

        # plot signal
        plot = cv2.plot.Plot2d_create(np.arange(nsamples, dtype=np.float64), samples.astype(np.float64))
        plot.setMinY(256 + 32)
        plot.setMaxY(-32)
        plot.setMinX(0)
        plot.setMaxX(nsamples)
        plot.setGridLinesNumber(5)
        plot.setShowText(False)  # callout for specific point, setPointIdxToPrint(index)
        plot.setPlotGridColor((64,) * 3)
        canvas1 = plot.render()
        # plot gradient
        plot = cv2.plot.Plot2d_create(np.arange(nsamples - 1) + 0.5, gradient.astype(np.float64))
        plot.setMinY(256 + 64)
        plot.setMaxY(-256 - 64)
        plot.setMinX(0)
        plot.setMaxX(nsamples)
        plot.setGridLinesNumber(5)
        plot.setShowText(False)  # callout for specific point, setPointIdxToPrint(index)
        plot.setPlotGridColor((64,) * 3)
        canvas2 = plot.render()

        # arrange vertically
        canvas = np.vstack([canvas1, canvas2])  # 600 wide, 800 tall

        # draw lines at edges (largest gradients)
        # plots are 600x400 pixels... and there's no way to plot multiple or plot lines in "plot space"
        px_falling = int(600 * (i_falling + 0.5) / nsamples)
        px_rising = int(600 * (i_rising + 0.5) / nsamples)
        cv2.line(canvas, (px_falling, 0), (px_falling, 400 * 2), color=(255, 0, 0))
        cv2.line(canvas, (px_rising, 0), (px_rising, 400 * 2), color=(255, 0, 0))

        # some text to describe the picture
        cv2.putText(canvas, f"{nsamples * args.stride:.0f} px, {p0} -> {p1}", (10, 350), cv2.FONT_HERSHEY_SIMPLEX, 0.75, (255, 255, 255), thickness=1, lineType=cv2.LINE_AA)

        cv2.putText(canvas, f"stride {args.stride} px, {nsamples} samples, sigma {args.sigma}", (10, 350 + 35), cv2.FONT_HERSHEY_SIMPLEX, 0.75, (255, 255, 255), thickness=1, lineType=cv2.LINE_AA)

        cv2.putText(canvas, f"distance: {distance:.{stride_decimals}f} px", (10, 350 + 70), cv2.FONT_HERSHEY_SIMPLEX, 0.75, (255, 255, 255), thickness=1, lineType=cv2.LINE_AA)

        # save for posterity
        if args.saveplot:
            cv2.imwrite(args.saveplot, canvas)

        if args.display:
            cv2.imshow("plot", canvas)

            if args.verbose:
                print("press Ctrl+C in the terminal, or press any key while the imshow() window is focused")

            while True:
                keycode = cv2.waitKey(100)

                if keycode == -1:
                    continue

                # some key...

                if args.verbose:
                    print(f"keycode: {keycode}")

                cv2.destroyAllWindows()
                break



总结
提示:显示的程序包含了opencv pilo,这个需要引入opencv-contrib-python模块:

原文链接:https://blog.csdn.net/hong3731/article/details/119649418

使用过程报错:

 module 'cv2' has no attribute 'plot'

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

python opencv卡尺测量边缘距离 的相关文章

  • 递归 lambda 表达式可能吗?

    我正在尝试编写一个调用自身的 lambda 表达式 但我似乎找不到任何语法 或者即使它是可能的 本质上我想将以下函数传输到以下 lambda 表达式中 我意识到这是一个愚蠢的应用程序 它只是添加 但我正在探索可以在 python 中使用 l
  • Sublime Text 插件开发中的全局 Python 包

    一 总结 我不知道 Sublime Text 插件开发人员如何使用 Sublime Text 查找全局 Python 包 而不是 Sublime Text 目录的 Python 包 Sublime Text使用自己的Python环境 而不是
  • 工作日重新订购 Pandas 系列

    使用 Pandas 我提取了一个 CSV 文件 然后创建了一系列数据来找出一周中哪几天崩溃最多 crashes by day bc DAY OF WEEK value counts 然后我将其绘制出来 但当然它按照与该系列相同的排名顺序绘制
  • 如果未引发异常,则通过 Python 单元测试

    在Python中unittest框架 是否有一种方法可以在未引发异常的情况下通过单元测试 否则会因 AssertRaise 而失败 如果我正确理解你的问题 你could做这样的事情 def test does not raise on va
  • Pandas:如果单元格包含特定文本则删除行

    pandas 中的这段代码不起作用 如果该列包含提供的任何文本 数字 我希望它删除该行 目前 我只能在单元格与我的代码中传递的确切文本匹配时才能使其工作 因为它只删除显示 Fin 的单元格不是金融或金融 df2 df df Team Fin
  • 在没有模型的情况下将自定义页面添加到 django admin

    我正在尝试在没有模型关联的情况下向管理员添加自定义页面 这就是我迄今为止所取得的成就 class MyCustomAdmin AdminSite def get urls self from django conf urls import
  • 没有名为 StringIO 的模块

    我有Python 3 6 我想从另一个名为 run py 的 python 文件执行名为 operation py 的 python 文件 In operation py I do from cStringIO import StringI
  • 如何使用文本相似性删除 pandas 数据框中相似(不重复)的行?

    我有数千个数据 这些数据可能相似也可能不相似 使用 python 的默认函数 drop duplicates 并没有真正的帮助 因为它们只检测相似的数据 例如 如果我的数据包含类似以下内容怎么办 嗨 早上好 嗨 早上好 Python 不会将
  • 将 OpenCV Mat 转换为数组(可能是 NSArray)

    我的 C C 技能很生疏 OpenCV 的文档也相当晦涩难懂 有没有办法获得cv Mat data属性转换为数组 NSArray 我想将其序列化为 JSON 我知道我可以使用 FileStorage 实用程序转换为 YAML XML 但这不
  • Django 的 request.FILES 出现 UnicodeDecodeError

    我在视图调用中有以下代码 def view request body u for filename f in request FILES items body body Filename filename n f read n 在某些情况下
  • Python新式类和__subclasses__函数

    有人可以向我解释为什么这有效 在 Python 2 5 中 class Foo object pass class Bar Foo pass print Foo subclasses 但这不是 class Foo pass class Ba
  • 使用“默认”环境变量启动新的子进程

    我正在编写一个构建脚本来解析依赖的共享库 及其共享库等 这些共享库在正常情况下是不存在的PATH环境变量 为了使构建过程正常工作 让编译器找到这些库 PATH已更改为包含这些库的目录 构建过程是这样的 加载器脚本 更改 PATH gt 基于
  • Mac OSX 10.6 上的 Python mysqldb 不工作

    我正在使用 Python 2 7 并尝试让 Django 项目在 MySQL 后端运行 我已经下载了 mysqldb 并按照此处的指南进行操作 http cd34 com blog programming python mysql pyth
  • SocketIO + Flask 检测断开连接

    我在这里有一个不同的问题 但意识到它可以简化为 如何检测客户端何时从页面断开连接 关闭其页面或单击链接 换句话说 套接字连接关闭 我想制作一个带有更新用户列表的聊天应用程序 并且我在 Python 上使用 Flask 当用户连接时 浏览器发
  • 动态过滤 pandas 数据框

    我正在尝试使用三列的阈值来过滤 pandas 数据框 import pandas as pd df pd DataFrame A 6 2 10 5 3 B 2 5 3 2 6 C 5 2 1 8 2 df df loc df A gt 0
  • 当数据库不是 Django 模型时,是否可以使用数据库中的表?

    是否可以从应用程序数据库中的表获取查询集 该表不是应用程序中的模型 如果我有一个不是名为 cartable 的模型的表 从概念上讲 我想这样做 myqueryset cartable objects all 有没有相对简单的方法来做到这一点
  • 深度估计的准确性 - 立体视觉

    我正在研究立体视觉 我对这个问题的深度估计的准确性感兴趣 这取决于几个因素 例如 适当的立体校准 旋转 平移和失真提取 图像分辨率 相机和镜头质量 失真越小 色彩捕捉正确 两个图像之间的匹配特征 假设我们没有低成本的相机和镜头 没有廉价的网
  • Django Admin 中的反向内联

    我有以下 2 个型号 现在我需要将模型 A 内联到模型 B 的页面上 模型 py class A models Model name models CharField max length 50 class B models Model n
  • 如何与其他用户一起使用 pyenv?

    如何与其他用户一起使用 pyenv 例如 如果我在用户 test 的环境中安装了 pyenv 则当我以 test 身份登录时可以使用 pyenv 但是 当我以其他用户 例如 root 身份登录时如何使用 pyenv 即使你这么做了 我也会s
  • 双击打开 ipython 笔记本

    相关文章 通过双击 osx 打开 ipython 笔记本 https stackoverflow com questions 16158893 open an ipython notebook via double click on osx

随机推荐

  • vue项目中使用pdf.js预览pdf文件

    项目要求需要预览pdf文件 网上找了很久 大多数都是推荐pdf js 自己起了解了一下 最后决定用pdf js 但是发现 在vue中使用这个很少 所以我就写这一篇帮助一下vue使用pdfjs的朋友 其实 这和前端框架无关的 直接使用pdf
  • 网络模拟(Network_simulation)

    https en wikipedia org wiki Network simulation In computer network research network simulation is a technique whereby a
  • CocoaPods创建管理类库的步骤

    用到的命令 1 git clone https github com jackLeong MySDK git 克隆一份远程仓库 2 git add fileName 把文件纳入git的缓存区 可使用 代替目录下的所有文件 3 git sta
  • Android 13 - Media框架(5)- NuPlayerDriver

    前面的章节中我们了解到上层调用setDataSource后 MediaPlayerService Client IMediaPlayer 会调用MediaPlayerFactory创建MediaPlayerBase Android为我们提供
  • 第3讲 Camera Sensor 数据流

    Camera Sensor Block Camera Sensor Output Format raw8 一个像素点用8bit来表示 范围0 256 raw10 一个像素点用10bit来表示 范围0 1024 raw12 一个像素点用12b
  • 数字化转型需要解决的五大问题

    更多专业文档请访问 www itilzj com 数字化 数字中国被列为十四五规划的核心之一 数字经济席卷各行各业 新一轮的商业马拉松枪声已经响起 无形的革新重塑着商业大环境 也赋予了我们丰富的想象空间 数字化转型需要解决的问题包括 1 解
  • Unity --- 文本的使用

    1 先讲讲legacy 旧版 的文本text 在一个画布上创建一个文本 这个文本也是一个游戏物体 且是画布的子物体 上面这个就是文本的inspector界面 在字体参数处我们可以点开框框选择新的字体资源 前提是我们有导入这个字体资源 行间距
  • MySQL之添加联合唯一索引

    一 联合唯一索引 项目中需要用到联合唯一索引 例如 有以下需求 每个人每一天只有可能产生一条记录 处了程序约定之外 数据库本身也可以设定 例如 t aa 表中有aa bb两个字段 如果不希望有2条一模一样的记录 即 aa字段的值可以重复 b
  • visio2016上下标

    之前的visio版本是有上下标的快捷按钮的 但是在visio2016中没有了 需要选中文字之后在 字体 选项卡 点击右下角的小箭头 在 位置 中选择上下标 或者使用快捷键 选中要成为上标的文字 ctrl shift 选中要成为下标的文字 c
  • Windows下修改VSCode工作区存储目录workspaceStorage

    VSCode会将每个工作区的一些配置 扩展 缓存等保存在一个默认的目录 在Windows下 此默认目录为 AppData Code User workspaceStorage 当存在多个工作空间或扩展时 需要使用大量的磁盘空间 而VSCod
  • EMC经典问答85问(75-77问)

    75 某个手持测试产品 可以电池供电 同时也可以采取外置适配器供电方式 适配器单独带负载辐射发射 RE 测试可以通过 手持产品在电池供电情况下辐射发射 RE 也可以通过 并且余量都比较大 但是在带外置适配器的情况下 却在 160M 频率左右
  • 23种设计模式概述

    设计模式共有23种 创建型模式5种 结构型模式7种 行为型模式11种 本章只做概念性说明 一 创建型模式 01 工厂模式 定义 又叫做静态工厂方法 定义一个用于创建对象的接口 让子类决定实例化哪一个类 工厂模式使得一个类的实例化延迟到子类
  • Unity笔记-打飞碟游戏

    目的 创建一个打飞碟游戏 简陋 游戏要求 游戏要分多个 round 飞碟数量每个 round 都是 n 个 但色彩 大小 发射位置 速度 角度 每次发射数量可以变化 游戏过程中 仅能创建 n 个飞碟 且不容许初始化阶段生成任何飞碟 飞碟线路
  • windows关闭开机自启动

    如题 有时候windows软件会开机自动打开某些软件 当你不希望它开机自启动的时候 就需要进行设置不让它开机自动 下面介绍几个方法关闭开机自启动 一 通过指令集进行设置 按下电脑上的windows R 然后输入msconfig 点击确定 可
  • 虚拟机硬盘简简单单扩容

    今天写这个博客就是为了 以后各位午饭们在使用虚拟机为虚拟机硬盘扩容时 不要再走那么多弯路了 环境介绍 虚拟机 VMware Workation 8 操作系统 windows server 2008 硬盘容量 10G windows serv
  • 4.Docker--Consul

    Docker Consul 引言 Consul是HashiCorp公司推出的开源工具 Consul由Go语言开发 部署起来非常容易 只需要极少的可执行程序和配置文件 具有绿色 轻量级的特点 Consul是分布式的 高可用的 可横向扩展的用于
  • 计算机运行慢提速小技巧,教你为Win7系统加速的五个技巧

    在我们使用电脑久了之后 不少用户就会发现系统的运行速度变慢了好多 这有可能是因为电脑使用久了之后电脑里面就会残余有一些垃圾文件 过多的垃圾文件占用了系统盘就会影响电脑的运行速度 那么用户可以选择使用第三方软件对电脑进行优化之外 还可以选择对
  • Python 接口并发测试详解

    一 接口并发测试简介 1 性能测试简介 性能测试是通过自动化测试工具模拟多种正常 峰值及异常负载条件对系统的各项性能指标进行的测试 负载测试和压力测试都属于性能测试 两者可以结合进行 通过负载测试 确定在各种工作负载下系统的性能 目标是测试
  • 发现新大陆

    csdn很久没有发博客了 当发现博客园可以进行一些骚操作之后 而CSDN又存在很多限制 相比较下 毅然选择了博客园 博主博客园首页链接地址 https www cnblogs com twq46 如果有想继续看博主的分享可以访问该链接
  • python opencv卡尺测量边缘距离

    opencv 卡尺法 测量边缘距离 参考来源 https github com crackwitz metrology demo 前言 一 测量方法 二 测量步骤 1 获取直线的像素 2 高斯滤波平滑曲线 3 计算跳变幅度值 4 计算距离值