当前位置: 首页 > article >正文

【python】OpenCV—findContours(4.2)

在这里插入图片描述

文章目录

  • 1、轮廓相关属性
    • 1.1、外接矩阵
    • 1.2、横纵比
    • 1.3、Extent
    • 1.4、Solidity
    • 1.5、等效直径
    • 1.6、方向
    • 1.7、掩模和像素点
    • 1.8、最大值最小值以及对应的位置
    • 1.9、平均强度
    • 1.10、极值点
    • 1.11、点多边形测试
    • 1.12、凸缺陷
    • 1.13、形状匹配
  • 2、完整代码
  • 3、涉及到的库函数
    • 3.1、cv2.convexHull()
    • 3.2、cv2.fitEllipse()
  • 4、参考

本博客介绍一些基于 findContours 的基本属性

1、轮廓相关属性

1.1、外接矩阵

import cv2 as cv
import numpy as np

img = cv.imread('1.png', 0)
print(img.shape)  # (246, 456), 灰度图,没有彩色通道

ret2, thresh = cv.threshold(img, 127, 255, cv.THRESH_BINARY_INV)  # 这里用了反二值化取前景
cv.imwrite("thresh.jpg", thresh)  # 保存二值化后的前景

"找轮廓"
contours, hierarchy2 = cv.findContours(thresh, 1, 2)
cnt = contours[0]  # 给的样例只有一个轮廓

"绘制轮廓"
color_image = cv.merge([img, img, img])
cv.drawContours(color_image, cnt, -1,(0, 0, 255),3)  # 把轮廓画在原图上
cv.imwrite("drawContours.jpg", color_image)

"求外接矩阵"
x, y, w, h = cv.boundingRect(cnt)
print(x, y, w, h)  # 145 10 215 216
# 绘制外接矩阵
img1 = img.copy()
cv.rectangle(img1, (x, y), (x+w, y+h), color=(0, 0, 0), thickness=5)
cv.imwrite("rect_thresh.jpg", img1)

输入图片 1.png
在这里插入图片描述

二值化求反,thresh.jpg

在这里插入图片描述

计算轮廓,并绘制,drawContours.jpg
在这里插入图片描述

求外接矩阵,rect_thresh.jpg
在这里插入图片描述

1.2、横纵比

"横纵比:它是物体的边界矩形的宽度与高度之比。"
aspect_ratio = float(w) / h
print(aspect_ratio)  # 0.9953703703703703

1.3、Extent

"Extent是等轮廓面积与外接矩形面积的比值"
area = cv.contourArea(cnt)  # 轮廓的面积
rect_area = w * h  # 外接矩阵的面积
extent = float(area) / rect_area
print(area, rect_area, extent)  # 26539.5 46440 0.5714793281653747

1.4、Solidity

"Solidity是轮廓面积与其凸包面积之比"
hull = cv.convexHull(cnt)
length = len(hull)
# 绘制凸包
img_tmp = cv.merge([img, img, img])
for i in range(length):
    cv.line(img_tmp, tuple(hull[i][0]), tuple(hull[(i + 1) % length][0]), (0, 0, 255), 2)
cv.imwrite("hull.jpg", img_tmp)
# 计算 solidity
hull_area = cv.contourArea(hull)
solidity = float(area) / hull_area
print(area, hull_area, solidity)  # 26539.5 29793.5 0.8907815463104368

凸包 hull.jpg

在这里插入图片描述

1.5、等效直径

"等效直径是指面积与轮廓面积相等的圆的直径"
equi_diameter = np.sqrt(4 * area / np.pi)
print(area, equi_diameter)  # 26539.5 183.82366794702702

1.6、方向

"方向是物体所指向的角度。下面的方法也给出了长轴和短轴的长度"
(x_Ellipse, y_Ellipse), (MA, ma), angle = cv.fitEllipse(cnt)
print(x_Ellipse, y_Ellipse, MA, ma, angle)
# 235.18746948242188 135.52052307128906 183.91519165039062 196.01829528808594 87.0858383178711
# 绘制结果
img_tmp = cv.merge([img, img, img])
cv.ellipse(img_tmp, ((x_Ellipse, y_Ellipse), (MA, ma), angle),
            (0, 0, 255), 2)  # 画图红色的椭圆
