首页 > 解决方案 > 如何使用 Java 实现 1 位抖动?

问题描述

最近,我们的老师让我们使用 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);
        }
    }
}

但输出最终看起来……很奇怪?

迷幻输出

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

标签: javaimagedithering

解决方案


我终于让它工作了!我改进了diff功能并更改if(382.5>(r+g+b))if(765==(r+g+b)).

我的DitheringUtils班级:

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);
        }
    }
}

谢谢大家!


推荐阅读