机器视觉学习-day13-凸包特征检测

1 凸包

凸包就是将一张图片中物体的最外层的点连接起来构成的凸多边形,它能包含物体中所有的像素点。

假如在图像中有一些点:

经过凸包检测并绘制之后,应该得到的结果:

2 基础凸包检测算法

1.先找到最左边和最右边的点

2.连接上面的两个点,将点集分为上半区和下半区,以上半区为例:

3. 找到上半区中离直线最远的点。

假设直线的方程为:,则点(x0,y0)到直线的公式为:

4. 把最远的像素点与之前最左和最右的像素点连接,把这两条直线明明为y1和y2。

5. 求出y1和y2的直线方程,把上半区所有的点带入计算与y1和y2的距离(分正负):

d=0时,表示点在y1或y2上,可以忽略
d>0时,表示点在y1或y2上方,这表示需要更新凸包点
d<0时,表示点在y1和y2下方,忽略

6. 当上一步出现d>0的情况时,需要更新凸包点,重新连接,形成新的y2和y3:

<think> 我们被要求使用Python和OpenCV,基于OTSU和凸包检测来实现粘连大米的分割。根据引用[1]的描述,流程大致包括:使用OTSU进行阈值分割,然后对二值图像进行凸包检测以分离粘连的大米颗粒,最后绘制外接矩形和重心点。 步骤分解: 1. 读取图像并转换为灰度图。 2. 使用OTSU方法进行阈值分割,得到二值图像。 3. 对二值图像进行形态学操作(可选,用于去除噪声或填充小孔)。 4. 查找轮廓,这里由于大米粘连,可能会形成大的轮廓块。 5. 对每个轮廓进行凸包检测,然后利用凸包缺陷(convexity defects)来分离粘连的部分。 6. 分离后,对每个独立的大米颗粒计算外接矩形和重心,并绘制。 但是,引用[1]中并没有给出详细的代码,因此我们需要根据常规方法来实现。 具体步骤: 1. 读取图像,转灰度。 2. 使用大津法(OTSU)二值化,注意可能需要先进行高斯滤波去噪。 3. 形态学操作(例如闭运算,先膨胀后腐蚀,用于填充小孔和连接邻近区域;或者开运算去除小噪声)。 4. 查找轮廓(注意使用合适的参数,比如外部轮廓RETR_EXTERNAL,或者树结构RETR_TREE)。 5. 对于每个轮廓,计算其凸包,然后计算凸包缺陷。 6. 凸包缺陷的深度可以用来判断粘连点。通常,当缺陷深度大于某个阈值时,我们可以认为该处是粘连点,然后根据这些点将轮廓分割。 7. 分割后,每个独立的区域可以视为一个大米颗粒,计算其外接矩形和重心。 然而,凸包缺陷分割轮廓的方法相对复杂。另一种思路是:利用凸包缺陷点来分割轮廓,然后使用分水岭算法或者对每个分割后的区域单独处理。 这里我们采用以下方法: - 首先,我们通过凸包缺陷找到可能的粘连点(即深度较大的点)。 - 然后,我们根据这些点将轮廓分割成多个部分。 - 但是,分割后的部分可能还不是独立的连通域,因此我们需要根据分割点将原轮廓拆分成多个轮廓,然后分别处理。 由于OpenCV没有直接提供分割轮廓的函数,我们需要自己实现。另一种更简单的方法是使用距离变换和分水岭算法,但题目要求使用凸包检测。 因此,我们考虑使用凸包缺陷来分离粘连的步骤: 1. 计算轮廓的凸包凸包缺陷。 2. 找出深度超过一定阈值的缺陷点(这些点通常位于粘连的凹陷处)。 3. 将轮廓在这些缺陷点处分割成多个轮廓。 但是,注意凸包缺陷返回的四个值:起始点,终点,凹陷点(即缺陷点),深度(即凹陷点到凸包的最远距离)。我们主要关注凹陷点和深度。 然而,直接根据缺陷点分割轮廓并不简单,因为一个轮廓可能有多个缺陷点,并且这些点可能属于不同的粘连区域。 另一种常见做法:根据凸包缺陷的深度点,在原二值图像上绘制这些点(作为分水岭的标记),然后应用分水岭算法。具体步骤如下: a. 对二值图像进行距离变换。 b. 在二值图像中,将每个独立的连通域(非粘连的)作为种子区域,同时将粘连区域的凹陷点(即缺陷点)作为背景标记(或者将凹陷点作为分割线)。 c. 但是,这种方法需要调整。 考虑到题目要求使用凸包检测,我们可以这样实现: - 首先,我们找到轮廓的凸包缺陷,然后根据深度阈值(比如大于轮廓最大内切圆半径的1/5等)找到可能的粘连点。 - 然后,我们在这些粘连点上绘制一条线段(例如,连接两个相邻的凹陷点)来分割粘连区域。但是这种方法需要精确确定分割线。 鉴于复杂性,这里我们采用另一种思路:使用凹点来分割轮廓,然后通过轮廓的层次关系重新建立多个轮廓。 由于实现起来较为复杂,我们可以参考OpenCV关于凸包缺陷分割粘连对象的示例。 然而,根据引用[1]的描述,它可能并没有使用分水岭,而是直接基于凸包缺陷进行分割。因此,我们尝试以下步骤: 步骤1:二值化(OTSU) 步骤2:查找轮廓(只查找最外层轮廓,因为粘连的大米可能形成一个大的轮廓) 步骤3:对每个轮廓计算凸包凸包缺陷 步骤4:根据凸包缺陷的深度,筛选出可能的粘连点(凹点) 步骤5:将筛选出的凹点作为分割点,将原轮廓分割成多个轮廓(这需要设计算法:例如,从轮廓的起点开始,遇到分割点就新建一个轮廓) 步骤6:对分割后的每个小轮廓计算外接矩形和重心 但是,步骤5的轮廓分割算法并不简单。另一种思路是:根据凹点,我们在二值图像上将这些点连接起来(形成分割线),然后重新查找轮廓。具体如下: 1. 创建一个与原图大小相同的黑色画布。 2. 将原二值图像的所有轮廓(白色)绘制到这个画布上(白色)。 3. 在画布上,将每个凹点用直线连接(例如,连接凸包缺陷返回的起始点和终点,但这样可能并不准确)或者连接相邻的凹点(需要排序)?或者直接在凹点处绘制一个黑色线条(用于断开连接)。 4. 然后重新查找轮廓,此时粘连的区域应该被分割开。 但是,如何绘制分割线?我们可以这样:对于每个轮廓的凸包缺陷,我们取深度较大的那些缺陷,然后获取该缺陷的起始点和终点,然后在二值图像上从凹陷点到起始点和终点的中点(或其他位置)画一条黑色直线?或者直接在凹陷点处画一个黑色小圆点?这样可能效果不好。 更常见的方法是:使用距离变换和分水岭算法,并结合凸包缺陷点作为标记。具体如下: - 步骤1:二值化(OTSU)并去除小噪声(形态学操作)。 - 步骤2:计算距离变换,并归一化。 - 步骤3:在距离变换图中,我们寻找峰值作为初始标记(这些峰值是每个颗粒的中心)。但是粘连区域可能只有一个峰值。 - 步骤4:同时,我们通过凸包缺陷找到的凹点也可以作为背景标记(或者作为分水岭的标记点)。 - 步骤5:将初始标记(前景:每个颗粒的中心点)和凹点(背景)合并成一个标记图像,然后应用分水岭算法。 但是,题目要求使用凸包检测,我们可以将凸包缺陷检测到的凹点作为额外的背景标记,然后应用分水岭算法。 因此,我们结合凸包缺陷和分水岭算法: 1. 读取图像,转为灰度图。 2. 高斯模糊去噪。 3. OTSU二值化(注意二值化后背景为0,前景为255)。 4. 形态学操作(可选,如开运算去除白噪声,闭运算填充黑洞)。 5. 通过二值图像获取前景区域,使用腐蚀操作获取确定的前景(即每个大米颗粒的中心区域),但腐蚀可能会将粘连区域分开。 6. 计算距离变换,并对距离变换图像进行阈值处理,得到一些峰值点作为可能的前景标记(即每个大米颗粒的中心)。 7. 查找二值图像的轮廓(此时轮廓是粘连的),然后对每个轮廓计算凸包凸包缺陷,将凸包缺陷的凹点(深度大于一定值)的位置作为背景标记(或者作为分水岭的“背景”标记?注意分水岭需要三种标记:前景、背景、未知区域)。 8. 创建一个标记图像(初始为0),将确定的前景区域标记为正整数(从1开始),将背景标记为另一个整数(例如255),而凹点位置也可以标记为背景(255)。 9. 然后,将剩余的区域标记为未知(0),应用分水岭算法。 10. 分水岭算法后,每个区域会被标记,然后我们就可以为每个区域绘制外接矩形和重心。 但是,这种方法的背景标记除了凹点,还应该包括边界区域。通常,分水岭算法中,我们通过膨胀获取背景,但这里我们使用凹点作为额外的背景标记。 由于引用[1]提到“基于OTSU+凸包检测的粘连大米分割”,因此我们重点在凸包检测部分。我们采用凸包缺陷来获取凹点,然后结合分水岭算法。 具体代码步骤: 1. 读取图像,转灰度,高斯模糊。 2. 二值化(OTSU)。 3. 形态学操作(闭运算,填充内部小孔;开运算去除小噪声)。 4. 确定前景区域: # 通过腐蚀操作得到每个对象的内部区域(但是腐蚀可能无法完全分离粘连,所以我们用距离变换) dist = cv2.distanceTransform(binary_image, cv2.DIST_L2, 5) # 归一化距离变换图像 cv2.normalize(dist, dist, 0, 1.0, cv2.NORM_MINMAX) # 对距离变换图像进行阈值处理,得到前景标记(即每个对象的中心点) _, sure_fg = cv2.threshold(dist, 0.5 * dist.max(), 255, cv2.THRESH_BINARY) sure_fg = np.uint8(sure_fg) 5. 确定背景区域:通常用膨胀操作,但我们这里同时考虑凸包缺陷点。 # 膨胀操作获取背景区域 sure_bg = cv2.dilate(binary_image, kernel, iterations=3) 6. 查找轮廓(整个二值图像),对每个轮廓计算凸包凸包缺陷,将深度大于一定阈值的凹点收集起来,作为背景标记点。 # 注意:凸包缺陷的凹点是在轮廓内部,所以这些点应该是前景和背景的交界处(即粘连的凹陷处),我们将其视为背景标记。 7. 创建标记图像:初始为0,将sure_fg(前景)标记为正整数(1,2,3...),将sure_bg区域(膨胀后的区域减去sure_fg)标记为背景(255),同时将凹点位置也标记为背景(255)。但是注意,分水岭算法要求标记是整数,背景通常是1,前景从2开始?或者按照常规:未知区域是0,背景是1,前景从2开始。 8. 然后应用分水岭算法。 然而,这种方法比较复杂,而且凹点作为背景标记的可靠性需要验证。 考虑到时间,我们采用另一种更简单的方法:利用凸包缺陷点来分割轮廓,而不使用分水岭。我们可以使用以下步骤(参考OpenCV官方文档中关于凸包缺陷分割手指数量的例子,但这里用于分割粘连物体): 1. 二值化,形态学操作,查找轮廓。 2. 对每个轮廓计算凸包凸包缺陷。 3. 根据凸包缺陷的深度(需要设定一个合适的阈值,比如轮廓周长的几分之一)来筛选凹点。 4. 将轮廓上的点按照凹点进行分割:将凹点作为分割点,将轮廓拆分成多个子轮廓。 5. 然后,每个子轮廓应该对应一个大米颗粒(注意,非粘连的颗粒可能没有凹点,所以整个轮廓就是一个颗粒)。 如何拆分轮廓?我们可以这样做: - 首先,轮廓是一个点集,我们按照顺序遍历轮廓点。 - 当我们遇到一个分割点(凹点)时,就将当前收集的点作为一个子轮廓,然后从下一个点开始重新收集,直到遇到下一个分割点。 - 但是,凹点可能不是按顺序的,因此我们需要先对凹点在轮廓中的位置(索引)进行排序。 具体步骤: a. 获取轮廓的所有点,并记录每个点在轮廓中的索引。 b. 计算凸包缺陷,获取每个缺陷的凹陷点(即凹点)在轮廓中的索引(注意:凸包缺陷返回的凹陷点是轮廓上的点,我们可以通过比较轮廓点的坐标来找到其在轮廓中的索引)。 c. 将所有的凹点索引收集起来,并排序。 d. 然后,将轮廓点按照这些凹点索引进行分割,得到多个子轮廓。 e. 对每个子轮廓计算外接矩形和重心。 但是,这种方法需要确保凹点确实在轮廓上,并且分割后的子轮廓是闭合的吗?注意,我们分割的是同一个轮廓,所以分割后的每个子轮廓应该是原轮廓的一部分,但不闭合。我们需要将分割点作为起点和终点,然后分别形成闭合轮廓?实际上,每个大米颗粒的轮廓应该是闭合的,但分割后可能不闭合,所以我们需要重新构建闭合轮廓。 另一种做法:在凹点处将轮廓断开(在二值图像上),然后重新查找轮廓。具体做法是:在二值图像上,将每个凹点周围的像素设为0(背景),从而断开粘连。然后重新查找轮廓,此时每个颗粒都是独立的连通域。 我们选择这种方法: 1. 在二值图像上,对于每个凹点,我们以该点为中心画一个小圆(半径根据实际情况,比如3-5个像素),颜色为0(背景),从而断开粘连。 2. 然后重新查找轮廓,此时每个大米颗粒就是独立的轮廓。 3. 对每个轮廓计算外接矩形和重心。 代码步骤: Step 1: 读取图像,转为灰度 Step 2: 高斯模糊 Step 3: OTSU二值化(注意:二值化后,前景为255,背景为0) Step 4: 形态学操作(闭运算填充孔洞,开运算去除噪声) Step 5: 查找轮廓(使用RETR_EXTERNAL,只检测外轮廓) Step 6: 创建一个和二值图同样大小的掩膜(全0),用于绘制轮廓(我们将所有轮廓绘制在这个掩膜上,白色) Step 7: 对每个轮廓: a. 计算凸包凸包缺陷 b. 遍历每个凸包缺陷,如果深度大于某个阈值(比如轮廓周长的1/20,但需要调整),则记录该凹点 c. 在掩膜图像上,在该凹点位置绘制一个小黑色圆(半径为3-5,具体根据图像大小调整) Step 8: 将掩膜图像作为新的二值图像(此时粘连处已经被黑色圆断开) Step 9: 在掩膜图像上重新查找轮廓(此时轮廓已经分开) Step 10: 对每个轮廓计算外接矩形和重心,并在原图上绘制 注意:在Step7中,我们是在一个单独的掩膜图像上操作,这个掩膜图像初始是绘制了所有轮廓(白色),然后我们在凹点处画黑点。这样,粘连的部分就被断开了。 这种方法简单易行。 下面我们编写代码: 注意:凸包缺陷函数cv2.convexityDefects返回的缺陷点集,每个缺陷是一个数组,包含[起点索引,终点索引,凹点索引,深度(距离)] 深度是定点到凸包的最短距离(以8-bit整数表示,实际距离需要除以256?),但实际返回的是浮点数?需要查看OpenCV文档。 在OpenCV中,凸包缺陷返回的深度是定点到凸包边的最远距离,是实际距离(浮点数),但我们需要根据图像大小设定阈值。 我们设定深度阈值为轮廓周长的0.02倍(经验值,可根据实际情况调整)。 代码示例: 由于没有具体的图像,我们按照一般步骤编写,可能需要根据实际图像调整参数。 注意:在绘制凹点处的黑圆时,我们是在一个初始为全0的掩膜上先绘制白色轮廓,然后在凹点处绘制黑圆。但是,我们也可以直接在原二值图像上操作(复制一份),然后在凹点处绘制黑圆。这样更简单。 因此,我们修改: 1. 复制二值图像(我们称为binary_image_copy)。 2. 对每个轮廓,计算凸包缺陷,对于深度大于阈值的凹点,在binary_image_copy上该点坐标处绘制一个黑色实心圆(直径5-10像素,根据图像中大米颗粒大小调整)。 3. 然后在binary_image_copy上重新查找轮廓。 这样,粘连的区域就被黑色圆点断开,形成独立的连通域。 最后,对每个轮廓计算外接矩形和重心。 代码结构: import cv2 import numpy as np # 1. 读取图像 image = cv2.imread('rice.png') gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) # 2. 高斯模糊 blurred = cv2.GaussianBlur(gray, (5,5), 0) # 3. OTSU二值化 _, binary = cv2.threshold(blurred, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU) # 4. 形态学操作:先闭运算填充内部孔洞,再开运算去除小噪声 kernel = np.ones((3,3), np.uint8) closing = cv2.morphologyEx(binary, cv2.MORPH_CLOSE, kernel, iterations=2) opening = cv2.morphologyEx(closing, cv2.MORPH_OPEN, kernel, iterations=2) # 5. 复制一份处理后的二值图像,用于后续断开粘连 binary_with_marks = opening.copy() # 6. 查找轮廓 contours, hierarchy = cv2.findContours(opening, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) # 7. 遍历每个轮廓,计算凸包凸包缺陷 for contour in contours: # 跳过小轮廓(根据面积过滤) if cv2.contourArea(contour) < 100: continue # 计算凸包 hull = cv2.convexHull(contour, returnPoints=False) # 计算凸包缺陷 defects = cv2.convexityDefects(contour, hull) if defects is not None: for i in range(defects.shape[0]): s, e, f, d = defects[i,0] # d是距离,需要除以256得到实际距离(根据文档),但实际测试发现直接使用d即可 # 计算轮廓周长 perimeter = cv2.arcLength(contour, True) # 设定深度阈值,这里取周长的0.02倍 if d > perimeter * 0.02: # 获取凹点坐标 concave_point = tuple(contour[f][0]) # 在二值图像上绘制黑色圆点(直径5) cv2.circle(binary_with_marks, concave_point, 3, (0,0,0), -1) # 8. 在断开粘连的二值图像上重新查找轮廓 contours, hierarchy = cv2.findContours(binary_with_marks, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) # 9. 遍历轮廓,计算外接矩形和重心 for contour in contours: # 跳过小轮廓 if cv2.contourArea(contour) < 100: continue # 外接矩形 x, y, w, h = cv2.boundingRect(contour) cv2.rectangle(image, (x, y), (x+w, y+h), (0,255,0), 2) # 重心 M = cv2.moments(contour) if M['m00'] != 0: cx = int(M['m10']/M['m00']) cy = int(M['m01']/M['m00']) cv2.circle(image, (cx, cy), 3, (0,0,255), -1) # 10. 显示结果 cv2.imshow('result', image) cv2.waitKey(0) cv2.destroyAllWindows() 注意:二值化时使用了THRESH_BINARY_INV,因为OTSU通常将背景设为0,前景设为255,但有时可能需要反转。具体取决于输入图像。如果大米是亮的,背景是暗的,则用THRESH_BINARY_INV可能不需要?这里我们假设输入图像是大米是暗的,背景是亮的?或者反过来。需要根据实际情况调整。 另外,凸包缺陷的深度d的阈值需要根据实际情况调整。如果断开不够,可以减小阈值;如果断开过多,则增大阈值。 以上代码实现了基于OTSU和凸包检测的粘连大米分割。 注意:由于没有实际图像,可能需要调整参数。 如果图像中大米颗粒之间粘连较少,凸包缺陷可能找不到足够的凹点,那么就不会断开,所以每个颗粒还是独立的。因此,这种方法在粘连较多时有效,非粘连时也不影响。 但是,凸包缺陷的深度阈值(0.02倍周长)可能需要根据实际情况调整。 最后,我们按照引用[1]的描述,绘制外接矩形和重心点。 由于引用[1]中提到“消除粘连后画出所有轮廓外接矩,及中心点”,所以我们的结果应该与引用[1]的图类似。 因此,我们提供上述方法作为解决方案。 注意:如果粘连非常严重,凸包缺陷可能无法检测到所有的凹点,此时可能需要其他方法。 相关问题: 1. 如何调整凸包缺陷的深度阈值? 2. 除了凸包缺陷,还有哪些方法可以分割粘连物体? 3. 如何评估大米分割的准确性? 下面我们生成相关问题。 </think> ### Python+OpenCV基于OTSU和凸包检测的粘连大米分割方法 #### 核心步骤 1. **预处理** - 灰度化:将RGB图像转为灰度图 - 高斯模糊:减少噪声干扰 - OTSU阈值分割:自动确定最佳阈值进行二值化 ```python gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) blurred = cv2.GaussianBlur(gray, (5, 5), 0) _, binary = cv2.threshold(blurred, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU) ``` 2. **形态学操作** - 闭运算填充内部孔洞 - 开运算去除小噪声 ```python kernel = np.ones((3, 3), np.uint8) closed = cv2.morphologyEx(binary, cv2.MORPH_CLOSE, kernel, iterations=2) opened = cv2.morphologyEx(closed, cv2.MORPH_OPEN, kernel, iterations=1) ``` 3. **轮廓检测凸包分析** - 查找所有外轮廓 - 计算凸包凸包缺陷 - 利用深度缺陷点分离粘连区域 ```python contours, _ = cv2.findContours(opened, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) for contour in contours: hull = cv2.convexHull(contour, returnPoints=False) defects = cv2.convexityDefects(contour, hull) if defects is not None: for i in range(defects.shape[0]): s, e, f, d = defects[i, 0] if d > 1000: # 深度阈值根据实际调整 # 在缺陷点位置绘制黑色圆点断开粘连 cv2.circle(opened, tuple(contour[f][0]), 5, (0, 0, 0), -1) ``` 4. **重新检测独立轮廓** ```python separated_contours, _ = cv2.findContours(opened, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) ``` 5. **绘制外接矩形与重心** ```python for cnt in separated_contours: # 外接矩形 x, y, w, h = cv2.boundingRect(cnt) cv2.rectangle(image, (x, y), (x+w, y+h), (0, 255, 0), 2) # 重心计算 M = cv2.moments(cnt) cx = int(M["m10"] / M["m00"]) cy = int(M["m01"] / M["m00"]) cv2.circle(image, (cx, cy), 3, (0, 0, 255), -1) ``` #### 关键参数说明 1. **深度阈值(d值)**:决定是否分离粘连的临界值 - 过大:无法分离紧密粘连 - 过小:过度分割单个米粒 - 经验公式:`d_threshold = contour_area / 50` (需实际调试) 2. **形态学核尺寸**:影响孔洞填充效果 - 过大:导致米粒变形 - 过小:无法填充间隙 #### 完整代码示例 ```python import cv2 import numpy as np def segment_rice(image_path): # 1. 图像读取与预处理 image = cv2.imread(image_path) gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) blurred = cv2.GaussianBlur(gray, (5, 5), 0) # 2. OTSU二值化 _, binary = cv2.threshold(blurred, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU) # 3. 形态学操作 kernel = np.ones((3, 3), np.uint8) closed = cv2.morphologyEx(binary, cv2.MORPH_CLOSE, kernel, iterations=2) opened = cv2.morphologyEx(closed, cv2.MORPH_OPEN, kernel, iterations=1) # 4. 凸包缺陷分析 contours, _ = cv2.findContours(opened, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) mask = opened.copy() for contour in contours: if cv2.contourArea(contour) < 100: continue hull = cv2.convexHull(contour, returnPoints=False) defects = cv2.convexityDefects(contour, hull) if defects is not None: for i in range(defects.shape[0]): s, e, f, d = defects[i, 0] if d > 1000: # 调整此阈值 cv2.circle(mask, tuple(contour[f][0]), 5, (0, 0, 0), -1) # 5. 分割后处理 final_contours, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) # 6. 绘制结果 for cnt in final_contours: x, y, w, h = cv2.boundingRect(cnt) cv2.rectangle(image, (x, y), (x+w, y+h), (0, 255, 0), 2) M = cv2.moments(cnt) if M["m00"] != 0: cx = int(M["m10"] / M["m00"]) cy = int(M["m01"] / M["m00"]) cv2.circle(image, (cx, cy), 3, (0, 0, 255), -1) return image # 使用示例 result = segment_rice("rice_image.jpg") cv2.imshow("Segmented Rice", result) cv2.waitKey(0) ``` #### 处理效果说明 最终输出图像将显示: 1. 绿色矩形框:每个米粒的外接矩形 2. 红色圆点:米粒的重心位置 3. 粘连区域通过凸包缺陷点分割为独立个体[^1] #### 典型应用场景 1. 粮食颗粒计数与分析 2. 生物细胞分割 3. 工业零件检测 4. 材料粒度分析 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值