java - Floyd-Steinberg算法在Java中的实现

标签 java algorithm image-processing java-2d

我正在尝试使用 java.awt.image.BufferedImage 在 Java 中实现 Floyd Steinberg 算法。

我使用了描述的算法 here 使用自定义调色板,我希望获得与维基百科示例(或由 Gimp 生成的示例)或多或少相同的图像,但我得到了一个非常不同的版本。

你可以看到我得到了什么

enter image description here

我显然遗漏了一些东西(输出图像的颜色不属于我的调色板),但我不知道是什么。

我做错了什么?

代码如下:

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.awt.image.IndexColorModel;
import java.io.File;
import java.io.IOException;

public class FloydSteinbergTest {

private static final Color[] PALETTE = new Color[]{
        new Color(221, 221, 221),
        new Color(19, 125, 62),
        new Color(179, 80, 188),
        new Color(107, 138, 201),
        new Color(177, 166, 39),
        new Color(65, 174, 56),
        new Color(208, 132, 153),
        new Color(64, 64, 64),
        new Color(154, 161, 161),
        new Color(46, 110, 137),
        new Color(126, 61, 181),
        new Color(46, 56, 141),
        new Color(79, 50, 31),
        new Color(53, 70, 27),
        new Color(150, 52, 48),
        new Color(25, 22, 22)};

public static void main(String[] args) {

    String lImgFile = "/tmp/test.jpg";
    try {
        // Load image
        BufferedImage lImage = ImageIO.read(new File(lImgFile));

        BufferedImage lOutImage = applyDitheredPalette(lImage, PALETTE);
        ImageIO.write(lOutImage, "png", new File("/tmp/out.png"));
    } catch (IOException lEx) {
        System.out.println(lEx.getMessage());
    }
}

/**
 * @param pPalette Color palette to apply.
 * @param pImage   Image to apply palette on.
 * @return {@link java.awt.image.BufferedImage} corresponding to pPalette applied on pImage using naive Floyd-Steinberg implementation
 */
public static BufferedImage applyDitheredPalette(BufferedImage pImage, Color[] pPalette) {
    int lWidth = pImage.getWidth();
    int lHeight = pImage.getHeight();
    IndexColorModel lColorModel = paletteToColorModel(pPalette);
    BufferedImage lImageOut = new BufferedImage(lWidth, lHeight, BufferedImage.TYPE_BYTE_INDEXED, lColorModel);
    for (int y = (lHeight - 1); y >= 0; y--) {
        for (int x = 0; x < lWidth; x++) {

            // Get original pixel color channels
            int lInitialPixelColor = pImage.getRGB(x, y);

            // Finding nearest color in the palette
            Color lNearestColor = getNearestColor(lInitialPixelColor, pPalette);

            // Set quantized pixel
            lImageOut.setRGB(x, y, lNearestColor.getRGB());

            // Applying Floyd-Steinberg dithering
            int quantizationError = lInitialPixelColor - lNearestColor.getRGB();

            if ((x + 1) < lWidth) {
                int lPixel = pImage.getRGB(x + 1, y);
                lImageOut.setRGB(x + 1, y, lPixel + (quantizationError * (7 / 16)));
            }

            if ((x - 1) > 0 && (y + 1) < lHeight) {
                int lPixel = pImage.getRGB(x - 1, y + 1);
                lImageOut.setRGB(x - 1, y + 1, lPixel + (quantizationError * (3 / 16)));
            }

            if ((y + 1) < lHeight) {
                int lPixel = pImage.getRGB(x, y + 1);
                lImageOut.setRGB(x, y + 1, lPixel + (quantizationError * (5 / 16)));
            }

            if ((x + 1 < lWidth) && (y + 1 < lHeight)) {
                int lPixel = pImage.getRGB(x + 1, y + 1);
                lImageOut.setRGB(x + 1, y + 1, lPixel + (quantizationError * (1 / 16)));
            }
            // End of Floyd-Steinberg dithering
        }
    }

    return lImageOut;
}

/**
 * @param pPalette to load color model from
 * @return {@link java.awt.image.IndexColorModel} Color model initialized using pPalette colors
 */
private static IndexColorModel paletteToColorModel(Color[] pPalette) {
    int lSize = pPalette.length;

    // Getting color component for each palette color
    byte[] lReds = new byte[lSize];
    byte[] lGreens = new byte[lSize];
    byte[] lBlues = new byte[lSize];

    for (int i = 0; i < lSize; i++) {
        Color lColor = pPalette[i];
        lReds[i] = (byte) lColor.getRed();
        lGreens[i] = (byte) lColor.getGreen();
        lBlues[i] = (byte) lColor.getBlue();
    }

    return new IndexColorModel(4, lSize, lReds, lGreens, lBlues);
}

/**
 * @param pColor   Color to approximate
 * @param pPalette Color palette to use for quantization
 * @return {@link java.awt.Color} nearest from pColor value took in pPalette
 */
private static Color getNearestColor(int pColor, Color[] pPalette) {
    Color lNearestColor = null;
    double lNearestDistance = Integer.MAX_VALUE;
    double lTempDist;
    for (Color lColor : pPalette) {
        Color lRgb = new Color(pColor);
        lTempDist = distance(lRgb.getRed(), lRgb.getGreen(), lRgb.getBlue(), lColor.getRed(), lColor.getGreen(), lColor.getBlue());
        if (lTempDist < lNearestDistance) {
            lNearestDistance = lTempDist;
            lNearestColor = lColor;
        }
    }
    return lNearestColor;
}

/**
 * @return Distance between 2 pixels color channels.
 */
private static double distance(int pR1, int pG1, int pB1, int pR2, int pG2, int pB2) {
    double lDist = Math.pow(pR1 - pR2, 2) + Math.pow(pG1 - pG2, 2) + Math.pow(pB1 - pB2, 2);
    return Math.sqrt(lDist);
}}

最佳答案

此站点用于提问,而不用于调试。但作为至少回答“我做错了什么?”这个问题的尝试:

  • (7/16) 将执行整数 除法,结果将为0。使用 (7.0/16.0) 代替
  • 您不能对 RGB 值进行算术运算!如果您有一个像 0x000000FF(蓝色)这样的 RGB 值,然后将它乘以 256,则结果将为 0x0000FF00(绿色)。 lPixel + (quantizationError * (3.0/16.0) 之类的计算必须单独为 R、G 和 B channel 完成
  • 您正在从下到上处理图像。然后在右下 像素之间分布错误(如维基百科网站上所述)不再有意义。从

    改变你的循环
    for (int y = (lHeight - 1); y >= 0; y--) 
    

    for (int y = 0; y < lHeight; y++) 
    
  • 您不能将量化误差直接存储在BufferedImage 的像素中,因为误差也可能是。图像不能处理这个。 (我对你的颜色模型也有疑问,但这只是一种直觉)

  • 您描述为“预期结果”的图像包含肯定包含在您的调色板中的颜色。

  • 最后:看看https://stackoverflow.com/a/5940260/3182664

关于java - Floyd-Steinberg算法在Java中的实现,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25391385/

相关文章:

java - android当url音频流时,音乐没有停止

java - 如何将 SOAP 方法转换为 Restful API?

java - 如何在 Java 中以排序的方式异步填充数组?

java - 通过蛮力搜索寻找最佳匹配

php - 如何保存经过处理的图像?

python - Pillow - 调整 GIF 的大小

java - JButton 中不透明像素的事件检测

ruby - 如何构建、排序和打印一棵树?

algorithm - 求解递归方程

image - 图像列表 "modes"