You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

将400x300 Bitmap转换为15000字节数组适配电子纸的问题求助

问题修复建议与替代实现思路

原代码核心问题分析

  1. 行遍历顺序颠倒:原代码从图像最后一行开始处理,导致字节数组的行顺序与电子纸要求的显示顺序相反,这是图像出现异常纹路的主要原因之一。
  2. 灰度映射逻辑不严谨:直接将平均亮度值转为Int作为灰度数组索引,若灰度等级数量小于256,会出现数组越界或映射不均匀的问题,导致文字细节丢失。
  3. 未做Gamma校正:人眼对亮度的感知是非线性的,直接使用线性亮度平均会导致中间亮度区域的细节被压缩,出现白纹。

修复后的代码实现

fun convertPictureToByteArray(bitmap: Bitmap, grayShadesArray: ByteArray): ByteArray {
    require(bitmap.width == 400 && bitmap.height == 300) { "Bitmap必须为400×300尺寸" }
    require(grayShadesArray.isNotEmpty()) { "灰度映射数组不能为空" }
    
    val byteArray = ByteArray(15000)
    val pixels = IntArray(bitmap.width * bitmap.height)
    bitmap.getPixels(pixels, 0, bitmap.width, 0, 0, bitmap.width, bitmap.height)
    
    val maxGrayIndex = grayShadesArray.size - 1
    val gamma = 2.2 // 匹配人眼感知的Gamma值,可根据实际显示效果调整
    
    var index = 0
    // 按从上到下的顺序逐行处理(若电子纸要求从下到上,可改为downTo 0)
    for (row in 0 until bitmap.height) {
        val rowStart = row * bitmap.width
        // 每行拆分为50组,每组8个像素
        for (group in 0 until bitmap.width / 8) {
            var totalCorrectedLuminance = 0.0
            val groupStart = rowStart + group * 8
            
            // 遍历组内8个像素,计算校正后的亮度总和
            for (i in 0 until 8) {
                val pixel = pixels[groupStart + i]
                val red = Color.red(pixel) / 255.0
                val green = Color.green(pixel) / 255.0
                val blue = Color.blue(pixel) / 255.0
                
                // 计算线性亮度并做Gamma校正
                val linearLuminance = 0.299 * red + 0.587 * green + 0.114 * blue
                val correctedLuminance = Math.pow(linearLuminance, 1.0 / gamma) * 255.0
                totalCorrectedLuminance += correctedLuminance
            }
            
            // 将平均亮度映射到灰度数组索引,防止越界
            val averageLuminance = totalCorrectedLuminance / 8.0
            val grayIndex = (averageLuminance / (255.0 / maxGrayIndex)).toInt()
                .coerceIn(0, maxGrayIndex)
            
            byteArray[index] = grayShadesArray[grayIndex]
            index++
        }
    }
    
    return byteArray
}

替代实现思路

1. 利用系统ColorMatrix做灰度转换

借助Android系统的颜色矩阵工具,先将原图转为灰度图,再进行分组处理,避免手动计算颜色的误差:

fun convertUsingColorMatrix(bitmap: Bitmap, grayShadesArray: ByteArray): ByteArray {
    require(bitmap.width == 400 && bitmap.height == 300) { "Bitmap必须为400×300尺寸" }
    require(grayShadesArray.isNotEmpty()) { "灰度映射数组不能为空" }
    
    // 创建灰度转换矩阵
    val grayMatrix = ColorMatrix().apply { setSaturation(0f) }
    val paint = Paint().apply {
        colorFilter = ColorMatrixColorFilter(grayMatrix)
    }
    
    // 生成灰度Bitmap
    val grayBitmap = Bitmap.createBitmap(bitmap.width, bitmap.height, bitmap.config)
    Canvas(grayBitmap).drawBitmap(bitmap, 0f, 0f, paint)
    
    val byteArray = ByteArray(15000)
    val pixels = IntArray(grayBitmap.width * grayBitmap.height)
    grayBitmap.getPixels(pixels, 0, grayBitmap.width, 0, 0, grayBitmap.width, grayBitmap.height)
    
    val maxGrayIndex = grayShadesArray.size - 1
    var index = 0
    
    for (row in 0 until grayBitmap.height) {
        val rowStart = row * grayBitmap.width
        for (group in 0 until grayBitmap.width / 8) {
            var totalBrightness = 0
            val groupStart = rowStart + group * 8
            // 灰度图RGB值一致,直接取红色通道即可
            for (i in 0 until 8) {
                totalBrightness += Color.red(pixels[groupStart + i])
            }
            val averageBrightness = totalBrightness / 8
            val grayIndex = (averageBrightness / (255.0 / maxGrayIndex)).toInt()
                .coerceIn(0, maxGrayIndex)
            byteArray[index] = grayShadesArray[grayIndex]
            index++
        }
    }
    
    grayBitmap.recycle()
    return byteArray
}

2. 引入抖动算法提升灰度细腻度

如果电子纸支持,可使用Floyd-Steinberg抖动算法,将当前像素的灰度误差扩散到周围像素,让低灰度等级的屏幕显示出更丰富的细节,避免白纹和文字模糊:

// 简化版Floyd-Steinberg抖动实现(需先将Bitmap转为单通道灰度图)
fun applyDithering(grayPixels: IntArray, width: Int, height: Int) {
    for (y in 0 until height - 1) {
        for (x in 1 until width - 1) {
            val oldPixel = grayPixels[y * width + x]
            val newPixel = if (oldPixel > 127) 255 else 0 // 示例:2级灰度抖动
            grayPixels[y * width + x] = newPixel
            val error = oldPixel - newPixel
            
            // 误差扩散到周围像素
            grayPixels[y * width + x + 1] += error * 7 / 16
            grayPixels[(y + 1) * width + x - 1] += error * 3 / 16
            grayPixels[(y + 1) * width + x] += error * 5 / 16
            grayPixels[(y + 1) * width + x + 1] += error * 1 / 16
        }
    }
}

内容的提问来源于stack exchange,提问作者brmbrm

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 01:25:03