去除aar依赖方式,增加远程仓库依赖。

pull/34/head 1.1
jason 6 years ago
parent c0a152cc0c
commit a2a2f37998
  1. 4
      .gitignore
  2. 1
      .idea/gradle.xml
  3. 13
      app/build.gradle
  4. BIN
      app/libs/nsfw.aar
  5. 14
      app/src/main/java/com/example/open_nsfw_android/MainActivity.kt
  6. 1
      nsfw/.gitignore
  7. 35
      nsfw/build.gradle
  8. 22
      nsfw/proguard-rules.pro
  9. 26
      nsfw/src/androidTest/java/com/zwy/nsfw/ExampleInstrumentedTest.java
  10. 1
      nsfw/src/main/AndroidManifest.xml
  11. BIN
      nsfw/src/main/assets/nsfw.tflite
  12. 212
      nsfw/src/main/java/com/zwy/nsfw/Classifier.java
  13. 53
      nsfw/src/main/java/com/zwy/nsfw/ClassifierFloatMobileNet.java
  14. 27
      nsfw/src/main/java/com/zwy/nsfw/api/NsfwBean.java
  15. 67
      nsfw/src/main/java/com/zwy/nsfw/api/NsfwHelper.java
  16. BIN
      nsfw/src/main/res/raw/nsfw.tflite
  17. 3
      nsfw/src/main/res/values/strings.xml
  18. 17
      nsfw/src/test/java/com/zwy/nsfw/ExampleUnitTest.java
  19. 2
      settings.gradle

4
.gitignore vendored

@ -10,6 +10,4 @@
.DS_Store
/build
/captures
.externalNativeBuild
/nsfw
nsfw
.externalNativeBuild

@ -10,6 +10,7 @@
<set>
<option value="$PROJECT_DIR$" />
<option value="$PROJECT_DIR$/app" />
<option value="$PROJECT_DIR$/nsfw" />
</set>
</option>
<option name="resolveModulePerSourceSet" value="false" />

