Skip to content

Commit

Permalink
Android demo: Add YUV -> RGB conversion Java implementation as fallba…
Browse files Browse the repository at this point in the history
…ck if native implementation is not found. This means that compiling libtensorflow_demo.so will only be strictly necessary for the Detection example (which uses native object tracking). A followup change will add graceful degradation in that case too.

Java conversion may be slower depending on the device, but should still be acceptable for demo purposes as the majority of the compute time will still be spent on TF inference passes.

Note that this has no effect on the necessity of libtensorflow_inference.so, which provides the actual TF support. However libtensorflow_inference.so may be added to applications via the prebuilt AAR, so no native compilation is necessary.

Partially addresses tensorflow#6385
Change: 155121431
  • Loading branch information
andrewharp authored and tensorflower-gardener committed May 4, 2017
1 parent dd140f7 commit fd69bb2
Show file tree
Hide file tree
Showing 6 changed files with 89 additions and 21 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -194,13 +194,12 @@ public void onImageAvailable(final ImageReader reader) {
yuvBytes[0],
yuvBytes[1],
yuvBytes[2],
rgbBytes,
previewWidth,
previewHeight,
yRowStride,
uvRowStride,
uvPixelStride,
false);
rgbBytes);

image.close();
} catch (final Exception e) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -273,13 +273,12 @@ public void onImageAvailable(final ImageReader reader) {
yuvBytes[0],
yuvBytes[1],
yuvBytes[2],
rgbBytes,
previewWidth,
previewHeight,
yRowStride,
uvRowStride,
uvPixelStride,
false);
rgbBytes);

image.close();
} catch (final Exception e) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,10 +65,6 @@
* Artistic Style" (https://arxiv.org/abs/1610.07629)
*/
public class StylizeActivity extends CameraActivity implements OnImageAvailableListener {
static {
System.loadLibrary("tensorflow_demo");
}

private static final Logger LOGGER = new Logger();

private static final String MODEL_FILE = "file:https:///android_asset/stylize_quantized.pb";
Expand Down Expand Up @@ -509,17 +505,17 @@ public void onImageAvailable(final ImageReader reader) {
final int yRowStride = planes[0].getRowStride();
final int uvRowStride = planes[1].getRowStride();
final int uvPixelStride = planes[1].getPixelStride();

ImageUtils.convertYUV420ToARGB8888(
yuvBytes[0],
yuvBytes[1],
yuvBytes[2],
rgbBytes,
previewWidth,
previewHeight,
yRowStride,
uvRowStride,
uvPixelStride,
false);
rgbBytes);

image.close();
} catch (final Exception e) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,6 @@
public class TensorFlowMultiBoxDetector implements Classifier {
private static final Logger LOGGER = new Logger();

static {
System.loadLibrary("tensorflow_demo");
}

// Only return this many results with at least this confidence.
private static final int MAX_RESULTS = Integer.MAX_VALUE;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,6 @@
public class TensorFlowYoloDetector implements Classifier {
private static final Logger LOGGER = new Logger();

static {
System.loadLibrary("tensorflow_demo");
}

// Only return this many results with at least this confidence.
private static final int MAX_RESULTS = 5;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,14 @@
public class ImageUtils {
@SuppressWarnings("unused")
private static final Logger LOGGER = new Logger();

static {
try {
System.loadLibrary("tensorflow_demo");
} catch (UnsatisfiedLinkError e) {
LOGGER.w("Native library not found, native RGB -> YUV conversion may be unavailable.");
}
}

/**
* Utility method to compute the allocated size in bytes of a YUV420SP image
Expand Down Expand Up @@ -83,10 +91,84 @@ public static void saveBitmap(final Bitmap bitmap, final String filename) {
}
}

// This value is 2 ^ 18 - 1, and is used to clamp the RGB values before their ranges
// are normalized to eight bits.
static final int kMaxChannelValue = 262143;

// Always prefer the native implementation if available.
private static boolean useNativeConversion = true;

public static void convertYUV420ToARGB8888(
byte[] yData,
byte[] uData,
byte[] vData,
int width,
int height,
int yRowStride,
int uvRowStride,
int uvPixelStride,
int[] out) {
if (useNativeConversion) {
try {
convertYUV420ToARGB8888(
yData, uData, vData, out, width, height, yRowStride, uvRowStride, uvPixelStride, false);
return;
} catch (UnsatisfiedLinkError e) {
LOGGER.w("Native YUV -> RGB implementation not found, falling back to Java implementation");
useNativeConversion = false;
}
}

int i = 0;
for (int y = 0; y < height; y++) {
int pY = yRowStride * y;
int uv_row_start = uvRowStride * (y >> 1);
int pUV = uv_row_start;
int pV = uv_row_start;

for (int x = 0; x < width; x++) {
int uv_offset = pUV + (x >> 1) * uvPixelStride;
out[i++] =
YUV2RGB(
convertByteToInt(yData, pY + x),
convertByteToInt(uData, uv_offset),
convertByteToInt(vData, uv_offset));
}
}
}

private static int convertByteToInt(byte[] arr, int pos) {
return arr[pos] & 0xFF;
}

private static int YUV2RGB(int nY, int nU, int nV) {
nY -= 16;
nU -= 128;
nV -= 128;
if (nY < 0) nY = 0;

// This is the floating point equivalent. We do the conversion in integer
// because some Android devices do not have floating point in hardware.
// nR = (int)(1.164 * nY + 2.018 * nU);
// nG = (int)(1.164 * nY - 0.813 * nV - 0.391 * nU);
// nB = (int)(1.164 * nY + 1.596 * nV);

final int foo = 1192 * nY;
int nR = foo + 1634 * nV;
int nG = foo - 833 * nV - 400 * nU;
int nB = foo + 2066 * nU;

nR = Math.min(kMaxChannelValue, Math.max(0, nR));
nG = Math.min(kMaxChannelValue, Math.max(0, nG));
nB = Math.min(kMaxChannelValue, Math.max(0, nB));

return 0xff000000 | ((nR << 6) & 0x00ff0000) | ((nG >> 2) & 0x0000FF00) | ((nB >> 10) & 0xff);
}

/**
* Converts YUV420 semi-planar data to ARGB 8888 data using the supplied width
* and height. The input and output must already be allocated and non-null.
* For efficiency, no error checking is performed.
* Converts YUV420 semi-planar data to ARGB 8888 data using the supplied width and height. The
* input and output must already be allocated and non-null. For efficiency, no error checking is
* performed.
*
* @param input The array of YUV 4:2:0 input data.
* @param output A pre-allocated array for the ARGB 8:8:8:8 output data.
Expand Down

0 comments on commit fd69bb2

Please sign in to comment.