Skip to content

Commit 52de478

Browse files
committed
Use imageutils native method source code instead of binaries
1 parent cd19ac5 commit 52de478

File tree

12 files changed

+555
-9
lines changed

12 files changed

+555
-9
lines changed

README.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,16 @@ This project is a way to get started with TensorFlow Image Classifier quickly.
99

1010
I am not planning to maintain it. If you need an updated version, build it yourself using hints from this [blog post][blog-post].
1111

12+
13+
## Native libraries
14+
15+
Native compiled libraries are embedded in the `1.3.0` tag, so you won't need to install the NDK.
16+
However, this means that you cannot change the `org.tensorflow.demo.env.ImageUtils` class.
17+
Here's what you need to do if you want, for example, to use a different package name:
18+
19+
* Install the [NDK and build tools][ndk]
20+
* Checkout the `1.3.0-cmake` tag
21+
* Modify line 7 of the `app/src/main/cpp/imageutils_jni.cpp` file to specify your new package name
22+
1223
[blog-post]: http://nilhcem.com/android/custom-tensorflow-classifier
24+
[ndk]: https://developer.android.com/studio/projects/add-native-code.html

app/CMakeLists.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
cmake_minimum_required(VERSION 3.4.1)
2+
3+
file(GLOB_RECURSE tensorflow_demo_sources src/main/cpp/*.*)
4+
add_library(tensorflow_demo SHARED ${tensorflow_demo_sources})
5+
6+
target_link_libraries(tensorflow_demo)

app/build.gradle

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,21 +9,24 @@ android {
99
targetSdkVersion 26
1010
versionCode 1
1111
versionName '1.0'
12-
}
13-
buildTypes {
14-
release {
15-
minifyEnabled false
16-
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
12+
13+
externalNativeBuild {
14+
cmake {
15+
cppFlags ""
16+
}
17+
}
18+
ndk {
19+
abiFilters 'x86', 'x86_64', 'armeabi-v7a', 'arm64-v8a'
1720
}
1821
}
19-
sourceSets {
20-
main {
21-
jniLibs.srcDirs = ['libs']
22+
23+
externalNativeBuild {
24+
cmake {
25+
path "CMakeLists.txt"
2226
}
2327
}
2428
}
2529

2630
dependencies {
27-
compile fileTree(dir: 'libs', include: ['*.jar'])
2831
compile 'org.tensorflow:tensorflow-android:1.3.0'
2932
}
-710 KB
Binary file not shown.
-406 KB
Binary file not shown.

app/libs/x86/libtensorflow_demo.so

-726 KB
Binary file not shown.
-714 KB
Binary file not shown.

app/src/main/cpp/imageutils_jni.cc

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
#include <jni.h>
2+
3+
#include "rgb2yuv.h"
4+
#include "yuv2rgb.h"
5+
6+
#define IMAGEUTILS_METHOD(METHOD_NAME) \
7+
Java_org_tensorflow_demo_env_ImageUtils_##METHOD_NAME // NOLINT
8+
9+
#ifdef __cplusplus
10+
extern "C" {
11+
#endif
12+
13+
JNIEXPORT void JNICALL
14+
IMAGEUTILS_METHOD(convertYUV420SPToARGB8888)(
15+
JNIEnv *env, jclass clazz, jbyteArray input, jintArray output,
16+
jint width, jint height, jboolean halfSize);
17+
18+
JNIEXPORT void JNICALL IMAGEUTILS_METHOD(convertYUV420ToARGB8888)(
19+
JNIEnv *env, jclass clazz, jbyteArray y, jbyteArray u, jbyteArray v,
20+
jintArray output, jint width, jint height, jint y_row_stride,
21+
jint uv_row_stride, jint uv_pixel_stride, jboolean halfSize);
22+
23+
JNIEXPORT void JNICALL IMAGEUTILS_METHOD(convertYUV420SPToRGB565)(
24+
JNIEnv *env, jclass clazz, jbyteArray input, jbyteArray output, jint width,
25+
jint height);
26+
27+
JNIEXPORT void JNICALL
28+
IMAGEUTILS_METHOD(convertARGB8888ToYUV420SP)(
29+
JNIEnv *env, jclass clazz, jintArray input, jbyteArray output,
30+
jint width, jint height);
31+
32+
JNIEXPORT void JNICALL
33+
IMAGEUTILS_METHOD(convertRGB565ToYUV420SP)(
34+
JNIEnv *env, jclass clazz, jbyteArray input, jbyteArray output,
35+
jint width, jint height);
36+
37+
#ifdef __cplusplus
38+
}
39+
#endif
40+
41+
JNIEXPORT void JNICALL
42+
IMAGEUTILS_METHOD(convertYUV420SPToARGB8888)(
43+
JNIEnv *env, jclass clazz, jbyteArray input, jintArray output,
44+
jint width, jint height, jboolean halfSize) {
45+
jboolean inputCopy = JNI_FALSE;
46+
jbyte *const i = env->GetByteArrayElements(input, &inputCopy);
47+
48+
jboolean outputCopy = JNI_FALSE;
49+
jint *const o = env->GetIntArrayElements(output, &outputCopy);
50+
51+
if (halfSize) {
52+
ConvertYUV420SPToARGB8888HalfSize(reinterpret_cast<uint8_t *>(i),
53+
reinterpret_cast<uint32_t *>(o), width,
54+
height);
55+
} else {
56+
ConvertYUV420SPToARGB8888(reinterpret_cast<uint8_t *>(i),
57+
reinterpret_cast<uint8_t *>(i) + width * height,
58+
reinterpret_cast<uint32_t *>(o), width, height);
59+
}
60+
61+
env->ReleaseByteArrayElements(input, i, JNI_ABORT);
62+
env->ReleaseIntArrayElements(output, o, 0);
63+
}
64+
65+
JNIEXPORT void JNICALL IMAGEUTILS_METHOD(convertYUV420ToARGB8888)(
66+
JNIEnv *env, jclass clazz, jbyteArray y, jbyteArray u, jbyteArray v,
67+
jintArray output, jint width, jint height, jint y_row_stride,
68+
jint uv_row_stride, jint uv_pixel_stride, jboolean halfSize) {
69+
jboolean inputCopy = JNI_FALSE;
70+
jbyte *const y_buff = env->GetByteArrayElements(y, &inputCopy);
71+
jboolean outputCopy = JNI_FALSE;
72+
jint *const o = env->GetIntArrayElements(output, &outputCopy);
73+
74+
if (halfSize) {
75+
ConvertYUV420SPToARGB8888HalfSize(reinterpret_cast<uint8_t *>(y_buff),
76+
reinterpret_cast<uint32_t *>(o), width,
77+
height);
78+
} else {
79+
jbyte *const u_buff = env->GetByteArrayElements(u, &inputCopy);
80+
jbyte *const v_buff = env->GetByteArrayElements(v, &inputCopy);
81+
82+
ConvertYUV420ToARGB8888(
83+
reinterpret_cast<uint8_t *>(y_buff), reinterpret_cast<uint8_t *>(u_buff),
84+
reinterpret_cast<uint8_t *>(v_buff), reinterpret_cast<uint32_t *>(o),
85+
width, height, y_row_stride, uv_row_stride, uv_pixel_stride);
86+
87+
env->ReleaseByteArrayElements(u, u_buff, JNI_ABORT);
88+
env->ReleaseByteArrayElements(v, v_buff, JNI_ABORT);
89+
}
90+
91+
env->ReleaseByteArrayElements(y, y_buff, JNI_ABORT);
92+
env->ReleaseIntArrayElements(output, o, 0);
93+
}
94+
95+
JNIEXPORT void JNICALL IMAGEUTILS_METHOD(convertYUV420SPToRGB565)(
96+
JNIEnv *env, jclass clazz, jbyteArray input, jbyteArray output, jint width,
97+
jint height) {
98+
jboolean inputCopy = JNI_FALSE;
99+
jbyte *const i = env->GetByteArrayElements(input, &inputCopy);
100+
101+
jboolean outputCopy = JNI_FALSE;
102+
jbyte *const o = env->GetByteArrayElements(output, &outputCopy);
103+
104+
ConvertYUV420SPToRGB565(reinterpret_cast<uint8_t *>(i),
105+
reinterpret_cast<uint16_t *>(o), width, height);
106+
107+
env->ReleaseByteArrayElements(input, i, JNI_ABORT);
108+
env->ReleaseByteArrayElements(output, o, 0);
109+
}
110+
111+
JNIEXPORT void JNICALL
112+
IMAGEUTILS_METHOD(convertARGB8888ToYUV420SP)(
113+
JNIEnv *env, jclass clazz, jintArray input, jbyteArray output,
114+
jint width, jint height) {
115+
jboolean inputCopy = JNI_FALSE;
116+
jint *const i = env->GetIntArrayElements(input, &inputCopy);
117+
118+
jboolean outputCopy = JNI_FALSE;
119+
jbyte *const o = env->GetByteArrayElements(output, &outputCopy);
120+
121+
ConvertARGB8888ToYUV420SP(reinterpret_cast<uint32_t *>(i),
122+
reinterpret_cast<uint8_t *>(o), width, height);
123+
124+
env->ReleaseIntArrayElements(input, i, JNI_ABORT);
125+
env->ReleaseByteArrayElements(output, o, 0);
126+
}
127+
128+
JNIEXPORT void JNICALL
129+
IMAGEUTILS_METHOD(convertRGB565ToYUV420SP)(
130+
JNIEnv *env, jclass clazz, jbyteArray input, jbyteArray output,
131+
jint width, jint height) {
132+
jboolean inputCopy = JNI_FALSE;
133+
jbyte *const i = env->GetByteArrayElements(input, &inputCopy);
134+
135+
jboolean outputCopy = JNI_FALSE;
136+
jbyte *const o = env->GetByteArrayElements(output, &outputCopy);
137+
138+
ConvertRGB565ToYUV420SP(reinterpret_cast<uint16_t *>(i),
139+
reinterpret_cast<uint8_t *>(o), width, height);
140+
141+
env->ReleaseByteArrayElements(input, i, JNI_ABORT);
142+
env->ReleaseByteArrayElements(output, o, 0);
143+
}

app/src/main/cpp/rgb2yuv.cc

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
/* Copyright 2015 The TensorFlow Authors. All Rights Reserved.
2+
3+
Licensed under the Apache License, Version 2.0 (the "License");
4+
you may not use this file except in compliance with the License.
5+
You may obtain a copy of the License at
6+
7+
http://www.apache.org/licenses/LICENSE-2.0
8+
9+
Unless required by applicable law or agreed to in writing, software
10+
distributed under the License is distributed on an "AS IS" BASIS,
11+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
See the License for the specific language governing permissions and
13+
limitations under the License.
14+
==============================================================================*/
15+
16+
// These utility functions allow for the conversion of RGB data to YUV data.
17+
18+
#include "rgb2yuv.h"
19+
20+
static inline void WriteYUV(const int x, const int y, const int width,
21+
const int r8, const int g8, const int b8,
22+
uint8_t* const pY, uint8_t* const pUV) {
23+
// Using formulas from http://msdn.microsoft.com/en-us/library/ms893078
24+
*pY = ((66 * r8 + 129 * g8 + 25 * b8 + 128) >> 8) + 16;
25+
26+
// Odd widths get rounded up so that UV blocks on the side don't get cut off.
27+
const int blocks_per_row = (width + 1) / 2;
28+
29+
// 2 bytes per UV block
30+
const int offset = 2 * (((y / 2) * blocks_per_row + (x / 2)));
31+
32+
// U and V are the average values of all 4 pixels in the block.
33+
if (!(x & 1) && !(y & 1)) {
34+
// Explicitly clear the block if this is the first pixel in it.
35+
pUV[offset] = 0;
36+
pUV[offset + 1] = 0;
37+
}
38+
39+
// V (with divide by 4 factored in)
40+
#ifdef __APPLE__
41+
const int u_offset = 0;
42+
const int v_offset = 1;
43+
#else
44+
const int u_offset = 1;
45+
const int v_offset = 0;
46+
#endif
47+
pUV[offset + v_offset] += ((112 * r8 - 94 * g8 - 18 * b8 + 128) >> 10) + 32;
48+
49+
// U (with divide by 4 factored in)
50+
pUV[offset + u_offset] += ((-38 * r8 - 74 * g8 + 112 * b8 + 128) >> 10) + 32;
51+
}
52+
53+
void ConvertARGB8888ToYUV420SP(const uint32_t* const input,
54+
uint8_t* const output, int width, int height) {
55+
uint8_t* pY = output;
56+
uint8_t* pUV = output + (width * height);
57+
const uint32_t* in = input;
58+
59+
for (int y = 0; y < height; y++) {
60+
for (int x = 0; x < width; x++) {
61+
const uint32_t rgb = *in++;
62+
#ifdef __APPLE__
63+
const int nB = (rgb >> 8) & 0xFF;
64+
const int nG = (rgb >> 16) & 0xFF;
65+
const int nR = (rgb >> 24) & 0xFF;
66+
#else
67+
const int nR = (rgb >> 16) & 0xFF;
68+
const int nG = (rgb >> 8) & 0xFF;
69+
const int nB = rgb & 0xFF;
70+
#endif
71+
WriteYUV(x, y, width, nR, nG, nB, pY++, pUV);
72+
}
73+
}
74+
}
75+
76+
void ConvertRGB565ToYUV420SP(const uint16_t* const input, uint8_t* const output,
77+
const int width, const int height) {
78+
uint8_t* pY = output;
79+
uint8_t* pUV = output + (width * height);
80+
const uint16_t* in = input;
81+
82+
for (int y = 0; y < height; y++) {
83+
for (int x = 0; x < width; x++) {
84+
const uint32_t rgb = *in++;
85+
86+
const int r5 = ((rgb >> 11) & 0x1F);
87+
const int g6 = ((rgb >> 5) & 0x3F);
88+
const int b5 = (rgb & 0x1F);
89+
90+
// Shift left, then fill in the empty low bits with a copy of the high
91+
// bits so we can stretch across the entire 0 - 255 range.
92+
const int r8 = r5 << 3 | r5 >> 2;
93+
const int g8 = g6 << 2 | g6 >> 4;
94+
const int b8 = b5 << 3 | b5 >> 2;
95+
96+
WriteYUV(x, y, width, r8, g8, b8, pY++, pUV);
97+
}
98+
}
99+
}

app/src/main/cpp/rgb2yuv.h

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/* Copyright 2015 The TensorFlow Authors. All Rights Reserved.
2+
3+
Licensed under the Apache License, Version 2.0 (the "License");
4+
you may not use this file except in compliance with the License.
5+
You may obtain a copy of the License at
6+
7+
http://www.apache.org/licenses/LICENSE-2.0
8+
9+
Unless required by applicable law or agreed to in writing, software
10+
distributed under the License is distributed on an "AS IS" BASIS,
11+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
See the License for the specific language governing permissions and
13+
limitations under the License.
14+
==============================================================================*/
15+
16+
#ifndef ORG_TENSORFLOW_JNI_IMAGEUTILS_RGB2YUV_H_
17+
#define ORG_TENSORFLOW_JNI_IMAGEUTILS_RGB2YUV_H_
18+
19+
#include <stdint.h>
20+
21+
#ifdef __cplusplus
22+
extern "C" {
23+
#endif
24+
25+
void ConvertARGB8888ToYUV420SP(const uint32_t* const input,
26+
uint8_t* const output, int width, int height);
27+
28+
void ConvertRGB565ToYUV420SP(const uint16_t* const input, uint8_t* const output,
29+
const int width, const int height);
30+
31+
#ifdef __cplusplus
32+
}
33+
#endif
34+
35+
#endif // ORG_TENSORFLOW_JNI_IMAGEUTILS_RGB2YUV_H_

0 commit comments

Comments
 (0)