cv.ellipse(img_tmp, ((x_Ellipse, y_Ellipse), (ma, MA), angle),
            (255, 0, 0), 2)  # 画图蓝色的椭圆
cv.imwrite("Ellipse.jpg", img_tmp)

Ellipse.jpg
在这里插入图片描述

1.7、掩模和像素点

"掩模和像素点"
mask = np.zeros(img.shape, np.uint8)  #(246, 456)
print(mask.shape)
# 前景置为白色
cv.drawContours(mask, [cnt], 0, 255, -1)
cv.imwrite("mask.jpg", mask)
pixelpoints = np.transpose(np.nonzero(mask))  # 转置
print(pixelpoints.shape)  # (26847, 2), 可以看到和 area 计算出来的差距不大

mask.jpg

在这里插入图片描述

可以看到计算出来的面积和之前 area 计算出来的差距不大

1.8、最大值最小值以及对应的位置

"最大值最小值以及对应的位置"
min_val, max_val, min_loc, max_loc = cv.minMaxLoc(img, mask=mask)
print(min_val, max_val, min_loc, max_loc)  # 0.0 127.0 (242, 11) (244, 32)
# 绘制最小最大值点的位置
img_tmp = cv.merge([img, img, img])
cv.circle(img_tmp, min_loc, 10, color=(0, 0, 255), thickness=-1)
cv.circle(img_tmp, max_loc, 10, color=(255, 0, 0), thickness=-1)
cv.imwrite("minMax.jpg", img_tmp)

最大值和最小值,以及其所在的位置 minMax.jpg

在这里插入图片描述

1.9、平均强度

"平均颜色或平均强度"
# 在这里,我们可以找到一个物体的平均颜色。也可以是灰度模式下物体的平均强度。我们再次使用相同的掩膜来完成。
mean_val = cv.mean(img, mask=mask)
print(mean_val[0])  # 1.8326069951949937,原图的前景基本都是黑色的
mean_val = cv.mean(img)
print(mean_val[0])  # 194.10819604906575,整个原图很多区域是白色,平均值上来了

1.10、极值点

"极值点是指物体的最上方、最下方、最右边和最左边的点"
leftmost = tuple(cnt[cnt[:, :, 0].argmin()][0])  # (np.int32(145), np.int32(122))
rightmost = tuple(cnt[cnt[:, :, 0].argmax()][0])  # (np.int32(359), np.int32(142))
topmost = tuple(cnt[cnt[:, :, 1].argmin()][0])  # (np.int32(242), np.int32(10))
bottommost = tuple(cnt[cnt[:, :, 1].argmax()][0])  # (np.int32(209), np.int32(225))
print(leftmost, rightmost, topmost, bottommost)
# 绘制极值点
img_tmp = cv.merge([img, img, img])
cv.circle(img_tmp, leftmost, 10, color=(0, 0, 255), thickness=-1)
cv.circle(img_tmp, rightmost, 10, color=(255, 0, 0), thickness=-1)
cv.circle(img_tmp, topmost, 10, color=(0, 255, 0), thickness=-1)
cv.circle(img_tmp, bottommost, 10, color=(255, 255, 0), thickness=-1)
cv.imwrite("most.jpg", img_tmp)

most.jpg
在这里插入图片描述

1.11、点多边形测试

"点多边形测试"
# 这个函数查找图像中一个点和轮廓线之间的最短距离。它返回距离,当点在轮廓外时为负,当点在轮廓内时为正,如果点在轮廓上则为零。
dist = cv.pointPolygonTest(cnt, (50, 50), True)
print(dist)  # -111.89280584559492

dist = cv.pointPolygonTest(cnt, (154, 92), True)
print(dist)  # -0.0

dist = cv.pointPolygonTest(cnt, (154, 96), True)
print(dist)  # 1.4142135623730951

1.12、凸缺陷

