色彩转换算法
实现真彩色到256色的转换,关键就是如何从24位真彩色的颜色中选出256种颜色,使图像失真较小。主要的算法有:
(1)取颜色高位算法,例如:分别取R高3位,G高2位,B高3位组成8字节获取256种颜色。
(2)流行色算法。算法的基本思路是:对彩色图像中所有色彩出现的次数进行统计分析,从而选取频率最高的N(256)种颜色,为这N(256)种颜色建立调色板。算法特点:算法简单容易实现,变换效果好,但一些出现频率较低,但对人眼视觉效果明显的信息将丢失。
(3)八叉树颜色量化算法。算法基本思路是:将图像中使用的RGB颜色值分布到层状的八叉树中。八叉树的深度可达9层,即根节点层加上分别表示8位的R、G、B值的每一位的8层节点。较低的节点层对应于较不重要的RGB值的位(右边的位),因此,为了提高效率和节省内存,可以去掉最低部的2 ~ 3层,这样不会对结果有太大的影响。叶节点编码存储像素的个数和R、G、B颜色分量的值;而中间的节点组成了从最顶层到叶节点的路径。这是一种高效的存储方式,既可以存储图像中出现的颜色和其出现的次数,也不会浪费内存来存储图像中不出现的颜色。算法特点:效率高,效果好。
JAVA真彩色转换算法实现
这里,我们用以下两种方式实现色彩的转换:
(1)直接使用JAVA画图功能实现
(2)使用流行色算法实现
(一)使用JAVA画图功能实现转换的方法
这里我们用JAVA的图像库实现颜色的转换,我们先看看直接使用JAVA画图功能实现转换的代码及实现效果。
实现的基本思路是
(1)创建TYPE_BYTE_INDEXED类型的BufferedImage图像对象。
(2)获取该对象的画图对象Graphics2D,将真彩色图像绘制到该图像上。
(3)对新建的对象进行输出,就得到了256色图像。
下面是实现代码
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
public class ColorTrueTo256 {
public static BufferedImage trueTo256(BufferedImage imgSrc) {
int width = imgSrc.getWidth();
int height = imgSrc.getHeight();
BufferedImage imgDst = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_INDEXED);
Graphics2D g2d = imgDst.createGraphics();
g2d.drawImage(imgSrc, 0, 0, width, height, null);
g2d.dispose();
return imgDst;
}
public static void main(String[] argv) throws IOException {
BufferedImage imgSrc = ImageIO.read(new File("landscape.jpg"));
BufferedImage imgDst = trueTo256(imgSrc);
ImageIO.write(imgDst, "gif", new File("landscape-1-256.gif"));
}
}
我们对以下图像进行处理:
处理效果如下图
(二)使用流行色算法实现的方法及效果
用流行色算法实现转换,大致的算法如下:
(1)准备一个长度为4096的数组,代表4096种颜色。对图中的每一个像素,取R,G,B的最高四位,拼成一个12位的整数,对应的数组元素加1。
(2)全部统计完后,就得到了这4096种颜色的使用频率。这其中,可能有一些颜色一次也没用到,即对应的数组元素为零。将这些为零的数组元素清除出去。
(3)将这剩余的数按从大到小的顺序排列,这样,前256种颜色就是用的最多的颜色,它们将作为调色板上的256种颜色。
(4)对于剩下的颜色并不是简单的丢弃,而是用前面的256种颜色中的一种来代替,代替的原则是找有最小平方误差的那个。
实现代码如下:
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
public class TrueTo256 {
public static int colorTransfer(int rgb) {
int r = (rgb&0x0F00000)>>20;
int g = (rgb&0x000F000)>>12;
int b = (rgb&0x00000F0)>>4;
return (r<<8|g<<4|b);
}
public static int colorRevert(int rgb) {
int r = (rgb&0x0F00)<<12;
int g = (rgb&0x000F0)<<8;
int b = (rgb&0x00000F)<<4;
return (r|g|b);
}
private static int getDouble(int a, int b) {
int red = ((a&0x0F00)>>8) - ((b&0x0F00)>>8);
int grn = ((a&0x00F0)>>4) - ((b&0x00F0)>>4);
int blu = (a&0x000F) - (b&0x000F);
return red*red + blu*blu + grn*grn;
}
public static int getSimulatorColor(int rgb, int[] rgbs, int m) {
int r = 0;
int lest = getDouble(rgb, rgbs[r]);
for (int i=1; i < m; i++) {
int d2 = getDouble(rgb, rgbs[i]);
if (lest > d2) {
lest = d2;
r = i;
}
}
return rgbs[r];
}
public static void transferTo256(int[][] rgbs) {
int n = 4096;
int m = 256;
int[] colorV = new int[n];
int[] colorIndex = new int[n];
//初始化
for (int i=0; i < n; i++) {
colorV[i] = 0;
colorIndex[i] = i;
}
//颜色转换
for (int x = 0; x < rgbs.length; x++) {
for (int y = 0; y < rgbs[x].length; y++) {
rgbs[x][y] = colorTransfer(rgbs[x][y]);
colorV[rgbs[x][y]]++;
}
}
//出现频率排序
boolean exchange;
int r;
for (int i=0; i < n; i++) {
exchange = false;
for (int j=n-2; j>=i; j--) {
if (colorV[colorIndex[j+1]] > colorV[colorIndex[j]]) {
r = colorIndex[j];
colorIndex[j] = colorIndex[j+1];
colorIndex[j+1] = r;
exchange = true;
}
}
if (!exchange) break;
}
//颜色排序位置
for (int i=0; i < n; i++) {
colorV[colorIndex[i]] = i;
}
for (int x = 0; x < rgbs.length; x++) {
for (int y = 0; y < rgbs[x].length; y++) {
if (colorV[rgbs[x][y]] >= m) {
rgbs[x][y] = colorRevert(getSimulatorColor(rgbs[x][y], colorIndex, m));
} else {
rgbs[x][y] = colorRevert(rgbs[x][y]);
}
}
}
}
public static void transferToRed(int[][] rgbs) {
for (int x = 0; x < rgbs.length; x++) {
for (int y = 0; y < rgbs[x].length; y++) {
rgbs[x][y] = rgbs[x][y]&0x00FF0000;
}
}
}
public static void transferToGreen(int[][] rgbs) {
for (int x = 0; x < rgbs.length; x++) {
for (int y = 0; y < rgbs[x].length; y++) {
rgbs[x][y] = rgbs[x][y]&0x00FF00;
}
}
}
public static void transferToBlue(int[][] rgbs) {
for (int x = 0; x < rgbs.length; x++) {
for (int y = 0; y < rgbs[x].length; y++) {
rgbs[x][y] = rgbs[x][y]&0x00FF;
}
}
}
public static BufferedImage trueTo256(BufferedImage img) {
int[][] rgbs = new int[img.getWidth()][img.getHeight()];
BufferedImage cloneImg = new BufferedImage(img.getWidth(), img.getHeight(),
BufferedImage.TYPE_INT_RGB);
for (int x=0; x < rgbs.length; x++) {
for (int y=0; y < rgbs[x].length; y++) {
rgbs[x][y] = img.getRGB(x, y);
}
}
transferTo256(rgbs);
for (int x=0; x < rgbs.length; x++) {
for (int y=0; y < rgbs[x].length; y++) {
cloneImg.setRGB(x, y, rgbs[x][y]);
}
}
return cloneImg;
}
public static void main(String[] argv) throws IOException {
BufferedImage imgSrc = ImageIO.read(new File("landscape.jpg"));
BufferedImage imgDst = trueTo256(imgSrc);
ImageIO.write(imgDst, "gif", new File("landscape-2-256.gif"));
}
}
用该算法对上面的图片进行处理,实现效果如下: