将400x300 Bitmap转换为15000字节数组适配电子纸的问题求助
问题修复建议与替代实现思路
原代码核心问题分析
- 行遍历顺序颠倒:原代码从图像最后一行开始处理,导致字节数组的行顺序与电子纸要求的显示顺序相反,这是图像出现异常纹路的主要原因之一。
- 灰度映射逻辑不严谨:直接将平均亮度值转为
Int作为灰度数组索引,若灰度等级数量小于256,会出现数组越界或映射不均匀的问题,导致文字细节丢失。 - 未做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
相关产品推荐
相关产品推荐

