删除并测量一条线 openCV

如何解决删除并测量一条线 openCV

链接到底部的所有图片 我在一个箭头上画了一条线来捕捉那个箭头的角度。我想然后删除箭头,只保留线,并使用 cv2.minAreaRect 来确定角度。到目前为止,除了删除原始箭头之外,我已经完成了所有工作,这会导致 cv2.minAreaRect 边界框生成的角度不正确。

真的,我只想用贯穿箭头的粗体黑线来测量角度,而不是箭头本身。如果有人有想法使这项工作或更简单的方法,请告诉我。谢谢

代码:

import numpy as np
import cv2

image = cv2.imread("templates/a_15.png")
image = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)
ret,thresh = cv2.threshold(image,127,255,0)
contours,hierarchy = cv2.findContours(thresh,1,2)
cont = contours[0]
rows,cols = image.shape[:2]
[vx,vy,x,y] = cv2.fitLine(cont,cv2.DIST_L2,0.01,0.01)
leftish = int((-x*vy/vx) + y)
rightish = int(((cols-x)*vy/vx)+y)
line = cv2.line(image,(cols-1,rightish),(0,leftish),0),10)

#  thresholding
thresh = cv2.threshold(line,cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]

# compute rotated bounding box based on all pixel values > 0 and
# use coordinates to compute a rotated bounding box of those coordinates
coordinates = np.column_stack(np.where(thresh > 0))
w = coordinates[0]
h = coordinates[1]

# Compute minimum rotated angle that contains entire image.
# Return angle values in the range [-90,0).
# As the rectangle rotates clockwise,angle values increase towards 0.
# Once 0 is reached,angle is set back to -90 degrees.

angle = cv2.minAreaRect(coordinates)[-1]

#  for angles less than -45 degrees,add 90 degrees to angle to take the inverse.
if angle < - 45:
     angle = -(90 + angle)
else:
     angle = -angle

