python opencv卡尺测量边缘距离
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) = p1dx = x1 - x0dy = y1 - y0length = np.hypot(dx, dy)if nsamples is not None:#stride = length / nsamplesfactor = 1 / nsampleselse:if stride is None:stride = 1.0factor = stride / lengthnsamples = int(round(length / stride))# map: src <- dst (use WARP_INVERSE_MAP flag for warpAffine)H = np.eye(3, dtype=np.float64) # homographyH[0:2, 0] = (dx, dy) # x unit vectorH[0:2, 1] = (-dy, dx) # y unit vector is x rotated by 90 degreesx=H[0:2, 0:2]H[0:2, 0:2] *= factorH[0:2, 2] = (x0, y0) # translate onto starting point# take affine part of homographyassert np.isclose(a=H[2], b=(0,0,1)).all() # we didn't touch those but let's better checkA = 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 vectorsamples.shape = (-1,)# INTER_CUBIC seems to break down beyond 1/32 sampling (discretizes).# there might be fixed point algorithms at workreturn 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 python3import 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) = p1dx = x1 - x0dy = y1 - y0length = np.hypot(dx, dy)if nsamples is not None:# stride = length / nsamplesfactor = 1 / nsampleselse:if stride is None:stride = 1.0factor = stride / lengthnsamples = int(round(length / stride))# map: src <- dst (use WARP_INVERSE_MAP flag for warpAffine)H = np.eye(3, dtype=np.float64) # homographyH[0:2, 0] = (dx, dy) # x unit vectorH[0:2, 1] = (-dy, dx) # y unit vector is x rotated by 90 degreesH[0:2, 0:2] *= factorH[0:2, 2] = (x0, y0) # translate onto starting point# take affine part of homographyassert np.isclose(a=H[2], b=(0, 0, 1)).all() # we didn't touch those but let's better checkA = 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 vectorsamples.shape = (-1,)# INTER_CUBIC seems to break down beyond 1/32 sampling (discretizes).# there might be fixed point algorithms at workreturn samplesdef 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+outputMp = M.copy()Mp[(0, 1), :] = Mp[(1, 0), :] # permute rowsMp[:, (0, 1)] = Mp[:, (1, 0)] # permute columnssamples = 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/Smoothnessmode='nearest' # border handling)# flatten row vectorsamples.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 = piecesreturn ((x0, y0), (x1, y1))def parse_bool(arg):if isinstance(arg, bool):return argif arg.lower() in ('yes', 'true', 't', 'y', '1'):return Trueelif arg.lower() in ('no', 'false', 'f', 'n', '0'):return Falseelse:raise argparse.ArgumentTypeError(f'Boolean value expected, got {arg!r} instead')def parse_float(arg):import astif '/' in arg:num, denom = arg.split('/', 1)num = ast.literal_eval(num)denom = ast.literal_eval(denom)result = num / denomelse:result = ast.literal_eval(arg)return result### main... ############################################################if __name__ == '__main__':# command line argument parsing# change defaults hereparser = 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 # invertim = im.astype(np.float32) # * np.float32(1/255)if args.verbose: print("done")# build transformp0, p1 = args.linensamples, M = build_transform(p0, p1, stride=args.stride)if args.verbose: print(f"taking {nsamples} samples along line {p0} -> {p1}...", end=" ", flush=True)# pick oneif 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 derivativeselse:assert False, "method needs to be opencv or scipy"if args.verbose: print("sampling done")# smoothing to remove noiseif 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.stridei_falling = np.argmin(gradient) # in samplesi_rising = np.argmax(gradient) # in samplesdistance = np.abs(i_rising - i_falling) * args.stride # in pixelsif args.verbose:print(f"distance: {distance:.{stride_decimals}f} pixels")else:print(distance)# this was the result. algorithm is done.# now follows displaying codeif args.display:gradient *= 255 / np.abs(gradient).max()# plot signalplot = 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 gradientplot = 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 verticallycanvas = 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 picturecv2.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 posterityif 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'
python opencv卡尺测量边缘距离相关推荐
- opencv 卡尺法 测量边缘距离
opencv 卡尺法 测量边缘距离 参考来源 :https://github.com/crackwitz/metrology-demo 文章目录 opencv 卡尺法 测量边缘距离 前言 一.测量方法 ...
- 使用Python+OpenCV+detectorn2实现社交距离检测
点击上方"小白学视觉",选择加"星标"或"置顶" 重磅干货,第一时间送达 介绍 美国和欧洲的许多城市现在都在谨慎地重新开放.人们被要求在外出 ...
- 用 Python 和 OpenCV 来测量相机到目标的距离
用 Python 和 OpenCV 来测量相机到目标的距离 http://python.jobbole.com/84378/ 几天前,一个叫 Cameron 的 PyImageSearch 读者发来邮 ...
- python+opencv实现机器视觉基础技术(2)(宽度测量,缺陷检测,医学检测
本篇博客接着讲解机器视觉的有关技术和知识.包括宽度测量,缺陷检测,医学处理. 一:宽度测量 在传统的自动化生产中,对于尺寸的测量,典型的方法就是千分尺.游标卡尺.塞尺等.而这些测量手段测量精度低 ...
- python opencv二值化图像_python opencv,读取彩色图像,提取三通道,图像二值化,提取图像的边缘...
python opencv,读取彩色图像,提取三通道,图像二值化,提取图像的边缘 python opencv 1,读取图像 2,图像变矩阵 3,图像转灰度图像 4,彩色图像是3D数组 5,灰度图像是2 ...
- Python OpenCV 边缘滤波保留(EPF)
Python OpenCV 365 天学习计划,与橡皮擦一起进入图像领域吧. Python OpenCV 基础知识铺垫 函数原型介绍 高斯双边滤波 均值迁移滤波 橡皮擦的小节 基础知识铺垫 前几篇博客 ...
- Python+OpenCV实现自动扫雷,挑战扫雷世界记录!
点击上方"小白学视觉",选择加"星标"或"置顶" 重磅干货,第一时间送达 本文转载自知乎Artrix https://zhuanlan.zh ...
- Python+OpenCV实现自动扫雷,创造属于自己的世界记录!
点击上方"小白学视觉",选择加"星标"或"置顶" 重磅干货,第一时间送达 本文转载自知乎Artrix https://zhuanlan.zh ...
- opencv 通过网络连接工业相机_单目摄像机测距(python+opencv)
点击上方"新机器视觉",选择加"星标"或"置顶" 重磅干货,第一时间送达 我的论文方向目前是使用单目摄像头实现机器人对人的跟随,首先单目摄像 ...
最新文章
- jquery学习之重要知识点
- numpy 笔记:finfo
- ai人工智能_人工智能能力问答中的人工智能不确定性
- Maven学习总结(44)——Maven构建时生命周期及其常用集成命令详解
- Linux c开发工程师的面试题,C+工程师常见的面试题总结
- php curl post登录与带cookie模拟登录随笔
- JZOJ5946. 【NOIP2018模拟11.02】时空幻境(braid)
- 【面试算法题】一维消除游戏
- Ubuntu系统实现简单c语言编程
- Spring:Spring支持的bean作用域有哪些
- 当前上下文中不存在名称 ViewBag
- Mysql 复制表结构
- maven html项目自动版本控制(时间戳) com.google.code.maven-replacer-plugin插件 前端代码自动添加版本号
- [附源码]Nodejs计算机毕业设计基于Yigo平台库房管理系统Express(程序+LW)
- SequoiaDB巨杉数据库成为唯一入选 “硅谷2016 大数据地形图”中国厂商, 企业级市场超越MongoDB等海外产品
- Fatal error loading the DB: Permission denied. Exiting.
- 基于web的员工信息管理系统
- allegro 倒圆角
- 报错https://chat.openai.com/ api/auth/ session 429怎么办
- 汉语是世界上最好的语言
热门文章
- linux c 进程间通信
- tcpdump for Android 移动端抓包
- DDoS CC 攻防
- %00截断攻击的探索
- windows平台下 c++获取 系统版本 网卡 内存 CPU 硬盘 显卡信息
- linux5.4iso,Redhat Linux5.4/5.5/5.8/6.0/6.3 ISO镜像文件下载
- CMake常见变量——Project和CMake相关信息
- 最清楚的mmap()详解与源码分析
- db2 日期英式写法_《学霸英语》16:美国人和英国人“表达日期”,差距竟然这么大!...
- html自适应pc窗口大小_自适应技术很难吗?为什么Shopyy平台将网站分为PC端和移动端...