@ -15,14 +15,8 @@ android {
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled true //
zipAlignEnabled true //
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
debug{
minifyEnabled true //
zipAlignEnabled true //
minifyEnabled false //
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
@ -40,8 +34,5 @@ dependencies {
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
implementation files('libs/nsfw.aar')
implementation 'org.tensorflow:tensorflow-lite:+'
implementation 'org.tensorflow:tensorflow-lite-gpu:+'
implementation project(path: ':nsfw')
}

Binary file not shown.

@ -1,5 +1,6 @@
package com.example.open_nsfw_android
import android.annotation.SuppressLint
import android.graphics.BitmapFactory
import android.os.Bundle
import android.support.v7.app.AppCompatActivity
@ -9,14 +10,15 @@ import kotlinx.android.synthetic.main.activity_main.*
class MainActivity : AppCompatActivity() {
var nsfwHelper: NsfwHelper? = null;
var nsfwHelper: NsfwHelper? = null
@SuppressLint("SetTextI18n")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
//assets 目录下的timg-10.jpeg为正常静态图片 ccc.gif 为动态正常图片 可用作测试
val b = BitmapFactory.decodeStream(getResources().getAssets().open("aaa.png"))
val b = BitmapFactory.decodeStream(resources.assets.open("aaa.png"))
iv.setImageBitmap(b)
nsfwHelper = NsfwHelper.getInstance(this, true, 1)
@ -25,11 +27,11 @@ class MainActivity : AppCompatActivity() {
//同步识别
val nsfwBean = nsfwHelper?.scanBitmapSyn(b)
Log.d("demo", nsfwBean.toString())
tvv.setText("识别成功:\n\tSFW score : ${nsfwBean?.sfw}\n\tNSFW score : ${nsfwBean?.nsfw}")
tvv.text = "识别成功:\n\tSFW score : ${nsfwBean?.sfw}\n\tNSFW score : ${nsfwBean?.nsfw}"
if (nsfwBean?.nsfw ?: 0f > 0.7) {
tvv.text = "${tvv.text.toString()} \n \t - 色情图片"
}else {
tvv.text = "${tvv.text.toString()} \n \t - 正常图片"
tvv.text = "${tvv.text} \n \t - 色情图片"
} else {
tvv.text = "${tvv.text} \n \t - 正常图片"
}
// //异步识别,接口回调识别结果
// nsfwHelper?.scanBitmap(b) { sfw, nsfw ->

1
nsfw/.gitignore vendored

@ -0,0 +1 @@
/build

@ -0,0 +1,35 @@
apply plugin: 'com.android.library'
android {
compileSdkVersion 28
defaultConfig {
minSdkVersion 15
targetSdkVersion 28
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
debug{
minifyEnabled false //
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.android.support:appcompat-v7:28.0.0'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
implementation 'org.tensorflow:tensorflow-lite:+'
implementation 'org.tensorflow:tensorflow-lite-gpu:+'
}

@ -0,0 +1,22 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
-keep class com.zwy.nsfw.api.**{*;}

@ -0,0 +1,26 @@
package com.zwy.nsfw;
import android.content.Context;
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.junit.Assert.*;
/**
* Instrumented test, which will execute on an Android device.
*
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
@RunWith(AndroidJUnit4.class)
public class ExampleInstrumentedTest {
@Test
public void useAppContext() {
// Context of the app under test.
Context appContext = InstrumentationRegistry.getTargetContext();
assertEquals("com.zwy.nsfw.test", appContext.getPackageName());
}
}

@ -0,0 +1 @@
<manifest package="com.zwy.nsfw"/>

Binary file not shown.

@ -0,0 +1,212 @@
package com.zwy.nsfw;
import android.app.Activity;
import android.content.res.AssetFileDescriptor;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.os.SystemClock;
import android.util.Log;
import com.zwy.nsfw.api.NsfwBean;
import org.tensorflow.lite.Interpreter;
import org.tensorflow.lite.gpu.GpuDelegate;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
/**
* A classifier specialized to label images using TensorFlow Lite.
*/
public abstract class Classifier {
public static final String TAG = "open_nsfw_android";
/**
* Dimensions of inputs.
*/
private static final int DIM_BATCH_SIZE = 1;
private static final int DIM_PIXEL_SIZE = 3;
/**
* Preallocated buffers for storing image data in.
*/
private int[] intValues = new int[getImageSizeX() * getImageSizeY()];
/**
* Options for configuring the Interpreter.
*/
private final Interpreter.Options tfliteOptions = new Interpreter.Options();
/**
* The loaded TensorFlow Lite model.
*/
private MappedByteBuffer tfliteModel;
/** Labels corresponding to the output of the vision model. */
// private List<String> labels;
/**
* Optional GPU delegate for accleration.
*/
private GpuDelegate gpuDelegate = null;
/**
* An instance of the driver class to run model inference with Tensorflow Lite.
*/
protected Interpreter tflite;
/**
* A ByteBuffer to hold image data, to be feed into Tensorflow Lite as inputs.
*/
protected ByteBuffer imgData = null;
/**
* Creates a classifier with the provided configuration.
*
* @param activity The current Activity.
* @param numThreads The number of threads to use for classification.
* @return A classifier with the desired configuration.
*/
public static Classifier create(Activity activity, Boolean isAddGpuDelegate, int numThreads)
throws IOException {
return new ClassifierFloatMobileNet(activity, isAddGpuDelegate, numThreads);
}
/**
* An immutable result returned by a Classifier describing what was recognized.
*/
/**
* Initializes a {@code Classifier}.
*/
protected Classifier(Activity activity, Boolean isGPU, int numThreads) throws IOException {
tfliteModel = loadModelFile(activity);
if (isGPU) {
gpuDelegate = new GpuDelegate();
tfliteOptions.addDelegate(gpuDelegate);
}
tfliteOptions.setNumThreads(numThreads);
tflite = new Interpreter(tfliteModel, tfliteOptions);
imgData =
ByteBuffer.allocateDirect(
DIM_BATCH_SIZE
* getImageSizeX()
* getImageSizeY()
* DIM_PIXEL_SIZE
* getNumBytesPerChannel());
imgData.order(ByteOrder.LITTLE_ENDIAN);
Log.d(TAG, "Tensorflow Lite Image Classifier Initialization Success.");
}
/**
* Memory-map the model file in Assets.
*/
private MappedByteBuffer loadModelFile(Activity activity) throws IOException {
AssetFileDescriptor fileDescriptor = activity.getAssets().openFd(getModelPath());
FileInputStream inputStream = new FileInputStream(fileDescriptor.getFileDescriptor());
FileChannel fileChannel = inputStream.getChannel();
long startOffset = fileDescriptor.getStartOffset();
long declaredLength = fileDescriptor.getDeclaredLength();
return fileChannel.map(FileChannel.MapMode.READ_ONLY, startOffset, declaredLength);
}
/**
* Writes Image data into a {@code ByteBuffer}.
*/
private void convertBitmapToByteBuffer(Bitmap bitmap_) {
if (imgData == null || bitmap_ == null) {
return;
}
Bitmap bitmap = Bitmap.createScaledBitmap(bitmap_, 224, 224, false);
imgData.rewind();
// intValues= ImageUtil.bitmap2BGR(bitmap);
//把每个像素的颜色值转为int 存入intValues
bitmap.getPixels(intValues, 0, bitmap.getWidth(), 0, 0, bitmap.getWidth(), bitmap.getHeight());
// bitmap.copyPixelsToBuffer(imgData);
// Convert the image to floating point.
int pixel = 0;
long startTime = SystemClock.uptimeMillis();
for (int i = 0; i < getImageSizeX(); ++i) {
for (int j = 0; j < getImageSizeY(); ++j) {
//操作每一个像素
//拿出每一个像素点对应的R、G、B的int值
//对每一个int值减去阈值 R-123 B-104 G-117
//将R、G、B 利用 B、G、R顺序重新写入数组
//将数组传入tflite获取回传结果
final int color = intValues[pixel++];
int r1 = Color.red(color) - 123;
int g1 = Color.green(color) - 117;
int b1 = Color.blue(color) - 104;
imgData.putFloat(b1);
imgData.putFloat(g1);
imgData.putFloat(r1);
}
}
long endTime = SystemClock.uptimeMillis();
Log.d(TAG, "Timecost to put values into ByteBuffer: " + (endTime - startTime) + "ms");
}
public NsfwBean run(Bitmap bitmap) {
convertBitmapToByteBuffer(bitmap);
long startTime = SystemClock.uptimeMillis();
float[][] labelProbArray = new float[1][2];
tflite.run(imgData, labelProbArray);
long endTime = SystemClock.uptimeMillis();
Log.d(TAG, "Timecost to run model inference: " + (endTime - startTime) + "ms");
return new NsfwBean(labelProbArray[0][0], labelProbArray[0][1]);
}
/**
* Closes the interpreter and model to release resources.
*/
public void close() {
if (tflite != null) {
tflite.close();
tflite = null;
}
if (gpuDelegate != null) {
gpuDelegate.close();
gpuDelegate = null;
}
tfliteModel = null;
}
/**
* Get the image size along the x axis.
*
* @return
*/
public abstract int getImageSizeX();
/**
* Get the image size along the y axis.
*
* @return
*/
public abstract int getImageSizeY();
/**
* Get the name of the model file stored in Assets.
*
* @return
*/
protected abstract String getModelPath();
/**
* Get the number of bytes that is used to store a single color channel value.
*
* @return
*/
protected abstract int getNumBytesPerChannel();
}

@ -0,0 +1,53 @@
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
package com.zwy.nsfw;
import android.app.Activity;
import java.io.IOException;
/**
* This TensorFlowLite classifier works with the float MobileNet model.
*/
public class ClassifierFloatMobileNet extends Classifier {
public ClassifierFloatMobileNet(Activity activity, Boolean isAddGpuDelegate, int numThreads)
throws IOException {
super(activity, isAddGpuDelegate, numThreads);
}
@Override
public int getImageSizeX() {
return 224;
}
@Override
public int getImageSizeY() {
return 224;
}
@Override
protected String getModelPath() {
return "nsfw.tflite";
}
@Override
protected int getNumBytesPerChannel() {
return 4;
}
}

@ -0,0 +1,27 @@
package com.zwy.nsfw.api;
public class NsfwBean {
private float sfw;
private float nsfw;
public NsfwBean(float sfw, float nsfw) {
this.sfw = sfw;
this.nsfw = nsfw;
}
public float getSfw() {
return sfw;
}
public float getNsfw() {
return nsfw;
}
@Override
public String toString() {
return "NsfwBean{" +
"sfw=" + sfw +
", nsfw=" + nsfw +
'}';
}
}

@ -0,0 +1,67 @@
package com.zwy.nsfw.api;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.graphics.Bitmap;
import android.util.Log;
import com.zwy.nsfw.Classifier;
import java.io.IOException;
public class NsfwHelper {
@SuppressLint("StaticFieldLeak")
private static NsfwHelper nsfwHelper;
private Activity activity;
private Classifier classifier;
public static NsfwHelper getInstance(Activity activity, Boolean isAddGpuDelegate, int numThreads) {
synchronized (NsfwHelper.class) {
if (nsfwHelper == null) {
nsfwHelper = new NsfwHelper(activity, isAddGpuDelegate, numThreads);
}
}
return nsfwHelper;
}
private NsfwHelper(Activity activity, Boolean isAddGpuDelegate, int numThreads) {
try {
this.activity = activity;
classifier = Classifier.create(activity, isAddGpuDelegate, numThreads);
} catch (IOException e) {
e.printStackTrace();
Log.e(Classifier.TAG, "Tensorflow Lite Image Classifier Initialization Error,e:" + e);
}
}
public NsfwBean scanBitmapSyn(Bitmap bitmap) {
return classifier.run(bitmap);
}
public void scanBitmap(final Bitmap bitmap, final OnScanBitmapListener onScanBitmapListener) {
new Thread(new Runnable() {
@Override
public void run() {
final NsfwBean nsfwBean = scanBitmapSyn(bitmap);
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
onScanBitmapListener.onSuccess(nsfwBean.getSfw(), nsfwBean.getNsfw());
}
});
}
}).start();
}
public void destroy() {
classifier.close();
activity = null;
nsfwHelper = null;
}
public static interface OnScanBitmapListener {
void onSuccess(float sfw, float nsfw);
}
}

Binary file not shown.

@ -0,0 +1,3 @@
<resources>
<string name="app_name">nsfw</string>
</resources>

@ -0,0 +1,17 @@
package com.zwy.nsfw;
import org.junit.Test;
import static org.junit.Assert.*;
/**
* Example local unit test, which will execute on the development machine (host).
*
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
public class ExampleUnitTest {
@Test
public void addition_isCorrect() {
assertEquals(4, 2 + 2);
}
}

@ -1 +1 @@
include ':app'
include ':app',':nsfw'

Loading…
Cancel
Save