压缩为 png 时位图颜色变化
Posted
技术标签:
【中文标题】压缩为 png 时位图颜色变化【英文标题】:Bitmap color change while compressing to png 【发布时间】:2011-07-05 03:17:08 【问题描述】:我目前正在开发一个作为类项目的 steganogrpahy android 应用程序。我创建了一个对象,它将在另一个图像中编码一个图像并返回一个编码位图。此代码在单独的线程中运行。
new Thread(new Runnable()
public void run()
Bitmap encoded_image = null;
Encryptor encryptor = new Encryptor();
encoded_image = encryptor.encode_image_in_image(
image_location,message_image_location);
).start();
在对位图进行编码后,我将位图传递给我创建的文件浏览器活动,以将位图保存为 png 图像。此方法适用于较小的图像,但是,当对较大的图像进行编码并传递给子活动时,应用程序会冻结并返回到主活动。
private void pass_image_to_file_browser( Bitmap image )
Intent intent = new Intent(Encrypt.this,FileBrowser.class);
intent.putExtra( Intent.EXTRA_STREAM, image );
startActivity( intent );
@Override
public void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
Bundle bundle = this.getIntent().getExtras();
Bitmap image = bundle.getParacable(Intent.EXTRA_STREAM);
我假设一个大的位图太大而无法使用意图在活动之间发送,所以我决定简单地将图像保存在一个临时位置并将图像的位置传递给子活动。然后将 png 图像保存在用户指定的位置并删除临时图像文件。
private void save_bitmap( Bitmap image, String location )
FileOutputStream fileOutputStream = new FileOutputStream(location);
BufferedOutputStream buffered_output_stream = new
BufferOutputStream(fileOutputStream);
image.compress(CompressFormat.PNG, 0, buffered_output_stream);
buffered_output_stream.flush();
buffered_output_stream.close();
这解决了将大位图从一个活动发送到另一个活动的问题,但是产生了一个我无法解决的新问题。在将文件位置传递给子活动之前保存的临时图像和使用文件浏览器重新保存图像后的图像文件都稍微改变了颜色。这种颜色变化肉眼无法识别,但是在解码图像时会导致很多问题。 我的一个想法是 Bitmap.Config 正在从 ARGB_8888 更改为 ARGB_4444 或 RGB_565 但是,在调试后情况并非如此。位图被实例化为 ARGB_8888 并保存为 ARGB_8888 位图,并且在两者之间永远不会更改。如果我将整个位图传递给文件浏览器活动,并且我在两个地方保存的位图完全相同,该代码仍然有效。我对还有什么可能导致这种情况没有任何想法。我正在寻找有关其他可能导致问题的建议。抱歉,我打算在两种情况下都在输出上发布图像,但是堆栈溢出不会让我达到我的声誉水平谢谢。
【问题讨论】:
虽然调试显示正在保存的位图有 ARGB_8888 压缩后的 png 图像的颜色配置检查似乎在 RGB_565。此外,输出图像似乎不包含 alpha 通道。 搜索后我发现另一个问题讨论相同的问题。 steganography 似乎无论我做什么,位图总是保存为 RGB_565,即使我没有操作任何像素并返回传入的确切位图。我不确定如何将它传递给另一个活动通过意图和解析位图确实可以,但是如果修复了导致位图在压缩时转换为 RGB_565 的原因。在主要活动中压缩位图是问题。 【参考方案1】:好的,在担心线程和我用来编码位图的算法浪费了很多时间之后,问题变得更简单了。在解码要使用消息编码的图像文件时,我正在使用options.inPreferredConfig = Config.ARGB_8888;
在调试期间,我正在检查以确保它没有更改为 RGB_565。尽管位图对象作为 ARBG_8888 加载,但图像文件不包含 alpha 通道,因此即使位图有一个用于 alpha 级别的字节,并且允许我通过Bitmap.setPixel( x, y, color)
编辑像素的 alpha 字节,位图对象从不认识到它设置了 alpha 值。压缩位图时压缩为 RGB_565,因为对象认为没有 Alpha 通道。不知何故,通过将位图传递给子活动并解析它,这个问题得到了解决。我猜当对象被重新创建时,我设置的 alpha 值被识别了。要解决不将位图传递给子活动的问题,必须在从文件中解码位图后添加一个 Alpha 通道。我找到了这样做的函数here。
private Bitmap adjustOpacity( Bitmap bitmap )
int width = bitmap.getWidth();
int height = bitmap.getHeight();
Bitmap dest = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
int[] pixels = new int[width * height];
bitmap.getPixels(pixels, 0, width, 0, 0, width, height);
dest.setPixels(pixels, 0, width, 0, 0, width, height);
return dest;
我不确定是否有更有效的方法
【讨论】:
【参考方案2】:对于任何偶然发现它的人,比如我,想知道为什么当你用 PNG 写出来后,bitnap 颜色会发生变化。我发现在调用 bitmap.compress(PNG, 100, fileout) 之前使用 bitmap.setPreMultiplied(false) 可以保留图像的颜色(如果它具有 alpha 通道值)。 Setpremultiplied 防止写入磁盘的像素与 alpha 通道相乘,从而生成不同的像素颜色值。
【讨论】:
以上是关于压缩为 png 时位图颜色变化的主要内容,如果未能解决你的问题,请参考以下文章
将位图 (bmp) 转换为具有透明度的 png (Windows c++)