Skip to content

Instantly share code, notes, and snippets.

@mebjas
Created July 12, 2021 15:13
Show Gist options
  • Save mebjas/a3a6b39e7288d23a2fc7188fa883dc5f to your computer and use it in GitHub Desktop.
Save mebjas/a3a6b39e7288d23a2fc7188fa883dc5f to your computer and use it in GitHub Desktop.
YUV_420_888 Image to Bitmap using ScriptIntrinsicYuvToRGB
// yuv2rgb-lib.cc JNI code.
namespace {
void yuv420toNv21(int image_width, int image_height, const int8_t* y_buffer,
const int8_t* u_buffer, const int8_t* v_buffer, int y_pixel_stride,
int uv_pixel_stride, int y_row_stride, int uv_row_stride,
int8_t *nv21) {
// Copy Y channel.
for(int y = 0; y < image_height; ++y) {
int destOffset = image_width * y;
int yOffset = y * y_row_stride;
memcpy(nv21 + destOffset, y_buffer + yOffset, image_width);
}
if (v_buffer - u_buffer == sizeof(int8_t)) {
// format = nv21
// TODO: If the format is VUVUVU & pixel stride == 1 we can simply the copy
// with memcpy. In Android Camera2 I have mostly come across UVUVUV packaging
// though.
}
// Copy UV Channel.
int idUV = image_width * image_height;
int uv_width = image_width / 2;
int uv_height = image_height / 2;
for(int y = 0; y < uv_height; ++y) {
int uvOffset = y * uv_row_stride;
for (int x = 0; x < uv_width; ++x) {
int bufferIndex = uvOffset + (x * uv_pixel_stride);
// V channel.
nv21[idUV++] = v_buffer[bufferIndex];
// U channel.
nv21[idUV++] = u_buffer[bufferIndex];
}
}
}
} // nampespace
extern "C" {
jboolean Java_com_example_androidcv_camera_processing_YuvConvertor_yuv420toNv21(
JNIEnv *env, jclass clazz,
jint image_width, jint image_height, jobject y_byte_buffer,
jobject u_byte_buffer, jobject v_byte_buffer, jint y_pixel_stride,
jint uv_pixel_stride, jint y_row_stride, jint uv_row_stride,
jbyteArray nv21_array) {
auto y_buffer = static_cast<jbyte*>(env->GetDirectBufferAddress(y_byte_buffer));
auto u_buffer = static_cast<jbyte*>(env->GetDirectBufferAddress(u_byte_buffer));
auto v_buffer = static_cast<jbyte*>(env->GetDirectBufferAddress(v_byte_buffer));
jbyte* nv21 = env->GetByteArrayElements(nv21_array, nullptr);
if (nv21 == nullptr || y_buffer == nullptr || u_buffer == nullptr
|| v_buffer == nullptr) {
// Log this.
return false;
}
yuv420toNv21(image_width, image_height, y_buffer, u_buffer, v_buffer,
y_pixel_stride, uv_pixel_stride, y_row_stride, uv_row_stride,
nv21);
env->ReleaseByteArrayElements(nv21_array, nv21, 0);
return true;
}
} // extern "C"
class YuvConvertor {
private final Allocation in, out;
private final ScriptIntrinsicYuvToRGB script;
static {
System.loadLibrary("yuv2rgb-lib");
}
public YuvConvertor(Context context, int width, int height) {
RenderScript rs = RenderScript.create(context);
this.script = ScriptIntrinsicYuvToRGB.create(
rs, Element.U8_4(rs));
// NV21 YUV image of dimension 4 X 4 has following packing:
// YYYYYYYYYYYYYYYYVUVUVUVU
// With each pixel (of any channel) taking 8 bits.
int yuvByteArrayLength = (int) (width * height * 1.5f);
Type.Builder yuvType = new Type.Builder(rs, Element.U8(rs))
.setX(yuvByteArrayLength);
this.in = Allocation.createTyped(
rs, yuvType.create(), Allocation.USAGE_SCRIPT);
Type.Builder rgbaType = new Type.Builder(rs, Element.RGBA_8888(rs))
.setX(width)
.setY(height);
this.out = Allocation.createTyped(
rs, rgbaType.create(), Allocation.USAGE_SCRIPT);
}
@Nullable
public Bitmap toBitmap(Image image) {
if (image.getFormat() != ImageFormat.YUV_420_888) {
throw new IllegalArgumentException("Only supports YUV_420_888.");
}
byte[] yuvByteArray = toNv21(image);
if (yuvByteArray == null) {
return null;
}
in.copyFrom(yuvByteArray);
script.setInput(in);
script.forEach(out);
// Allocate memory for the bitmap to return. If you have a reusable Bitmap
// I recommending using that.
Bitmap bitmap = Bitmap.createBitmap(
image.getWidth(), image.getHeight(), Config.ARGB_8888);
out.copyTo(bitmap);
return bitmap;
}
@Nullable
public static byte[] toNv21(Image image) {
byte[] nv21 = new byte[(int) (image.getWidth() * image.getHeight() * 1.5f)];
if (!yuv420toNv21(
image.getWidth(),
image.getHeight(),
image.getPlanes()[0].getBuffer(), // Y buffer
image.getPlanes()[1].getBuffer(), // U buffer
image.getPlanes()[2].getBuffer(), // V buffer
image.getPlanes()[0].getPixelStride(), // Y pixel stride
image.getPlanes()[1].getPixelStride(), // U/V pixel stride
image.getPlanes()[0].getRowStride(), // Y row stride
image.getPlanes()[1].getRowStride(), // U/V row stride
nv21)) {
return null;
}
return nv21;
}
public static native boolean yuv420toNv21(
int imageWidth,
int imageHeight,
ByteBuffer yByteBuffer,
ByteBuffer uByteBuffer,
ByteBuffer vByteBuffer,
int yPixelStride,
int uvPixelStride,
int yRowStride,
int uvRowStride,
byte[] nv21Output);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment