原文链接:https://blog.csdn.net/weixin_34406061/article/details/92425842
本文只做细微改动
package com.example;
import java.awt.image.BufferedImage;
/**
* Floyd-Steinberg抖动算法 工具类
*
* @author duxuefu
* @date 2019-09-16
*/
public class BMPConverterUtil {
/**
* 黑白屏
*/
public final static int PALETTE_BW = 0;
/**
* 黑白红屏
*/
public final static int PALETTE_BWR = 1;
/**
* 黑白黄屏
*/
public final static int PALETTE_BWY = 2;
/**
* 取二值化屏幕类型
*
* @param deviceType 屏幕类型
* @return RGBTriple[]
* @see RGBTriple
*/
public static RGBTriple[] getPalette(int deviceType) {
final RGBTriple[] palette;
if (deviceType == 1) {
//黑白红价签
palette = new RGBTriple[]{new RGBTriple(0, 0, 0), new RGBTriple(255, 255, 255), new RGBTriple(255, 0, 0)};
} else if (deviceType == 2) {
//黑白黄价签
palette = new RGBTriple[]{new RGBTriple(0, 0, 0), new RGBTriple(255, 255, 255), new RGBTriple(255, 255, 0)};
} else {
//黑白价签
palette = new RGBTriple[]{new RGBTriple(0, 0, 0), new RGBTriple(255, 255, 255)};
}
return palette;
}
/**
* 核心算法,二值化处理
*
* @param image 图片
* @param palette 屏幕类型
* @return 二值化数组, 0表示黑,1表示白,2表示红或黄(假定红和黄色不同时存在)
*/
public static byte[][] floydSteinbergDither(RGBTriple[][] image, RGBTriple[] palette) {
byte[][] result = new byte[image.length][image[0].length];
for (int y = 0; y < image.length; y++) {
for (int x = 0; x < image[y].length; x++) {
RGBTriple currentPixel = image[y][x];
byte index = findNearestColor(currentPixel, palette);
result[y][x] = index;
for (int i = 0; i < 3; i++) {
int error = (currentPixel.channels[i] & 0xff) - (palette[index].channels[i] & 0xff);
if (x + 1 < image[0].length) {
image[y][x + 1].channels[i] = plus_truncate_uchar(image[y][x + 1].channels[i], (error * 7) >> 4);
}
if (y + 1 < image.length) {
if (x - 1 > 0) {
image[y + 1][x - 1].channels[i] = plus_truncate_uchar(image[y + 1][x - 1].channels[i], (error * 3) >> 4);
}
image[y + 1][x].channels[i] = plus_truncate_uchar(image[y + 1][x].channels[i], (error * 5) >> 4);
if (x + 1 < image[0].length) {
image[y + 1][x + 1].channels[i] = plus_truncate_uchar(image[y + 1][x + 1].channels[i], (error) >> 4);
}
}
}
}
}
return result;
}
/**
* 转换图片
*
* @param bufferedImage 原图
* @param newBufferedImage 二值化后图片
* @param deviceType 屏幕类型
*/
public static void floydSteinberg(BufferedImage bufferedImage, BufferedImage newBufferedImage, int deviceType) {
int width = bufferedImage.getWidth();
int height = bufferedImage.getHeight();
RGBTriple[][] image = new RGBTriple[width][height];
for (int i = 0; i < width; i++) {
for (int j = 0; j < height; j++) {
int pixel = bufferedImage.getRGB(i, j);
int rx = (pixel & 0xff0000) >> 16;
int gx = (pixel & 0xff00) >> 8;
int bx = (pixel & 0xff);
RGBTriple rgbTriple = new RGBTriple(rx, gx, bx);
image[i][j] = rgbTriple;
}
}
byte[][] to = floydSteinbergDither(image, getPalette(deviceType));
for (int i = 0; i < to.length; i++) {
for (int j = 0; j < to[i].length; j++) {
System.out.println(to[i][j]);
if (to[i][j] == 0) {
// 处理黑色
newBufferedImage.setRGB(i, j, 0);
} else if (to[i][j] == 1) {
// 处理白色
newBufferedImage.setRGB(i, j, (255 << 16) + (255 << 8) + 255);
} else if (to[i][j] == 2 && deviceType == 1) {
// 处理红色
newBufferedImage.setRGB(i, j, (255 << 16));
} else if (to[i][j] == 2 && deviceType == 2) {
// 处理黄色
newBufferedImage.setRGB(i, j, (255 << 16) + (255 << 8));
}
}
}
}
/**
* @param a
* @param b
* @return
*/
private static byte plus_truncate_uchar(byte a, int b) {
if ((a & 0xff) + b < 0) {
return 0;
} else if ((a & 0xff) + b > 255) {
return (byte) 255;
} else {
return (byte) (a + b);
}
}
/**
* @param color
* @param palette
* @return
*/
private static byte findNearestColor(RGBTriple color, RGBTriple[] palette) {
int minDistanceSquared = 255 * 255 + 255 * 255 + 255 * 255 + 1;
byte bestIndex = 0;
for (byte i = 0; i < palette.length; i++) {
int Rdiff = (color.channels[0] & 0xff) - (palette[i].channels[0] & 0xff);
int Gdiff = (color.channels[1] & 0xff) - (palette[i].channels[1] & 0xff);
int Bdiff = (color.channels[2] & 0xff) - (palette[i].channels[2] & 0xff);
int distanceSquared = Rdiff * Rdiff + Gdiff * Gdiff + Bdiff * Bdiff;
if (distanceSquared < minDistanceSquared) {
minDistanceSquared = distanceSquared;
bestIndex = i;
}
}
return bestIndex;
}
/**
* 二值化颜色类
*
* @author duxuefu
* @date 2019-09-16
*/
public static class RGBTriple {
public final byte[] channels;
public RGBTriple(int R, int G, int B) {
channels = new byte[]{(byte) R, (byte) G, (byte) B};
}
}
}
测试类
package binaryzation;
import com.jd.jr.innovation.common.util.BMPConverterUtil;
import org.junit.Test;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
/**
* 类描述
*
* @author duxuefu
* @date 2019-09-16
*/
public class FloydSteinbergTest {
@Test
public void binaryzation() {
// 原图
String from = "d:/image/5b7620e1N058c3b11.jpg";
// 二值化后图片
String toFloyd = "d:/image/Floyd.png";
String toFloydForRed = "d:/image/FloydForRed.png";
String toFloydForYellow = "d:/image/FloydForYellow.png";
try {
// 黑白
BufferedImage bufferedImage = ImageIO.read(new File(from));
BufferedImage binaryBufferedImage = new BufferedImage(bufferedImage.getWidth(), bufferedImage.getHeight(), BufferedImage.TYPE_INT_BGR);
BMPConverterUtil.floydSteinberg(bufferedImage, binaryBufferedImage, BMPConverterUtil.PALETTE_BW);
ImageIO.write(binaryBufferedImage, "png", new File(toFloyd));
// 黑白红
BufferedImage binaryBufferedImageForRed = new BufferedImage(bufferedImage.getWidth(), bufferedImage.getHeight(), BufferedImage.TYPE_INT_BGR);
BMPConverterUtil.floydSteinberg(bufferedImage, binaryBufferedImageForRed, BMPConverterUtil.PALETTE_BWR);
ImageIO.write(binaryBufferedImageForRed, "png", new File(toFloydForRed));
// 黑白黄
BufferedImage binaryBufferedImageForYellow = new BufferedImage(bufferedImage.getWidth(), bufferedImage.getHeight(), BufferedImage.TYPE_INT_BGR);
BMPConverterUtil.floydSteinberg(bufferedImage, binaryBufferedImageForYellow, BMPConverterUtil.PALETTE_BWY);
ImageIO.write(binaryBufferedImageForYellow, "png", new File(toFloydForYellow));
} catch (IOException e) {
e.printStackTrace();
}
}
}
测试效果
原图
5b7620e1N058c3b11.jpg
黑白图
Floyd.png
黑白红
FloydForRed.png
黑白黄
FloydForYellow.png
网友评论