"凸缺陷"
# 物体与凸包的任何偏差都可以被认为是凸缺陷。
# 记住,为了找到凸缺陷,我们必须传递returnPoints = False。
# 它返回一个数组,其中每一行包含这些值—[起始点、结束点、最远点、到最远点的近似距离]。我们可以使用一个可视化图像。
# 我们从开始点到结束点画一条线,然后在最远的点上画一个圆。记住,返回的前三个值是cnt的索引。所以我们必须从cnt中引入这些值。
img = cv.imread('1.png')
img_gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
ret, thresh = cv.threshold(img_gray, 127, 255, cv.THRESH_BINARY_INV)
contours, hierarchy = cv.findContours(thresh, 2, 1)
cnt = contours[0]
hull = cv.convexHull(cnt, returnPoints=False)
defects2 = cv.convexityDefects(cnt, hull)
for i in range(defects2.shape[0]):
    s, e, f, d = defects2[i, 0]
    start = tuple(cnt[s][0])
    end = tuple(cnt[e][0])
    far = tuple(cnt[f][0])
    cv.line(img, start, end, [0, 255, 0], 2)
    cv.circle(img, far, 5, [0, 0, 255], -1)
cv.imwrite('convexityDefects.jpg', img)

convexityDefects.jpg
在这里插入图片描述

1.13、形状匹配

star1 = cv.imread("2.png", cv.IMREAD_GRAYSCALE)
star2 = cv.imread("3.png", cv.IMREAD_GRAYSCALE)
ret1, thresh1 = cv.threshold(star1, 127, 255, cv.THRESH_BINARY_INV)
contours1, hierarchy1 = cv.findContours(thresh1, 2, 1)
cnt1 = contours1[0]

ret2, thresh2 = cv.threshold(star2, 127, 255, cv.THRESH_BINARY_INV)
contours2, hierarchy2 = cv.findContours(thresh2, 2, 1)
cnt2 = contours2[0]

ret = cv.matchShapes(cnt1, cnt1, 1, 0.0)
print(ret)  # 0.0
ret = cv.matchShapes(cnt2, cnt2, 1, 0.0)
print(ret)  # 0.0
ret = cv.matchShapes(cnt1, cnt2, 1, 0.0)
print(ret)  # 5.551115123125783e-17

2.png
在这里插入图片描述

3.png
在这里插入图片描述

图片 3 是图片 2 经过旋转 180° 得到的,可以看到,即便是旋转,形状匹配的相似度还是非常高的

2、完整代码

import cv2 as cv
import numpy as np

img = cv.imread('1.png', 0)
print(img.shape)  # (246, 456), 灰度图,没有彩色通道

ret2, thresh = cv.threshold(img, 127, 255, cv.THRESH_BINARY_INV)  # 这里用了反二值化取前景
cv.imwrite("thresh.jpg", thresh)  # 保存二值化后的前景

"找轮廓"
contours, hierarchy2 = cv.findContours(thresh, 1, 2)
cnt = contours[0]  # 给的样例只有一个轮廓

"绘制轮廓"
color_image = cv.merge([img, img, img])
cv.drawContours(color_image, cnt, -1,(0, 0, 255),3)  # 把轮廓画在原图上
cv.imwrite("drawContours.jpg", color_image)

"求外接矩阵"
x, y, w, h = cv.boundingRect(cnt)
print(x, y, w, h)  # 145 10 215 216
# 绘制外接矩阵
img1 = img.copy()
cv.rectangle(img1, (x, y), (x+w, y+h), color=(0, 0, 0), thickness=5)
cv.imwrite("rect_thresh.jpg", img1)

"横纵比:它是物体的边界矩形的宽度与高度之比。"
aspect_ratio = float(w) / h
print(aspect_ratio)  # 0.9953703703703703

"Extent是等轮廓面积与外接矩形面积的比值"
area = cv.contourArea(cnt)  # 轮廓的面积
rect_area = w * h  # 外接矩阵的面积
extent = float(area) / rect_area
print(area, rect_area, extent)  # 26539.5 46440 0.5714793281653747


"Solidity是轮廓面积与其凸包面积之比"
hull = cv.convexHull(cnt)
length = len(hull)
# 绘制凸包
img_tmp = cv.merge([img, img, img])
for i in range(length):
    cv.line(img_tmp, tuple(hull[i][0]), tuple(hull[(i + 1) % length][0]), (0, 0, 255), 2)
cv.imwrite("hull.jpg", img_tmp)
# 计算 solidity
hull_area = cv.contourArea(hull)
solidity = float(area) / hull_area
print(area, hull_area, solidity)  # 26539.5 29793.5 0.8907815463104368

"等效直径是指面积与轮廓面积相等的圆的直径"
equi_diameter = np.sqrt(4 * area / np.pi)
print(area, equi_diameter)  # 26539.5 183.82366794702702

"方向是物体所指向的角度。下面的方法也给出了长轴和短轴的长度"
(x_Ellipse, y_Ellipse), (MA, ma), angle = cv.fitEllipse(cnt)
print(x_Ellipse, y_Ellipse, MA, ma, angle)
# 235.18746948242188 135.52052307128906 183.91519165039062 196.01829528808594 87.0858383178711
# 绘制结果
img_tmp = cv.merge([img, img, img])
cv.ellipse(img_tmp, ((x_Ellipse, y_Ellipse), (MA, ma), angle),
            (0, 0, 255), 2)  # 画图红色的椭圆
cv.ellipse(img_tmp, ((x_Ellipse, y_Ellipse), (ma, MA), angle),
            (255, 0, 0), 2)  # 画图蓝色的椭圆
cv.imwrite("Ellipse.jpg", img_tmp)

"掩模和像素点"
mask = np.zeros(img.shape, np.uint8)  #(246, 456)
print(mask.shape)
# 前景置为白色
cv.drawContours(mask, [cnt], 0, 255, -1)
cv.imwrite("mask.jpg", mask)
pixelpoints = np.transpose(np.nonzero(mask))  # 转置
print(pixelpoints.shape)  # (26847, 2), 可以看到和 area 计算出来的差距不大

"最大值最小值以及对应的位置"
min_val, max_val, min_loc, max_loc = cv.minMaxLoc(img, mask=mask)
print(min_val, max_val, min_loc, max_loc)  # 0.0 127.0 (242, 11) (244, 32)
# 绘制最小最大值点的位置
img_tmp = cv.merge([img, img, img])
cv.circle(img_tmp, min_loc, 10, color=(0, 0, 255), thickness=-1)
cv.circle(img_tmp, max_loc, 10, color=(255, 0, 0), thickness=-1)
cv.imwrite("minMax.jpg", img_tmp)

"平均颜色或平均强度"
# 在这里,我们可以找到一个物体的平均颜色。也可以是灰度模式下物体的平均强度。我们再次使用相同的掩膜来完成。
mean_val = cv.mean(img, mask=mask)
print(mean_val[0])  # 1.8326069951949937,原图的前景基本都是黑色的
mean_val = cv.mean(img)
print(mean_val[0])  # 194.10819604906575,整个原图很多区域是白色,平均值上来了

"极值点是指物体的最上方、最下方、最右边和最左边的点"
leftmost = tuple(cnt[cnt[:, :, 0].argmin()][0])  # (np.int32(145), np.int32(122))
rightmost = tuple(cnt[cnt[:, :, 0].argmax()][0])  # (np.int32(359), np.int32(142))
topmost = tuple(cnt[cnt[:, :, 1].argmin()][0])  # (np.int32(242), np.int32(10))
bottommost = tuple(cnt[cnt[:, :, 1].argmax()][0])  # (np.int32(209), np.int32(225))
print(leftmost, rightmost, topmost, bottommost)
# 绘制极值点
img_tmp = cv.merge([img, img, img])
cv.circle(img_tmp, leftmost, 10, color=(0, 0, 255), thickness=-1)
cv.circle(img_tmp, rightmost, 10, color=(255, 0, 0), thickness=-1)
cv.circle(img_tmp, topmost, 10, color=(0, 255, 0), thickness=-1)
cv.circle(img_tmp, bottommost, 10, color=(255, 255, 0), thickness=-1)
cv.imwrite("most.jpg", img_tmp)

"点多边形测试"
# 这个函数查找图像中一个点和轮廓线之间的最短距离。它返回距离,当点在轮廓外时为负,当点在轮廓内时为正,如果点在轮廓上则为零。
dist = cv.pointPolygonTest(cnt, (50, 50), True)
print(dist)  # -111.89280584559492

dist = cv.pointPolygonTest(cnt, (154, 92), True)
print(dist)  # -0.0

dist = cv.pointPolygonTest(cnt, (154, 96), True)
print(dist)  # 1.4142135623730951


"凸缺陷"
# 物体与凸包的任何偏差都可以被认为是凸缺陷。
# 记住,为了找到凸缺陷,我们必须传递returnPoints = False。
# 它返回一个数组,其中每一行包含这些值—[起始点、结束点、最远点、到最远点的近似距离]。我们可以使用一个可视化图像。
# 我们从开始点到结束点画一条线,然后在最远的点上画一个圆。记住,返回的前三个值是cnt的索引。所以我们必须从cnt中引入这些值。
img = cv.imread('1.png')
img_gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
ret, thresh = cv.threshold(img_gray, 127, 255, cv.THRESH_BINARY_INV)
contours, hierarchy = cv.findContours(thresh, 2, 1)
cnt = contours[0]
hull = cv.convexHull(cnt, returnPoints=False)
defects2 = cv.convexityDefects(cnt, hull)
for i in range(defects2.shape[0]):
    s, e, f, d = defects2[i, 0]
    start = tuple(cnt[s][0])
    end = tuple(cnt[e][0])
    far = tuple(cnt[f][0])
    cv.line(img, start, end, [0, 255, 0], 2)
    cv.circle(img, far, 5, [0, 0, 255], -1)
cv.imwrite('convexityDefects.jpg', img)


"形状匹配"
star1 = cv.imread("2.png", cv.IMREAD_GRAYSCALE)
star2 = cv.imread("3.png", cv.IMREAD_GRAYSCALE)
ret1, thresh1 = cv.threshold(star1, 127, 255, cv.THRESH_BINARY_INV)
contours1, hierarchy1 = cv.findContours(thresh1, 2, 1)
cnt1 = contours1[0]

ret2, thresh2 = cv.threshold(star2, 127, 255, cv.THRESH_BINARY_INV)
contours2, hierarchy2 = cv.findContours(thresh2, 2, 1)
cnt2 = contours2[0]

ret = cv.matchShapes(cnt1, cnt1, 1, 0.0)
print(ret)  # 0.0
ret = cv.matchShapes(cnt2, cnt2, 1, 0.0)
print(ret)  # 0.0
ret = cv.matchShapes(cnt1, cnt2, 1, 0.0)
print(ret)  # 5.551115123125783e-17

3、涉及到的库函数

3.1、cv2.convexHull()

在这里插入图片描述

cv2.convexHull() 是 OpenCV 库中的一个函数,用于计算二维点集的凸包。凸包是指能够包含所有给定点的最小凸多边形。这个函数在图像处理和计算机视觉中非常有用,特别是在形状分析和物体识别等领域。

一、函数原型

hull = cv2.convexHull(points[, hull[, clockwise[, returnPoints]]])
  • points:输入的二维点集,通常是一个形状为 Nx1 或 1xN 的二维数组(N为点的数量),或者是一个形状为 Nx1x2 的三维数组。
  • hull:输出参数,用于存储凸包的索引或点。如果不提供,则函数会分配一个新的数组。
  • clockwise:方向标志。如果为 True,则输出凸包的顶点将按顺时针方向排列;如果为 False,则按逆时针方向排列。默认值为 False。
  • returnPoints:如果为 True,则函数返回凸包上的点;如果为 False,则返回凸包的顶点在输入点集中的索引。默认值为 True。

二、返回值

  • 当 returnPoints=True 时,返回一个数组,包含凸包上的点。
  • 当 returnPoints=False 时,返回一个数组,包含凸包顶点在输入点集中的索引。

三、示例

以下是一个简单的示例,演示如何使用 cv2.convexHull() 计算并绘制一个点集的凸包:

python

import cv2  
import numpy as np  
  
# 创建一个随机点集  
points = np.random.randint(0, 255, (30, 1, 2), dtype="uint8")  
  
# 计算凸包  
hull = cv2.convexHull(points)  
  
# 创建一个黑色图像  
image = np.zeros((512, 512, 3), dtype="uint8")  
  
# 绘制原始点集  
for i in range(len(points)):  
    cv2.circle(image, tuple(points[i][0]), 5, (0, 255, 0), -1)  
  
# 绘制凸包的边  
for i in range(len(hull)):  
    if i == 0:  
        cv2.line(image, tuple(points[hull[i][0], 0]), tuple(points[hull[-1][0], 0]), (0, 0, 255), 2)  
    else:  
        cv2.line(image, tuple(points[hull[i - 1][0], 0]), tuple(points[hull[i][0], 0]), (0, 0, 255), 2)  
  
# 显示结果  
cv2.imshow("Convex Hull", image)  
cv2.waitKey(0)  
cv2.destroyAllWindows()

