🌞欢迎来到人工智能的世界
🌈博客主页:卿云阁💌欢迎关注🎉点赞👍收藏⭐️留言📝
📆首发时间:🌹2026年4月16日🌹
✉️希望可以和大家一起完成进阶之路!
🙏作者水平很有限,如果发现错误,请留言轰炸哦!万分感谢!
目录
加载图像和裁剪
检测蓝色底部容器上边界
检测黄色尺子的右边界
定位试管内壁 x 范围
过渡带分位点法检测泥水界面与上液面
计算SV
返回值
加载图像和裁剪
def load_and_resize(path, dim=RESIZE_DIM): img = cv2.imread(path) if img is None: raise FileNotFoundError(f"无法读取图像: {path}") return cv2.resize(img, (dim, dim))| 维度 | 内容 | 说明 |
| 输入 (Input) | path(String) | 图像文件的物理存储路径(如"D:/data/sv30.png")。 |
dim(Integer) | 目标分辨率。默认值由RESIZE_DIM定义(如 756)。 | |
| 输出 (Output) | img(Numpy Array) | 一个三维矩阵,形状为(dim, dim, 3)。 |
RESIZE_DIM = 400 # 这里为了对比明显,我们缩放到 400x400 img_resized = load_and_resize(image_path, dim=RESIZE_DIM) resized_h, resized_w, resized_c = img_resized.shape img_raw_rgb = cv2.cvtColor(img_raw, cv2.COLOR_BGR2RGB) img_resized_rgb = cv2.cvtColor(img_resized, cv2.COLOR_BGR2RGB)改变尺寸和转换到RGB空间进行可视化
检测蓝色底部容器上边界
y(液柱分析下截止线)
def detect_container_top(hsv, lo=CONTAINER_HSV_LO, hi=CONTAINER_HSV_HI, min_px=CONTAINER_MIN_ROW): """检测蓝色底部容器上边界 y(液柱分析下截止线)""" mask = cv2.inRange(hsv, lo, hi) ys = np.where(mask.sum(axis=1) > min_px)[0] return int(ys.min()) if len(ys) else hsv.shape[0]| 维度 | 内容 | 说明 |
| 输入 (Input) | hsv | 转换到 HSV 空间的图像矩阵。 |
lo / hi | 蓝色底座的 HSV 阈值范围。 | |
min_px | 每一行必须含有多少个蓝色像素才算底座。 | |
| 输出 (Output) | int(y 坐标) | 底座上边缘的纵坐标。后续处理将以此为污泥底部的截止线。 |
mask = cv2.inRange(hsv, lo, hi)在 HSV 颜色空间中,提取处于 lo(低阈值)和 hi(高阈值)之间的像素。CONTAINER_HSV_LO
通常设定为蓝色的范围。执行后,图像中所有属于底座的蓝色部分会变成白色(255),其余地方
变成黑色(0)。这张黑白图就叫 mask(掩模)。
步骤 1 输出: mask (掩模)
数据类型: <class 'numpy.ndarray'>
矩阵形状: (200, 200) (变成单通道黑白图了)
像素取值: 只有 [ 0 255] (0代表黑色/非底座, 255代表白色/底座)
ys = np.where(mask.sum(axis=1) > min_px)[0]mask.sum(axis=1):这是关键动作。它把每一行所有的像素值加起来。如果某一行有很多白色像
素(底座所在地),这个总和就会非常大。
> min_px:这是一个过滤门槛。只有当这一行中蓝色的像素点超过 min_px(例如 50 个点)时,
我们才承认“这一行确实属于底座”。
步骤 2-A 输出: mask.sum(axis=1) (每行像素总和) 矩阵形状: (200,) (二维图像被压扁成了一维数组!) 举例看前5行(y=0~4)的值: [0 0 0 0 0] (全黑,总和为0) 举例看底座处(y=155~159)的值: [40800 40800 40800 40800 40800] (有白色像素,数值很大
步骤 2-B 输出: ys (满足条件的 Y 坐标集合) 数据内容: [150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199] 解释: 这些 Y 坐标所在的行,蓝色像素的数量达标了。
np.where(...)[0]:找出所有满足条件的行索引(y 坐标)。
return int(ys.min()) if len(ys) else hsv.shape[0]ys.min():在所有检测到底座的行中,取索引最小的那一行。在屏幕坐标系中,y 值越小代表位置
越靠上。所以这就是底座的上边缘。
else hsv.shape[0]:这是一个保底逻辑。如果图中根本没看到底座,就返回图像的总高度(即底部
的最下沿),防止后续切片报错。
步骤 3 输出: 最终结果 返回的 Y 坐标: 150 解释: ys 集合中最小的值,也就是在屏幕上位置最高的那个边缘。
检测黄色尺子的右边界
detect_ruler_right
def detect_ruler_right(hsv, lo=RULER_HSV_LO, hi=RULER_HSV_HI, min_area=RULER_MIN_AREA): """检测黄色标尺右边界 x(若无标尺返回 0)""" mask = cv2.inRange(hsv, lo, hi) k = cv2.getStructuringElement(cv2.MORPH_RECT, (7, 7)) mask = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, k, iterations=4) num, lbl, st, _ = cv2.connectedComponentsWithStats(mask) cands = [(i, st[i, cv2.CC_STAT_AREA]) for i in range(1, num) if st[i, cv2.CC_STAT_AREA] > min_area] if not cands: return 0 rl = max(cands, key=lambda x: x[1])[0] return int(st[rl, cv2.CC_