You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

三星Galaxy A54 5G(Android14)Camera2 YUV转RGBA偏绿问题求助

三星Galaxy A54 5G YUV420转RGBA偏色修复及HDRViewFinder代码迁移方案

一、YUV转RGBA偏色问题解决

三星A54 5G的Camera2输出YUV420格式存在非标准实现,主要是UV通道顺序反转或色彩空间不匹配导致白色/灰色区域偏绿/黄,华为等设备无此问题是因为遵循标准NV21/YV12规范。

1. 先确认YUV子格式

通过Image对象的Planes信息判断具体格式:

Image.Plane[] planes = image.getPlanes();
// 检查UV平面的像素步长,YV12的U/V平面像素步长均为1,NV21则是2
boolean isYV12 = planes[1].getPixelStride() == 1 && planes[2].getPixelStride() == 1;

三星A54大概率输出YV12,而多数转换工具默认按NV21处理,直接导致UV错位偏色。

2. 针对性修复方案

方案1:交换UV通道后再转换

手动调换U和V缓冲区数据,适配YV12转NV21的场景:

Image.Plane[] planes = image.getPlanes();
ByteBuffer yBuf = planes[0].getBuffer();
ByteBuffer uBuf = planes[1].getBuffer();
ByteBuffer vBuf = planes[2].getBuffer();

// 保存U通道数据
byte[] uData = new byte[uBuf.remaining()];
uBuf.get(uData);
uBuf.clear();

// 将V通道数据写入U缓冲区
byte[] vData = new byte[vBuf.remaining()];
vBuf.get(vData);
uBuf.put(vData);
vBuf.clear();

// 将原U通道数据写入V缓冲区
vBuf.put(uData);

// 之后用原有转换工具(如ScriptIntrinsicYuvToRGB)处理调整后的Planes

方案2:强制指定色彩转换矩阵

部分三星设备默认使用BT.2020色彩空间,而转换工具默认用BT.709,需手动替换转换矩阵:

// BT.2020到sRGB的转换矩阵
float[] colorMatrix = {
    1.6605f, -0.1246f, -0.0182f, 0,
    -0.5876f, 1.3808f, 0.0466f, 0,
    -0.0729f, -0.2562f, 1.0416f, 0,
    0, 0, 0, 1
};

// 在RenderScript转换中应用矩阵
RenderScript rs = RenderScript.create(context);
ScriptIntrinsicYuvToRGB yuvConverter = ScriptIntrinsicYuvToRGB.create(rs, Element.U8_4(rs));
yuvConverter.setInput(Allocation.createFromByteBuffer(rs, yBuf, Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_SCRIPT));
yuvConverter.setColorMatrix(colorMatrix);

Allocation rgbAlloc = Allocation.createTyped(rs, Type.createXY(rs, Element.U8_4(rs), width, height));
yuvConverter.forEach(rgbAlloc);

Bitmap outputBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
rgbAlloc.copyTo(outputBitmap);

方案3:修正官方YuvToRgbConverter

修改Camera2示例中的YuvToRgbConverter,增加YV12格式判断:

public void convert(Image image, Bitmap output) {
    Image.Plane[] planes = image.getPlanes();
    boolean isYV12 = planes[1].getPixelStride() == 1 && planes[2].getPixelStride() == 1;
    if (isYV12) {
        // 交换U和V平面
        Image.Plane tempPlane = planes[1];
        planes[1] = planes[2];
        planes[2] = tempPlane;
    }
    // 执行原转换逻辑
    // ...(保留原YuvToRgbConverter的代码)
}

二、HDRViewFinder的hdrmerge改写为原生Android代码

完全可以实现,核心是复刻RenderScript版的多帧HDR合成逻辑,以下是简化的原生Java实现:

1. 核心合成逻辑

public class HdrFrameMerger {
    // 多帧YUV融合,exposureWeights为各帧的曝光权重(暗帧权重高,亮帧权重低)
    public byte[] mergeHdrFrames(List<byte[]> yuvFrames, List<Float> exposureWeights, int width, int height) {
        int ySize = width * height;
        int uvSize = ySize / 2;
        byte[] mergedYuv = new byte[ySize + uvSize];

        // 融合Y通道(高光压制、阴影提升)
        for (int i = 0; i < ySize; i++) {
            float weightedSum = 0f;
            float weightTotal = 0f;
            for (int frameIdx = 0; frameIdx < yuvFrames.size(); frameIdx++) {
                byte yByte = yuvFrames.get(frameIdx)[i];
                int yValue = yByte & 0xFF;
                // 模拟原RenderScript的权重分配:亮像素降低亮帧权重,暗像素提升暗帧权重
                float pixelWeight = exposureWeights.get(frameIdx) * (yValue < 128 ? 1.5f : 0.5f);
                weightedSum += yValue * pixelWeight;
                weightTotal += pixelWeight;
            }
            mergedYuv[i] = (byte) Math.min(Math.max(weightedSum / weightTotal, 0), 255);
        }

        // 融合UV通道(简化处理,实际需和Y通道对齐)
        for (int i = ySize; i < mergedYuv.length; i++) {
            float weightedSum = 0f;
            float weightTotal = 0f;
            for (int frameIdx = 0; frameIdx < yuvFrames.size(); frameIdx++) {
                byte uvByte = yuvFrames.get(frameIdx)[i];
                int uvValue = uvByte & 0xFF;
                float pixelWeight = exposureWeights.get(frameIdx);
                weightedSum += uvValue * pixelWeight;
                weightTotal += pixelWeight;
            }
            mergedYuv[i] = (byte) Math.min(Math.max(weightedSum / weightTotal, 0), 255);
        }

        return mergedYuv;
    }

    // 可选:帧对齐(基于特征点匹配,复杂场景可引入OpenCV简化开发)
    public List<byte[]> alignFrames(List<byte[]> yuvFrames, int width, int height) {
        // 此处实现简单的平移对齐,针对轻微抖动场景
        // ...
        return yuvFrames;
    }
}

2. 注意事项

  • 若要和原RenderScript效果完全一致,需严格复刻其像素权重计算逻辑和帧对齐算法,原hdrmerge会根据像素亮度动态调整单帧权重,避免高光过曝或阴影过暗。
  • 合成计算量较大,务必放在AsyncTask或Coroutine(Kotlin)后台线程执行,避免阻塞UI。

内容的提问来源于stack exchange,提问作者Ladislav

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.25 02:56:02