# rotate image
(h,w) = image.shape[:2]
center = (w // 2,h // 2)  # image center
RM = cv2.getRotationMatrix2D(center,angle,1.0)
rotated = cv2.warpAffine(image,RM,(w,h),flags=cv2.INTER_CUBIC,borderMode=cv2.BORDER_REPLICATE)

#  correction angle for validation
cv2.putText(rotated,"Angle {:.2f} degrees".format(angle),(10,30),cv2.FONT_HERSHEY_DUPLEX,0.9,2)

# output
print("[INFO] angle: {:.3f}".format(angle))
cv2.imshow("Line",line)
cv2.imshow("Input",image)
cv2.imshow("Rotated",rotated)
cv2.waitKey(0)

图片

original current results goal

解决方法

这是一个可能的解决方案。主要思想是识别箭头的“尖端”和“尾部”近似一些关键点。确定两端后,您可以绘制一条连接两个点的线。知道哪个端点是尖端也是一个优势,因为这样您就可以从恒定点测量角度。

实现这一目标的方法不止一种。我选择了我过去应用过的东西:我将使用 this 方法来识别整体形状的端点。我的假设是尖端会比尾部产生更多的点。之后,我会将所有端点分为两组:tip 和 tail。我可以为此使用 K-Means,因为它将返回两个集群的平均中心。之后,我们有了可以用一条线轻松连接的尖端和尾部点。这些是步骤:

  1. 将图像转换为灰度
  2. 获取图像的骨架,将形状标准化为width像素的1
  3. 应用链接中描述的方法来获取箭头的端点
  4. 将端点划分为两个集群并使用K-Means 获得它们的中心
  5. 用一条线连接两个端点

让我们看看代码:

# imports:
import cv2
import numpy as np

# image path
path = "D://opencvImages//"
fileName = "CoXeb.png"

# Reading an image in default mode:
inputImage = cv2.imread(path + fileName)

# Grayscale conversion:
grayscaleImage = cv2.cvtColor(inputImage,cv2.COLOR_BGR2GRAY)
grayscaleImage = 255 - grayscaleImage

# Extend the borders for the skeleton:
extendedImg = cv2.copyMakeBorder(grayscaleImage,5,cv2.BORDER_CONSTANT)

# Store a deep copy of the crop for results:
grayscaleImageCopy = cv2.cvtColor(extendedImg,cv2.COLOR_GRAY2BGR)

# Compute the skeleton:
skeleton = cv2.ximgproc.thinning(extendedImg,None,1)

第一步是获取箭的骨架。正如我所说,在识别形状端点的基于卷积的方法之前需要此步骤。计算骨架将形状标准化为一个像素 width。但是,有时,如果形状太靠近“画布”边界,骨架可能会显示一些伪影。使用边框扩展可以避免这种情况。箭头的骨架是这样的:

检查该图像。如果我们确定端点,尖端将显示至少 3 个点,而尾部至少 1。这很方便 - 尖端总是比尾部有更多的点。如果我们能检测到这些点就好了……幸运的是,我们可以:

# Threshold the image so that white pixels get a value of 0 and
# black pixels a value of 10:
_,binaryImage = cv2.threshold(skeleton,128,10,cv2.THRESH_BINARY)

# Set the end-points kernel:
h = np.array([[1,1,1],[1,1]])

# Convolve the image with the kernel:
imgFiltered = cv2.filter2D(binaryImage,-1,h)

# Extract only the end-points pixels,those with
# an intensity value of 110:
binaryImage = np.where(imgFiltered == 110,255,0)
# The above operation converted the image to 32-bit float,# convert back to 8-bit uint
binaryImage = binaryImage.astype(np.uint8)

此端点检测方法 convolves 骨架具有特殊的 kernel 标识端点。它返回一个二进制图像,其中所有端点的值都为 110。在对这个中间结果进行阈值处理后,我们得到了这个图像,它代表了箭头端点:

很好,如您所见,我们可以将点分成两个集群并获得它们的集群中心。听起来像是 K-Means 的工作,因为这正是它所做的。不过,我们首先需要处理我们的数据,因为 K-Means 对 float 数据的定义形状数组进行操作:

# Find the X,Y location of all the end-points
# pixels:
Y,X = binaryImage.nonzero()

# Reshape the arrays for K-means
Y = Y.reshape(-1,1)
X = X.reshape(-1,1)
Z = np.hstack((X,Y))

# K-means operates on 32-bit float data:
floatPoints = np.float32(Z)

# Set the convergence criteria and call K-means:
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER,1.0)
ret,label,center = cv2.kmeans(floatPoints,2,criteria,cv2.KMEANS_RANDOM_CENTERS)

# Set the cluster count,find the points belonging
# to cluster 0 and cluster 1:
cluster1Count = np.count_nonzero(label)
cluster0Count = np.shape(label)[0] - cluster1Count

print("Elements of Cluster 0: "+str(cluster0Count))
print("Elements of Cluster 1: " + str(cluster1Count))

最后两行分别打印分配给 Cluster 0 Cluster 1 的端点。输出这个:

Elements of Cluster 0: 3
Elements of Cluster 1: 2

正如预期的那样 - 好吧,有点。似乎簇 0 是尖端,簇 2 是尾部!但尾巴实际上得到了 2 分。如果你仔细观察骨架的图像,你会看到尾部有一个小分叉。这就是为什么我们实际上得到了两分而不是一分。好的,让我们获取中心点并在原始输入上绘制它们:

# Look for the cluster of max number of points
# That cluster will be the tip of the arrow:
maxCluster = 0
if cluster1Count > cluster0Count:
    maxCluster = 1

# Check out the centers of each cluster:
matRows,matCols = center.shape

# Store the ordered end-points here:
orderedPoints = [None] * 2
# Let's identify and draw the two end-points
# of the arrow:
for b in range(matRows):
    # Get cluster center:
    pointX = int(center[b][0])
    pointY = int(center[b][1])
    # Get the "tip"
    if b == maxCluster:
        color = (0,255)
        orderedPoints[0] = (pointX,pointY)
    # Get the "tail"
    else:
        color = (255,0)
        orderedPoints[1] = (pointX,pointY)
    # Draw it:
    cv2.circle(grayscaleImageCopy,(pointX,pointY),3,color,-1)
    cv2.imshow("End-Points",grayscaleImageCopy)
    cv2.waitKey(0)

