双层环形的算法逻辑整理

关于检测仪器开发过程中使用的“双层环形的算法逻辑”

废话不多说,直接上干货:我们把开发过程中,把双层环形的算法逻辑认真梳理一下

1. 函数定义与导入:
首先,该代码定义了一个measure函数,这个函数接收两个参数:input_path和output_path。这两个参数预计是图像文件的路径,其中input_path是待处理的图像路径,output_path是处理后图像的保存路径。然后,代码导入了两个库:

matplotlib和cv2:

matplotlib库用于显示图像;

cv2库用于进行图像处理。

2. 读取图像与转换为灰度图像:
使用cv2.imread()函数,我们读取了指定路径的图像文件。然后,使用cv2.cvtColor()函数将读取的图像从彩色转换为灰度图像。

3. 二值化处理:
对灰度图像进行阈值处理,使用的是cv2.threshold()函数。这个函数将根据图像的直方图自动计算阈值,从而实现自动阈值分割。

4. 提取ROI:
这一步中,调用了一个名为cutimg()的自定义函数。这个函数可能会对图像进行一些形态学操作以平滑图像并去除噪声,还会找到ROI的轮廓,并根据轮廓面积进行排序,提取最大的轮廓。

5. K-means聚类分割:
接下来,代码调用了另一个自定义函数Kmeans_seg()。这个函数将ROI图像展平为一维数组,然后使用K-means算法将其分为3个部分。根据这三个聚类中心,将一维数组重新构造为原始图像的形状,并将像素值更改为对应的标签值。

6. 标注:
在这一步中,代码调用了第三个自定义函数annotate_image()。这个函数可能会在图像上绘制一些线条、圆圈等以标记图像中的特定区域或对象。如果函数返回的是整数类型,则说明标注失败,程序会返回一些默认值并终止执行。否则,程序会继续执行并保存标注后的图像。

7. 显示与返回结果:
最后,使用plt.imshow()函数显示标注后的图像,然后使用plt.show()函数显示图像窗口。此外,还返回了一些计算结果,如各种特征值的平均值、最大值、最小值等。这些结果可能用于后续的分析或处理。

8. 主程序执行:
如果这段代码作为主程序运行(而不是作为模块导入),则会执行以上流程。调用measure函数,传入输入图像路径和输出图像路径,然后显示结果并返回计算结果。

 

二、代码说明

1、自定义函数:Nrotate(angle, valuex, valuey, pointx, pointy),

它接受五个参数:angle(旋转角度),valuex(一个包含x坐标值的列表或数组),valuey(一个包含y坐标值的列表或数组),pointx(旋转中心的x坐标),pointy(旋转中心的y坐标)。

函数的主要功能:是对给定坐标点 (valuex[i], valuey[i]) 进行逆时针旋转 angle 度。

代码中使用了NumPy库进行向量操作和数学库中的三角函数(math.cos和math.sin)来实现旋转。具体的步骤如下:

将valuex和valuey转换为NumPy数组,方便进行向量操作。

计算旋转后的x坐标 nRotatex,通过 (valuex-pointx)*math.cos(angle) - (valuey-pointy)*math.sin(angle) + pointx 公式得到。这个公式是二维旋转变换的数学表达式,将每个点 (valuex[i], valuey[i]) 绕 (pointx, pointy) 逆时针旋转 angle 度。

计算旋转后的y坐标 nRotatey,通过 (valuex-pointx)*math.sin(angle) + (valuey-pointy)*math.cos(angle) + pointy 公式得到。同样,这个公式也是二维旋转变换的数学表达式。

返回旋转后的坐标 nRotatex 和 nRotatey。

 

2、自定义函数Srotate

它接受五个参数:angle(旋转角度),valuex(包含x坐标值的列表或数组),valuey(包含y坐标值的列表或数组),pointx(旋转中心的x坐标),pointy(旋转中心的y坐标)。

函数的作用是:对给定的坐标点 (valuex[i], valuey[i]) 进行顺时针旋转 angle 度。

代码使用了NumPy库进行向量操作和数学库中的三角函数(math.cos和math.sin)来实现旋转。具体的步骤如下:

将 valuex 和 valuey 转换为NumPy数组,以便进行向量操作。

计算旋转后的x坐标 sRotatex,通过公式 (valuex-pointx)*math.cos(angle) + (valuey-pointy)*math.sin(angle) + pointx 来计算。这个公式是二维旋转变换的数学表达式,将每个点 (valuex[i], valuey[i]) 绕 (pointx, pointy) 顺时针旋转 angle 度。

