【Python】向量叉积和凸包 | 引射线法 | 葛立恒扫描法

news2024/12/25 23:49:49

   猛戳!跟哥们一起玩蛇啊 👉 《一起玩蛇》🐍

 💭 写在前面:这个系列似乎反响不错, 所以我继续水下去 (bushi)。本篇博客是关于经典的 Cross Product and Convex Hull (向量叉积和凸包)的,我们将介绍引射线法,葛立恒扫描法。在讲解之前我会对前置知识做一个简单的介绍,比如向量叉积,如何确定直线是在顺时针上还是逆时针上等。算法讲解部分是为后面练习题做准备的,比如如何判断内点是否在多边形内,如何计算多边形面积等,还将简单介绍一下葛立恒扫描法,在提供的练习题中就能碰到。练习代码量200行左右,如果感兴趣想尝试做的话,需要有一定的耐心。练习题的环境为 Google Colaboratory(K80 GPU)Jupyter Notebook:colab

【高级软件实习】向量叉积和凸包 | 引射线法 | 判断点是否在多边形内部 | 葛立恒扫描法 | Cross Product and Convex Hul


Ⅰ. 前置知识

0x00 向量叉积(Cross product)

📚 概念:向量积 (Cross product),也可以称之为 "向量叉积" 。我更愿意称之为 "向量叉积",因为可以顾名思义 —— 叉积,交叉乘积!

" 叉积,叉积……积……?!   积你太美!"

 咳咳…… 它是一种在向量空间中向量的二元运算。

向量叉积不同于点积!其运算结果是一个向量,而非标量。

并且两个向量的叉积与这两个向量垂直。表示方法为 v_1 \times v_2 ,期中 v 代表向量。

💭 定义如下: 

模长:这里的 \theta 表示两向量之间的夹角(共起点的前提下),范围 0^{\circ} \leq \theta \leq 180^{\circ} ,它位于这两个矢量所定义的平面上。

方向:a向量与b向量的向量积的方向与这两个向量所在平面垂直,且遵守右手定则。

  • c 的长度在数值上等于以 a,b,夹角为 \theta 组成的平行四边形面积。
  • 因为在不同的坐标系中 c 可能不同,所以运算结果 c 是一个伪向量。

两向量的三角形的面积:

令向量 a 和 b 都在平面 xy 上:

0x01 确定顺时针逆时针(Clockwise or Counter-clockwise)

❓ 有什么好办法,可以确定点在直线 a,b 是在顺时针上还是逆时针上?

我们可以用 叉积  "暗中观察" 点是否在直线 a\, b 的顺时针或逆时针方向上:

  • ∵  (b-a)\times (c-a)  的叉积指向显示的前方,∴  C 点在逆时针方向。
  • ∵  (b-a)\times (d-a)  的叉积指向显示的后方,∴  D 点在顺时针方向。

0x02 交叉点(Intersection)

 当每条线的端点位于另一条线的不同侧面时,两条线就会交叉:

Ⅱ. 算法讲解部分

0x00 判断内点是否在多边形内(Inner points)

❓ 思考:如何检查平面上的一个点(point)是否在多边形内部?

这里我介绍两种常用的方法,只在一侧法 引射线法

① "只在一侧" 法:

只在一侧 (only on the one side) ,当一个点在每个多边形边的一侧(顺时针或逆时针)时,该点就在多边形的内部。

② 引射线法:

从目标点出发引一条射线,观察这条射线和多边形所有边的交点数目。如果有奇数个交点,则说明在内部,如果有偶数个交点,则说明在外部。

图中的红点是需要测试的点(我已标出),我们要确定它是否位于多边形内。 

💡 解决方案:将多边形的每一边与测试点的 Y (垂直)坐标进行比较,并编译一个结点列表,其中每个节点是一个边与测试点的 Y 阈值相交的点。在此示例中的多边形的 8 条边越过 Y 阈值,而其他 6 条边则没有。那么,如果有奇数测试点每边的节点数,那就说明它在多边形内。如果测试点的每一侧都有偶数个节点,那么它在多边形之外。

在本示例中,测试点左侧有5个节点,右侧有3个节点。由于 5 和 3 是奇数,该测试点在多边形内。(注意:该算法不关心多边形是顺时针还是逆时针跟踪)

