Skip to content

Commit 1316c85

Browse files
committed
update yuv2buf
1 parent 68109dd commit 1316c85

File tree

10 files changed

+380
-340
lines changed

10 files changed

+380
-340
lines changed

Camera2Basic/utils/src/main/java/com/example/android/camera/utils/Yuv.java

Lines changed: 73 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,19 @@
33
import android.graphics.ImageFormat;
44
import android.media.Image;
55

6+
import androidx.annotation.IntDef;
7+
8+
import java.lang.annotation.Retention;
9+
import java.lang.annotation.RetentionPolicy;
610
import java.nio.ByteBuffer;
711

812

9-
/*
10-
Taken from https://github.com/gordinmitya/yuv2buf
11-
unit-test, demo application and performance benchmarks are available there
12-
*/
1313
abstract public class Yuv {
1414
/*
15-
Intro to YUV image formats:
15+
This file is part of https://github.com/gordinmitya/yuv2buf.
16+
Follow the link to find demo app, performance benchmarks and unit tests.
17+
18+
Intro to YUV image formats:
1619
YUV_420_888 - is a generic format that can be represented as I420, YV12, NV21, and NV12.
1720
420 means that for each 4 luminosity pixels we have 2 chroma pixels: U and V.
1821
@@ -31,7 +34,8 @@ abstract public class Yuv {
3134
3235
* YV12 and NV12 are the same as previous formats but with swapped order of V and U. (U then V)
3336
34-
Visualization of these 4 formats: https://user-images.githubusercontent.com/9286092/89119601-4f6f8100-d4b8-11ea-9a51-2765f7e513c2.jpg
37+
Visualization of these 4 formats:
38+
https://user-images.githubusercontent.com/9286092/89119601-4f6f8100-d4b8-11ea-9a51-2765f7e513c2.jpg
3539
3640
It's guaranteed that image.getPlanes() always returns planes in order Y U V for YUV_420_888.
3741
https://developer.android.com/reference/android/graphics/ImageFormat#YUV_420_888
@@ -42,23 +46,17 @@ Because I420 and NV21 are more widely supported (RenderScript, OpenCV, MNN)
4246
More about each format: https://www.fourcc.org/yuv.php
4347
*/
4448

45-
46-
public enum Type {
47-
YUV_NV21(ImageFormat.NV21),
48-
YUV_I420(ImageFormat.YUV_420_888);
49-
50-
public final int format;
51-
52-
Type(int format) {
53-
this.format = format;
54-
}
49+
@Retention(RetentionPolicy.SOURCE)
50+
@IntDef({ImageFormat.NV21, ImageFormat.YUV_420_888})
51+
public @interface YuvType {
5552
}
5653

5754
public static class Converted {
58-
public final Type type;
55+
@YuvType
56+
public final int type;
5957
public final ByteBuffer buffer;
6058

61-
private Converted(Type type, ByteBuffer buffer) {
59+
private Converted(@YuvType int type, ByteBuffer buffer) {
6260
this.type = type;
6361
this.buffer = buffer;
6462
}
@@ -67,7 +65,8 @@ private Converted(Type type, ByteBuffer buffer) {
6765
/*
6866
Api.
6967
*/
70-
public static Type detectType(Image image) {
68+
@YuvType
69+
public static int detectType(Image image) {
7170
return detectType(wrap(image));
7271
}
7372

@@ -91,20 +90,20 @@ private static ImageWrapper wrap(Image image) {
9190

9291
private static PlaneWrapper wrap(int width, int height, Image.Plane plane) {
9392
return new PlaneWrapper(
94-
width,
95-
height,
96-
plane.getBuffer(),
97-
plane.getRowStride(),
98-
plane.getPixelStride()
93+
width,
94+
height,
95+
plane.getBuffer(),
96+
plane.getRowStride(),
97+
plane.getPixelStride()
9998
);
10099
}
101100

102-
// CameraX api. If you DO need it – just uncomment lines below.
103-
// not included by default see https://github.com/android/camera-samples/pull/330
104-
/*
105-
import androidx.camera.core.ImageProxy;
106-
107-
public static Type detectType(ImageProxy image) {
101+
/*
102+
CameraX api. If you don't need it – just comment lines below.
103+
*/
104+
/*
105+
@YuvType
106+
public static int detectType(ImageProxy image) {
108107
return detectType(wrap(image));
109108
}
110109
@@ -128,11 +127,11 @@ private static ImageWrapper wrap(ImageProxy image) {
128127
129128
private static PlaneWrapper wrap(int width, int height, ImageProxy.PlaneProxy plane) {
130129
return new PlaneWrapper(
131-
width,
132-
height,
133-
plane.getBuffer(),
134-
plane.getRowStride(),
135-
plane.getPixelStride()
130+
width,
131+
height,
132+
plane.getBuffer(),
133+
plane.getRowStride(),
134+
plane.getPixelStride()
136135
);
137136
}
138137
*/
@@ -146,16 +145,17 @@ private static PlaneWrapper wrap(int width, int height, ImageProxy.PlaneProxy pl
146145
other pixelStride are not possible
147146
@see #ImageWrapper.checkFormat()
148147
*/
149-
static Type detectType(ImageWrapper image) {
150-
151-
if (image.u.pixelStride == 1)
152-
return Type.YUV_I420;
153-
else
154-
return Type.YUV_NV21;
148+
@YuvType
149+
static int detectType(ImageWrapper image) {
150+
if (image.u.pixelStride == 1) {
151+
return ImageFormat.YUV_420_888;
152+
} else {
153+
return ImageFormat.NV21;
154+
}
155155
}
156156

157157
static Converted toBuffer(ImageWrapper image, ByteBuffer reuse) {
158-
Type type = detectType(image);
158+
final int type = detectType(image);
159159
ByteBuffer output = prepareOutput(image, reuse);
160160
removePadding(image, type, output);
161161
return new Converted(type, output);
@@ -165,19 +165,24 @@ private static ByteBuffer prepareOutput(ImageWrapper image, ByteBuffer reuse) {
165165
int sizeOutput = image.width * image.height * 3 / 2;
166166
ByteBuffer output;
167167
if (reuse == null
168-
|| reuse.capacity() < sizeOutput
169-
|| reuse.isReadOnly()
170-
|| !reuse.isDirect()) {
168+
|| reuse.capacity() < sizeOutput
169+
|| reuse.isReadOnly()
170+
|| !reuse.isDirect()) {
171171
output = ByteBuffer.allocateDirect(sizeOutput);
172-
} else
172+
} else {
173173
output = reuse;
174+
}
174175
output.rewind();
175176
return output;
176177
}
177178

178179
// Input buffers are always direct as described in
179180
// https://developer.android.com/reference/android/media/Image.Plane#getBuffer()
180-
private static void removePadding(ImageWrapper image, Type type, ByteBuffer output) {
181+
private static void removePadding(
182+
ImageWrapper image,
183+
@YuvType final int type,
184+
ByteBuffer output
185+
) {
181186
int sizeLuma = image.y.width * image.y.height;
182187
int sizeChroma = image.u.width * image.u.height;
183188

@@ -188,7 +193,7 @@ private static void removePadding(ImageWrapper image, Type type, ByteBuffer outp
188193
output.put(image.y.buffer);
189194
}
190195

191-
if (type.equals(Type.YUV_I420)) {
196+
if (type == ImageFormat.YUV_420_888) {
192197
if (image.u.rowStride > image.u.width) {
193198
removePaddingCompact(image.u, output, sizeLuma);
194199
removePaddingCompact(image.v, output, sizeLuma + sizeChroma);
@@ -203,17 +208,23 @@ private static void removePadding(ImageWrapper image, Type type, ByteBuffer outp
203208
removePaddingNotCompact(image, output, sizeLuma);
204209
} else {
205210
output.position(sizeLuma);
206-
output.put(image.v.buffer);
207-
byte lastOne = image.u.buffer.get(image.u.buffer.capacity() - 1);
208-
output.put(lastOne);
211+
ByteBuffer uv = image.v.buffer;
212+
final int properUVSize = image.v.height * image.v.rowStride - 1;
213+
if (uv.capacity() > properUVSize) {
214+
uv = clipBuffer(image.v.buffer, 0, properUVSize);
215+
}
216+
output.put(uv);
217+
final byte lastOne = image.u.buffer.get(image.u.buffer.capacity() - 1);
218+
output.put(output.capacity() - 1, lastOne);
209219
}
210220
}
211221
output.rewind();
212222
}
213223

214224
private static void removePaddingCompact(PlaneWrapper plane, ByteBuffer dst, int offset) {
215-
if (plane.pixelStride != 1)
225+
if (plane.pixelStride != 1) {
216226
throw new IllegalArgumentException("use removePaddingCompact with pixelStride == 1");
227+
}
217228

218229
ByteBuffer src = plane.buffer;
219230
int rowStride = plane.rowStride;
@@ -226,8 +237,9 @@ private static void removePaddingCompact(PlaneWrapper plane, ByteBuffer dst, int
226237
}
227238

228239
private static void removePaddingNotCompact(ImageWrapper image, ByteBuffer dst, int offset) {
229-
if (image.u.pixelStride != 2)
240+
if (image.u.pixelStride != 2) {
230241
throw new IllegalArgumentException("use removePaddingNotCompact pixelStride == 2");
242+
}
231243

232244
int width = image.u.width;
233245
int height = image.u.height;
@@ -268,22 +280,22 @@ static class ImageWrapper {
268280
private void checkFormat() {
269281
if (y.pixelStride != 1) {
270282
throw new IllegalArgumentException(String.format(
271-
"Pixel stride for Y plane must be 1 but got %d instead",
272-
y.pixelStride
283+
"Pixel stride for Y plane must be 1 but got %d instead",
284+
y.pixelStride
273285
));
274286
}
275287
if (u.pixelStride != v.pixelStride || u.rowStride != v.rowStride) {
276288
throw new IllegalArgumentException(String.format(
277-
"U and V planes must have the same pixel and row strides " +
278-
"but got pixel=%d row=%d for U " +
279-
"and pixel=%d and row=%d for V",
280-
u.pixelStride, u.rowStride,
281-
v.pixelStride, v.rowStride
289+
"U and V planes must have the same pixel and row strides " +
290+
"but got pixel=%d row=%d for U " +
291+
"and pixel=%d and row=%d for V",
292+
u.pixelStride, u.rowStride,
293+
v.pixelStride, v.rowStride
282294
));
283295
}
284296
if (u.pixelStride != 1 && u.pixelStride != 2) {
285297
throw new IllegalArgumentException(
286-
"Supported pixel strides for U and V planes are 1 and 2"
298+
"Supported pixel strides for U and V planes are 1 and 2"
287299
);
288300
}
289301
}

Camera2Basic/utils/src/main/java/com/example/android/camera/utils/YuvToRgbConverter.kt

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ class YuvToRgbConverter(context: Context) {
4040
private var inputAllocation: Allocation? = null
4141
private var outputAllocation: Allocation? = null
4242

43-
// You can also pass ImageProxy from CameraX directly
43+
// for CameraX pass ImageProxy
4444
@Synchronized
4545
fun yuvToRgb(image: Image, output: Bitmap) {
4646
val converted = Yuv.toBuffer(image, reuseBuffer)
@@ -49,17 +49,13 @@ class YuvToRgbConverter(context: Context) {
4949
if (inputAllocation == null
5050
|| inputAllocation!!.type.x != image.width
5151
|| inputAllocation!!.type.y != image.height
52-
|| inputAllocation!!.type.yuv != converted.type.format
52+
|| inputAllocation!!.type.yuv != converted.type
5353
|| bytes.size != converted.buffer.capacity()
5454
) {
55-
val yuvFormat = when (converted.type!!) {
56-
Yuv.Type.YUV_I420 -> ImageFormat.YUV_420_888
57-
Yuv.Type.YUV_NV21 -> ImageFormat.NV21
58-
}
5955
val yuvType: Type.Builder = Type.Builder(rs, Element.U8(rs))
6056
.setX(image.width)
6157
.setY(image.height)
62-
.setYuvFormat(yuvFormat)
58+
.setYuvFormat(converted.type)
6359
inputAllocation = Allocation.createTyped(rs, yuvType.create(), Allocation.USAGE_SCRIPT)
6460
bytes = ByteArray(converted.buffer.capacity())
6561
val rgbaType: Type.Builder = Type.Builder(rs, Element.RGBA_8888(rs))

0 commit comments

Comments
 (0)