OpenCV 中提供了 150 多种颜色空间转换方法。但我们将只研究两个,它们是使用最广泛的:BGR \leftrightarrow Gray 和 BGR \leftrightarrow HSV。<br>
对于颜色转换,我们使用函数 cv.cvtColor(input_image, flag),其中 flag 确定转换的类型。
对于 BGR \rightarrow 灰色转换,我们使用标志 cv.COLOR_BGR2GRAY。同样,对于 BGR \rightarrow HSV,我们使用标志 cv.COLOR_BGR2HSV。要获取其他标志,只需在 Python 终端中运行以下命令:&amp;lt;span&amp;gt;&amp;lt;span&amp;gt;&amp;lt;span id="MathJax-Span-14" class="mrow"&amp;gt;&amp;lt;span id="MathJax-Span-15" class="mo"&amp;gt;&rarr;&amp;lt;span&amp;gt;&amp;lt;span&amp;gt;&amp;lt;span id="MathJax-Span-17" class="mrow"&amp;gt;&amp;lt;span id="MathJax-Span-18" class="mo"&amp;gt;&rarr;
>>> 将 CV2 导入为 CV >>>标志 = [i for i in dir(CV) if i.startswith('COLOR_')] >>> print( 标志 )
注意对于HSV,色调范围为[0,179],饱和度范围为[0,255],值范围为[0,255]。不同的软件使用不同的规模。因此,如果要将 OpenCV 值与它们进行比较,则需要对这些范围进行归一化。
对象跟踪
现在我们知道了如何将 BGR 图像转换为 HSV,我们可以使用它来提取有色对象。在 HSV 中,表示颜色比在 BGR 颜色空间中更容易。在我们的应用程序中,我们将尝试提取一个蓝色的物体。所以这里是方法:
- 拍摄视频的每一帧
- 从 BGR 转换为 HSV 颜色空间
- 我们将 HSV 图像的阈值设置为一系列蓝色
- 现在单独提取蓝色物体,我们可以在该图像上做任何我们想做的事
1 import cv2 as cv 2 import numpy as np 3 4 # 初始化摄像头 5 cap = cv.VideoCapture(0) 6 7 while True: 8 # 读取一帧 9 ret, frame = cap.read() 10 if not ret: 11 break # 如果无法读取帧,则退出循环 12 13 # 将BGR图像转换为HSV 14 hsv = cv.cvtColor(frame, cv.COLOR_BGR2HSV) 15 16 # 定义HSV中蓝色的范围 17 lower_blue = np.array([110, 50, 50]) 18 upper_blue = np.array([130, 255, 255]) 19 20 # 根据蓝色范围创建掩码 21 mask = cv.inRange(hsv, lower_blue, upper_blue) 22 23 # 使用掩码和原始帧进行按位与操作,获取蓝色部分 24 res = cv.bitwise_and(frame, frame, mask=mask) 25 26 # 显示结果 27 cv.imshow('Frame', frame) 28 cv.imshow('Mask', mask) 29 cv.imshow('Result', res) 30 31 # 按'q'键退出 32 if cv.waitKey(5) & 0xFF == ord('q'): 33 break 34 35 # 释放摄像头资源并关闭所有窗口 36 cap.release() 37 cv.destroyAllWindows()
二、图像的几何变换
- 学习对图像应用不同的几何变换,如平移、旋转、仿射变换等。
- 使用以下函数:cv.getPerspectiveTransform
2.1转换
OpenCV 提供了两个转换函数,cv.warpAffine 和 cv.warpPerspective,我们可以使用它们执行各种转换。cv.warpAffine 采用 2x3 转换矩阵,而 cv.warpPerspective 采用 3x3 转换矩阵作为输入。
缩放
缩放只是调整图像的大小。OpenCV 带有一个函数 cv.resize() 用于此。可以手动指定图像的大小,也可以指定缩放因子。使用了不同的插值方法。更优选的插值方法是cv.INTER_AREA用于缩小和cv.INTER_CUBIC(慢速)&cv.INTER_LINEAR用于缩放。默认情况下,插值方法 cv.INTER_LINEAR 用于所有调整大小目的。我们可以使用以下任一方法调整输入图像的大小:
1 img = cv.imread(r'C:\Users\19225\PycharmProjects\test\src\user\static\5.jpg') 2 assert img is not None, "无法读取文件,请使用 os.path.exists() 检查文件是否存在" 3 # 缩放图片,fx和fy分别表示水平和垂直方向的缩放比例,这里都设置为2,表示将图片放大两倍 4 # 注意:Python中函数调用时,参数之间使用逗号分隔,且不要在参数名之间加空格 5 res_fx_fy = cv.resize(img, None, fx=2, fy=2, interpolation=cv.INTER_CUBIC) 6 7 cv.imshow('Original Image', img) 8 cv.imshow('Resized with fx and fy', res_fx_fy) 9 cv.waitKey(0) # 等待按键 10 cv.destroyAllWindows() # 关闭所有窗口 11 12 # 保存图像(可选) 13 cv.imwrite('resized_fx_fy.jpg', res_fx_fy)
哈哈哈,,,,,,,,,,,,,,一屏截不下
三、平移
1 img = cv.imread(r'C:\Users\19225\PycharmProjects\test\src\user\static\5.jpg', cv.IMREAD_GRAYSCALE) 2 assert img is not None, "无法读取文件,请使用 os.path.exists() 检查文件是否存在" 3 # 获取图像的行数和列数 4 width, height= img.shape 5 # 定义平移矩阵 6 M = np.float32([[1, 0, 100], [0, 1, 50]]) 7 dst = cv.warpAffine(img, M, (width, height)) # 没有先后顺序 8 cv.imshow('img', dst) 9 cv.waitKey(0) 10 cv.destroyAllWindows()
cv.warpAffine 函数的第三个参数是输出图像的大小,其形式应为(宽度、高度)。记住宽度=列数,高度=行数
四、旋转
OpenCV 提供了一个函数 cv.getRotationMatrix2D。看看下面的例子,它将图像相对于中心旋转 90 度,而不进行任何缩放。
1 # 旋转 2 img = cv.imread(r'C:\Users\19225\PycharmProjects\test\src\user\static\9.png', cv.IMREAD_GRAYSCALE) 3 assert img is not None, "无法读取文件,请使用 os.path.exists() 检查文件是否存在" 4 # 获取图像的尺寸 5 height, width = img.shape # 注意:高度在前,宽度在后 6 7 # 旋转中心为图像中心 8 center = (width // 2, height // 2) 9 10 # 生成旋转矩阵,旋转90度,缩放因子为1 11 M = cv.getRotationMatrix2D(center, 90, 1) 12 13 # 计算旋转后图像的新尺寸 14 # 注意:当旋转90度或270度时,宽度和高度会互换 15 new_width, new_height = height, width 16 17 # 应用旋转 18 dst = cv.warpAffine(img, M, (new_width, new_height)) 19 20 # 显示结果图像 21 cv.imshow('Rotated Image', dst) 22 cv.waitKey(0) # 等待按键 23 cv.destroyAllWindows() # 关闭所有窗口
五、形态学转变
形态变换是基于图像形状的一些简单操作。它通常在二进制图像上执行。它需要两个输入,一个是我们的原始图像,第二个被称为结构元素或内核,它决定了操作的性质。两个基本的形态算子是侵蚀和扩张。然后它的变体形式,如打开、关闭、梯度等也开始发挥作用。我们将在下图的帮助下一一看到它们:
原图
5.1. 侵蚀
侵蚀的基本概念就像土壤侵蚀一样,它侵蚀了前景物体的边界(始终尝试保持前景为白色)。那么它有什么作用呢?内核在图像中滑动(如在 2D 卷积中)。只有当内核下的所有像素都为 1 时,原始图像中的像素(1 或 0)才会被视为 1,否则它将被侵蚀(变为零)。
因此,发生的情况是,所有靠近边界的像素都将被丢弃,具体取决于内核的大小。因此,图像中前景物体的厚度或大小减小,或者只是白色区域减小。它对于去除小的白噪声(正如我们在colorspace章节中看到的那样),分离两个连接的对象等很有用。
1 # 侵蚀 2 img = cv.imread(r'C:\Users\19225\PycharmProjects\test\src\user\static\9.png', cv.IMREAD_GRAYSCALE) 3 assert img is not None, "无法读取文件,请使用 os.path.exists() 检查文件是否存在" 4 # 定义一个5x5的内核 5 kernel = np.ones((5, 5), np.uint8) 6 # 对图像进行侵蚀操作 7 img_erosion = cv.erode(img, kernel, iterations=1) 8 9 cv.imshow('Rotated Image', img_erosion) 10 cv.waitKey(0) # 等待按键 11 cv.destroyAllWindows() # 关闭所有窗口
5.2. 扩张
1 膨胀 = cv.dilate(img,kernel,iterations = 1)
变换
OpenCV 提供了两个转换函数,**cv.warpAffine** 和 cv.warpPerspective**,可以进行各种转换。 **cv.warpAffine 采用 2x3 变换矩阵,而 cv.warpPerspective 采用 3x3 变换矩阵作为输入。
缩放
缩放是调整图片的大小。 OpenCV 使用 cv.resize() 函数进行调整。可以手动指定图像的大小,也可以指定比例因子。可以使用不同的插值方法。对于下采样(图像上缩小),最合适的插值方法是 cv.INTER_AREA 对于上采样(放大),最好的方法是 cv.INTER_CUBIC (速度较慢)和 cv.INTER_LINEAR (速度较快)。默认情况下,所使用的插值方法都是 cv.INTER_AREA 。你可以使用如下方法调整输入图片大小:
import numpy as np
import cv2 as cv
img = cv.imread('messi5.jpg')
res = cv.resize(img,None,fx=2, fy=2, interpolation = cv.INTER_CUBIC)
#OR
height, width = img.shape[:2]
res = cv.resize(img,(2*width, 2*height), interpolation = cv.INTER_CUBIC)
平移变换
平移变换是物体位置的移动。如果知道 (x,y) 方向的偏移量,假设为 (t_x,t_y)**,则可以创建如下转换矩阵 **M:
您可以将变换矩阵存为 np.float32 类型的 numpy 数组,并将其作为 cv.warpAffine 的第二个参数。请参见以下转换(100,50)的示例:
import numpy as np
import cv2 as cv
img = cv.imread('messi5.jpg',0)
rows,cols = img.shape
M = np.float32([[1,0,100],[0,1,50]])
dst = cv.warpAffine(img,M,(cols,rows))
cv.imshow('img',dst)
cv.waitKey(0)
cv.destroyAllWindows()
警告
cv.warpAffine 函数的第三个参数是输出图像的大小,其形式应为(宽度、高度)。记住宽度=列数,高度=行数。
结果:
旋转
以 角度旋转图片的转换矩阵形式为:
但 Opencv 提供了可变旋转中心的比例变换,所以你可以在任意位置旋转图片,修改后的转换矩阵为:
其中:
为了找到这个转换矩阵,opencv 提供了一个函数, cv.getRotationMatrix2D 。请查看下面的示例,它将图像相对于中心旋转 90 度,而不进行任何缩放。
img = cv.imread('messi5.jpg',0)
rows,cols = img.shape
# cols-1 and rows-1 are the coordinate limits.
M = cv.getRotationMatrix2D(((cols-1)/2.0,(rows-1)/2.0),90,1)
dst = cv.warpAffine(img,M,(cols,rows))
结果:
仿射变换
在仿射变换中,原始图像中的所有平行线在输出图像中仍然是平行的。为了找到变换矩阵,我们需要从输入图像中取三个点及其在输出图像中的对应位置。然后 cv.getAffineTransform 将创建一个 2x3 矩阵,该矩阵将传递给 cv.warpAffine
1 import numpy as np 2 import cv2 as cv 3 from matplotlib import pyplot as plt 4 5 img = cv.imread(r'C:\Users\19225\PycharmProjects\test\src\user\static\9.png') 6 7 rows, cols, ch = img.shape 8 pts1 = np.float32([[50, 50], [200, 50], [50, 200]]) 9 pts2 = np.float32([[10, 100], [200, 50], [100, 250]]) 10 M = cv.getAffineTransform(pts1, pts2) 11 dst = cv.warpAffine(img, M, (cols, rows)) 12 plt.subplot(121), plt.imshow(img), plt.title('Input') 13 plt.subplot(122), plt.imshow(dst), plt.title('Output') 14 plt.show()
透视变换
对透视转换,你需要一个 3x3 变换矩阵。即使在转换之后,直线也将保持直线。要找到这个变换矩阵,需要输入图像上的 4 个点和输出图像上的相应点。在这四点中,任意三点不应该共线。然后通过 cv.getPerspectiveTransform 找到变换矩阵。然后对这个 3x3 变换矩阵使用 **cv.warpPerspective**
1 import numpy as np 2 import cv2 as cv 3 from matplotlib import pyplot as plt 4 5 img = cv.imread(r'C:\Users\19225\PycharmProjects\test\src\user\static\9.png') 6 7 rows, cols, ch = img.shape 8 pts1 = np.float32([[56, 65], [368, 52], [28, 387], [389, 390]]) 9 pts2 = np.float32([[0, 0], [200, 0], [0, 200], [200, 200]]) 10 M = cv.getPerspectiveTransform(pts1, pts2) 11 dst = cv.warpPerspective(img, M, (200, 200)) 12 plt.subplot(121), plt.imshow(img), plt.title('Input') 13 plt.subplot(122), plt.imshow(dst), plt.title('Output') 14 plt.show()
六、图像阈值
函数:cv.threshold,cv.adaptiveThreshold
如果像素值大于阈值,则会被赋为一个值(可能为白色),否则会赋为另一个值(可能为黑色)。使用的函数是 cv.threshold。第一个参数是源图像,它应该是灰度图像。第二个参数是阈值,用于对像素值进行分类。第三个参数是 maxval,它表示像素值大于(有时小于)阈值时要给定的值。opencv 提供了不同类型的阈值,由函数的第四个参数决定。不同的类型有:
1 import cv2 as cv 2 from cffi.backend_ctypes import xrange 3 from matplotlib import pyplot as plt 4 5 img = cv.imread(r'C:\Users\19225\PycharmProjects\test\src\user\static\9.png', 0) 6 ret, thresh1 = cv.threshold(img, 127, 255, cv.THRESH_BINARY) 7 ret, thresh2 = cv.threshold(img, 127, 255, cv.THRESH_BINARY_INV) 8 ret, thresh3 = cv.threshold(img, 127, 255, cv.THRESH_TRUNC) 9 ret, thresh4 = cv.threshold(img, 127, 255, cv.THRESH_TOZERO) 10 ret, thresh5 = cv.threshold(img, 127, 255, cv.THRESH_TOZERO_INV) 11 titles = ['Original Image', 'BINARY', 'BINARY_INV', 'TRUNC', 'TOZERO', 'TOZERO_INV'] 12 images = [img, thresh1, thresh2, thresh3, thresh4, thresh5] 13 for i in xrange(6): 14 plt.subplot(2, 3, i + 1), plt.imshow(images[i], 'gray') 15 plt.title(titles[i]) 16 plt.xticks([]), plt.yticks([]) 17 plt.show()
自适应阈值
我们采用自适应阈值。在此,算法计算图像的一个小区域的阈值。因此,我们得到了同一图像不同区域的不同阈值,对于不同光照下的图像,得到了更好的结果
它有三个“特殊”输入参数,只有一个输出参数
Adaptive Method-它决定如何计算阈值。
- cv.ADAPTIVE_THRESH_MEAN_C 阈值是指邻近地区的平均值。
- cv.ADAPTIVE_THRESH_GAUSSIAN_C 阈值是权重为高斯窗的邻域值的加权和。
Block Size-它决定了计算阈值的窗口区域的大小。
C-它只是一个常数,会从平均值或加权平均值中减去该值。
1 import cv2 as cv 2 from cffi.backend_ctypes import xrange 3 from matplotlib import pyplot as plt 4 5 img = cv.imread(r'C:\Users\19225\PycharmProjects\test\src\user\static\9.png', 0) 6 img = cv.medianBlur(img, 5) 7 ret, th1 = cv.threshold(img, 127, 255, cv.THRESH_BINARY) 8 th2 = cv.adaptiveThreshold(img, 255, cv.ADAPTIVE_THRESH_MEAN_C, \ 9 cv.THRESH_BINARY, 11, 2) 10 th3 = cv.adaptiveThreshold(img, 255, cv.ADAPTIVE_THRESH_GAUSSIAN_C, \ 11 cv.THRESH_BINARY, 11, 2) 12 titles = ['Original Image', 'Global Thresholding (v = 127)', 13 'Adaptive Mean Thresholding', 'Adaptive Gaussian Thresholding'] 14 images = [img, th1, th2, th3] 15 for i in xrange(4): 16 plt.subplot(2, 2, i + 1), plt.imshow(images[i], 'gray') 17 plt.title(titles[i]) 18 plt.xticks([]), plt.yticks([]) 19 plt.show()
Otsu 二值化
在全局阈值化中,我们使用一个任意的阈值,对吗?那么,我们如何知道我们选择的值是好的还是不好的呢?答案是,试错法。但是考虑一个双峰图像(简单来说,双峰图像是一个直方图有两个峰值的图像)。对于那个图像,我们可以近似地取这些峰值中间的一个值作为阈值,对吗?这就是 Otsu 二值化所做的。所以简单来说,它会自动从双峰图像的图像直方图中计算出阈值。(对于非双峰图像,二值化将不准确。)
为此,我们使用了 cv.threshold 函数,但传递了一个额外的符号 cv.THRESH_OTSU 。对于阈值,只需传入零。然后,该算法找到最佳阈值,并作为第二个输出返回 retval。如果不使用 otsu 阈值,则 retval 与你使用的阈值相同。
1 import cv2 as cv 2 from cffi.backend_ctypes import xrange 3 from matplotlib import pyplot as plt 4 5 img = cv.imread(r'C:\Users\19225\PycharmProjects\test\src\user\static\9.png', 0) 6 # 全局阈值 7 ret1, th1 = cv.threshold(img, 127, 255, cv.THRESH_BINARY) 8 # Otsu 阈值 9 ret2, th2 = cv.threshold(img, 0, 255, cv.THRESH_BINARY + cv.THRESH_OTSU) 10 # 经过高斯滤波的 Otsu 阈值 11 blur = cv.GaussianBlur(img, (5, 5), 0) 12 ret3, th3 = cv.threshold(blur, 0, 255, cv.THRESH_BINARY + cv.THRESH_OTSU) 13 # 画出所有的图像和他们的直方图 14 images = [img, 0, th1, 15 img, 0, th2, 16 blur, 0, th3] 17 titles = ['Original Noisy Image', 'Histogram', 'Global Thresholding (v=127)', 18 'Original Noisy Image', 'Histogram', "Otsu's Thresholding", 19 'Gaussian filtered Image', 'Histogram', "Otsu's Thresholding"] 20 for i in xrange(3): 21 plt.subplot(3, 3, i * 3 + 1), plt.imshow(images[i * 3], 'gray') 22 plt.title(titles[i * 3]), plt.xticks([]), plt.yticks([]) 23 plt.subplot(3, 3, i * 3 + 2), plt.hist(images[i * 3].ravel(), 256) 24 plt.title(titles[i * 3 + 1]), plt.xticks([]), plt.yticks([]) 25 plt.subplot(3, 3, i * 3 + 3), plt.imshow(images[i * 3 + 2], 'gray') 26 plt.title(titles[i * 3 + 2]), plt.xticks([]), plt.yticks([]) 27 plt.show()
七、OpenCV中的轮廓
函数:cv.findContours() ,cv.drawContours()
-
什么是轮廓?
轮廓可以简单地解释为连接具有相同颜色或强度的所有连续点(沿边界)的曲线。轮廓是用于形状分析以及对象检测和识别的有用工具。 - 为了获得更高的准确性,请使用二进制图像。因此,在找到轮廓之前,请应用阈值或 Canny 边缘检测。
- 从OpenCV 3.2开始,**findContours()** 不再修改源图像。
- 在OpenCV中,找到轮廓就像从黑色背景中找到白色物体。因此请记住,要找到的对象应该是白色,背景应该是黑色。
1 import cv2 as cv 2 3 img = cv.imread(r'C:\Users\19225\PycharmProjects\test\src\user\static\9.png') 4 5 imgray = cv.cvtColor(img, cv.COLOR_BGR2GRAY) 6 ret, thresh = cv.threshold(imgray, 127, 255, 0) 7 contours, hierarchy = cv.findContours(thresh, cv.RETR_TREE, cv.CHAIN_APPROX_SIMPLE) 8 cv.drawContours(img, contours, -1, (0, 255, 0), 3) 9 cv.imshow('image', img) 10 cv.waitKey(0) 11 cv.destroyAllWindows()
在 cv.findContours() 函数中有三个参数,第一个是源图像,第二个是轮廓检索模式,第三个是轮廓逼近方法。并输出轮廓和层次。轮廓是图像中所有轮廓的Python列表。每个单独的轮廓都是对象边界点的(x,y)坐标的Numpy数组。
八、模板匹配
模板匹配是一种在较大图像中搜索和查找模板图像位置的方法。为此,OpenCV 带有一个函数 cv.matchTemplate() 。它只是在输入图像上滑动模板图像(如在 2D 卷积中),并比较模板图像下的模板和输入图像的补丁
如果输入图像的大小(WxH)且模板图像的大小(wxh),则输出图像的大小为(W-w + 1,H-h + 1)。得到结果后,可以使用 cv.minMaxLoc() 函数查找最大/最小值的位置。将其作为矩形的左上角,取(w,h)作为矩形的宽度和高度。那个矩形是你的模板区域。
如果使用cv.TM_SQDIFF函数作为比较的方法, 最小值作为匹配值。
OpenCV 中的模板匹配
1 import cv2 as cv 2 from matplotlib import pyplot as plt 3 4 img = cv.imread(r'C:\Users\19225\PycharmProjects\test\src\user\static\9.png', 0) 5 img2 = img.copy() 6 template = cv.imread(r'C:\Users\19225\PycharmProjects\test\src\user\static\6.jpg', 0) 7 w, h = template.shape[::-1] 8 # All the 6 methods for comparison in a list 9 methods = ['cv.TM_CCOEFF', 'cv.TM_CCOEFF_NORMED', 'cv.TM_CCORR', 10 'cv.TM_CCORR_NORMED', 'cv.TM_SQDIFF', 'cv.TM_SQDIFF_NORMED'] 11 for meth in methods: 12 img = img2.copy() 13 method = eval(meth) 14 # Apply template Matching 15 res = cv.matchTemplate(img, template, method) 16 min_val, max_val, min_loc, max_loc = cv.minMaxLoc(res) 17 # If the method is TM_SQDIFF or TM_SQDIFF_NORMED, take minimum 18 if method in [cv.TM_SQDIFF, cv.TM_SQDIFF_NORMED]: 19 top_left = min_loc 20 else: 21 top_left = max_loc 22 bottom_right = (top_left[0] + w, top_left[1] + h) 23 cv.rectangle(img, top_left, bottom_right, 255, 2) 24 plt.subplot(121), plt.imshow(res, cmap='gray') 25 plt.title('Matching Result'), plt.xticks([]), plt.yticks([]) 26 plt.subplot(122), plt.imshow(img, cmap='gray') 27 plt.title('Detected Point'), plt.xticks([]), plt.yticks([]) 28 plt.suptitle(meth) 29 plt.show()
(其中一个比较截图)
模板与多个对象匹配
假设您正在搜索的对象在图中出现了多次, cv.minMaxLoc() 将不会为你提供所有的匹配点。在这种情况下,我们将使用阈值。所以在这个例子中,我们将使用着名游戏 Mario 的截图,并在其中找到硬币
1 import cv2 as cv 2 import numpy as np 3 from matplotlib import pyplot as plt 4 img_rgb = cv.imread('mario.png') 5 img_gray = cv.cvtColor(img_rgb, cv.COLOR_BGR2GRAY) 6 template = cv.imread('mario_coin.png',0) 7 w, h = template.shape[::-1] 8 res = cv.matchTemplate(img_gray,template,cv.TM_CCOEFF_NORMED) 9 threshold = 0.8 10 loc = np.where( res >= threshold) 11 for pt in zip(*loc[::-1]): 12 cv.rectangle(img_rgb, pt, (pt[0] + w, pt[1] + h), (0,0,255), 2) 13 cv.imwrite('res.png',img_rgb)
九、霍夫圆变化
使用霍夫变换来查找图像中的圆圈
圆圈在数学上表示为
1 import numpy as np 2 import cv2 as cv 3 4 img = cv.imread(r'C:\Users\19225\PycharmProjects\test\src\user\static\9.png', 0) 5 img = cv.medianBlur(img, 5) 6 cimg = cv.cvtColor(img, cv.COLOR_GRAY2BGR) 7 circles = cv.HoughCircles(img, cv.HOUGH_GRADIENT, 1, 20, 8 param1=50, param2=30, minRadius=0, maxRadius=0) 9 circles = np.uint16(np.around(circles)) 10 for i in circles[0, :]: 11 # draw the outer circle 12 cv.circle(cimg, (i[0], i[1]), i[2], (0, 255, 0), 2) 13 # draw the center of the circle 14 cv.circle(cimg, (i[0], i[1]), 2, (0, 0, 255), 3) 15 cv.imshow('detected circles', cimg) 16 cv.waitKey(0) 17 cv.destroyAllWindows()
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· 写一个简单的SQL生成工具