以 BufferedImage.TYPE_BYTE_BINARY 作为输出的 Sobel 边缘检测
Sobel edge detection with BufferedImage.TYPE_BYTE_BINARY as output
我正在做一项学校作业,我们应该对图像进行 sobel 边缘检测。我们应该用 sobel 核 och 进行卷积,然后计算每个像素的梯度幅度。之后,我们应该使用阈值方法为像素赋予值 255(白色)或 0(黑色),具体取决于阈值。边缘检测的输出图像必须是BufferedImage.TYPE_BYTE_BINARY类型。我使用灰度图像作为输入,但最终结果看起来很奇怪……它绝对不会检测到边缘。
我用谷歌搜索并设法找到了工作代码(,请参阅标记的正确答案),但是,这里的输出图像是 BufferedImage.TYPE_INT_RGB 类型的,这是不允许的。 . 在这道题中,同样使用了一个BufferedImage.TYPE.INT.RGB作为边缘检测的输入。
非常感谢帮助解决这个问题!
我执行程序时的结果。边缘检测结果在最右边
边缘检测结果应该是什么样子。
我的代码:
/**
* turns an image to a grayscale version of the image
*/
public void alterImageGrayScale() throws IOException {
imageGrayScale = new BufferedImage(imageOriginal.getWidth(), imageOriginal.getHeight(), BufferedImage.TYPE_BYTE_GRAY);
for(int i = 0; i < imageOriginal.getWidth(); i++) {
for(int j = 0; j < imageOriginal.getHeight(); j++) {
Color c = new Color(imageOriginal.getRGB(i, j));
int red = c.getRed();
int green = c.getGreen();
int blue = c.getBlue();
int gray = (int) (0.2126*red + 0.7152*green + 0.0722*blue);
imageGrayScale.setRGB(i, j, new Color(gray, gray, gray).getRGB());
}
}
}
/**
* edge detection
* @throws IOException
*/
public void alterEdgeDetection() throws IOException {
imageBlackAndWhite = new BufferedImage(imageGrayScale.getWidth(), imageGrayScale.getHeight(), BufferedImage.TYPE_INT_RGB);
int x = imageGrayScale.getWidth();
int y = imageGrayScale.getHeight();
int threshold = 250;
for (int i = 1; i < x - 1; i++) {
for (int j = 1; j < y - 1; j++) {
int val00 = imageGrayScale.getRGB(i - 1, j - 1);
int val01 = imageGrayScale.getRGB(i - 1, j);
int val02 = imageGrayScale.getRGB(i - 1, j + 1);
int val10 = imageGrayScale.getRGB(i, j - 1);
int val11 = imageGrayScale.getRGB(i, j);
int val12 = imageGrayScale.getRGB(i, j + 1);
int val20 = imageGrayScale.getRGB(i + 1, j - 1);
int val21 = imageGrayScale.getRGB(i + 1, j);
int val22 = imageGrayScale.getRGB(i + 1, j + 1);
int gradientX = ((-1 * val00) + (0 * val01) + (1 * val02)) + ((-2 * val10) + (0 * val11) + (2 * val12))
+ ((-1 * val20) + (0 * val21) + (1 * val22));
int gradientY = ((-1 * val00) + (-2 * val01) + (-1 * val02)) + ((0 * val10) + (0 * val11) + (0 * val12))
+ ((1 * val20) + (2 * val21) + (1 * val22));
int gradientValue = (int) Math.sqrt(Math.pow(gradientX, 2) + Math.pow(gradientY, 2));
//???? feel like something should be done here, but dont know what
if(threshold > gradientValue) {
imageBlackAndWhite.setRGB(i, j, new Color(0, 0, 0).getRGB());
} else {
imageBlackAndWhite.setRGB(i, j, new Color(255, 255, 255).getRGB());
}
}
}
}
根据评论中的回复,将边缘检测计算出的灰度图像转换为TYPE_BYTE_BINARY
.
类型的图像应该就足够了
下面是一个MCVE,加载问题中链接到的灰度图像(包含边缘检测结果),并将其转换为二值图像。
对于二值图像的转换,有一个阈值可以通过屏幕底部的滑块进行修改:它决定了将哪个灰度值分别转换为黑色或白色像素。
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.GridLayout;
import java.awt.image.BufferedImage;
import java.net.URL;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.SwingUtilities;
public class ImageToBinary
{
public static void main(String[] args) throws Exception
{
BufferedImage input =
ImageIO.read(new URL("https://i.stack.imgur.com/jvOan.png"));
BufferedImage output = convertToBinary(input, 10);
SwingUtilities.invokeLater(() -> createAndShowGui(input, output));
}
private static void createAndShowGui(
BufferedImage input, BufferedImage output)
{
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel p = new JPanel(new GridLayout(1,2));
JLabel outputLabel = new JLabel(new ImageIcon(output));
p.add(new JLabel(new ImageIcon(input)));
p.add(outputLabel);
f.getContentPane().setLayout(new BorderLayout());
f.getContentPane().add(p, BorderLayout.NORTH);
JSlider slider = new JSlider(0, 256, 10);
slider.addChangeListener(e ->
{
int threshold = slider.getValue();
BufferedImage newOutput = convertToBinary(input, threshold);
outputLabel.setIcon(new ImageIcon(newOutput));
});
f.getContentPane().add(slider, BorderLayout.SOUTH);
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
private static BufferedImage convertToBinary(
BufferedImage input, int threshold)
{
int w = input.getWidth();
int h = input.getHeight();
BufferedImage output = new BufferedImage(
w, h, BufferedImage.TYPE_BYTE_BINARY);
int blackRgb = Color.BLACK.getRGB();
int whiteRgb = Color.WHITE.getRGB();
for (int y = 0; y < h; y++)
{
for (int x = 0; x < w; x++)
{
int rgb = input.getRGB(x, y);
int r = (rgb >> 16) & 0xFF;
int g = (rgb >> 8) & 0xFF;
int b = (rgb) & 0xFF;
int gray = (int) (0.2126 * r + 0.7152 * g + 0.0722 * b);
if (gray >= threshold)
{
output.setRGB(x, y, whiteRgb);
}
else
{
output.setRGB(x, y, blackRgb);
}
}
}
return output;
}
}
我正在做一项学校作业,我们应该对图像进行 sobel 边缘检测。我们应该用 sobel 核 och 进行卷积,然后计算每个像素的梯度幅度。之后,我们应该使用阈值方法为像素赋予值 255(白色)或 0(黑色),具体取决于阈值。边缘检测的输出图像必须是BufferedImage.TYPE_BYTE_BINARY类型。我使用灰度图像作为输入,但最终结果看起来很奇怪……它绝对不会检测到边缘。
我用谷歌搜索并设法找到了工作代码(
非常感谢帮助解决这个问题!
我执行程序时的结果。边缘检测结果在最右边
边缘检测结果应该是什么样子。
我的代码:
/**
* turns an image to a grayscale version of the image
*/
public void alterImageGrayScale() throws IOException {
imageGrayScale = new BufferedImage(imageOriginal.getWidth(), imageOriginal.getHeight(), BufferedImage.TYPE_BYTE_GRAY);
for(int i = 0; i < imageOriginal.getWidth(); i++) {
for(int j = 0; j < imageOriginal.getHeight(); j++) {
Color c = new Color(imageOriginal.getRGB(i, j));
int red = c.getRed();
int green = c.getGreen();
int blue = c.getBlue();
int gray = (int) (0.2126*red + 0.7152*green + 0.0722*blue);
imageGrayScale.setRGB(i, j, new Color(gray, gray, gray).getRGB());
}
}
}
/**
* edge detection
* @throws IOException
*/
public void alterEdgeDetection() throws IOException {
imageBlackAndWhite = new BufferedImage(imageGrayScale.getWidth(), imageGrayScale.getHeight(), BufferedImage.TYPE_INT_RGB);
int x = imageGrayScale.getWidth();
int y = imageGrayScale.getHeight();
int threshold = 250;
for (int i = 1; i < x - 1; i++) {
for (int j = 1; j < y - 1; j++) {
int val00 = imageGrayScale.getRGB(i - 1, j - 1);
int val01 = imageGrayScale.getRGB(i - 1, j);
int val02 = imageGrayScale.getRGB(i - 1, j + 1);
int val10 = imageGrayScale.getRGB(i, j - 1);
int val11 = imageGrayScale.getRGB(i, j);
int val12 = imageGrayScale.getRGB(i, j + 1);
int val20 = imageGrayScale.getRGB(i + 1, j - 1);
int val21 = imageGrayScale.getRGB(i + 1, j);
int val22 = imageGrayScale.getRGB(i + 1, j + 1);
int gradientX = ((-1 * val00) + (0 * val01) + (1 * val02)) + ((-2 * val10) + (0 * val11) + (2 * val12))
+ ((-1 * val20) + (0 * val21) + (1 * val22));
int gradientY = ((-1 * val00) + (-2 * val01) + (-1 * val02)) + ((0 * val10) + (0 * val11) + (0 * val12))
+ ((1 * val20) + (2 * val21) + (1 * val22));
int gradientValue = (int) Math.sqrt(Math.pow(gradientX, 2) + Math.pow(gradientY, 2));
//???? feel like something should be done here, but dont know what
if(threshold > gradientValue) {
imageBlackAndWhite.setRGB(i, j, new Color(0, 0, 0).getRGB());
} else {
imageBlackAndWhite.setRGB(i, j, new Color(255, 255, 255).getRGB());
}
}
}
}
根据评论中的回复,将边缘检测计算出的灰度图像转换为TYPE_BYTE_BINARY
.
下面是一个MCVE,加载问题中链接到的灰度图像(包含边缘检测结果),并将其转换为二值图像。
对于二值图像的转换,有一个阈值可以通过屏幕底部的滑块进行修改:它决定了将哪个灰度值分别转换为黑色或白色像素。
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.GridLayout;
import java.awt.image.BufferedImage;
import java.net.URL;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.SwingUtilities;
public class ImageToBinary
{
public static void main(String[] args) throws Exception
{
BufferedImage input =
ImageIO.read(new URL("https://i.stack.imgur.com/jvOan.png"));
BufferedImage output = convertToBinary(input, 10);
SwingUtilities.invokeLater(() -> createAndShowGui(input, output));
}
private static void createAndShowGui(
BufferedImage input, BufferedImage output)
{
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel p = new JPanel(new GridLayout(1,2));
JLabel outputLabel = new JLabel(new ImageIcon(output));
p.add(new JLabel(new ImageIcon(input)));
p.add(outputLabel);
f.getContentPane().setLayout(new BorderLayout());
f.getContentPane().add(p, BorderLayout.NORTH);
JSlider slider = new JSlider(0, 256, 10);
slider.addChangeListener(e ->
{
int threshold = slider.getValue();
BufferedImage newOutput = convertToBinary(input, threshold);
outputLabel.setIcon(new ImageIcon(newOutput));
});
f.getContentPane().add(slider, BorderLayout.SOUTH);
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
private static BufferedImage convertToBinary(
BufferedImage input, int threshold)
{
int w = input.getWidth();
int h = input.getHeight();
BufferedImage output = new BufferedImage(
w, h, BufferedImage.TYPE_BYTE_BINARY);
int blackRgb = Color.BLACK.getRGB();
int whiteRgb = Color.WHITE.getRGB();
for (int y = 0; y < h; y++)
{
for (int x = 0; x < w; x++)
{
int rgb = input.getRGB(x, y);
int r = (rgb >> 16) & 0xFF;
int g = (rgb >> 8) & 0xFF;
int b = (rgb) & 0xFF;
int gray = (int) (0.2126 * r + 0.7152 * g + 0.0722 * b);
if (gray >= threshold)
{
output.setRGB(x, y, whiteRgb);
}
else
{
output.setRGB(x, y, blackRgb);
}
}
}
return output;
}
}