这是生成的图像:

尖端总是以红色绘制,而尾部以蓝色绘制。非常酷,让我们将这些点存储在 orderedPoints 列表中,并在新的“画布”中绘制最后一条线,尺寸与原始图像相同:

# Store the tip and tail points:
p0x = orderedPoints[1][0]
p0y = orderedPoints[1][1]
p1x = orderedPoints[0][0]
p1y = orderedPoints[0][1]

# Create a new "canvas" (image) using the input dimensions:
imageHeight,imageWidth = binaryImage.shape[:2]
newImage = np.zeros((imageHeight,imageWidth),np.uint8)
newImage = 255 - newImage

# Draw a line using the detected points:
(x1,y1) = orderedPoints[0]
(x2,y2) = orderedPoints[1]
lineColor = (0,0)
cv2.line(newImage,(x1,y1),(x2,y2),lineColor,thickness=2)

cv2.imshow("Detected Line",newImage)
cv2.waitKey(0)

覆盖在原始图像上的线条和仅包含线条的新图像:

,

听起来您想测量线的角度,但是因为您正在测量在原始图像中绘制的线,所以现在必须过滤掉原始图像以获得该线​​的准确测量值...用你知道端点的坐标绘制?

我猜:

  • 制作更好的过滤器?
  • 在空白图像中绘制线条并检测那里的角度?
  • 根据已知坐标确定角度?

因为你只要求一条线,所以我试过了......只是制作了一个空白图像,在上面画了你检测到的线,然后在下游使用......

blankIm = np.ones((height,width,channels),dtype=np.uint8)
blankIm.fill(255)
line = cv2.line(blankIm,(cols-1,rightish),(0,leftish),0),10)

enter image description here

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -&gt; systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping(&quot;/hires&quot;) public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate&lt;String
使用vite构建项目报错 C:\Users\ychen\work&gt;npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-
参考1 参考2 解决方案 # 点击安装源 协议选择 http:// 路径填写 mirrors.aliyun.com/centos/8.3.2011/BaseOS/x86_64/os URL类型 软件库URL 其他路径 # 版本 7 mirrors.aliyun.com/centos/7/os/x86
报错1 [root@slave1 data_mocker]# kafka-console-consumer.sh --bootstrap-server slave1:9092 --topic topic_db [2023-12-19 18:31:12,770] WARN [Consumer clie
错误1 # 重写数据 hive (edu)&gt; insert overwrite table dwd_trade_cart_add_inc &gt; select data.id, &gt; data.user_id, &gt; data.course_id, &gt; date_format(
错误1 hive (edu)&gt; insert into huanhuan values(1,&#39;haoge&#39;); Query ID = root_20240110071417_fe1517ad-3607-41f4-bdcf-d00b98ac443e Total jobs = 1
报错1:执行到如下就不执行了,没有显示Successfully registered new MBean. [root@slave1 bin]# /usr/local/software/flume-1.9.0/bin/flume-ng agent -n a1 -c /usr/local/softwa
虚拟及没有启动任何服务器查看jps会显示jps,如果没有显示任何东西 [root@slave2 ~]# jps 9647 Jps 解决方案 # 进入/tmp查看 [root@slave1 dfs]# cd /tmp [root@slave1 tmp]# ll 总用量 48 drwxr-xr-x. 2
报错1 hive&gt; show databases; OK Failed with exception java.io.IOException:java.lang.RuntimeException: Error in configuring object Time taken: 0.474 se
报错1 [root@localhost ~]# vim -bash: vim: 未找到命令 安装vim yum -y install vim* # 查看是否安装成功 [root@hadoop01 hadoop]# rpm -qa |grep vim vim-X11-7.4.629-8.el7_9.x
修改hadoop配置 vi /usr/local/software/hadoop-2.9.2/etc/hadoop/yarn-site.xml # 添加如下 &lt;configuration&gt; &lt;property&gt; &lt;name&gt;yarn.nodemanager.res