You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

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

问题分析

  1. 无限循环的核心原因

    • 像素值判断逻辑完全错误:你写的val < curr*0.9 && val > curr*1.1是不可能同时成立的,这意味着代码永远不会添加新的轮廓点,但如果x被意外赋值为1(比如调试时的误操作),循环会一直执行;另外,每次循环的m是循环开始时的already_boxed大小,后续添加的点不会在本次循环中处理,若逻辑漏洞导致x始终为1,就会陷入无限循环。
    • 缺少索引越界检查:没有判断邻点是否超出图像范围,容易触发错误,间接导致循环异常。
  2. 重复点的原因

    • 检查邻点是否已在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

火山引擎 最新活动