压缩为PNG时位图颜色变化

4

我目前正在为课程项目开发一款隐写术安卓应用。我已经创建了一个对象,可以将一张图片编码到另一张图片中,并返回一个编码后的位图。这段代码在一个单独的线程中运行。

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通道。 - alex.m.brown
在搜索后,我发现另一个讨论相同问题的问题。隐写术 - alex.m.brown
似乎无论我做什么,位图总是保存为RGB_565格式,即使我不操作任何像素并返回传入的精确位图。我不确定通过意向传递它并解析位图会出现什么问题,但如果可以修复导致位图在压缩时转换为RGB_565的问题。要么,将位图压缩到主活动中是问题所在。 - alex.m.brown
2个回答

7

好的,在长时间地担心线程和编码位图算法后,问题变得简单了一些。在解码要嵌入消息的图像文件时,我使用了options.inPreferredConfig = Config.ARGB_8888;。在调试期间,我检查确保它没有更改为RGB_565。虽然位图对象已加载为ARBG_8888,但图像文件不包含alpha通道,因此即使位图具有用于alpha级别的字节,并且可以通过Bitmap.setPixel(x, y, color)编辑像素的alpha字节,但位图对象从未意识到它具有设置的alpha值。当压缩位图时,由于对象认为没有alpha通道,因此压缩为RGB_565。通过将位图传递给子活动并对其进行解析,以某种方式解决了这个问题。我猜想,当对象被重新创建时,我设置的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;
} 

我不确定是否有更高效的方法。


3

如果有人和我一样偶然发现在将位图导出成PNG格式后,颜色发生了变化,并想知道原因,那么请注意:在调用bitmap.compress(PNG, 100, fileout)方法之前,先使用bitmap.setPreMultiplied(false)方法可以保持图片的颜色不变,尤其是当图片具有Alpha通道值时。设置setpremultiplied属性可以防止像素被Alpha通道乘以,从而生成不同的像素颜色值。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接