计算旋转后的y坐标 sRotatey,通过公式 (valuey-pointy)*math.cos(angle) - (valuex-pointx)*math.sin(angle) + pointy 来计算。同样,这个公式也是二维旋转变换的数学表达式。

返回旋转后的坐标 sRotatex 和 sRotatey。

 

3、自定义函数myArea,它接受一个轮廓作为参数 cnt

函数的功能是:计算给定轮廓的最小外接矩形的面积,并返回一个布尔值。具体的步骤如下:

使用 cv.minAreaRect(cnt) 函数计算给定轮廓的最小外接矩形,返回结果存储在变量 rect 中。最小外接矩形是能够完全包围轮廓的最小面积矩形。

使用 cv.boxPoints(rect) 函数获取最小外接矩形的四个顶点坐标,并将结果存储在变量 box 中。这些顶点坐标的顺序是无序的。

将 box 中的顶点坐标转换为整数类型,使用 np.int0() 函数实现,并将结果重新赋值给 box。

使用 cv.contourArea(box) 函数计算由最小外接矩形的顶点构成的多边形的面积。

返回一个布尔值,表示计算得到的面积是否大于0。如果返回值为True,则表示最小外接矩形的面积大于0;如果返回值为False,则表示最小外接矩形的面积为0或小于0。

4、自定义函数line

它接受一些参数:x1,y1,x2,y2(表示线段的起点和终点坐标)、img0(图像对象)、contours_(包含轮廓的列表)、t_six(用于存储线段长度的列表)和 color(线段的颜色)。

函数的主要功能:是在给定图像上绘制一条线段,并计算该线段的长度。具体的步骤如下:

初始化变量 tmp1x,tmp1y,tmp0x,tmp0y,min1 和 min2。

从 contours_ 中获取第一个轮廓的x坐标和y坐标列表,分别存储在 listx0 和 listy0 中。

从 contours_ 中获取第二个轮廓的x坐标和y坐标列表,分别存储在 listx1 和 listy1 中。

如果 x1 和 x2 相等,则进入条件判断块:

遍历 listx0,如果找到一个点的x坐标与 x1 相等且 (y2-y1)(listy0[i]-y1) > 0,则将该点的y坐标赋值给 tmp0y。

遍历 listx1,如果找到一个点的x坐标与 x1 相等且 (y2-y1)(listy0[i]-y1) > 0,则将该点的y坐标赋值给 tmp1y。

将 tmp0x 和 tmp1x 设置为 x1。

如果 x1 和 x2 不相等,则进入条件判断块:

初始化变量 tmp 为一个较大的值。

遍历 listx0,对于每个点 listx0[i]:

如果 listx0[i] 不等于 x1,计算斜率的差值 tmp,并与 min1 进行比较。

如果 (y2-y1)*(listy0[i]-y1)>=0 且 (x2-x1)*(listx0[i]-x1)>=0,则更新 min1,tmp0x 和 tmp0y。

遍历 listx1,对于每个点 listx1[i]:

如果 listx1[i] 不等于 x1,计算斜率的差值 tmp,并与 min2 进行比较。

如果 (y2-y1)*(listy1[i]-y1)>=0 且 (x2-x1)*(listx1[i]-x1)>=0,则更新 min2,tmp1x 和 tmp1y。

使用勾股定理计算线段的长度 d,即

math.sqrt(pow((tmp0x- tmp1x), 2) + pow((tmp0y - tmp1y), 2))。

将线段的长度 d 添加到列表 t_six 中。

使用 cv2.line() 函数在图像 img0 上绘制从 (tmp0x, tmp0y) 到 (tmp1x, tmp1y) 的线段,颜色为 color,线宽为2。

5、自定义函数annotate_image

它接受两个参数:img(图像对象)和 imgray(灰度图像对象)。

函数的主要功能:是对输入的图像进行注释和分析。具体的步骤如下:

复制灰度图像 imgray,并将其存储在变量 imgray0 中。

使用 Canny 边缘检测算法对图像 img 进行处理,得到边缘图像 imgc。

对边缘图像 imgc 进行高斯模糊处理,使用内核大小 (7, 7)。

使用 cv2.findContours() 函数查找图像 imgc 中的轮廓,并存储在变量 contours 中。

将 contours 转换为列表,并将其存储在变量 contours_ 中。

如果轮廓的数量小于3,则打印错误信息并返回0。

根据轮廓的面积进行降序排序,将排序后的轮廓存储在 contours_ 中。

遍历轮廓列表,将满足条件的轮廓添加到 contours_ 中:

