一种确定六边形螺栓中心(形心)的Opencv方法——Python实现

前言

当前,国家电网正在研制用于更换电力金具的机器人,最基础的功能是电力螺栓的紧固和拆卸功能,这一功能的实现依赖于视觉检测和视觉定位算法,本文仅是其中的一小部分内容。

在这里插入图片描述

如何确定六边形角点和中心点(形心)

假设原始图片经过图像处理变成了中间这种黑白二值图,接下来通常要做的就是霍夫线变换求出六角螺栓的六条边线。

1、概率霍夫线变换确定直线|cv2.HoughLinesP()

在这里插入图片描述
经过霍夫线变换后可以得到大致的六条边线(上图绿色),但是由于螺栓圆角以及拍摄视角导致的螺栓边缘畸变,六条边线并不能形成完整的封闭六边形,这样就需要我们根据霍夫线变换得到的直线来推出相邻直线之间的交点。

在这里插入图片描述
有一个难点是,霍夫线变换输出的直线端点坐标矩阵并不是按照直线的顺序来排列的,比如上图的直线端点矩阵为:

[[[207 229 265 136]]

 [[107 234 196 237]]

 [[ 21 104  77  21]]

 [[101  12 187  15]]

 [[ 26 143  69 224]]

 [[221  38 265 120]]]

每一行代表一条直线(的两端点坐标),但行与行之间并不一定是相邻直线。在不画出图像的时候,没法提前预知哪条直线对应哪条直线,这样就会给求角点带来困难。
在这里插入图片描述
比如下图,line2和line3求角点(交点)时,对应的点超出了图像的范围,而且正确的角点应该是line3和line5的交点,line5和line2的交点,所以求角点之前,必须知道直线的排序。
在这里插入图片描述

2、确定直线顺序

在这里插入图片描述
霍夫线变换输出矩阵第一行对应作为line1,即确定了point1和point2,对应的line2和line3应该是line1左右两条直线,point3和point5组成line2,point4和point6组成line3,大致按上图依次找出point1,2,…12,找到对应的端点坐标,就可以找出对应的角点,比如根据point1和point2确定的line1,point3和point5确定的line2,point4和point6确定的line3就可以求出两个角点,大致按下表对应关系:

【角点1】:line1(point1,2)和line2(point3,5)交点
【角点2】:line2(point3,5)和line3(point7,9)交点
【角点3】:line3(point7,9)和line4(point11,12)交点
【角点4】:line4(point11,12)和line5(point10,8)交点
【角点5】:line5(point10,8)和line6(point6,4)交点
【角点6】:line6(point6,4)和line1(point2,1)交点

针对point1,根据距离最小原则在另外十个点中找出合理的相邻端点,可以找到point3点,point3和point5霍夫线变换输出矩阵的同一行,进而可找到point5。
而point2则是与point1是霍夫线变换输出矩阵的同一行,可以由point2找到point4,进而找到point6。
再由point5找到point7,进而找到point9,由point6找到point8,进而找到point10。
再根据point9找到point11,进而找到point12(或者根据point10找到point12,进而找到point11)。

3、确定角点(直线交点)

