Matlab图形轮廓提取代码重复点及无限循环问题修复求助
如何修复轮廓提取代码中的无限循环与重复点问题?
问题背景
我正在编写一个提取图形轮廓的程序,逻辑是通过判断原始点周围的点是否处于该点的边界范围内来实现。我尝试用变量a、b、c、d来避免处理已经纳入轮廓的点,但当前代码存在两个问题:
- 代码陷入无限循环
already_boxed变量中出现重复点(比如(119,288)重复)
现有Matlab代码如下:
while 1 [m M] = size(already_boxed); x=0; for n = 1:m a=0; b=0; c=0; d=0; for w = 1:m if already_boxed(w,:) == [(already_boxed(n,1)+1), already_boxed(n,2)] a=1; end if already_boxed(w,:) == [(already_boxed(n,1)-1), already_boxed(n,2)] b=1; end if already_boxed(w,:) == [already_boxed(n,1), (already_boxed(n,2)+1)] c=1; end if already_boxed(w,:) == [already_boxed(n,1), (already_boxed(n,2)-1)] d=1; end end if a==0 if subimages((already_boxed(n,1)+1), already_boxed(n,2))<subimages(already_boxed(n,1),already_boxed(n,2))*0.9 && subimages((already_boxed(n,1)+1), already_boxed(n,2))>subimages(already_boxed(n,1),already_boxed(n,2))*1.1 already_boxed = [already_boxed; (already_boxed(n,1)+1), already_boxed(n,2)]; x=1; end end if b ==0 if subimages((already_boxed(n,1)-1), already_boxed(n,2))<subimages(already_boxed(n,1),already_boxed(n,2))*0.9 && subimages((already_boxed(n,1)-1), already_boxed(n,2))>subimages(already_boxed(n,1),already_boxed(n,2))*1.1 already_boxed = [already_boxed; (already_boxed(n,1)-1), already_boxed(n,2)]; x=1; end end if c==0 if subimages(already_boxed(n,1), (already_boxed(n,2)+1))<subimages(already_boxed(n,1),already_boxed(n,2))*0.9 && subimages(already_boxed(n,1), (already_boxed(n,2)+1))>subimages(already_boxed(n,1),already_boxed(n,2))*1.1 already_boxed = [already_boxed; already_boxed(n,1), (already_boxed(n,2)+1)]; x=1; end end if d ==0 if subimages(already_boxed(n,1), (already_boxed(n,2)-1))<subimages(already_boxed(n,1),already_boxed(n,2))*0.9 && subimages(already_boxed(n,1), (already_boxed(n,2)-1))>subimages(already_boxed(n,1),already_boxed(n,2))*1.1 already_boxed = [already_boxed; already_boxed(n,1), (already_boxed(n,2)-1)]; x=1; end end end if x == 0 break end end
问题分析
无限循环的核心原因
- 像素值判断逻辑完全错误:你写的
val < curr*0.9 && val > curr*1.1是不可能同时成立的,这意味着代码永远不会添加新的轮廓点,但如果x被意外赋值为1(比如调试时的误操作),循环会一直执行;另外,每次循环的m是循环开始时的already_boxed大小,后续添加的点不会在本次循环中处理,若逻辑漏洞导致x始终为1,就会陷入无限循环。 - 缺少索引越界检查:没有判断邻点是否超出图像范围,容易触发错误,间接导致循环异常。
- 像素值判断逻辑完全错误:你写的
重复点的原因
- 检查邻点是否已在
already_boxed中的方式效率极低且易出错:嵌套循环遍历所有点做矩阵匹配,不仅速度慢,还可能因为匹配逻辑的疏漏导致漏判,从而重复添加同一个点。
- 检查邻点是否已在
修复方案
1. 修正像素值判断逻辑
把不可能成立的&&改成基于相对误差的判断,更直观且符合边界检测的需求:
curr_val = subimages(curr_row, curr_col); new_val = subimages(curr_row+1, curr_col); if abs(new_val - curr_val)/curr_val > 0.1 % 符合边界条件 end
2. 高效避免重复点
用Matlab原生的ismember函数替代嵌套循环,既高效又准确地检查邻点是否已存在:
right_point = [curr_row+1, curr_col]; a = ismember(right_point, already_boxed, 'rows');
3. 确保循环能正确终止
只有当真正添加了新的有效点时才设置x=1,否则保持x=0触发循环终止;同时添加索引越界检查,避免异常。
修改后的完整代码
while true [m, ~] = size(already_boxed); x = 0; % 缓存当前轮廓点,避免循环中修改原矩阵影响遍历 current_points = already_boxed; for n = 1:m curr_row = current_points(n,1); curr_col = current_points(n,2); curr_val = subimages(curr_row, curr_col); % 检查四个邻点是否已在轮廓中 right_point = [curr_row+1, curr_col]; a = ismember(right_point, already_boxed, 'rows'); left_point = [curr_row-1, curr_col]; b = ismember(left_point, already_boxed, 'rows'); top_point = [curr_row, curr_col+1]; c = ismember(top_point, already_boxed, 'rows'); bottom_point = [curr_row, curr_col-1]; d = ismember(bottom_point, already_boxed, 'rows'); % 处理右邻点,添加索引越界检查 if ~a if curr_row+1 <= size(subimages,1) && curr_col <= size(subimages,2) new_val = subimages(curr_row+1, curr_col); if abs(new_val - curr_val)/curr_val > 0.1 already_boxed = [already_boxed; right_point]; x = 1; end end end % 处理左邻点 if ~b if curr_row-1 >= 1 && curr_col >= 1 new_val = subimages(curr_row-1, curr_col); if abs(new_val - curr_val)/curr_val > 0.1 already_boxed = [already_boxed; left_point]; x = 1; end end end % 处理上邻点 if ~c if curr_row >=1 && curr_col+1 <= size(subimages,2) new_val = subimages(curr_row, curr_col+1); if abs(new_val - curr_val)/curr_val > 0.1 already_boxed = [already_boxed; top_point]; x = 1; end end end % 处理下邻点 if ~d if curr_row >=1 && curr_col-1 >=1 new_val = subimages(curr_row, curr_col-1); if abs(new_val - curr_val)/curr_val > 0.1 already_boxed = [already_boxed; bottom_point]; x = 1; end end end end % 无新点添加则终止循环 if x == 0 break end end % 最终去重,彻底消除重复点 already_boxed = unique(already_boxed, 'rows');
额外优化建议
- 对于大图像场景,可以用逻辑矩阵标记已访问的点,比
ismember效率更高; - 如果需要更精准的轮廓提取,可以考虑结合Matlab自带的
edge函数做预处理,再进行轮廓追踪。
内容的提问来源于stack exchange,提问作者FoxEvolved




