【问题标题】:Edge detection in Java on a PPM using the Sobel Operator使用 Sobel 算子在 PPM 上的 Java 边缘检测
【发布时间】:2016-01-08 01:59:00
【问题描述】:

我正在尝试使用 Sobel 算子进行边缘检测,但我得到了一个奇怪的一式三份图像。

图像以 PPM 开始,我将其存储为 Color 的多维数组:

我将其转换为灰度,这似乎工作得很好:

但是当我尝试寻找边缘时,事情变得……奇怪:

鉴于代码是用 Java 编写的,所以比较冗长,所以我只包含了转换函数。如果适合发全篇,我会的。

private Color[][] sobelConvert(Color[][] image) {
    Color[][] newPPM = new Color[maxX][maxY];

    for (int y = 0; y < maxY; y++) {
        for (int x = 0; x < maxX; x++) {
            int a = testForColor(x-1, y-1, image);
            int b = testForColor(x-1, y,   image);
            int c = testForColor(x-1, y+1, image);

            int d = testForColor(x, y-1, image);
            int e = testForColor(x, y+1,   image);

            int f = testForColor(x+1, y-1, image);
            int g = testForColor(x+1, y,   image);
            int h = testForColor(x+1, y+1, image);

            int eH = (c + 2*e + h) - (a + 2*d + f);
            int eV = (f + 2*g + h) - (a + 2*b + c);

            int edgyness = (int) Math.sqrt((eH * eH) + (eV * eV));
            if (edgyness > 255) {
                edgyness = 255;
            }
            if (edgyness < 0) {
                edgyness = 0;
            }
            newPPM[x][y] = new Color(edgyness, edgyness, edgyness);
        }
    }
    return newPPM;
}

testForColor() 进行一些范围检查,并返回 Color 对象中的 RGB 值之一 - 这样我想我可以计算出亮度。

private int testForColor(int x, int y, Color[][] ppm) {
    if (x < 0 || x >= maxX) {
        return 0;
    }

    if (y < 0 || y >= maxY) {
        return 0;
    }

    return ppm[x][y].getGreen();
}

编辑:添加代码以读取和写入 PPM:

public Edgeness(String fileName) throws Exception {
    this.fileName = fileName;
    boolean foundDims = false;
    Color c = null;
    int r, g, b;

    String line;

    try {
        BufferedReader br = new BufferedReader(new FileReader(fileName));
        while ((line = br.readLine()) != null) {
            //System.out.println(line);

            // Test if the line has any lenght (ignore empty lines)
            // First line.  We will ignore it.
            // First character is a #.  Ignore it.  (this is bad - should check the whole line.  TODO.)
            if (line.length() > 0 && !line.equals("P3") && !(line.charAt(0) == '#')) {
                // Check for image dimensions.  Only do this once.  I assume regex's take longer
                // than testing a boolean.
                if (!foundDims) {
                    Pattern pImDim = Pattern.compile("(\\d+) (\\d+)");
                    Matcher mImDim = pImDim.matcher(line);
                    if (mImDim.find()) {
                        maxX = Integer.parseInt(mImDim.group(1));
                        maxY = Integer.parseInt(mImDim.group(2));
                        ppm = new Color[maxX][maxY];
                    }
                    foundDims = true;
                }

                // Hmm.  Last capture is kept, the rest are overwritten.  so.. we split instead.
                // https://stackoverflow.com/questions/3537878/how-to-capture-an-arbitrary-number-of-groups-in-javascript-regexp
                String[] rgbVals = line.split(" ");
                if (rgbVals.length % 3 == 0) {
                    for (int i = 0; i < rgbVals.length; i += 3) {
                        r = Integer.parseInt(rgbVals[i]);
                        g = Integer.parseInt(rgbVals[i+1]);
                        b = Integer.parseInt(rgbVals[i+2]);
                        c = new Color(r, g, b);
                        addNextColor(c);
                    }
                }
            }
        }
        br.close();
    } finally {
    }
}
private void addNextColor(Color c) {
    ppm[currentX][currentY] = c;
    currentY++;
    if (currentY >= maxY) {
        currentX++;
        currentY = 0;
    }
}

还有将 PPM 保存到文件的功能。如果我加载一个 PPM,然后立即调用 render(),生成的图像就是一个精确的副本。同样,如果我保存灰度图像,我会得到上面包含的图像。

private void render(Color[][] image, String fileName) throws IOException {
    ArrayList<String> output = new ArrayList<String>();
    output.add("P3");
    output.add(maxX + " " + maxY);
    output.add("255");

    for (int x = 0; x < maxX; x++) {
        StringBuilder sb = new StringBuilder();
        for (int y = 0; y < maxY; y++) {
            if (image[x][y] != null) {
                sb.append(image[x][y].getRed() + " " + image[x][y].getGreen() + " " + image[x][y].getBlue() + " ");
            } else {
                sb.append("0 0 0 ");
            }
        }
        output.add(sb.toString());
    }

    BufferedWriter bw = new BufferedWriter(new FileWriter(fileName));
    for (String s : output) {
        bw.write(s);
        bw.newLine();
    }
    bw.close();
}

我使用 ImageMagik 的转换程序将 PPM 输出转换为 PNG。

我在http://blog.saush.com/2011/04/20/edge-detection-with-the-sobel-operator-in-ruby/ 找到了一个用 Ruby 编写的实现,当它适应 Java 时,产生了相同的结果。

FWIW,这是来自 Reddit 的每日编程挑战。

【问题讨论】:

  • 请将您用于读取/写入 PPM 图像的代码发布到Color[][]/从Color[][]

标签: java edge-detection


【解决方案1】:

问题出在其他地方 - 方法正常 - 可能在显示中

尝试通过保存使用 a 生成的图像来检查每个阶段

BufferedImage bim=new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
int[] pix=new int[w*h];
for(i=0; i<pix.length; i++) pix[i]=(p[i%w][i/w].getBlue()<<16)|(p[i%w][i/w].getBlue()<<8)|p[i%w][i/w].getBlue()|0xff000000;
bim.setRGB(0, 0, w, h, pix, o, w);

try {
ImageIO.write(bim, "png", new File(path+".png"));
} catch (IOException ex) { ex.printStackTrace(); }

其中 p 是您的 ppm 数组

【讨论】:

  • 我不知道是否将其标记为正确 - 但这有助于暴露问题。根本问题是我错误地阅读了 PPM。这被错误的 Render() 函数“纠正”了。感谢这里的提示 - 这是一个巨大的帮助!
【解决方案2】:

该算法看起来或多或少是正确的,当然似乎不会导致图像出现如此奇怪的失真。当然,检查这一点的简单方法是让这个方法返回它的输入,它应该只给你灰度图像。我至少会试一试,以确定问题出在哪里,或者缩小范围。

我在家里有一个示例,它执行完整的 Canny 边缘检测(其中一步是 sobel 边缘检测),逐步打印出中间图像。如果我下班后你仍然遇到问题,我希望能给你一些建议。

【讨论】:

    猜你喜欢
    • 2021-06-02
    • 1970-01-01
    • 2014-10-17
    • 1970-01-01
    • 1970-01-01
    • 2016-10-03
    • 2015-08-09
    • 2011-02-25
    • 2018-12-12
    相关资源
    最近更新 更多