参考链接1(两直线求角点python程序
参考链接2(两直线求角点理论

4、确定中心点(形心)

中心点确定方法有两种,一种比较简单,一种比较复杂,公式如下:

A代表六边形面积,Cx,Cy代表中心点横纵坐标
【简易方法】:
在这里插入图片描述
【复杂方法】:
在这里插入图片描述
前面这个是计算面积的高斯面积公式(也叫鞋带公式)
在这里插入图片描述
对于复杂方法,要求凸多边形序列(原因)要按顺时针或者逆时针计算(其中有一种方式面积为负值,记得取绝对值),无论顺时针还是逆时针坐标是没问题的。

Python代码

import cv2
import numpy as np
import math

# 计算两直线交点坐标
def calc_abc_from_line_2d(x0, y0, x1, y1):
    a = y0 - y1
    b = x1 - x0
    c = x0 * y1 - x1 * y0
    return a, b, c


def get_line_cross_point(line1, line2):
    a0, b0, c0 = calc_abc_from_line_2d(*line1)
    a1, b1, c1 = calc_abc_from_line_2d(*line2)
    D = a0 * b1 - a1 * b0
    if D == 0:
        return None
    x = (b0 * c1 - b1 * c0) / D
    y = (a1 * c0 - a0 * c1) / D
    return x, y


img = cv2.imread('5.png')
cv2.imshow('origin', img)    # 显示原图
print("图片高和宽:", img.shape[0], img.shape[1])    # 图片高,宽
img = img[int(131-125):int(131+130), int(149-143):int(140+141)]    # 图片截取ROI,去掉边缘的线

gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
cv2.imshow('gray', gray)    # 显示灰度图

edges = cv2.Canny(gray, 250, 255, apertureSize=5)
cv2.imshow('edges', edges)    # 显示Canny边缘

lines = cv2.HoughLinesP(edges, 1, np.pi/180, 50, minLineLength=10, maxLineGap=40)    # 概率霍夫线变换
for line in lines:
    x1, y1, x2, y2 = line[0]
    cv2.line(img, (x1, y1), (x2, y2), (0, 255, 0), 2)    # 在图上画出直线
# print(lines)
# cv2.imshow('show', img)
# cv2.waitKey()

# 找出任一直线相邻最近的直线,并求出对应的交点坐标
# 6条直线,12个端点,哪两个点距离最近
point1_x = lines[0][0][0]
point1_y = lines[0][0][1]
point2_x = lines[0][0][2]
point2_y = lines[0][0][3]
distance1 = []
distance2 = []
distance3 = []
distance4 = []
distance5 = []
distance6 = []

if len(lines) == 6:
    # 根据point1,point2求出point3,point4,进而求出point5,point6
    for i in range(1, len(lines)):
        for j in range(0, 2):
            if j != 1:
                distance1.append((point1_x - lines[i][0][j]) * (point1_x - lines[i][0][j]) + (point1_y - lines[i][0][j+1]) * (point1_y - lines[i][0][j+1]))
                distance2.append((point2_x - lines[i][0][j]) * (point2_x - lines[i][0][j]) + (point2_y - lines[i][0][j+1]) * (point2_y - lines[i][0][j+1]))
            if j != 0:
                distance1.append((point1_x - lines[i][0][j+1]) * (point1_x - lines[i][0][j+1]) + (point1_y - lines[i][0][j+2]) * (point1_y - lines[i][0][j+2]))
                distance2.append((point2_x - lines[i][0][j+1]) * (point2_x - lines[i][0][j+1]) + (point2_y - lines[i][0][j+2]) * (point2_y - lines[i][0][j+2]))

    index1 = math.ceil((distance1.index(min(distance1)) + 1) / 2)
    index1b = math.floor((distance1.index(min(distance1)) + 1) / 2)
    index2 = math.ceil((distance2.index(min(distance2)) + 1) / 2)
    index2b = math.floor((distance2.index(min(distance2)) + 1) / 2)

    if index1 == index1b:
        point3_x = lines[index1][0][2]
        point3_y = lines[index1][0][3]
        point5_x = lines[index1][0][0]
        point5_y = lines[index1][0][1]
    else:
        point3_x = lines[index1][0][0]
        point3_y = lines[index1][0][1]
        point5_x = lines[index1][0][2]
        point5_y = lines[index1][0][3]

    if index2 == index2b:
        point4_x = lines[index2][0][2]
        point4_y = lines[index2][0][3]
        point6_x = lines[index2][0][0]
        point6_y = lines[index2][0][1]
    else:
        point4_x = lines[index2][0][0]
        point4_y = lines[index2][0][1]
        point6_x = lines[index2][0][2]
        point6_y = lines[index2][0][3]

    LINES = lines.tolist()
    del LINES[0]
    del LINES[index1-1]
    del LINES[index2-2]
    LINES = np.array(LINES)

    # 根据point5,point6求出point7,point8,进而求出point9,point10
    for i in range(0, len(LINES)):
        for j in range(0, 2):
            if j != 1:
                distance3.append((point5_x - LINES[i][0][j]) * (point5_x - LINES[i][0][j]) + (point5_y - LINES[i][0][j + 1]) * (point5_y - LINES[i][0][j + 1]))
                distance4.append((point6_x - LINES[i][0][j]) * (point6_x - LINES[i][0][j]) + (point6_y - LINES[i][0][j + 1]) * (point6_y - LINES[i][0][j + 1]))
            if j != 0:
                distance3.append((point5_x - LINES[i][0][j + 1]) * (point5_x - LINES[i][0][j + 1]) + (point5_y - LINES[i][0][j + 2]) * (point5_y - LINES[i][0][j + 2]))
                distance4.append((point6_x - LINES[i][0][j + 1]) * (point6_x - LINES[i][0][j + 1]) + (point6_y - LINES[i][0][j + 2]) * (point6_y - LINES[i][0][j + 2]))

    index3 = math.ceil((distance3.index(min(distance3)) - 1) / 2)
    index3b = math.floor((distance3.index(min(distance3)) - 1) / 2)
    index4 = math.ceil((distance4.index(min(distance4)) - 1) / 2)
    index4b = math.floor((distance4.index(min(distance4)) - 1) / 2)

    if index3 == index3b:
        point7_x = LINES[index3][0][2]
        point7_y = LINES[index3][0][3]
        point9_x = LINES[index3][0][0]
        point9_y = LINES[index3][0][1]
    else:
        point7_x = LINES[index3][0][0]
        point7_y = LINES[index3][0][1]
        point9_x = LINES[index3][0][2]
        point9_y = LINES[index3][0][3]
    if index4 == index4b:
        point8_x = LINES[index4][0][2]
        point8_y = LINES[index4][0][3]
        point10_x = LINES[index4][0][0]
        point10_y = LINES[index4][0][1]
    else:
        point8_x = LINES[index4][0][0]
        point8_y = LINES[index4][0][1]
        point10_x = LINES[index4][0][2]
        point10_y = LINES[index4][0][3]

    LINEs = LINES.tolist()
    if index3 < index4:
        del LINEs[index3]
        del LINEs[index4 - 1]
    else:
        del LINEs[index4]
        del LINEs[index3 - 1]

    LINEs = np.array(LINEs)

    # 根据point9,point10,求出point11,point12
    for i in range(0, len(LINEs)):
        for j in range(0, 2):
            if j != 1:
                distance5.append((point9_x - LINEs[i][0][j]) * (point9_x - LINEs[i][0][j]) + (point9_y - LINEs[i][0][j + 1]) * (point9_y - LINEs[i][0][j + 1]))
            if j != 0:
                distance5.append((point9_x - LINEs[i][0][j + 1]) * (point9_x - LINEs[i][0][j + 1]) + (point9_y - LINEs[i][0][j + 2]) * (point9_y - LINEs[i][0][j + 2]))

    index5 = math.ceil((distance5.index(min(distance5)) - 1) / 2)
    index5b = math.floor((distance5.index(min(distance5)) - 1) / 2)

    if index5 == index5b:
        point11_x = LINEs[index5][0][2]
        point11_y = LINEs[index5][0][3]
        point12_x = LINEs[index5][0][0]
        point12_y = LINEs[index5][0][1]
    else:
        point11_x = LINEs[index5][0][0]
        point11_y = LINEs[index5][0][1]
        point12_x = LINEs[index5][0][2]
        point12_y = LINEs[index5][0][3]

    # 计算相邻直线之间的交点
    cross_pt1 = get_line_cross_point([point1_x, point1_y, point2_x, point2_y], [point3_x, point3_y, point5_x, point5_y])
    print("交点坐标1:", cross_pt1)
    cv2.circle(img, (int(cross_pt1[0]), int(cross_pt1[1])), 2, (0, 0, 255), 2)

    cross_pt2 = get_line_cross_point([point3_x, point3_y, point5_x, point5_y], [point7_x, point7_y, point9_x, point9_y])
    print("交点坐标2:", cross_pt2)
    cv2.circle(img, (int(cross_pt2[0]), int(cross_pt2[1])), 2, (0, 0, 255), 2)

    cross_pt3 = get_line_cross_point([point7_x, point7_y, point9_x, point9_y], [point11_x, point11_y, point12_x, point12_y])
    print("交点坐标3:", cross_pt3)
    cv2.circle(img, (int(cross_pt3[0]), int(cross_pt3[1])), 2, (0, 0, 255), 2)

    cross_pt4 = get_line_cross_point([point11_x, point11_y, point12_x, point12_y], [point10_x, point10_y, point8_x, point8_y])
    print("交点坐标4:", cross_pt4)
    cv2.circle(img, (int(cross_pt4[0]), int(cross_pt4[1])), 2, (0, 0, 255), 2)

    cross_pt5 = get_line_cross_point([point10_x, point10_y, point8_x, point8_y], [point6_x, point6_y, point4_x, point4_y])
    print("交点坐标5:", cross_pt5)
    cv2.circle(img, (int(cross_pt5[0]), int(cross_pt5[1])), 2, (0, 0, 255), 2)

    cross_pt6 = get_line_cross_point([point6_x, point6_y, point4_x, point4_y], [point2_x, point2_y, point1_x, point1_y])
    print("交点坐标6:", cross_pt6)
    cv2.circle(img, (int(cross_pt6[0]), int(cross_pt6[1])), 2, (0, 0, 255), 2)

    # 延长直线到交点
    cv2.line(img, (int(cross_pt1[0]), int(cross_pt1[1])), (int(cross_pt2[0]), int(cross_pt2[1])), (0, 255, 0), 2)
    cv2.line(img, (int(cross_pt2[0]), int(cross_pt2[1])), (int(cross_pt3[0]), int(cross_pt3[1])), (0, 255, 0), 2)
    cv2.line(img, (int(cross_pt3[0]), int(cross_pt3[1])), (int(cross_pt4[0]), int(cross_pt4[1])), (0, 255, 0), 2)
    cv2.line(img, (int(cross_pt4[0]), int(cross_pt4[1])), (int(cross_pt5[0]), int(cross_pt5[1])), (0, 255, 0), 2)
    cv2.line(img, (int(cross_pt5[0]), int(cross_pt5[1])), (int(cross_pt6[0]), int(cross_pt6[1])), (0, 255, 0), 2)
    cv2.line(img, (int(cross_pt6[0]), int(cross_pt6[1])), (int(cross_pt1[0]), int(cross_pt1[1])), (0, 255, 0), 2)

    # 求形心坐标(简单求法)
    center_x1 = (cross_pt1[0] + cross_pt2[0] + cross_pt3[0] + cross_pt4[0] + cross_pt5[0] + cross_pt6[0]) / 6
    center_y1 = (cross_pt1[1] + cross_pt2[1] + cross_pt3[1] + cross_pt4[1] + cross_pt5[1] + cross_pt6[1]) / 6

    # 求形心坐标(复杂求法)
    # 1、计算六边形面积
    A = 0
    A += cross_pt1[0] * cross_pt2[1] - cross_pt2[0] * cross_pt1[1]
    A += cross_pt2[0] * cross_pt3[1] - cross_pt3[0] * cross_pt2[1]
    A += cross_pt3[0] * cross_pt4[1] - cross_pt4[0] * cross_pt3[1]
    A += cross_pt4[0] * cross_pt5[1] - cross_pt5[0] * cross_pt4[1]
    A += cross_pt5[0] * cross_pt6[1] - cross_pt6[0] * cross_pt5[1]
    A += cross_pt6[0] * cross_pt1[1] - cross_pt1[0] * cross_pt6[1]
    A = abs(A)/2
    print("面积为:", A)

    # 2、计算六边形形心
    center_x2, center_y2 = 0, 0
    center_x2 += (cross_pt1[0] + cross_pt2[0]) * (cross_pt1[0] * cross_pt2[1] - cross_pt2[0] * cross_pt1[1])
    center_x2 += (cross_pt2[0] + cross_pt3[0]) * (cross_pt2[0] * cross_pt3[1] - cross_pt3[0] * cross_pt2[1])
    center_x2 += (cross_pt3[0] + cross_pt4[0]) * (cross_pt3[0] * cross_pt4[1] - cross_pt4[0] * cross_pt3[1])
    center_x2 += (cross_pt4[0] + cross_pt5[0]) * (cross_pt4[0] * cross_pt5[1] - cross_pt5[0] * cross_pt4[1])
    center_x2 += (cross_pt5[0] + cross_pt6[0]) * (cross_pt5[0] * cross_pt6[1] - cross_pt6[0] * cross_pt5[1])
    center_x2 += (cross_pt6[0] + cross_pt1[0]) * (cross_pt6[0] * cross_pt1[1] - cross_pt1[0] * cross_pt6[1])

    center_y2 += (cross_pt1[1] + cross_pt2[1]) * (cross_pt1[0] * cross_pt2[1] - cross_pt2[0] * cross_pt1[1])
    center_y2 += (cross_pt2[1] + cross_pt3[1]) * (cross_pt2[0] * cross_pt3[1] - cross_pt3[0] * cross_pt2[1])
    center_y2 += (cross_pt3[1] + cross_pt4[1]) * (cross_pt3[0] * cross_pt4[1] - cross_pt4[0] * cross_pt3[1])
    center_y2 += (cross_pt4[1] + cross_pt5[1]) * (cross_pt4[0] * cross_pt5[1] - cross_pt5[0] * cross_pt4[1])
    center_y2 += (cross_pt5[1] + cross_pt6[1]) * (cross_pt5[0] * cross_pt6[1] - cross_pt6[0] * cross_pt5[1])
    center_y2 += (cross_pt6[1] + cross_pt1[1]) * (cross_pt6[0] * cross_pt1[1] - cross_pt1[0] * cross_pt6[1])
    center_x2 = abs(center_x2 / (6 * A))
    center_y2 = abs(center_y2 / (6 * A))
    print("方法一形心坐标:", center_x1, center_y1)
    print("方法二形心坐标:", center_x2, center_y2)
    print("偏差△x,△y:", abs(center_x1-center_x2), abs(center_y1-center_y2))
    cv2.circle(img, (int(center_x1), int(center_y1)), 2, (0, 0, 255), 2)    # 红色
    cv2.circle(img, (int(center_x2), int(center_y2)), 2, (255, 0, 0), 2)    # 蓝色


cv2.imshow('show', img)
cv2.waitKey()

上两张图片,取任一图片配合上述pyrhon程序即可。
请添加图片描述
请添加图片描述
再上一组检测结果:

图片高和宽: 262 282
交点坐标1(201.8869956218199, 237.1984380546681)
交点坐标2(73.71299435028249, 232.8779661016949)
交点坐标3(12.211351017890191, 117.0260333127699)
交点坐标4(83.48453326033398, 11.38899534629072)
交点坐标5(209.07167630057805, 15.769942196531792)
交点坐标6(269.61482820976494, 128.6003616636528)
面积为: 42621.005056368536
方法一形心坐标: 141.66372979344493 123.8102894459347
方法二形心坐标: 141.36025317025906 124.36577031627374
偏差△x,△y: 0.30347662318587254 0.5554808703390393
  • 7
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 6
    评论
在Android Studio中生成随机六边形,你可以使用Canvas API结合随机颜色和坐标来绘制。以下是一个简单的步骤: 1. 首先,确保你了解如何使用Canvas和Shape类(例如Polygon)。 2. 创建一个自定义View,继承自`View`或`ShapeDrawable`。 3. 在`onDraw()`方法中,创建一个六边形状。你可以使用Math和计算来确定每个顶点的坐标,基于六边形中心点和边长。 4. 使用随机数来决定六边形的颜色。Android提供了`Random`类来生成随机数。 5. 绘制六边形,给每个顶点设置颜色。 下面是一个简化的示例代码片段: ```java public class RandomHexagonView extends View { private int centerX, centerY; private float sideLength; private Paint paint; public RandomHexagonView(Context context, @Nullable AttributeSet attrs) { super(context, attrs); init(); } private void init() { centerX = getWidth() / 2; centerY = getHeight() / 2; sideLength = Math.min(getWidth(), getHeight()) / 3; // 假设边长是视图宽度或高度的三分之一 paint = new Paint(); paint.setStyle(Paint.Style.FILL); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); // 生成随机颜色 Random random = new Random(); int color = Color.argb(255, random.nextInt(256), random.nextInt(256), random.nextInt(256)); // 计算六边形的顶点坐标 float[] hexagonPoints = getHexagonPoints(centerX, centerY, sideLength); // 绘制六边形 canvas.drawPolygon(hexagonPoints, paint, color); } private float[] getHexagonPoints(float centerX, float centerY, float sideLength) { // 六边形的顶点计算公式略复杂,可以使用三角函数和数学方法实现 // 这里省略具体的计算,你需要在网上查找相关算法或者自己实现这部分代码 // 返回一个包含6个顶点坐标的数组 } } ```
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

xiaohouzi112233

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值