如何将图片黑白化

soulmachine 2010-02-06 05:45:58
hi,各位,java中将图片黑白化(不是灰度图像,是黑白花,只有白色和黑色),我经过多番google,写了一个程序,但是发现有个微小的问题。应该是 alpha 分量引起的,在BufferedImage类中,有一个方法getRGB ,返回的是一个整数,4字节,包含一个alpha,在处理时,我不太了解alpha的作用,就没管它。但是现在看来这个分量对于颜色是有影响的。

我写了一个函数,monochrome,将图片黑白化,然后又写了一个函数,isMonochrome,检测每个像素,看是否是黑白色,总是失败。
我的问题是:
1. TYPE_INT_ARGB 和 TYPE_INT_RGB 之间有什么区别?
2. 请大家耐心看一下我的代码,看看问题出在哪里?


import java.awt.Image;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Iterator;
import javax.imageio.ImageIO;
import javax.imageio.ImageReader;
import javax.imageio.ImageWriter;
import javax.imageio.stream.ImageInputStream;
import javax.imageio.stream.ImageOutputStream;
import java.awt.image.BufferedImage;


/*
* 用于把一个图片转化为12x18的01矩阵
*
* @author soulmachine@gmail.com
* @version 1.0, 02/04/2010
*/
public class BiImage {
/** 将文件读取成Image 对象,便于操作 */
//protected Image image;

/** 图片的宽 */
protected int width;

/** 图片的高 */
protected int height;

/** 将image中的像素读取出来,存放在本变量中 */
protected int pixels[];

/** The constructor */
public BiImage() {

}

/**
* 判断一个图片文件的类型。
* 前提是,已知该文件是图片;本函数仅读取文件头部两个字节进行判断。
* 虽然可以多读几个字节会更精确,这里没必要,因为已知是图片了。
*
* @param file
* @return 图片类型后缀
* @throws IOException
*/

private static String getImageType(String filePath) throws IOException{
File f = new File(filePath);
FileInputStream in = null;
String type = null;
byte[] bytes = { 0, 0 }; // 用于存放文件头两个字节

in = new FileInputStream(f);

in.read(bytes, 0, 2);

if (((bytes[0] & 0xFF) == 0x47) && ((bytes[1] & 0xFF) == 0x49)) { // GIF
type = "gif";
} else if (((bytes[0] & 0xFF) == 0x89) && ((bytes[1] & 0xFF) == 0x50)) { // PNG
type = "png";
} else if (((bytes[0] & 0xFF) == 0xFF) && ((bytes[1] & 0xFF) == 0xD8)) { // JPG
type = "jpg";
} else if (((bytes[0] & 0xFF) == 0x42) && ((bytes[1] & 0xFF) == 0x4D)) { // BMP
type = "bmp";
} else { // not supported type
// System.out.println("not supported type!");
}

in.close();

return type;
}


/**
* 判断一个TYPE_INT_ARGB彩色是靠近白色还是靠近黑色
*
* @param pixel 一个 TYPE_INT_ARGB颜色
* @return 对应的黑色或白色
*/
private static int convertToBlackWhite(int pixel) {
int result = 0;

//int alpha = (pixel >> 24) & 0xff; // not used
int red = (pixel >> 16) & 0xff;
int green = (pixel >> 8) & 0xff;
int blue = (pixel) & 0xff;

result = 0xff000000; // 这样,白色就为全F,即 -1

int tmp = red * red + green * green + blue * blue;
if(tmp > 3*128*128){ // 大于,则是白色
result += 0x00ffffff;
} else { // 是黑色

}

return result;
}
/**
* 从磁盘文件读取图片
*
* @param imageFile 文件路径
* @return BufferedImage对象,失败为null
* @throws IOException
*/
public static BufferedImage readImageFromFile(String imageFile) throws IOException{
BufferedImage bi;

// 获取某种图片格式的reader对象
Iterator<ImageReader> readers = ImageIO.getImageReadersByFormatName(
BiImage.getImageType(imageFile));
ImageReader reader = (ImageReader)readers.next();
// 为该reader对象设置输入源
ImageInputStream iis = ImageIO.createImageInputStream(
new File(imageFile));
reader.setInput(iis);

// 创建图片对象
bi = reader.read(0);

readers = null;
reader = null;
iis = null;

return bi;
}

/**
* 将图片写入磁盘文件
*
* @param imgFile 文件路径
* @param bi BufferedImage 对象
* @return 无
*/
public static void writeImageToFile(String imgFile, BufferedImage bi)
throws IOException {
// 写图片到磁盘上
Iterator<ImageWriter> writers = ImageIO.getImageWritersByFormatName(
imgFile.substring(imgFile.lastIndexOf('.') + 1));
ImageWriter writer = (ImageWriter) writers.next();
// 设置输出源
File f = new File(imgFile);
ImageOutputStream ios;

ios = ImageIO.createImageOutputStream(f);
writer.setOutput(ios);
// 写入到磁盘
writer.write(bi);
}



/**
* 初始化函数
*
* @param imageFile 文件路径
* @return 无
* @throws IOException
* @throws IOException
* @exception 无
*/
public void initialize(String imageFile) throws IOException{
BufferedImage bi = readImageFromFile(imageFile);

// 得到宽和高
width = bi.getWidth(null);
height = bi.getHeight(null);

// 读取像素
pixels = new int[width * height];
bi.getRGB(0, 0, width, height, pixels, 0, width);

bi = null;
}

/**
* 将图片转化为黑白图片
*
* @param imgFile 输出文件路径
* @return
*/
public BufferedImage monochrome(String imgFile) {
int newPixels[] = new int[width * height];
for(int i = 0; i < width * height; i++) {
newPixels[i] = convertToBlackWhite(pixels[i]);
}

BufferedImage bi = new BufferedImage(width, height,
BufferedImage.TYPE_INT_RGB);
bi.setRGB(0, 0, width, height, newPixels, 0, width);
newPixels = null;

try {
BiImage.writeImageToFile(imgFile, bi);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

return bi;
}

/**
* 判断一张图片是否是黑白图片
*
* @param imgFile
* @return 是,返回true,灰度或彩色图片,返回false
*/
private static boolean isMonochrome(String imgFile) {
BufferedImage bi = null;
boolean result = false;
int w = 0, h = 0;
int i = 0, j = 0;

try {
bi = readImageFromFile(imgFile);

} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

w = bi.getWidth();
h = bi.getHeight();
int count = 0; //黑白像素个数
int n = 0; // 非黑白个数
for(j = 0; j < h; j++)
for(i = 0; i < w; i++) {
int rgb = bi.getRGB(i, j);
rgb &= 0x00FFFFFF;
if((rgb != 0x00FFFFFF) && (rgb != 0)){ // 既不是白色也不是黑色
n++;
break;
}
else {
count ++;
}
}
System.out.println(count);
System.out.println(n);
if((i == w) && (j == h)) {
result = true;
} else {
result = false;
}

return result;
}

public static void main(String[] args) {

BiImage bi = new BiImage();
try {
bi.initialize(args[0]);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

bi.monochrome(args[1]); // 黑白化,输出到磁盘
// 从磁盘读取刚生成的文件,检测每个像素,是否是黑白两色
System.out.println(BiImage.isMonochrome(args[1])); // 总是false,检测失败,靠
}
}
...全文
2506 15 打赏 收藏 转发到动态 举报
写回复
用AI写文章
15 条回复
切换为时间正序
请发表友善的回复…
发表回复
soulmachine 2010-02-06
  • 打赏
  • 举报
回复
knightzhuwei,我崇拜你,问题果然在这里。难道Java中运算符优先级和C语言有很大不同吗,太诡异了。
newPixels[i] = gray << 16 + gray << 8 + gray;

newPixels[i]= (gray < <16)+ (gray < <8)+ gray;

这两者有区别吗,按说括号是不必要的吧。

不过,问题解决了,感谢各位,给分结贴!各位真的好热心,再次感谢!
knightzhuwei 2010-02-06
  • 打赏
  • 举报
回复
[Quote=引用 12 楼 soulmachine 的回复:]
万分感谢各位。现在疑问解决了。

不过我有个新的疑问。现在这个黑白化思路很简单,就是 r*r +g*g +b*b < 3*128*128,则为黑色,大于则是白色。当一张白色背景,有一个淡黄颜色的字,用这方法一黑白化,就全部是白色了。我把这张图片用photoshop,光影魔术手之类的软件黑白话,这些软件能正常转化,字变成了黑色,没有消失。

请问下大家,这是怎么做到的?

于是我继续google,发现把图片黑白化一般是先转化为灰度图,再转化为黑白图(gray大于128,为白色,小于,为黑色,很简单),公式是 Gray = 0.212671 * R + 0.715160 * G + 0.072169 * B 。我按这个写了一个灰度化函数,不过转化出来的图很奇怪,代码如下:

Java code/**
* 将彩色图转化为灰度图
*
*@param imgFile 图片文件路径
*@return 一个图片对象*/public BufferedImage gray(String imgFile) {int newPixels[]=newint[width* height];for(int i=0; i< width* height; i++) {int r= (pixels[i]>>16)&0xff;int g= (pixels[i]>>8)&0xff;int b= (pixels[i])&0xff;// 方法1,图像很奇怪int gray= (int)(0.229* r+0.587* g+0.114* b);
newPixels[i]= gray<<16+ gray<<8+ gray;//方法2,图像偏蓝,不是灰度图//newPixels[i] = gray; }// 基于 newPixels 构造一个 BufferedImage BufferedImage bi=new BufferedImage(width, height,
BufferedImage.TYPE_INT_RGB);
bi.setRGB(0,0, width, height, newPixels,0, width);
newPixels=null;try {// 写入磁盘 BiImage.writeImageToFile(imgFile, bi);
}catch (IOException e) {// TODO Auto-generated catch block e.printStackTrace();
}return bi;
}

参考资料:http://topic.csdn.net/t/20040705/16/3147285.html
[/Quote]
我也搞了很久 甚至用笔花了半天 最后发现原来是运算符优先级问题。。。汗。。
这句应该加括号 newPixels[i]= (gray<<16)+ (gray<<8)+ gray;
这样出来的灰度图就很完美了
小贝壳666 2010-02-06
  • 打赏
  • 举报
回复
改用TYPE_INT_ARGB 试下
soulmachine 2010-02-06
  • 打赏
  • 举报
回复
万分感谢各位。现在疑问解决了。

不过我有个新的疑问。现在这个黑白化思路很简单,就是 r*r +g*g +b*b < 3*128*128,则为黑色,大于则是白色。当一张白色背景,有一个淡黄颜色的字,用这方法一黑白化,就全部是白色了。我把这张图片用photoshop,光影魔术手之类的软件黑白话,这些软件能正常转化,字变成了黑色,没有消失。

请问下大家,这是怎么做到的?

于是我继续google,发现把图片黑白化一般是先转化为灰度图,再转化为黑白图(gray大于128,为白色,小于,为黑色,很简单),公式是 Gray = 0.212671 * R + 0.715160 * G + 0.072169 * B 。我按这个写了一个灰度化函数,不过转化出来的图很奇怪,代码如下:


/**
* 将彩色图转化为灰度图
*
* @param imgFile 图片文件路径
* @return 一个图片对象
*/
public BufferedImage gray(String imgFile) {
int newPixels[] = new int[width * height];
for(int i = 0; i < width * height; i++) {
int r = (pixels[i] >> 16) & 0xff;
int g = (pixels[i] >> 8) & 0xff;
int b = (pixels[i]) & 0xff;

// 方法1,图像很奇怪
int gray = (int)(0.229 * r + 0.587 * g + 0.114 * b);
newPixels[i] = gray << 16 + gray << 8 + gray;
//方法2,图像偏蓝,不是灰度图
//newPixels[i] = gray;
}

// 基于 newPixels 构造一个 BufferedImage
BufferedImage bi = new BufferedImage(width, height,
BufferedImage.TYPE_INT_RGB);
bi.setRGB(0, 0, width, height, newPixels, 0, width);
newPixels = null;

try {
// 写入磁盘
BiImage.writeImageToFile(imgFile, bi);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

return bi;
}


参考资料:http://topic.csdn.net/t/20040705/16/3147285.html
小贝壳666 2010-02-06
  • 打赏
  • 举报
回复
[Quote=引用 6 楼 soulmachine 的回复:]
恩,非常感谢,我再认真测试了一下,对于PNG,GIF,BMP,是没有问题的,检测通过
1. JPG检测通不过,我觉得还是alpha通道没有处理好。所以,小问题就是,为什么JPG黑白化失败?
2. alpha没有处理的原因,是因为我不太明白 TYPE_INT_ARGB 和 TYPE_INT_RGB 的区别。我的意思是,java在保存图片时,会不会修改某些像素的值,导致检测通不过?
[/Quote]
我个人认为,LZ说的不太对,我觉得alpha通道与你最好反回FALSE没有关系。
再说经测试黑白化是成功的,只是读到判断时不好用。
小贝壳666 2010-02-06
  • 打赏
  • 举报
回复
[Quote=引用 6 楼 soulmachine 的回复:]
恩,非常感谢,我再认真测试了一下,对于PNG,GIF,BMP,是没有问题的,检测通过
1. JPG检测通不过,我觉得还是alpha通道没有处理好。所以,小问题就是,为什么JPG黑白化失败?
2. alpha没有处理的原因,是因为我不太明白 TYPE_INT_ARGB 和 TYPE_INT_RGB 的区别。我的意思是,java在保存图片时,会不会修改某些像素的值,导致检测通不过?
[/Quote]

用 TYPE_INT_ARGB 处理alpha通道
小贝壳666 2010-02-06
  • 打赏
  • 举报
回复
TYPE_INT_ARGB 代表图像8位RGBA像素的彩色包装成整数部分。
TYPE_INT_RGB 代表图像8位RGB像素的彩色包装成整数部分。

RGBA是对原始的RGB的扩展,它加入了第四个参数:alpha通道。
knightzhuwei 2010-02-06
  • 打赏
  • 举报
回复
也就是说 A像素的颜色信息存为jpg格式再读取出来后和原来A像素的信息有可能是不相等的
knightzhuwei 2010-02-06
  • 打赏
  • 举报
回复
jpg是有损压缩格式
存储的时候本身不是按照rgb的方式存储的 读取的时候需要解压缩 转换为rgb的颜色信息 而这个过程是有损的 会产生非黑白的颜色信息也就不足为奇了
soulmachine 2010-02-06
  • 打赏
  • 举报
回复
恩,非常感谢,我再认真测试了一下,对于PNG,GIF,BMP,是没有问题的,检测通过
1. JPG检测通不过,我觉得还是alpha通道没有处理好。所以,小问题就是,为什么JPG黑白化失败?
2. alpha没有处理的原因,是因为我不太明白 TYPE_INT_ARGB 和 TYPE_INT_RGB 的区别。我的意思是,java在保存图片时,会不会修改某些像素的值,导致检测通不过?
小贝壳666 2010-02-06
  • 打赏
  • 举报
回复
图片保存为PNG BMP 格式的好用,结果为TRUE;
图片保存为JPG JPEG格式的依然可以黑白化,但结果为FALSE;
小贝壳666 2010-02-06
  • 打赏
  • 举报
回复
png图片好用,JPG不好用
小贝壳666 2010-02-06
  • 打赏
  • 举报
回复
alpha对于黑白化应该没有影响。
knightzhuwei 2010-02-06
  • 打赏
  • 举报
回复
alpha肯定是指不透明度 对jpg之类的图片应该没有影响 你所指的小问题是什么?
Inhibitory 2010-02-06
  • 打赏
  • 举报
回复
我用png和图片试过了, 没有任何问题.
估计是你的图片格式, 不防用png试试.
下面的输出结果
Biao: ~/Desktop $ ls
1.png BiImage.java 如何将图片黑白化.webarchive
Biao: ~/Desktop $ javac BiImage.java
Biao: ~/Desktop $ java BiImage 1.png 2.png
230652
0
true
Biao: ~/Desktop $ javac BiImage.java
Biao: ~/Desktop $ java BiImage 1.png 3.png
230652
0
true
Biao: ~/Desktop $ java BiImage 4.png 5.png
366444
0
true
Biao: ~/Desktop $
老照片编辑器前端源码,包含白变彩色,动画化图片等老照片编辑器前端源码,包含白变彩色,动画化图片等老照片编辑器前端源码,包含白变彩色,动画化图片等老照片编辑器前端源码,包含白变彩色,动画化图片等老照片编辑器前端源码,包含白变彩色,动画化图片等老照片编辑器前端源码,包含白变彩色,动画化图片等老照片编辑器前端源码,包含白变彩色,动画化图片等老照片编辑器前端源码,包含白变彩色,动画化图片等老照片编辑器前端源码,包含白变彩色,动画化图片等老照片编辑器前端源码,包含白变彩色,动画化图片等老照片编辑器前端源码,包含白变彩色,动画化图片等老照片编辑器前端源码,包含白变彩色,动画化图片等老照片编辑器前端源码,包含白变彩色,动画化图片等老照片编辑器前端源码,包含白变彩色,动画化图片等老照片编辑器前端源码,包含白变彩色,动画化图片等老照片编辑器前端源码,包含白变彩色,动画化图片等老照片编辑器前端源码,包含白变彩色,动画化图片等老照片编辑器前端源码,包含白变彩色,动画化图片等老照片编辑器前端源码,包含白变彩色,动画化图片等老照片编辑器前端源码,包含白变彩色,动画化图片

62,614

社区成员

发帖
与我相关
我的任务
社区描述
Java 2 Standard Edition
社区管理员
  • Java SE
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