使用 Sobel 运算符在 PPM 上 Java 进行边缘检测
Edge detection in Java on a PPM using the Sobel Operator
我正在尝试使用 Sobel 运算符进行边缘检测,但我得到了一个奇怪的三重图像。
图像以 PPM 形式开始使用,我将其存储为颜色的多维数组:
我将它转换为灰度,这似乎工作得很好:
但是当我试图寻找边缘时,事情变得......很奇怪:
鉴于代码在 Java 中,它相当冗长,所以我只包含了转换函数。如果它适合post整个事情,我会的。
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()
进行一些范围检查,returns 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.
//
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 的每日编程挑战。
该算法看起来或多或少是正确的,当然似乎不会导致图像出现如此奇怪的失真。当然,检查这一点的简单方法是将此方法 return 作为其输入,它应该只为您提供灰度图像。我至少会试一试,以准确查明问题出在哪里,或者缩小范围。
我在家里有一个例子,它做一个完整的 Canny 边缘检测(其中一个步骤是 sobel 边缘检测)逐步打印出中间图像。如果我下班回来后您仍然遇到问题,我希望可以给您一些提示。
问题出在其他地方 - 该方法工作正常 - 在显示中可能
尝试通过保存用
生成的图像来检查每个阶段
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 数组
我正在尝试使用 Sobel 运算符进行边缘检测,但我得到了一个奇怪的三重图像。
图像以 PPM 形式开始使用,我将其存储为颜色的多维数组:
我将它转换为灰度,这似乎工作得很好:
但是当我试图寻找边缘时,事情变得......很奇怪:
鉴于代码在 Java 中,它相当冗长,所以我只包含了转换函数。如果它适合post整个事情,我会的。
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()
进行一些范围检查,returns 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.
//
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 的每日编程挑战。
该算法看起来或多或少是正确的,当然似乎不会导致图像出现如此奇怪的失真。当然,检查这一点的简单方法是将此方法 return 作为其输入,它应该只为您提供灰度图像。我至少会试一试,以准确查明问题出在哪里,或者缩小范围。
我在家里有一个例子,它做一个完整的 Canny 边缘检测(其中一个步骤是 sobel 边缘检测)逐步打印出中间图像。如果我下班回来后您仍然遇到问题,我希望可以给您一些提示。
问题出在其他地方 - 该方法工作正常 - 在显示中可能
尝试通过保存用
生成的图像来检查每个阶段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 数组