Java 中的 matlab bwmorph(img, 'thin') 实现出错了

matlab bwmorph(img, 'thin') implementation in Java gone wrong

我正在 Java ImageJ 中实现 matlab 'bwmorph(img, 'thin')' 算法。我几乎在整个网络上进行了搜索,发现了一些效果更好的类似实现,但我在我的代码中找不到问题所在。有什么想法吗?

我的代码:

    public void run(ImageProcessor ip) {
        MakeBinary(ip);
        int sum2 = processThin(ip);
        int sum = -1;
        while (sum2 != sum) {
            sum = sum2;
            sum2 = processThin(ip);
        }
    }

    public int processThin(ImageProcessor ipOriginal) {
        int sum = 0;
        // first iteration
        ImageProcessor ip = ipOriginal.duplicate();
        for (int i = 1; i < ip.getWidth() -1; i++)
            for (int j = 1; j < ip.getHeight() -1; j++) {
                int[] neighbors = selectNeighbors(ip, i, j);
                if (G1(neighbors) == 1 && G2(neighbors) >= 2 && G2(neighbors) <= 3 && G3(neighbors) == 0)
                    ip.putPixel(i,j, 0);
            }
        // second iteration
        for (int i = 1; i < ip.getWidth() -1; i++)
            for (int j = 1; j < ip.getHeight()-1; j++) {
                int[] neighbors = selectNeighbors(ip, i, j);
                if (G1(neighbors) == 1 && G2(neighbors) >= 2 && G2(neighbors) <= 3 && G3prime(neighbors) == 0)
                    ip.putPixel(i,j, 0);
            }

        for(int i = 0; i < ip.getWidth(); i++)
            for(int j = 0; j < ip.getHeight(); j++) {
                if (ip.getPixel(i,j) != 0) sum++;
                ipOriginal.putPixel(i, j, ip.getPixel(i, j));
            }
        return sum;
    }

    private int G1(int[] input) {
        int xh = 0;
        for (int i = 1; i <= 4; i++) {
            if (input[2 * i - 1] == 0 && (input[2 * i] == 1 || (2 * i + 1 <= 8 ? input[2 * i + 1] == 1 : input[1] == 1)))
                xh += 1;
        }
        return xh;
    }

    private int G2(int[] input) {
        int n1 = 0, n2 = 0;
        n1 = toInt(toBool(input[4]) || toBool(input[3])) + toInt(toBool(input[1]) || toBool(input[2])) +
                toInt(toBool(input[8]) || toBool(input[7])) + toInt(toBool(input[6]) || toBool(input[5]));
        n2 = toInt(toBool(input[2]) || toBool(input[3])) + toInt(toBool(input[1]) || toBool(input[8])) +
                toInt(toBool(input[6]) || toBool(input[7])) + toInt(toBool(input[4]) || toBool(input[5]));
        return Math.min(n1,n2);
    }

    private int G3 (int[] input){
        return toInt((toBool(input[2]) || toBool(input[3]) || !toBool(input[8])) && toBool(input[1]));
    }

    private int G3prime (int[] input){
        return toInt((toBool(input[6]) || toBool(input[7]) || !toBool(input[4])) && toBool(input[5]));
    }

    private boolean toBool(int i ){
        return i == 1;
    }
    private int toInt(boolean i) {
        return i ? 1 : 0;
    }
    private int[] selectNeighbors(ImageProcessor ip, int i, int j) {
        int[] result = new int[9];
        result[1] = ip.getPixel(i+1,j);
        result[2] = ip.getPixel(i+1,j+1);
        result[3] = ip.getPixel(i,j+1);
        result[4] = ip.getPixel(i-1,j+1);
        result[5] = ip.getPixel(i-1,j);
        result[6] = ip.getPixel(i-1,j-1);
        result[7] = ip.getPixel(i,j-1);
        result[8] = ip.getPixel(i+1,j-1);

        for (int x = 0; x < result.length; x++)
            if (result[x] != 0) result[x] = 1;
        return result;
    }

主要问题似乎与水平线有关,但不仅如此。 注意:我添加了toBooltoInt方法来处理方便的数据类型,之前的代码是二进制的,结果显然是一样的。

编辑: 在编辑代码并省略两次迭代之间的修改之后,我现在得到了这个结果。

代码现在看起来像这样。

public int processThin(ImageProcessor ip) {
        int sum = 0;
        // first iteration
        int[][] mask = new int[ip.getWidth()][ip.getHeight()];
        for (int i = 1; i < ip.getWidth() -1; i++)
            for (int j = 1; j < ip.getHeight() -1; j++) {
                int[] neighbors = selectNeighbors(ip, i, j);
                if (G1(neighbors) == 1 && G2(neighbors) >= 2 && G2(neighbors) <= 3 && G3(neighbors) == 0)
                    mask[i][j]++;
            }
        // second iteration
        for (int i = 1; i < ip.getWidth() -1; i++)
            for (int j = 1; j < ip.getHeight()-1; j++) {
                int[] neighbors = selectNeighbors(ip, i, j);
                if (G1(neighbors) == 1 && G2(neighbors) >= 2 && G2(neighbors) <= 3 && G3prime(neighbors) == 0)
                    mask[i][j]++;
            }

        for(int i = 0; i < ip.getWidth(); i++)
            for(int j = 0; j < ip.getHeight(); j++) {
                if (mask[i][j] != 0) sum++;
                ip.putPixel(i, j, mask[i][j] > 0 ? 0 : ip.getPixel(i,j));
            }
        return sum;
    }

您原始代码中的问题是您写入了输入图像。在第一次迭代中,从左到右移动,您删除了连续的像素,因为每个像素在修改前一个像素后,都有一个背景像素作为邻居。

有多种不同的方法可以实现细化操作,但是像您的代码一样就地工作的最简单方法确实需要在细化的每次迭代中两次遍历图像:

  1. 遍历图像并标记所有候选像素。这些是具有背景邻居的像素。标记像素可以像将像素值设置为给定常量一样简单,例如 42(假设背景为 0,前景为 1 或 255 或您决定的任何值)。

  2. 再次检查图像并针对每个标记的像素,确定删除它是否会改变前景的几何形状。如果没有,请将其删除。在本次测试中,将尚未去除的标记像素作为前景。