【问题标题】:How to implement 1-Bit Dithering using Java?如何使用 Java 实现 1 位抖动?
【发布时间】:2021-01-29 11:26:31
【问题描述】:

最近,我们的老师让我们使用 Java 将彩色图像转换为 1 位图像。经过一番实验,我得到了以下结果:

BufferedImage image = ...
for (int y = 0; y < image.getHeight(); y++) {
  for (int x = 0; x < image.getWidth(); x++) {
    int clr = image.getRGB(x, y);
    int  r   = (clr & 0x00ff0000) >> 16;
    int  g = (clr & 0x0000ff00) >> 8;
    int  b  =  clr & 0x000000ff;
                
    double mono = 0.2126*r + 0.7152*g + 0.0722*b;
                
    int c = mono < 128 ? 1 : 0;
    
    //Adding to image buffer
    buffer.add(c);
  }
}

好吧,它有效,但不幸的是,很多细节都丢失了。这是一个比较:

原文:

输出:

我想要什么:(总部:https://i.stack.imgur.com/vlEAE.png

我正在考虑在我的转换器中添加抖动,但我还没有找到可行的方法,更不用说任何伪代码了。

谁能帮帮我?

编辑:

所以我创建了一个DitheringUtils-class:

import java.awt.Color;
import java.awt.image.BufferedImage;

public class DitheringUtils {
    
    public static BufferedImage dithering(BufferedImage image) {
        Color3i[] palette = new Color3i[] {
            new Color3i(0, 0, 0),
            new Color3i(255, 255, 255)
        };
        
        int width = image.getWidth();
        int height = image.getHeight();
        
        Color3i[][] buffer = new Color3i[height][width];
        
        for(int y=0;y<height;y++) {
            for(int x=0;x<width;x++) {
                buffer[y][x] = new Color3i(image.getRGB(x, y));
            }
        }
        
        for(int y=0; y<image.getHeight();y++) {
            for(int x=0; x<image.getWidth();x++) {
                Color3i old = buffer[y][x];
                Color3i nem = findClosestPaletteColor(old, palette);
                image.setRGB(x, y, nem.toColor().getRGB());
                
                Color3i error = old.sub(nem);
                
                if (x+1 < width)         buffer[y  ][x+1] = buffer[y  ][x+1].add(error.mul(7./16));
                if (x-1>=0 && y+1<height) buffer[y+1][x-1] = buffer[y+1][x-1].add(error.mul(3./16));
                if (y+1 < height)         buffer[y+1][x  ] = buffer[y+1][x  ].add(error.mul(5./16));
                if (x+1<width && y+1<height)  buffer[y+1][x+1] = buffer[y+1][x+1].add(error.mul(1./16));
            }
        }
        
        return image;
    }

    private static Color3i findClosestPaletteColor(Color3i match, Color3i[] palette) {
        Color3i closest = palette[0];
        
        for(Color3i color : palette) {
            if(color.diff(match) < closest.diff(match)) {
                closest = color;
            }
        }
        
        return closest;
    }
}

class Color3i {
    
    private int r, g, b;

    public Color3i(int c) {
        Color color = new Color(c);
        this.r = color.getRed();
        this.g = color.getGreen();
        this.b = color.getBlue();
    }
    
    public Color3i(int r, int g, int b) {
        this.r = r;
        this.g = g;
        this.b = b;
    }

    public Color3i add(Color3i o) {
        return new Color3i(r + o.r, g + o.g, b + o.b);
    }
    
    public Color3i sub(Color3i o) {
        return new Color3i(r - o.r, g - o.g, b - o.b);
    }
    
    public Color3i mul(double d) {
        return new Color3i((int) (d * r), (int) (d * g), (int) (d * b));
    }
    
    public int diff(Color3i o) {
        return Math.abs(r - o.r) +  Math.abs(g - o.g) +  Math.abs(b - o.b);
    }

    public int toRGB() {
        return toColor().getRGB();
    }
    
    public Color toColor() {
        return new Color(clamp(r), clamp(g), clamp(b));
    }
    
    public int clamp(int c) {
        return Math.max(0, Math.min(255, c));
    }
}

并将我的功能更改为:

for (int y = 0; y < dithImage.getHeight(); ++y) {
    for (int x = 0; x < dithImage.getWidth(); ++x) {
        final int clr = dithImage.getRGB(x, y);
        final int r = (clr & 0xFF0000) >> 16;
        final int g = (clr & 0xFF00) >> 8;
        final int b = clr & 0xFF;
                
        if(382.5>(r+g+b)) {
            buffer.add(0);
        } else {
            buffer.add(1);
        }
    }
}

但输出结果看起来……很奇怪?

我真的不明白为什么会有这样的波浪。

【问题讨论】:

  • 根据Wikipedia about that algorithm,您可以使用1位深度。编辑:在页面末尾甚至有一个将 32 位转换为 16 位的示例。
  • 只有 2 种颜色,期待什么,对我来说快速查看看起来不错。您可以尝试使用由灰色阴影(从纯白色到纯黑色)组成的图像,看看它是如何转换的。表示如果转换成比例,一半应该是白色,一半是黑色(其他转换只有在图片“平衡”不佳时才有用)。
  • 我添加了一个“目标”图像,以便您看到我想要的。遗憾的是,由于压缩,您看不到任何抖动模式,但我希望它仍然可以提供帮助。
  • 不,我不想将图像转换为灰度。

标签: java image dithering


【解决方案1】:

我终于让它工作了!我改进了diff函数并将if(382.5&gt;(r+g+b))更改为if(765==(r+g+b))

我的DitheringUtils-class:

import java.awt.Color;
import java.awt.image.BufferedImage;

public class DitheringUtils {
    
    public static BufferedImage dithering(BufferedImage image) {
        Color3i[] palette = new Color3i[] {
            new Color3i(0, 0, 0),
            new Color3i(255, 255, 255)
        };
        
        int width = image.getWidth();
        int height = image.getHeight();
        
        Color3i[][] buffer = new Color3i[height][width];
        
        for(int y=0;y<height;y++) {
            for(int x=0;x<width;x++) {
                buffer[y][x] = new Color3i(image.getRGB(x, y));
            }
        }
        
        for(int y=0; y<image.getHeight();y++) {
            for(int x=0; x<image.getWidth();x++) {
                Color3i old = buffer[y][x];
                Color3i nem = findClosestPaletteColor(old, palette);
                image.setRGB(x, y, nem.toColor().getRGB());
                
                Color3i error = old.sub(nem);
                
                if (x+1 < width)         buffer[y  ][x+1] = buffer[y  ][x+1].add(error.mul(7./16));
                if (x-1>=0 && y+1<height) buffer[y+1][x-1] = buffer[y+1][x-1].add(error.mul(3./16));
                if (y+1 < height)         buffer[y+1][x  ] = buffer[y+1][x  ].add(error.mul(5./16));
                if (x+1<width && y+1<height)  buffer[y+1][x+1] = buffer[y+1][x+1].add(error.mul(1./16));
            }
        }
        
        return image;
    }

    private static Color3i findClosestPaletteColor(Color3i match, Color3i[] palette) {
        Color3i closest = palette[0];
        
        for(Color3i color : palette) {
            if(color.diff(match) < closest.diff(match)) {
                closest = color;
            }
        }
        
        return closest;
    }
}

class Color3i {
    
    private int r, g, b;

    public Color3i(int c) {
        Color color = new Color(c);
        this.r = color.getRed();
        this.g = color.getGreen();
        this.b = color.getBlue();
    }
    
    public Color3i(int r, int g, int b) {
        this.r = r;
        this.g = g;
        this.b = b;
    }

    public Color3i add(Color3i o) {
        return new Color3i(r + o.r, g + o.g, b + o.b);
    }
    
    public Color3i sub(Color3i o) {
        return new Color3i(r - o.r, g - o.g, b - o.b);
    }
    
    public Color3i mul(double d) {
        return new Color3i((int) (d * r), (int) (d * g), (int) (d * b));
    }
    
    public int diff(Color3i o) {
        int Rdiff = o.r - r;
        int Gdiff = o.g - g;
        int Bdiff = o.b - b;
        int distanceSquared = Rdiff * Rdiff + Gdiff * Gdiff + Bdiff * Bdiff;
        return distanceSquared;
    }

    public int toRGB() {
        return toColor().getRGB();
    }
    
    public Color toColor() {
        return new Color(clamp(r), clamp(g), clamp(b));
    }
    
    public int clamp(int c) {
        return Math.max(0, Math.min(255, c));
    }
}

最终写入函数:

for (int y = 0; y < dithImage.getHeight(); ++y) {
    for (int x = 0; x < dithImage.getWidth(); ++x) {
        final int clr = dithImage.getRGB(x, y);
        final int r = (clr & 0xFF0000) >> 16;
        final int g = (clr & 0xFF00) >> 8;
        final int b = clr & 0xFF;
                
        if(765==(r+g+b)) {
            buffer.add(0);
        } else {
            buffer.add(1);
        }
    }
}

谢谢大家!

【讨论】:

    【解决方案2】:
    BufferedImage image = ...
    for (int y = 0; y < image.getHeight(); y++) {
      for (int x = 0; x < image.getWidth(); x++) {
        Color color = new Color(image.getRGB(x, y));
        int red = color.getRed();
        int green = color.getGreen();
        int blue = color.getBlue();
        
        
        int mono = (red+green+blue)/255;
        
        //Adding to image buffer
        int col = (0 << 24) | (mono << 16) | (mono << 8) | mono;
        
        image.setRGB(x,y,col);
      }
    }
    

    试试这个

    您做错的是,您没有尝试将图片转换为灰度,而是尝试转换为黑白。

    【讨论】:

    • 我不确定这是否是我的错,由于某种原因,我的图像现在只包含零。发生了什么?
    • 不,我弄错了,对不起,让我改正
    • @PugsAreCute 我已经编辑了问题,检查一次
    • @PugsAreCute 您是否在最后一张图片中编辑了带有噪音的问题?如果是这种情况,那么我会再次编辑它,在此之前检查一下它是否有效
    • 它会生成灰度图像,但我的转换器应该生成仅包含 2 种颜色(黑色和白色)的单色图像。不过还是谢谢你。
    猜你喜欢
    • 2021-09-08
    • 1970-01-01
    • 2016-07-05
    • 1970-01-01
    • 2019-12-31
    • 2021-11-21
    • 2013-11-17
    • 2017-07-01
    • 1970-01-01
    相关资源
    最近更新 更多