На моем устройстве Samsung Galaxy A54 5G с Android 14 я пробую API камеры2 и делаю снимки в формате YUV 420. Устройство поддерживает только JPEG и YUV 420. Я пробовал множество методов преобразования растрового изображения YUV 420 в RGBA и его отображения/сохранения, но независимо от того, какой метод, все они работают более или менее и возвращают правильное, но зеленоватое изображение. Среди прочего я попробовал следующие методы:
Как упоминалось ранее, все вышеперечисленные методы возвращают зеленоватое (или желтоватое) изображение, где зеленоватый оттенок виден в основном на белых/серых областях, как в примере ниже, взятом с моим приложением для разработки:
В качестве правильного результата преобразования я ожидал бы что-то очень похожее на приведенный ниже справочный снимок экрана:
Обновление №1: Попробовал некоторые доступные коды и примеры на устройстве Huawei, цвета правильные! Например, самый простой способ использования ScriptIntrinsicYuvToRGB дает правильные цвета на устройстве Huawei, но возвращает зеленоватое изображение на Samsung. Видимо, это странное поведение Samsung.
Обновление №2: Демонстрация HDRViewFinder, доступная в образцах камер Google, дает правильный цвет, но, к сожалению, эту часть рендеринга я не могу использовать на платформе Xamarin.Android. Можно ли как-то переписать код hdrmerge на собственный код Android?
Это все еще не 100% удовлетворительное решение, но, по крайней мере, оно дает гораздо лучшие цвета после преобразования YUV -> RGB. Плюс он медленный для изображений с очень высоким разрешением. Код ниже представляет собой комбинацию this ответа и this ядра рендерскрипта:
case ImageFormatType.Yuv420888:
var planes = img.GetPlanes();
var yPlane = planes[0];
var uPlane = planes[1];
var vPlane = planes[2];
var rgbBytes = new int[img.Height * img.Width];
var idx = 0;
var yBuffer = yPlane.Buffer;
var yPixelStride = yPlane.PixelStride;
var yRowStride = yPlane.RowStride;
var uBuffer = uPlane.Buffer;
var uPixelStride = uPlane.PixelStride;
var uRowStride = uPlane.RowStride;
var vBuffer = vPlane.Buffer;
var vPixelStride = vPlane.PixelStride;
var vRowStride = vPlane.RowStride;
for (var row = 0; row < img.Height; row++) {
for (var col = 0; col < img.Width; col++) {
var Y = (byte)yBuffer.Get(col * yPixelStride + row * yRowStride);
var U = (byte)uBuffer.Get(col / 2 * uPixelStride + row / 2 * uRowStride);
var V = (byte)vBuffer.Get(col / 2 * vPixelStride + row / 2 * vRowStride);
// Convert YUV to RGB, JFIF transform with fixed-point math.
// Formulas can be found here: https://en.wikipedia.org/wiki/YCbCr#JPEG_conversion
// R = Y + 1.402 * (V - 128)
// G = Y - 0.34414 * (U - 128) - 0.71414 * (V - 128)
// B = Y + 1.772 * (U - 128)
var R = Y + 1436 / 1024 * (V - 128);
var G = Y - 46549 / 131072 * (U - 128) - 93604 / 131072 * (V - 128);
var B = Y + 1814 / 1024 * (U - 128);
R = MathUtils.Clamp(R, 0, 255);
G = MathUtils.Clamp(G, 0, 255);
B = MathUtils.Clamp(B, 0, 255);
rgbBytes[idx++] = BitConverter.ToInt32(new byte[] { (byte)B, (byte)G, (byte)R, 0xFF }, 0);
}
}
FinalBitmap = Bitmap.CreateBitmap(rgbBytes, img.Width, img.Height, Bitmap.Config.Argb8888);
img.Close();
break;
Мне потребовались годы, чтобы понять, но проблема наконец решена!
В настройках запроса на захват камеры перед захватом мне пришлось изменить настройки баланса белого с «Авто» на «Флуоресцентный», и зеленый оттенок окончательного изображения исчез!