轮廓的面积小于等于 contours_[-1] 的面积的 0.9 倍。

轮廓的周长小于等于 contours_[-1] 的周长的 0.9 倍。

如果 contours_ 的数量小于3,则打印错误信息并返回0。

从 contours_ 中取出前两个轮廓,分别存储在 contours0_ 和 contours1_ 中。

在图像 imgray0 上绘制轮廓 contours0_,颜色为红色,线宽为2。

在图像 imgray 上绘制轮廓 contours_ 的前三个轮廓,颜色为红色,线宽为2。

使用 cv2.moments() 函数计算第一个轮廓 contours_[0] 的各阶矩,并将结果存储在变量 M 中。

计算第一个轮廓的质心坐标 cx 和 cy。

调用名为 annotate_loop 的函数,对 contours0_ 进行标注和分析,返回一系列结果,并将结果存储在一系列变量中。

类似地,分别对 contours1_ 和 contours2_ 调用 annotate_loop 函数,获取相应的结果。

将所有结果以及中心轮廓 contours_[0] 的标注和分析结果返回。

 

6、自定义函数annotate_loop

它接受多个参数,包括中心点坐标 cx 和 cy、轮廓列表 contours_、图像对象 img 和颜色值 color。

函数的主要功能:是对轮廓进行标注和分析。具体的步骤如下:

创建一个空列表 t_six,用于存储后续的结果。

从轮廓 contours_[0] 中提取 x 和 y 坐标,并将它们存储在 listx1 和 listy1 中。

从轮廓 contours_[1] 中提取 x 和 y 坐标,并将它们存储在 listx2 和 listy2 中。

获取轮廓 contours_[0] 和 contours_[1] 的点数,分别存储在 m 和 n 中。

调用名为 fast_distance 的函数,计算两个轮廓之间的距离和其他相关值,并将结果存储在列表 list_ 中。

将距离值和其他值从 list_ 中提取出来,分别存储在 x1、y1、x2、y2、d、maxt_all 和 ave_all 中。

计算整体同心度和整体偏心率,并分别存储在 condentricity_all 和 eccentricity_all 中。

将最薄厚度 d 添加到列表 t_six 中。

在图像 img 上绘制两个轮廓之间的连线,并使用指定的颜色和线宽。

根据旋转角度 60 度和 120 度计算两个新点的坐标,并将它们存储在 sx1、sy1、sx2 和 sy2 中。

调用名为 line 的函数,绘制从中心点到这两个新点的连线,并在绘制过程中进行标注和分析,将结果存储在列表 t_six 中。

绘制另外两条经过中心点的对称线,并调用 line 函数进行标注和分析,将结果存储在列表 t_six 中。

调用名为 comp_t_six 的函数,对列表 t_six 进行计算和分析,获取一系列结果,并将结果存储在各自的变量中。

将所有结果作为返回值返回。

7、自定义函数comp_t_six

它接受两个参数 t_six 和 contours_。

函数的主要功能:是对六个点的厚度进行计算和分析。具体的步骤如下:

使用 cv2.minAreaRect 函数计算轮廓 contours_[0] 的最小外接矩形,获取矩形的中心坐标 (d_x, d_y),宽和高以及旋转角度。这些值将被忽略,仅保留 (d_x, d_y) 作为最小外接矩形的宽度和高度。

如果 d_x 大于 d_y,则交换 d_x 和 d_y 的值,确保 d_x 代表较小的值,d_y 代表较大的值。

计算 d_xy,即宽度和高度的平均值。

计算椭圆度,使用公式 (d_y - d_x) / d_xy,其中 d_y - d_x 表示宽度和高度之差,d_xy 表示宽度和高度的平均值。

计算六个点的厚度的总和 sum,遍历 t_six 列表,将每个点的厚度值加到总和中。

计算六个点的厚度的平均值 ave_six,使用公式 sum / len(t_six)。

计算六个点的厚度的最小值 mint_six,使用 min 函数找到列表 t_six 中的最小值。

计算六个点的厚度的最大值 maxt_six,使用 max 函数找到列表 t_six 中的最大值。

计算六个点的厚度的同心度 condentricity_six,使用公式 mint_six / maxt_six。

计算六个点的厚度的偏心率 eccentricity_six,使用公式 (maxt_six - mint_six) / maxt_six。

计算面积 area,使用公式 π(Davg - tavg)tavg,其中 π 是圆周率,Davg 是宽度和高度的平均值,tavg 是六个点的厚度的平均值。

将所有计算结果作为返回值返回。

8、自定义函数Kmeans_seg

它接受一个参数 img0,表示输入图像。

