How to save image YUV_420_888?

I created my own camera app using camera2 API. I started with the sample " camera2Raw ", and I added support for YUV_420_888 instead of JPEG. But now I wonder how I save images in ImageSaver !?

Here is my run method code:

@Override public void run() { boolean success = false; int format = mImage.getFormat(); switch(format) { case ImageFormat.RAW_SENSOR:{ DngCreator dngCreator = new DngCreator(mCharacteristics, mCaptureResult); FileOutputStream output = null; try { output = new FileOutputStream(mFile); dngCreator.writeImage(output, mImage); success = true; } catch (IOException e) { e.printStackTrace(); } finally { mImage.close(); closeOutput(output); } break; } case ImageFormat.YUV_420_888:{ ByteBuffer buffer = mImage.getPlanes()[0].getBuffer(); byte[] bytes = new byte[buffer.remaining()]; buffer.get(bytes); FileOutputStream output = null; try { output = new FileOutputStream(mFile); output.write(bytes); success = true; } catch (IOException e) { e.printStackTrace(); } finally { mImage.close(); closeOutput(output); } break; } default: Log.e(TAG, "Cannot save image, unexpected image format:" + format); } // Decrement reference count to allow ImageReader to be closed to free up resources. mReader.close(); // If saving the file succeeded, update MediaStore. if (success) { MediaScannerConnection.scanFile(mContext, new String[] { mFile.getPath()}, /*mimeTypes*/ null, new MediaScannerConnection.MediaScannerConnectionClient() { @Override public void onMediaScannerConnected() { // Do nothing } @Override public void onScanCompleted(String path, Uri uri) { Log.i(TAG, "Scanned " + path + ":"); Log.i(TAG, "-> uri=" + uri); } }); } } 

I tried to save YUV images as JPEG, but this way I get only one plane, and the saved data does not make any sense to me ...

What is the correct way to save a YUV image? Convert it to RGB (what is YUV then?)? Or with the YuvImage class?

+2
source share
2 answers

As a rule, you do not save the YUV image as a file, and there are no built-in functions for this. Moreover, there is no standard image format encoding for such YUV data. YUV is usually an intermediate form of data convenient for the camera conveyor and subsequent conversion to other formats.

If you really insist on it, you can write buffers for three channels as unregistered byte data to a file, and then open them in another place and restore. Be sure to save other important information, for example, step data. That's what I'm doing. Below are the relevant lines from the file format switch instruction I am using, as well as comments on the arguments:

 File file = new File(SAVE_DIR, mFilename); FileOutputStream output = null; ByteBuffer buffer; byte[] bytes; boolean success = false; switch (mImage.getFormat()){ (... other image data format cases ...) // YUV_420_888 images are saved in a format of our own devising. First write out the // information necessary to reconstruct the image, all as ints: width, height, U-,V-plane // pixel strides, and U-,V-plane row strides. (Y-plane will have pixel-stride 1 always.) // Then directly place the three planes of byte data, uncompressed. // // Note the YUV_420_888 format does not guarantee the last pixel makes it in these planes, // so some cases are necessary at the decoding end, based on the number of bytes present. // An alternative would be to also encode, prior to each plane of bytes, how many bytes are // in the following plane. Perhaps in the future. case ImageFormat.YUV_420_888: // "prebuffer" simply contains the meta information about the following planes. ByteBuffer prebuffer = ByteBuffer.allocate(16); prebuffer.putInt(mImage.getWidth()) .putInt(mImage.getHeight()) .putInt(mImage.getPlanes()[1].getPixelStride()) .putInt(mImage.getPlanes()[1].getRowStride()); try { output = new FileOutputStream(file); output.write(prebuffer.array()); // write meta information to file // Now write the actual planes. for (int i = 0; i<3; i++){ buffer = mImage.getPlanes()[i].getBuffer(); bytes = new byte[buffer.remaining()]; // makes byte array large enough to hold image buffer.get(bytes); // copies image from buffer to byte array output.write(bytes); // write the byte array to file } success = true; } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { Log.v(appFragment.APP_TAG,"Closing image to free buffer."); mImage.close(); // close this to free up buffer for other images if (null != output) { try { output.close(); } catch (IOException e) { e.printStackTrace(); } } } break; } 

Since the device can exactly match how the data is interleaved, it can be difficult to extract channels Y, U, V from this encoded information. To see the MATLAB implementation, see how to read and extract a file like this.

+3
source

If you have a YuvImage object, you can convert it to Jpeg using the compressToJpeg function, for example.

 ByteArrayOutputStream out = new ByteArrayOutputStream(); YuvImage yuvImage = new YuvImage(data, ImageFormat.NV21, width, height, null); yuvImage.compressToJpeg(new Rect(0, 0, width, height), 50, out); byte[] imageBytes = out.toByteArray(); Bitmap image = BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.length); iv.setImageBitmap(image); 

You should set the width and height of the image explicitly, though.

+1
source

Source: https://habr.com/ru/post/1262467/


All Articles