在这个示例中,我们首先生成了一个随机点集,然后使用 cv2.convexHull() 计算其凸包,并在一个黑色图像上绘制了原始点集和凸包的边。

3.2、cv2.fitEllipse()

cv2.fitEllipse() 是 OpenCV 库中的一个函数,用于根据一组二维点拟合一个椭圆。这个函数在图像处理、计算机视觉和图像分析中非常有用,特别是在检测和分析具有椭圆形状的对象时。

一、函数原型

ellipse = cv2.fitEllipse(points)
  • points:输入的二维点集,通常是一个形状为 Nx1 或 1xN 的二维数组(N为点的数量),或者是一个形状为 Nx1x2 的三维数组。这些点应该是位于椭圆上的或者足够接近椭圆以至于可以被视为椭圆上的一部分的点。

二、返回值

返回一个 RotatedRect 对象,该对象包含了拟合椭圆的参数。具体来说,这个对象包含了椭圆的中心点 (cx, cy)、椭圆的长轴和短轴的长度 (major_axis_length, minor_axis_length),以及椭圆长轴相对于水平轴的旋转角度 angle(以度为单位,顺时针方向为正)。

三、示例

以下是一个简单的示例,演示如何使用 cv2.fitEllipse() 拟合一个椭圆并绘制它:

import cv2  
import numpy as np  
  
# 创建一个包含椭圆上点的集合  
points = np.array([  
    [100, 100], [200, 150], [300, 100],  
    [250, 50], [150, 50], [100, 100]  # 注意这里最后一个点是重复的,用于闭合形状(虽然对于拟合椭圆不是必需的)  
], dtype="float32")  
  
# 拟合椭圆  
ellipse = cv2.fitEllipse(points)  
  
# 创建一个黑色图像  
image = np.zeros((400, 400, 3), dtype="uint8")  
  
# 绘制拟合的椭圆  
cv2.ellipse(image, ellipse, (0, 255, 0), 2)  
  
# 显示结果  
cv2.imshow("Fitted Ellipse", image)  
cv2.waitKey(0)  
cv2.destroyAllWindows()

在这个示例中,我们首先定义了一个包含椭圆上点的集合(注意这些点应该是实际位于椭圆上的,或者足够接近以至于可以被拟合算法视为椭圆上的一部分)。然后,我们使用 cv2.fitEllipse() 函数拟合了一个椭圆,并在一个黑色图像上绘制了它。最后,我们显示了这个图像。

需要注意的是,虽然在这个示例中我们提供了一个闭合的形状(最后一个点与第一个点重复),但这对于 cv2.fitEllipse() 函数来说并不是必需的。函数会根据提供的点集自动计算椭圆的最佳拟合。

4、参考

基于OpenCV的轮廓检测(2)


http://www.kler.cn/news/366025.html

相关文章:

  • 文件下载漏洞
  • 论文速读:YOLO-G,用于跨域目标检测的改进YOLO(Plos One 2023)
  • C++面向对象编程学习
  • CSS行块标签的显示方式
  • Python游戏开发超详细第二课/一个小游戏等制作过程(入门级篇共2节)
  • Lucas带你手撕机器学习——套索回归
  • 【Go语言】
  • 简述特征降维的几种方式
  • IDEA中一个窗口打开多个项目-区别于eclipse
  • Netty-TCP服务端粘包、拆包问题(两种格式)
  • 使用Flask实现本机的模型部署
  • 【制造业&电子产品】电脑电子元件检测系统源码&数据集全套:改进yolo11-TADDH
  • 【贪心算法】(第十四篇)
  • 【前端学习路线】从入门到进阶(含学习资料链接和笔记)
  • 架构师备考专栏-导航页
  • ceph rgw使用sts Security Token Service
  • 钡铼技术边缘计算2DIN2DO工业无线路由器R40A
  • 【动手学强化学习】part4-时序差分算法
  • 电脑技巧:路由器知识介绍
  • 基于MATLAB(DCT DWT)
  • OpenCV视觉分析之目标跟踪(1)计算密集光流的类DISOpticalFlow的介绍
  • ffmpeg视频滤镜:定向模糊-dblur
  • 实战-任意文件下载
  • 爱奇艺大数据多 AZ 统一调度架构
  • Loess 局部权重回归
  • 构建中小企业设备管理平台:Spring Boot应用