用2个字节或更少的字节表达RGB图像的可能性探索


       昨晚睡之前想,如果总结一个Bitmap中含有的RGB值,然后如果最常用的颜色在3万种左右,那么可以统计为一个表,名为colorList,直接用把Bitmap中对应的像素替换为最接近的颜色的索引指,假设颜色表限制容量为最长出现的32767种,那么索引只需要两个字节即可表达;如果颜色表限制为最多256种颜色,最只需要1个字节的索引就可以替换1个像素,从而达到压缩作用。

       实际上这个demo实现非常粗糙,颜色表其实用字典树去实现可以省下很多的读写时间,但是今天还是工作日,暂时时间不足,直接贴上这次的实验代码吧。

 

2字节版本
ByteMap:

package com.example.chenjiezhu.bmpcompresstest;

/**
 * Created by chenjiezhu on 2020/1/17.
 */

public class ByteMap {
    public int width, height;
    public short byteMap[];
}

ByteMapUtil

package com.example.chenjiezhu.bmpcompresstest;

import android.graphics.Bitmap;
import android.util.Log;

import java.nio.IntBuffer;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

/**
 * Created by chenjiezhu on 2020/1/17.
 */

public class ByteMapUtil {

    private int width;
    private int height;
    private class RgbBlock{
        public byte rChannel;
        public byte gChannel;
        public byte bChannel;
        public int color;
        public int repeatTime;
    }
    private List<RgbBlock> colorList = new ArrayList<>();

    public List<RgbBlock> getColorList() {
        return colorList;
    }

    public ByteMap bitmapToByteMap(Bitmap bitmap) {
        width = bitmap.getWidth();
        height = bitmap.getHeight();
        int pixels[] = new int[width * height];
        bitmap.copyPixelsToBuffer(IntBuffer.wrap(pixels));
        for (int i = 0; i < pixels.length; i++) {
            addPixels(pixels[i]);
        }
        Log.i("cjztest", "run successed");
        sortList();
        Log.i("cjztest", "sort successed");

        //        showAll();
        return outputByteMap(pixels, width, height);
    }

    private ByteMap outputByteMap(int pixels[], int width, int height) {
        short byteMap[] = new short[pixels.length];
        for(int i = 0; i < byteMap.length; i++){
            int color = pixels[i] & 0x00FFFFFF;
            byte colorR = (byte) (color >> 16 & 0xFF);
            byte colorG = (byte) (color >> 8 & 0xFF);
            byte colorB = (byte) (color & 0xFF);

            int rgbMinDiff = Integer.MAX_VALUE;
            int rgbMinDiffIndexInColorList = 0;
            for(int j = 0; j < colorList.size(); j++){
                RgbBlock block = colorList.get(j);
                int rgbdiff = Math.abs(block.rChannel - colorR) + Math.abs(block.gChannel - colorG) + Math.abs(block.bChannel - colorB);
                if(rgbdiff < rgbMinDiff){
                    rgbMinDiff = rgbdiff;
                    rgbMinDiffIndexInColorList = j;
                }
            }
            byteMap[i] = (short) (rgbMinDiffIndexInColorList & 0xFFFF);
        }
        ByteMap bMap = new ByteMap();
        bMap.byteMap = byteMap;
        bMap.width = width;
        bMap.height = height;
//        for(int i = 0; i < byteMap.length; i++) {
//            Log.i("cjztest", String.format("byte : %d" , byteMap[i] & 0xFF));
//        }
        return bMap;
    }

    private void sortList() {
        //保留最常用的256*256种颜色
        Collections.sort(colorList, new Comparator<RgbBlock>() {
            @Override
            public int compare(RgbBlock o1, RgbBlock o2) {
                if(o1.repeatTime < o2.repeatTime){
                    return  1;
                } else if(o1.repeatTime == o2.repeatTime){
                    return 0;
                } else {
                    return -1;
                }
            }
        });
        while(colorList.size() > 256 * 256){
            colorList.remove(colorList.size() - 1);
        }
        //按照颜色进行排序
        Collections.sort(colorList, new Comparator<RgbBlock>() {
            @Override
            public int compare(RgbBlock o1, RgbBlock o2) {
                if(o1.color > o2.color){
                    return  1;
                } else if(o1.color == o2.color){
                    return 0;
                } else {
                    return -1;
                }
            }
        });
    }

    private void showAll() {
        RgbBlock item = null;
        Iterator<RgbBlock> it = colorList.iterator();
        while(it.hasNext() && (item = it.next()) != null){
            Log.i("cjztest", String.format("color: %02X%02X%02X, repeat: %d", item.rChannel, item.gChannel, item.bChannel, item.repeatTime));
        }
    }

