Преобразовать android.media.Image(YUV_420_888) в растровое изображение
Я пытаюсь выполнить обработку данных изображения предварительного просмотра камеры с помощью camera2 api, как предлагается здесь: Обработка данных предварительного просмотра камеры с помощью Android L и Camera2 API.
Я успешно получаю обратные вызовы, используя onImageAvailableListener, но для будущей обработки мне нужно получить растровое изображение из YUV_420_888 android.media.Image. Я искал похожие вопросы, но ни один из них не помог.
Не могли бы вы предложить мне, как конвертировать файл android.media.Image(YUV_420_888) в растровое изображение или, может быть, лучший способ прослушивания кадров предварительного просмотра?
Ответы
Ответ 1
Я пишу некоторый код об этом, и он просматривает данные YUV и записывает их в JPEG-данные, и я могу использовать его для сохранения в виде растрового изображения, байт [] или других. (Вы можете увидеть класс "Распределение" ),
В документе SDK говорится: "Для эффективной обработки YUV с помощью android.rderscript: создайте выделение RenderScript с поддерживаемым типом YUV, флагом IO_INPUT и одним из размеров, возвращаемых getOutputSizes (Allocation.class). Затем получите Поверхность с getSurface()."
вот код, надеюсь, он вам поможет: https://github.com/pinguo-yuyidong/Camera2/blob/master/camera2/src/main/rs/yuv2rgb.rs
Ответ 2
Для более простого решения смотрите мою реализацию здесь:
Преобразование YUV 420_888 в растровое изображение (полный код)
Функция принимает media.image в качестве входных данных и создает три распределения RenderScript на основе y-, u- и v-плоскостей. Он следует логике YUV_420_888, как показано на этой иллюстрации в Википедии.
![enter image description here]()
Тем не менее, здесь у нас есть три отдельные плоскости изображения для Y, U и V-каналов, поэтому я принимаю их как три байта [], то есть распределения U8. Распределение y- имеет размер width * height bytes, в то время как распределение u- и v имеет размер size * height/4 байта каждый, отражая тот факт, что каждый байт u- покрывает 4 пикселя (то же самое для каждого v байта).
Ответ 3
Вы можете сделать это с помощью встроенного встроенного Renderscript, ScriptIntrinsicYuvToRGB
. Код, полученный из Camera2 api Imageformat.yuv_420_888, приводит к повороту изображения:
@Override
public void onImageAvailable(ImageReader reader)
{
// Get the YUV data
final Image image = reader.acquireLatestImage();
final ByteBuffer yuvBytes = this.imageToByteBuffer(image);
// Convert YUV to RGB
final RenderScript rs = RenderScript.create(this.mContext);
final Bitmap bitmap = Bitmap.createBitmap(image.getWidth(), image.getHeight(), Bitmap.Config.ARGB_8888);
final Allocation allocationRgb = Allocation.createFromBitmap(rs, bitmap);
final Allocation allocationYuv = Allocation.createSized(rs, Element.U8(rs), yuvBytes.array().length);
allocationYuv.copyFrom(yuvBytes.array());
ScriptIntrinsicYuvToRGB scriptYuvToRgb = ScriptIntrinsicYuvToRGB.create(rs, Element.U8_4(rs));
scriptYuvToRgb.setInput(allocationYuv);
scriptYuvToRgb.forEach(allocationRgb);
allocationRgb.copyTo(bitmap);
// Release
bitmap.recycle();
allocationYuv.destroy();
allocationRgb.destroy();
rs.destroy();
image.close();
}
private ByteBuffer imageToByteBuffer(final Image image)
{
final Rect crop = image.getCropRect();
final int width = crop.width();
final int height = crop.height();
final Image.Plane[] planes = image.getPlanes();
final byte[] rowData = new byte[planes[0].getRowStride()];
final int bufferSize = width * height * ImageFormat.getBitsPerPixel(ImageFormat.YUV_420_888) / 8;
final ByteBuffer output = ByteBuffer.allocateDirect(bufferSize);
int channelOffset = 0;
int outputStride = 0;
for (int planeIndex = 0; planeIndex < 3; planeIndex++)
{
if (planeIndex == 0)
{
channelOffset = 0;
outputStride = 1;
}
else if (planeIndex == 1)
{
channelOffset = width * height + 1;
outputStride = 2;
}
else if (planeIndex == 2)
{
channelOffset = width * height;
outputStride = 2;
}
final ByteBuffer buffer = planes[planeIndex].getBuffer();
final int rowStride = planes[planeIndex].getRowStride();
final int pixelStride = planes[planeIndex].getPixelStride();
final int shift = (planeIndex == 0) ? 0 : 1;
final int widthShifted = width >> shift;
final int heightShifted = height >> shift;
buffer.position(rowStride * (crop.top >> shift) + pixelStride * (crop.left >> shift));
for (int row = 0; row < heightShifted; row++)
{
final int length;
if (pixelStride == 1 && outputStride == 1)
{
length = widthShifted;
buffer.get(output.array(), channelOffset, length);
channelOffset += length;
}
else
{
length = (widthShifted - 1) * pixelStride + 1;
buffer.get(rowData, 0, length);
for (int col = 0; col < widthShifted; col++)
{
output.array()[channelOffset] = rowData[col * pixelStride];
channelOffset += outputStride;
}
}
if (row < heightShifted - 1)
{
buffer.position(buffer.position() + rowStride - length);
}
}
}
return output;
}