0x01 计算多边形的面积

💡 思路:

  • 按逆时针方向对顶点进行排序。
  • 找到 (i+1) 个顶点位于第 i 个节点和第 i+1 个节点的边缘的顺时针方向的三角形,并积累三角形的面积。
  • 删除三角形并再次重复该过程,直到没有顶点为止。

将所有边缘的叉积加起来,然后除以2。

sum += float(x1 * y2 - x2 * y1);

根据一条边的方向,添加或减去三角形的面积。

 令人惊讶的是,只留下了多边形的面积: 

 🔑 提示:其类似于边的叉积之和

0x02 葛立恒扫描法(Graham Scan)

凸包计算(Computing a convex hull),给定平面点:

葛立恒扫描法(Graham Scan)

葛立恒扫描法(Graham's scan)是一种计算一组的平面点的凸包的算法。以在1972年发表该算法的葛立恒命名。

  1. 先找到左下角的点 P_0 (一定在凸包上)。
  2. 以 P_0 为原点,将其他的点按照极坐标排序,角度小的排在前,若角度相同,距离近的排在前。
  3. P_0,P_1 入栈,从 P_2 (第三个点)开始,若 P_2  在直线 P_0 P_1(\, p\, [ i-1 ] \, p [ i ]\, )  的左边 ,则P_2 入栈,否则 P_1 出栈,一直遍历完后面所有点 (这里就需要向量叉乘来判断点在线的左边还是右边)。
  4. 最后留在栈中的点就是凸包上的点

Ⅲ. 练习(Assignment)

🔺 注意:需用 Python 实现,算法必须在不导入外部库的情况下实现,但允许使用 numpy、math、sort 相关函数。(如果不加以限制,直接导某包掉函数就能直接算凸包,那还练个锤子,知道的小伙伴可以在评论区扣出来)

环境推荐:Colab

任务1:计算多边形面积

给定由 N 个点构成的平面多边形 ,计算该多边形的面积。

input

Output

5

0 0

2 0

2 2

1 1

0 2

3

任务2:多边形坐标

给定的 N 个构成多边形的点和 M 个检查点,判断每个检查点是否在多边形内。

* 注:在边缘线上的点也视作在多边形内。

input

Output

5         // N points

0 0      // (x1, y1)

2 0      // (x2, y2)

2 2

1 1

0 2

2

0 0

0.5 0.5

-1 -1

Inside

Inside

Outside

读取 input1.txt , input2, input3.txt,将结果分别生成到 output1.txt , _output3.txt 。

这里的 txt 文件请复制粘贴下面的数据,请自行创建!

💬 input1.txt

80 27
24 87
66 71
38 31
73 83
8 49
79 89

💬 input2.txt

61 80
14 10
68 11
24 21
20 31
95 90
1 60
14 54
79 47
20 14
59 22
91 13
18 98
51 92
23 30
59 53
82 84
65 28
79 34
1 21
67 82
29 6
13 5
33 58
81 59
0 67
49 47
74 35
5 79
4 76
50 36
85 0
87 66
33 78
78 64
85 11
13 17
61 47
17 92
1 99
30 95
100 18
64 93
68 71
46 76
59 61
31 56
27 52
37 48
85 97
38 88
25 80
19 38
26 6
52 86
25 30
68 43
52 12
97 79
34 63

💬 input3.txt

46 44
15 54
59 6
85 50
59 98
77 92
32 88
99 12
34 37
0 83
88 61
83 69
37 1
24 90
21 100
28 95
67 44
18 33
79 59
80 88
94 94
22 30
89 30
9 83
68 77
45 95
56 100
28 5
31 52
14 49
80 81
95 57
96 28
80 27
87 29
42 52
0 91
9 72
65 78
8 26
40 25
6 30
68 19
54 58
55 53
13 46
30 14
32 45
50 68
85 23
44 100
12 99
14 6
45 93
9 49
55 2
44 93
29 35
9 2
90 85
38 45
33 13
67 89
59 51
6 94
15 48
75 72
7 58
51 49
59 51

输出文件命名为 output1, output2, output3,输出结果演示:

生成 output 文件后,每个 output 都要画出图像,这里就不需要大家自己画了,

提供了 display.py,这是用于生成图像的代码。

def display(input_txt_path, output_txt_path):
    import matplotlib.pyplot as plt
    
    '''
    input1:input_txt_path=input_example.txt的路径
    input2:output_txt_path=output_example.txt的路径
    return:保存conex_hull图像
    '''
    
    with open(input_txt_path, "r") as f:
        input_lines = f.readlines()
    with open(output_txt_path, "r") as f:
        output_lines = f.readlines()
        
    whole_points = input_lines
    area = round(float(output_lines[0]), 1)
    hull_points = output_lines[1:]

    x_list = [int(x.split(" ")[0]) for x in whole_points]
    y_list = [int(x.split(" ")[1]) for x in whole_points]
    plt.plot(x_list, y_list, marker='.', linestyle='None')

    hx_list = [int(x.split(" ")[0]) for x in hull_points]
    hy_list = [int(x.split(" ")[1]) for x in hull_points]

    plt.plot(hx_list, hy_list, marker='*', linestyle='None', markersize=10)

    title = plt.title(f'Area : {area}')
    plt.setp(title, color='r')
    plt.savefig(output_txt_path[:-3]+"png", bbox_inches='tight')


if __name__ == "__main__":
    input_txt_path = "./input_example.txt"
    output_txt_path = "./output_example.txt"
    display(input_txt_path, output_txt_path)

通过调用提供的 display 函数,生成的图像效果如下:

任务3:点是否在多边形内

给定 N 个点构成平面多边形,计算凸包及其面积。

input

Output

5         // N 个点

0 0      // (x1, y1)

2 0      // (x2, y2)

2 2

1 1

0 2

4

(0, 0), (2, 0), (2, 2), and (0, 2) are points of convex hull.

您可以从 point_in_polygon_input.txt 输入 5 个坐标,并将它们与在刚才实现的 "练习1"  中保存的output1, output2, output3.txt 的多边形进行比较,以 "in" 或 "out" 的形式输入5个坐标。

这里的 point_in_polygon_input.txt 仍然是要自己创建,复制粘贴手动创建:

point_in_ploygon_input.txt:

0 0
1 1
10 10
50 50
70 70

因此,与3个 output1-3.txt 的文件相比,必须生成 3 个output 文件,格式演示如下:

参考代码

# -*- coding: utf-8 -*-
import math

def read_points(p):
    L = []  
    with open(p, 'r') as FILE:
        line = FILE.readlines()

    Lx, Ly = [int(i.split(" ")[0]) for i in line], [int(i.split(" ")[1]) for i in line]
    
    cur, sz = 0, len(Lx)
    for cur in range(sz):
        x, y = Lx[cur], Ly[cur]
        L.append(
            (x, y)
        )

    return L

def get_rad(px, py):
    pi = math.pi

    a = px[0] - py[0]
    b = px[1] - py[1]

    if a == 0:
        if b:
            return pi / 2
        else:
            return -1

    rad = math.atan(float(b) / float(a))

    if rad < 0:
        return rad + pi
    else:
        return rad


def sort_points_tan(p, pk):
    L = []
    resL = []

    i = 0
    sz = len(p)
    for i in range(sz):
        L.append({"index": i, "rad": get_rad(p[i], pk)})
    L.sort(key=lambda k: (k.get('rad')))

    sz = len(L)
    for i in range(sz):
        resL.append(p[L[i]["index"]])

    return resL


def calc_area(points):
    sz = len(points)
    points.append(points[0])
    S = 0
    for i in range(sz):
        S += (points[i][0] + points[i + 1][0]) * (points[i + 1][1] - points[i][1])
    return abs(S) / 2


def convex_hull(p):
    p = list(set(p))
    
    k = 0
    sz = len(p)
    for i in range(1, sz):
        if p[i][1] < p[k][1]:
            k = i
        if (p[i][0] < p[k][0]) and (p[i][1] == p[k][1]):
            k = i

    pk = p[k]
    p.remove(p[k])
    p_sort = sort_points_tan(p, pk)
    L = [pk, p_sort[0]]

    sz = len(p_sort)
    for i in range(1, sz):
        while (( (L[-2][0] - L[-1][0]) * (p_sort[i][1] - L[-1][1]) - (p_sort[i][0] - L[-1][0]) * (L[-2][1] - L[-1][1]) ) > 0):
            L.pop()
        L.append(p_sort[i])

    return L


def check(sp, ep, p):
    if sp[0] < p[0] and ep[1] > p[1]: 
        return False
    if sp[1] == p[1] and ep[1] > p[1]: 
        return False
    if ep[1] == p[1] and sp[1] > p[1]: 
        return False
    if sp[1] == ep[1]: 
        return False
    if sp[1] > p[1] and ep[1] > p[1]: 
        return False
    if sp[1] < p[1] and ep[1] < p[1]: 
        return False
    if ep[0] - (ep[0] - sp[0]) * (ep[1] - p[1]) / (ep[1] - sp[1]) < p[0]: 
        return False
    return True


def inpoly(p, poly_points):
    cnt = 0
    i = 0
    sz = len(poly_points)
    for i in range(sz):
        p1, p2 = poly_points[i], poly_points[(i + 1) % sz]
        if check(p1, p2, p):
            cnt += 1

    if cnt % 2 == 1:
        return True
    else:
        return False



def write_in_out(test_points, poly_points, out_txt_path):
    with open(out_txt_path, "w") as FILE:
        i = 0
        for i in test_points:
            if inpoly(i, poly_points):
                FILE.write("in")
            else:
                FILE.write("out")
            FILE.write("\n")
def write_area(poly_points,out_path):
    res = convex_hull(poly_points)

    with open(out_path,"w") as FILE:
        FILE.write(str(calc_area(res)))
        FILE.write('\n')
        sz = len(res)

        for i in range(sz - 1) :
            FILE.write(str( res[i][0]))
            FILE.write(" ")
            FILE.write(str(res[i][1]))
            FILE.write("\n")
    return res


test_points = read_points("point_in_polygon_input.txt")
poly_out_path = "foxny_point_in_polygon_output1.txt" #####
poly_points = read_points("input1.txt")  ####
area = write_area(poly_points, "foxny_output1.txt")  ######
write_in_out(test_points , area, poly_out_path)

def display(input_txt_path, output_txt_path):
    import matplotlib.pyplot as plt
    
    '''
    input1 : input_txt_path = path of input_example.txt
    input2 : output_txt_path = path of output_example.txt
    return : save convex_hull image
    '''
    
    with open(input_txt_path, "r") as f:
        input_lines = f.readlines()
    with open(output_txt_path, "r") as f:
        output_lines = f.readlines()
        
    whole_points = input_lines
    area = round(float(output_lines[0]), 1)
    hull_points = output_lines[1:]

    x_list = [int(x.split(" ")[0]) for x in whole_points]
    y_list = [int(x.split(" ")[1]) for x in whole_points]
    plt.plot(x_list, y_list, marker='.', linestyle='None')

    hx_list = [int(x.split(" ")[0]) for x in hull_points]
    hy_list = [int(x.split(" ")[1]) for x in hull_points]

    plt.plot(hx_list, hy_list, marker='*', linestyle='None', markersize=10)

    title = plt.title(f'Area : {area}')
    plt.setp(title, color='r')
    plt.savefig(output_txt_path[:-3]+"png", bbox_inches='tight')

####################################################################################1
if __name__ == "__main__":
    input_txt_path = "input1.txt"
    output_txt_path = "foxny_output1.txt"
    display(input_txt_path, output_txt_path)

""

🚩 结果演示:

foxny_output1.png

foxny_ouput2.txt

3568.0
80 27
79 89
24 87
8 49
38 31

 foxny_output2.png

foxny_output2.txt

9049.5
85 0
100 18
97 79
95 90
85 97
1 99
0 67
1 21
13 5

foxny_output3.png

 foxny_output3.txt

8727.0
37 1
55 2
99 12
94 94
56 100
21 100
12 99
0 91
0 83
9 2

 foxny_point_in_polygon_output1.txt

out
out
out
in
in

 foxny_point_in_polygon_output2.txt

out
out
in
in
in

 foxny_point_in_polygon_output3.txt

out
out
in
in
in

📌 [ 笔者 ]   王亦优
📃 [ 更新 ]   2022.12.12
❌ [ 勘误 ]   /* 暂无 */
📜 [ 声明 ]   由于作者水平有限,本文有错误和不准确之处在所难免,
              本人也很想知道这些错误,恳望读者批评指正!

📜 参考资料 

Darel Rex Finley. Point-In-Polygon Algorithm — Determining Whether A Point Is Inside A Complex Polygon[EB/OL]. 1998,2006,2007[2022.12.12]. http://alienryderflex.com/polygon/.

C++reference[EB/OL]. []. http://www.cplusplus.com/reference/.

Microsoft. MSDN(Microsoft Developer Network)[EB/OL]. []. .

百度百科[EB/OL]. []. https://baike.baidu.com/.

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

最大正方形问题

最大正方形问题 作者&#xff1a;Grey 原文地址&#xff1a; 博客园&#xff1a;最大正方形问题 CSDN&#xff1a;最大正方形问题 题目描述 在一个由 ‘0’ 和 ‘1’ 组成的二维矩阵内&#xff0c;找到只包含 ‘1’ 的最大正方形&#xff0c;并返回其面积。 题目链接见&am…

数字式压力闭环放大器|比例溢流阀闭环控制器

控制不同带压力反馈信号输入&#xff08;0~10V或4~20mA&#xff09;比例压力阀、比例溢流阀、比例节流阀、比例插装阀&#xff0c;带位置反馈信号输入比例流量阀等。斜坡上升和下降时间独立调整(0.05~10 s)。10V参考电压输出外部电位器控制。最大驱动电流 0.4~3A&#xff0c;最…

4个月高效学习,我是如何从手工测试做到测试开发的?

向上的路很难走&#xff0c;但一旦踏上去&#xff0c;每一步都算数&#xff01; 为什么必须转型为测试开发&#xff1f; 不懂开发的手工测试是新时代“文盲” 在移动互联网和大数据时代&#xff0c;为满足市场和业务需求&#xff0c;互联网应用既要实现产品功能快速迭代&…

java字符串中常用的10个方法

文章目录前言一、字符串的构造1.使用常量进行直接赋值构造2.使用new String对象3.使用字符数组进行构造二、字符串的比较1.比较2.equals方法比较3. compareTo方法比较4.compareToIgnoreCase(String str)方法三、字符串的查找1.charAt(int index)方法2.indexOf(String str)方法四…

网络流量分析帮助企业提升OA应用性能(二)

需求简介 某外高桥公司的OA系统是其重要的业务系统&#xff0c;OA系统负责人表示&#xff0c;部分用户反馈&#xff0c;访问OA系统时比较慢。需要通过分析系统看一下实际情况。 信息部已对企业领导定义了独立的组&#xff0c;本次要主动分析领导们的使用体验快慢。如果OA系统…

mmdetection3d S3DIS (持续更新)

Mmdetection3d集成了大量3D深度学习算法&#xff0c;其中很大一部分可以在室内三维数据集S3DIS上运行。本节重点介绍S3DIS数据集及其在mmdetection3d中的预处理程序。 1 S3DIS S3DIS&#xff08;Stanford Large-Scale 3D Indoor Spaces Dataset &#xff09;数据集是斯坦福大学…

【R语言】白葡萄酒的EDA分析

白葡萄酒的EDA分析1.项目相关信息1.1 评估标准1.2 项目模板1.3 数据集列表1.4 项目示例1.5 数据选择1.5.1 选择1.5.2 详细数据说明1.5.3 有关项目提交的常见问题2.环境准备2.1 导入相关包2.2 加载数据集2 数据整理2.1 数据评估2.1.1 质量类问题2.1.2 结构性问题2.1 数据清洗2.1…

二叉树的代码实现和详解

树的定义 树是由n&#xff08;n>1&#xff09;个有限结点组成一个具有层次关系的集合。把它叫做“树”是因为它看起来像一棵倒挂的树&#xff0c;也就是说它是根朝上&#xff0c;而叶朝下的。 树具有以下特点&#xff1a; 1.每个结点有零个或多个子结点&#xff1b; 2.没有父…

ROS action简单使用示例

1、为什么要用action 在ros中&#xff0c;大部分情况下的信息我们使用publisher与subscriber的机制来处理&#xff0c;也就是topic的机制。它能处理一些不用带状态返回的数据处理。当涉及到需要数据返回的节点间数据交互时&#xff0c;首选也可以采用servicer的机制进行处理与…

图解Redisson如何实现分布式锁、锁续约?

文章目录一、基础0&#xff09;Redisson版本说明、案例案例1&#xff09;Redisson连接Redis的方式2&#xff09;用到的Redis命令3&#xff09;用到的lua脚本语义二、源码分析1、RLock获取RLock对象2、加锁流程0&#xff09;加锁流程图1&#xff09;加锁到哪台机器2&#xff09;…

一行代码获取股票、基金数据,并绘制K线图

大家好&#xff0c;今天这篇文章和大家分享一下如何利用Python获取股票、基金数据&#xff0c;并进行可视化&#xff0c;为金融分析&可视化先导篇&#xff0c;欢迎大家学习、点赞、收藏支持。 一、基础准备 环境&#xff1a;python 3.7 需要安装第三方模块&#xff1a;mpl…

Spring源码深度解析:十六、@Aspect方式的AOP下篇 - createProxy

一、前言 文章目录&#xff1a;Spring源码深度解析&#xff1a;文章目录 我们上篇已经分析到了 Spring将已经找到所有适用于当前bean 的Advisor 集合。下面就要创建代理对象了&#xff0c;而代理对象的创建是从 AbstractAutoProxyCreator#createProxy()开始。下面我们就来看看…

Spring Cloud实现Zuul自带的Debug功能

Zuul 中自带了一个 DebugFilter&#xff0c;一开始笔者也没明白这个 DebugFilter 有什么用&#xff0c;看名称很容易理解&#xff0c;它是用来调试的&#xff0c;可是你看它的源码几乎没什么逻辑&#xff0c;就 set 了两个值而已&#xff0c;代码如下所示。 Overridepublic Obj…

流媒体协议分析之webrtc之rtcp

TCP作为RTP控制协议&#xff0c;对于弱网下音视频质量和会话控制具有重要的作用。 1. RTCP Header V&#xff1a;RTCP的版本号&#xff0c;一定等于2&#xff1b; P&#xff1a;如果设置&#xff0c;填充位表示数据包包含末尾的附加填充八位字节&#xff0c;不属于控制信息&am…

CV-对比学习:概述【通过自监督学习,充分挖掘模型从海量无标注数据中学习通用知识的能力】

对比学习从目标来看&#xff0c;是要做在NLP类型类似Bert预训练的事&#xff0c;即通过自监督学习&#xff0c;充分挖掘模型从海量无标注数据中学习通用知识的能力。 大致分类 对比学习目前可大致可分为 基于负例的对比学习方法基于对比聚类的方法基于不对称网络结构的方法基…

PLC SECS/GEM解决方案,设计与应用

1 适用性 金南瓜SECS是最适应于全自动智能设备的选择。 适用行业&#xff1a;半导体、光伏、PCB等 全面支持E5、E30、E37、E40、E87、E90、E94、E116 PLC SECS/GEM具有怪兽级的强劲性能&#xff0c;处理性能高达10ms/条&#xff0c;全面升级的高适应性&#xff0c;易用友好的S…

30.前端笔记-CSS-CSS3的新特性

1、CSS3新增选择器 属性选择器&#xff0c;权重为10结构伪类选择器&#xff0c;权重为10伪元素选择器&#xff0c;权重为10 1.1 属性选择器 用属性选择器可以不用借助类或id选择器 语法&#xff1a; 正则表达式&#xff1a;^表示开头&#xff0c;$表示结尾,*表示任意 /*标…

怎么提高客服人员效率?

为了给客户提供更好的服务&#xff0c;很多企业会为自己网站配置客服服务&#xff0c;方便随时和客户沟通。但是凡事有利便有弊。虽然和客户接触的机会变多了&#xff0c;但是沟通不及时、回答不专业、问题处理时间长等问题也可能随之出现&#xff0c;反而会给客户带来不好的印…

[附源码]Python计算机毕业设计Django的个人理财系统

项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等等。 环境需要 1.运行环境&#xff1a;最好是python3.7.7&#xff0c;…

OpenCV-Python小应用(七):获取图片ROI的HSV取值范围

OpenCV-Python小应用&#xff08;七&#xff09;&#xff1a;获取图片ROI的HSV取值范围前言前提条件实验环境获取图片ROI的HSV取值范围参考文献前言 本文是个人使用OpenCV-Python的应用案例&#xff0c;由于水平有限&#xff0c;难免出现错漏&#xff0c;敬请批评改正。更多精彩…