【问题标题】:Image transparency darkened when saved using OpenCv使用 OpenCv 保存时图像透明度变暗
【发布时间】:2012-05-24 22:47:29
【问题描述】:

我创建了一个绘图应用程序,允许用户在其中绘制并保存图像,以便稍后重新加载以继续绘图。本质上,我将绘图作为位图传递给要保存的 JNI 层,并与加载以前的绘图相同。

我正在使用 OpenCv 来读写 png 文件。

我注意到图像的透明度有些奇怪。似乎是根据 OpenCv 上的黑色计算透明度?看看附上的图片,其中包含有透明胶片的线条。

通过将 int 数组传递给本机代码来纠正透明度,无需颜色转换:

通过将位图对象传递给本机代码来加深透明度,需要进行颜色转换:

可能会发生什么?

使用原生 Bitmap 获取像素方法保存图片:

if ((error = AndroidBitmap_getInfo(pEnv, jbitmap, &info)) < 0) {
    LOGE("AndroidBitmap_getInfo() failed! error:%d",error);
}

if (0 == error)
{
    if ((error = AndroidBitmap_lockPixels(pEnv, jbitmap, &pixels)) < 0) {
        LOGE("AndroidBitmap_lockPixels() failed ! error=%d", error);
    }
}

if (0 == error)
{
    if (info.format == ANDROID_BITMAP_FORMAT_RGBA_8888)
    {
        LOGI("ANDROID_BITMAP_FORMAT_RGBA_8888");
    }
    else
    {
        LOGI("ANDROID_BITMAP_FORMAT %d",info.format);
    }

    Mat bgra(info.height, info.width, CV_8UC4, pixels);
    Mat image;

    //bgra.copyTo(image);

    // fix pixel order RGBA -> BGRA
    cvtColor(bgra, image, COLOR_RGBA2BGRA);

    vector<int> compression_params;
    compression_params.push_back(CV_IMWRITE_PNG_COMPRESSION);
    compression_params.push_back(3);

    // save image
    if (!imwrite(filePath, image, compression_params))
    {
        LOGE("saveImage() -> Error saving image!");
        error = -7;
    }

    // release locked pixels
    AndroidBitmap_unlockPixels(pEnv, jbitmap);
}

使用原生 int 像素数组方法保存图像:

JNIEXPORT void JNICALL Java_com_vblast_smasher_Smasher_saveImageRaw
    (JNIEnv *pEnv, jobject obj, jstring jFilePath, jintArray jbgra, jint options, jint compression)
{
    jint*  _bgra = pEnv->GetIntArrayElements(jbgra, 0);
    const char *filePath = pEnv->GetStringUTFChars(jFilePath, 0);

    if (NULL != filePath)
    {
        Mat image;
        Mat bgra(outputHeight, outputWidth, CV_8UC4, (unsigned char *)_bgra);

        bgra.copyTo(image);

        if (0 == options)
        {
            // replace existing cache value
            mpCache->insert(filePath, image);
        }

        vector<int> compression_params;
        compression_params.push_back(CV_IMWRITE_PNG_COMPRESSION);
        compression_params.push_back(compression);

        // save image
        if (!imwrite(filePath, image))
        {
            LOGE("saveImage() -> Error saving image!");
        }
    }

    pEnv->ReleaseIntArrayElements(jbgra, _bgra, 0);
    pEnv->ReleaseStringUTFChars(jFilePath, filePath);
}

2012 年 5 月 25 日更新:
经过更多研究后,我发现如果我从位图中获取像素的 int 数组并将其直接传递给 JNI,而不是我目前所做的将整个位图传递给 JNI,则不会发生此问题图层然后获取像素并使用cvtColor 正确转换像素。我是否使用了正确的像素转换?

【问题讨论】:

  • compression 的值是多少?
  • 我尝试的值是 0、3 和 9。它们都产生相同的结果。
  • 这是什么版本,你从哪里下载的?
  • 我是从官方“网站”v2.4.0下载的。请参阅我的问题的更新部分。我转换颜色的方式有问题,或者颜色与转换混淆了。
  • 您真的需要进行转换吗?你确定吗?

标签: opencv android-ndk


【解决方案1】:

在 RGBA 像素中有两种表示 alpha 的方式,预乘或不预乘。通过预乘,R、G 和 B 值乘以 alpha 的百分比:color = (color * alpha) / 255。这简化了很多混合计算,并且经常在成像库内部使用。在保存为不使用预乘 alpha 的格式(例如 PNG)之前,颜色值必须是“未乘”:color = (255 * color) / alpha。如果不是,颜色会显得太暗;颜色越透明,颜色就越深。这看起来就像您在这里看到的效果。

【讨论】:

  • 嗯,这很可能是问题所在。我测试了在 java 层上写入一些已知的像素值,而不是在本机端使用 alpha 的计算来读取这些像素并正确读取这些像素值。现在我需要弄清楚如何转换所有像素保存并查看是否是问题所在。
  • 如何混合两个相乘的 alpha 像素?
  • @Jona,简单本身:对于图像topbottom 的通道(R、G、B、A),使用blended.c = ((255 - top.A) * bottom.c + top.A * top.c) / 255
  • 感谢您的帮助!我对事物的整个成像方面都很陌生,这是非常有趣的东西:)
【解决方案2】:

opencv 中没有所谓的透明图像。前景和背景图像适当混合以产生透明的错觉。检查this 看看它是如何完成的。

【讨论】:

  • 问题不是覆盖图像,而是正确保存它们以保持正确的透明度。保存的图像缺少一些东西。就好像 Alpha 通道在某个时候被交换了一样?
猜你喜欢
  • 2012-02-05
  • 1970-01-01
  • 1970-01-01
  • 2014-09-25
  • 2017-05-21
  • 1970-01-01
  • 2020-08-08
  • 1970-01-01
  • 2011-05-11
相关资源
最近更新 更多