函数的主要功能:是使用K均值聚类算法对图像进行分割。具体的步骤如下:

使用 cv2.cvtColor 函数将输入图像 img0 从BGR颜色空间转换为灰度图像,得到 img。

将图像展平为一维数组,将其形状变为 (图像高度 * 图像宽度, 1),并将数据类型转换为 np.float32。

定义迭代参数 criteria,包括迭代终止条件和最大迭代次数,这里设置为 20 次迭代,最大误差为 0.5。

定义聚类的标志 flags,使用随机初始聚类中心。

调用 cv2.kmeans 函数,对展平后的图像进行 K 均值聚类,聚类数为 3,传入迭代参数 criteria、迭代次数为 10 次和聚类标志 flags。函数返回紧凑度(compactness)、标签(labels)和聚类中心(centers)。

将标签重新变形为原始图像的形状,得到分割结果图像 img_output。

将标签值为2的像素设为255,表示聚类结果的一个类别。

将标签值为1的像素设为125,表示聚类结果的另一个类别。

将 img_output 转换为 np.uint8 类型的数组,并作为函数的返回值。

9、自定义函数cutimg

它接受两个参数 inputpic 和 bin。

函数的主要功能是对输入的图像进行处理和裁剪。具体的步骤如下:

记录开始时间 Tcut1。

使用 cv2.imread 函数读取输入图像 inputpic,得到原始图像 img0。

使用 cv2.imread 函数以灰度模式读取输入图像 inputpic,得到灰度图像 imgray。

使用 cv2.threshold 函数对灰度图像进行阈值分割,将像素值大于等于 bin 的像素设为255,其余像素设为0,得到二值化图像 imgray。

记录结束时间 Tcut2,并打印出阈值分割所花费的时间。

使用 cv2.Canny 函数对二值化图像 imgray 进行边缘检测,得到边缘图像 imgc。

使用 cv2.GaussianBlur 函数对边缘图像 imgc 进行高斯模糊,平滑边缘。

使用 plt.imsave 函数保存边缘图像 imgc。

使用 cv2.findContours 函数找到边缘图像 imgc 中的轮廓,得到轮廓列表 contours。

如果找到的轮廓数量小于1,则打印信息并返回0。

将轮廓列表 contours 按照轮廓面积进行降序排序。

如果最大面积的轮廓的面积的0.2倍大于该轮廓的实际面积,则打印信息并返回0。

将最大面积的轮廓的 x 和 y 坐标分别存储在 listx1 和 listy1 中。

根据轮廓的 x 和 y 坐标的最小值和最大值,裁剪原始图像 img0 得到感兴趣区域 imgray。

如果感兴趣区域的高度或宽度为0,或者高宽比小于0.7或宽高比小于0.7,则打印信息并返回0。

返回裁剪后的感兴趣区域 imgray。

10、自定义函数measure

它接受三个参数 inputpic、outputpath1 和 outputpath2。

函数的主要功能是对输入的图像进行测量和处理,并保存结果图像到指定路径。具体的步骤如下:

使用 cv2.imread 函数以灰度模式读取输入图像 inputpic,得到灰度图像 src。

设置阈值 triThe 和最大像素值 maxval。

使用 cv2.threshold 函数对灰度图像进行阈值分割,使用 OTSU 方法自动计算阈值,得到二值化图像,阈值存储在 bin 中。

根据输入图像路径获取图像名称 name_str。

记录开始时间 T1。

调用 cutimg 函数对输入图像进行处理和裁剪,得到裁剪后的图像 imgray。

记录结束时间 T2,并打印出裁剪所花费的时间。

如果 imgray 的类型为整数,表示裁剪失败,返回一些默认值。

调用 Kmeans_seg 函数对裁剪后的图像 imgray 进行 K-means 分割,得到分割结果图像 img。

使用 plt.imsave 函数保存分割结果图像 img。

记录结束时间 T3,并打印出 K-means 分割所花费的时间。

调用 annotate_image 函数对分割结果图像 img 和裁剪后的图像 imgray 进行测量和标注,得到测量结果。

如果测量结果的类型为整数,表示测量失败,返回一些默认值。

将测量结果中的各项值分别赋给对应的变量。

记录结束时间 T4,并打印出标注所花费的时间和总耗时。

使用 plt.imsave 函数分别保存标注结果图像 annotated_image 和原始标注结果图像 annotated_image0。

返回测量结果的各项值。

好啦,本期把开发过程的函数都梳理了一便,欢迎各位小伙伴指正,有任何问题欢迎致电子邮箱www.lansdia@163.com