    /**统计颜色种类**/
    private void addPixels(int pixel) {
        boolean isHadThisColor = false;
        RgbBlock item = null;
        byte rgb[] = new byte[]{(byte)(pixel >> 16 & 0xFF), (byte)(pixel >> 8 & 0xFF), (byte)(pixel & 0xFF)};
        Iterator<RgbBlock> it = colorList.iterator();
        while(it.hasNext() && (item = it.next()) != null){
            if(rgb[0] == item.rChannel && rgb[1] == item.gChannel && rgb[2] == item.bChannel){
                item.repeatTime ++;
                isHadThisColor = true;
                break;
            }
        }
        if(!isHadThisColor){
            RgbBlock rgbBlock = new RgbBlock();
            rgbBlock.rChannel = rgb[0];
            rgbBlock.gChannel = rgb[1];
            rgbBlock.bChannel = rgb[2];
            rgbBlock.color = pixel & 0x00FFFFFF;
            colorList.add(rgbBlock);
        }
    }

    public Bitmap byteMapToBitmap(ByteMap byteMap){
        Bitmap bitmap = Bitmap.createBitmap(byteMap.width, byteMap.height, Bitmap.Config.ARGB_8888);
        int index = 0;
        for(int y = 0; y < bitmap.getHeight(); y++){
            for(int x = 0; x < bitmap.getWidth(); x++) {
                int pixel = colorList.get(byteMap.byteMap[index++] & 0xFFFF).color | 0xFF000000;
                bitmap.setPixel(x, y, pixel);
            }
        }
        return bitmap;
    }
}

调用:

package com.example.chenjiezhu.bmpcompresstest;

import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.widget.ImageView;

public class MainActivity extends Activity {

    private ImageView iv_after, iv_before;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        iv_before = findViewById(R.id.iv_before);
        iv_after = findViewById(R.id.iv_after);
        Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher);
        iv_before.setImageBitmap(bitmap);
        ByteMapUtil byteMapUtil = new ByteMapUtil();
        //转换成字节图的测试
        ByteMap byteMap = byteMapUtil.bitmapToByteMap(bitmap);
        //字节图转换成位图的测试
        Bitmap bitmapForByteMap = byteMapUtil.byteMapToBitmap(byteMap);
        iv_after.setImageBitmap(bitmapForByteMap);
    }

}

activity_main layout:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <ImageView
        android:layout_weight="1"
        android:id="@+id/iv_before"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

    <ImageView
        android:layout_weight="1"
        android:id="@+id/iv_after"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</LinearLayout>

效果:

上面的图是原图,后面的图是压缩过后再展开的图。

 

 

github地址:https://github.com/cjzjolly/cjz_bmp_compress

将持续优化

 

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: 一个24位BMP图像文件可以通过以下步骤准备: 1. 打开一个图像编辑软件,如Photoshop或GIMP。 2. 创建一个新的图像文件,设置宽度和高度,选择24位颜色模式。 3. 绘制或导入你想要的图像。 4. 保存图像文件,选择BMP格式,确保选择24位颜色深度选项。 5. 输入文件名并保存。 这样就可以准备一个24位BMP图像文件了。 ### 回答2: 准备一个24位BMP图像文件需要以下步骤: 1. 确定图像的分辨率:确定图像的宽度和高度,例如256像素宽和256像素高。 2. 创建文件头:按照BMP文件格式的规定,创建一个14字节的文件头。文件头的内容包括文件类型、文件大小、保留字节等。 3. 创建信息头:按照BMP文件格式的规定,创建一个40字节的信息头。信息头的内容包括图像宽度、高度、像素位数等。 4. 创建调色板:24位BMP图像文件不需要调色板,直接跳过这一步骤。 5. 创建像素数据:按照图像的分辨率创建一个数组,数组的大小为图像宽度乘以图像高度。每个数组元素代表一个像素点的颜色信息,RGB通道每个通道占用8位,共24位。根据需求,可以为每个像素点分配RGB值。 6. 将文件头、信息头和像素数据按照BMP文件格式的规定进行组合。 7. 将组合后的数据写入文件:将组合后的数据写入一个文件中,文件后缀名为.bmp。 8. 保存文件:保存文件并命名。 以上是准备一个24位BMP图像文件的基本步骤,可以根据实际需求进行调整和扩展。 ### 回答3: 要准备一个24位BMP图像文件,你需要按照以下步骤进行操作: 1. 使用任何支持BMP格式的图像编辑软件打开一个图像文件。确保该文件是24位彩色图像,即每个像素由红、绿和蓝(RGB)三个分量组成。 2. 在图像编辑软件中,选择“另存为”或类似的选项,将该图像保存为BMP格式。在保存选项中,可能会要求选择BMP版本或设置存储选项,选择24位BMP格式或类似选项,确保不会损失任何颜色信息。 3. 如果需要,可以调整图像的尺寸、对比度、亮度或其他属性。这些调整通常在图像编辑软件的工具栏或菜单中提供。 4. 确保保存的BMP文件的扩展名是“.bmp”。 5. 完成后,保存BMP图像文件,并在需要的地方使用它。确保提供的文件路径是正确的,并验证图像文件的有效性。 请注意,以上步骤是一般情况下准备24位BMP图像文件的常见方。不同的图像编辑软件可能会有不同的界面和选项,因此具体步骤可能会有所变化。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值