Как ускорить конверсию YUV для быстрого предпросмотра камеры SkiaSharp?
Я пишу код для рендеринга камеры с помощью SkiaSharp. Это кроссплатформенный, но я столкнулся с проблемой при написании реализации для Android.
Мне нужно было конвертировать YUV_420_888 в RGB8888, потому что это то, что поддерживает SkiaSharp и с помощью этой ветки каким-то образом удалось показать изображения достойного качества на моем холсте SkiaSharp. Проблема в скорости. В лучшем случае я могу получить около 8 кадров в секунду, но обычно это всего 4 или 5 кадров в секунду. Оказалось, что самым большим фактором является конверсия. Теперь у меня есть около 3 версий моего конвертера ToRGB. Я даже закончил тем, что попробовал "небезопасный" код и параллельные циклы. Я покажу тебе мой лучший.
private unsafe byte[] ToRgb(byte[] yValuesArr, byte[] uValuesArr,
byte[] vValuesArr, int uvPixelStride, int uvRowStride)
{
var width = PixelSize.Width;
var height = PixelSize.Height;
var rgb = new byte[width * height * 4];
var partitions = Partitioner.Create(0, height);
Parallel.ForEach(partitions, range =>
{
var (item1, item2) = range;
Parallel.For(item1, item2, y =>
{
for (var x = 0; x < width; x++)
{
var yIndex = x + width * y;
var currentPosition = yIndex * 4;
var uvIndex = uvPixelStride * (x / 2) + uvRowStride * (y / 2);
fixed (byte* rgbFixed = rgb)
fixed (byte* yValuesFixed = yValuesArr)
fixed (byte* uValuesFixed = uValuesArr)
fixed (byte* vValuesFixed = vValuesArr)
{
var rgbPtr = rgbFixed;
var yValues = yValuesFixed;
var uValues = uValuesFixed;
var vValues = vValuesFixed;
var yy = *(yValues + yIndex);
var uu = *(uValues + uvIndex);
var vv = *(vValues + uvIndex);
var rTmp = yy + vv * 1436 / 1024 - 179;
var gTmp = yy - uu * 46549 / 131072 + 44 - vv * 93604 / 131072 + 91;
var bTmp = yy + uu * 1814 / 1024 - 227;
rgbPtr = rgbPtr + currentPosition;
*rgbPtr = (byte) (rTmp < 0 ? 0 : rTmp > 255 ? 255 : rTmp);
rgbPtr++;
*rgbPtr = (byte) (gTmp < 0 ? 0 : gTmp > 255 ? 255 : gTmp);
rgbPtr++;
*rgbPtr = (byte) (bTmp < 0 ? 0 : bTmp > 255 ? 255 : bTmp);
rgbPtr++;
*rgbPtr = 255;
}
}
});
});
return rgb;
}
Вы также можете найти его в моем репо. Вы можете также найти в этом же репо ту часть, где я рендерил вывод в SkiaSharp
Для предварительного просмотра размером 1440x1080 на моем телефоне этот код занимает около 120 мс. Даже если все остальные части оптимизированы, максимум, что я могу получить, это 8fps. И нет, это не мое оборудование, потому что встроенная камера работает без сбоев. Кстати, 1440x1080 - это результат моего алгоритма ChooseOptimalSize, который я получил из примеров монодроидов API-интерфейса Camera2 для Android. Я не знаю, является ли это лучшим способом или ему не хватает логики для определения fps и определения размера предварительного просмотра, чтобы сделать его быстрее.
1 ответ
SkiaSharp поддерживает рисование на GPU? Если вы подключите камеру к SurfaceTexture, вы можете использовать кадры предварительного просмотра в качестве текстур GL и эффективно визуализировать их в сцену OpenGL.
Даже если нет, вы все равно можете получить более быстрые результаты, отправляя кадры в GPU и считывая их обратно в CPU с помощью чего-то вроде glReadPixels, так как это сделает преобразование RGB внутри GPU.