使用OpenCV保存时,图像透明度变暗

4
我创建了一个绘画应用程序,在其中允许用户绘制并保存图像以便以后重新加载继续绘制。基本上,我将绘图作为位图传递到JNI层进行保存,并使用相同的方法来加载先前的绘图。

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

在图像的透明度方面,我注意到一些奇怪的地方。似乎透明度是针对OpenCv中的黑色计算出来的?请看附带的图片,它们包含具有透明度的线条。

通过将int数组传递给本地代码来正确处理透明度,不需要进行颜色转换: enter image description here

通过将Bitmap对象传递给本地代码来加深透明度,需要进行颜色转换: enter image description here

可能会发生什么问题?

使用本机Bitmap get pixel方法保存图像:

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);
}
更新 05/25/12:
经过进一步的研究,我发现如果我直接从位图获取int像素数组并将其直接传递给JNI,则不会出现此问题,而不是当前所做的将整个位图传递到JNI层,然后获取像素并使用cvtColor正确转换像素。 我是否使用了正确的像素转换?

compression 的值是多少? - karlphillip
这是哪个版本,你从哪里下载的? - karlphillip
我从官方“网站”下载了v2.4.0版本。请查看我的问题的更新部分。我转换颜色的方式有问题,或者颜色在转换过程中混淆了。 - Jona
你真的需要进行转换吗?你确定吗? - karlphillip
嗯...如果在进行颜色转换和保存到磁盘之前显示图像,它看起来正常吗? - karlphillip
显示剩余4条评论
2个回答

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

嗯,那很可能就是问题所在。我在Java层测试了几个已知的像素值,然后使用除以alpha的计算方法在本地端读取这些像素,并正确地读取了这些像素值。现在我需要弄清楚如何转换所有像素并保存,看看是否存在问题。 - Jona
你如何混合两个乘以 alpha 像素? - Jona
@Jona,非常简单:对于图像“top”和“bottom”的通道(R、G、B、A),使用blended.c = ((255-top.A)*bottom.c+top.A*top.c)/255进行混合。 - Mark Ransom
谢谢你的帮助!我对图像处理方面非常陌生,这是非常有趣的东西 :) - Jona

-1
在OpenCV中没有所谓的透明图像。前景和背景图像会被适当混合以产生透明效果。请查看this以了解如何实现。

问题不在于叠加图像,而在于正确保存它们并保持透明度。保存的图像似乎缺少某些东西,就好像 alpha 通道在某个时刻被交换了一样? - Jona

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