更新初始化方式,初始化时默认关闭GPU加速防止部分手机奔溃

pull/34/head 1.2.8
dev_zwy@aliyun.com 6 years ago
parent 0123fd9243
commit de513b60e3
  1. 2
      .idea/misc.xml
  2. 8
      app/build.gradle
  3. 69
      app/src/main/java/com/example/open_nsfw_android/MainActivity.kt
  4. 5
      app/src/main/java/com/example/open_nsfw_android/MyNsfwBean.kt
  5. 4
      build.gradle
  6. 1
      gradle.properties
  7. 17
      nsfw/build.gradle
  8. 210
      nsfw/src/main/java/com/zwy/nsfw/Classifier.java
  9. 42
      nsfw/src/main/java/com/zwy/nsfw/api/NSFWHelper.kt
  10. 27
      nsfw/src/main/java/com/zwy/nsfw/api/NsfwBean.java
  11. 90
      nsfw/src/main/java/com/zwy/nsfw/api/NsfwHelper.java
  12. 197
      nsfw/src/main/java/com/zwy/nsfw/core/Classifier.kt
  13. 10
      nsfw/src/main/java/com/zwy/nsfw/core/NSFWConfig.kt
  14. 3
      nsfw/src/main/java/com/zwy/nsfw/core/NsfwBean.kt

@ -33,7 +33,7 @@
</profile-state> </profile-state>
</entry> </entry>
</component> </component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" project-jdk-name="1.8" project-jdk-type="JavaSDK"> <component name="ProjectRootManager" version="2" languageLevel="JDK_1_7" project-jdk-name="1.8" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" /> <output url="file://$PROJECT_DIR$/build/classes" />
</component> </component>
<component name="ProjectType"> <component name="ProjectType">

@ -10,8 +10,8 @@ android {
applicationId "com.example.open_nsfw_android" applicationId "com.example.open_nsfw_android"
minSdkVersion 15 minSdkVersion 15
targetSdkVersion 28 targetSdkVersion 28
versionCode 4 versionCode 5
versionName "1.2.7" versionName "1.2.8"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
} }
buildTypes { buildTypes {
@ -38,6 +38,6 @@ dependencies {
implementation 'com.github.CymChad:BaseRecyclerViewAdapterHelper:2.9.30' implementation 'com.github.CymChad:BaseRecyclerViewAdapterHelper:2.9.30'
implementation 'com.github.LuckSiege.PictureSelector:picture_library:v2.2.3' implementation 'com.github.LuckSiege.PictureSelector:picture_library:v2.2.3'
// implementation project(path: ':nsfw') implementation project(path: ':nsfw')
implementation 'com.github.devzwy:open_nsfw_android:1.2.7' // implementation 'com.github.devzwy:open_nsfw_android:1.2.7'
} }

@ -2,6 +2,7 @@ package com.example.open_nsfw_android
import android.Manifest import android.Manifest
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.app.ProgressDialog
import android.content.Intent import android.content.Intent
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.graphics.BitmapFactory import android.graphics.BitmapFactory
@ -15,18 +16,21 @@ import com.luck.picture.lib.PictureSelector
import com.luck.picture.lib.config.PictureConfig import com.luck.picture.lib.config.PictureConfig
import com.luck.picture.lib.config.PictureMimeType import com.luck.picture.lib.config.PictureMimeType
import com.luck.picture.lib.entity.LocalMedia import com.luck.picture.lib.entity.LocalMedia
import com.zwy.nsfw.api.NsfwHelper import com.zwy.nsfw.api.NSFWHelper
import com.zwy.nsfw.core.NSFWConfig
import kotlinx.android.synthetic.main.activity_main.* import kotlinx.android.synthetic.main.activity_main.*
import kotlin.concurrent.thread
class MainActivity : AppCompatActivity(), View.OnClickListener { class MainActivity : AppCompatActivity(), View.OnClickListener {
var nsfwHelper: NsfwHelper? = null var nsfwHelper: NSFWHelper? = null
var mainAdapter: MainAdapter? = null var mainAdapter: MainAdapter? = null
var index = 0 var index = 0
var listData: ArrayList<MyNsfwBean> = ArrayList<MyNsfwBean>() var listData: ArrayList<MyNsfwBean> = ArrayList()
var selectList: List<LocalMedia>? = null var selectList: List<LocalMedia>? = null
var progressDialog: ProgressDialog? = null
@SuppressLint("SetTextI18n") @SuppressLint("SetTextI18n")
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
@ -35,10 +39,13 @@ class MainActivity : AppCompatActivity(), View.OnClickListener {
initAdapter() initAdapter()
initClickListener() initClickListener()
tv_version.text = "当前版本:${this.packageManager.getPackageInfo(packageName, 0).versionName}" tv_version.text = "当前版本:${this.packageManager.getPackageInfo(packageName, 0).versionName}"
if (ContextCompat.checkSelfPermission(this, if (ContextCompat.checkSelfPermission(
Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED){ //表示未授权时 this,
Manifest.permission.WRITE_EXTERNAL_STORAGE
) != PackageManager.PERMISSION_GRANTED
) { //表示未授权时
//进行授权 //进行授权
ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE),1); ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE), 1);
} }
} }
@ -88,10 +95,11 @@ class MainActivity : AppCompatActivity(), View.OnClickListener {
} }
private fun initNsfwHelper() { private fun initNsfwHelper() {
nsfwHelper = NsfwHelper.getInstance(this, true, 4) nsfwHelper = NSFWHelper.init(NSFWConfig(assets))
} }
private fun reScFromImgs(list: List<LocalMedia>) { private fun reScFromImgs(list: List<LocalMedia>) {
progressDialog = ProgressDialog.show(this, "提示", "请稍后")
index = 0 index = 0
mainAdapter?.setNewData(null) mainAdapter?.setNewData(null)
listData = ArrayList<MyNsfwBean>() listData = ArrayList<MyNsfwBean>()
@ -99,35 +107,44 @@ class MainActivity : AppCompatActivity(), View.OnClickListener {
for (lm in list) { for (lm in list) {
val bitmap = BitmapFactory.decodeFile(lm.path) val bitmap = BitmapFactory.decodeFile(lm.path)
listData.add(MyNsfwBean(0.0f, 0.0f, lm.path, bitmap)) listData.add(MyNsfwBean(0.0f, 0.0f, lm.path, bitmap))
nsfwHelper?.scanBitmap(bitmap) { sfw, nsfw -> val nsfwBean = nsfwHelper?.scanBitmap(bitmap)!!
listData[index].sfw = sfw listData[index].sfw = nsfwBean.sfw
listData[index].nsfw = nsfw listData[index].nsfw = nsfwBean.nsfw
mainAdapter?.addData(listData[index]) // rv.scrollToPosition(index)
mainAdapter?.notifyItemInserted(index) index++
rv.scrollToPosition(index) }
index++ runOnUiThread {
} mainAdapter?.setNewData(listData)
mainAdapter?.notifyDataSetChanged()
progressDialog?.dismiss()
} }
}).start() }).start()
} }
private fun reScAssetsImgs() { private fun reScAssetsImgs() {
progressDialog = ProgressDialog.show(this, "提示", "请稍后")
index = 0 index = 0
mainAdapter?.setNewData(null) mainAdapter?.setNewData(null)
listData = ArrayList<MyNsfwBean>() listData = ArrayList<MyNsfwBean>()
for (a in resources.assets.list("img")) { thread(true) {
val path = "img/${a}" for (a in resources.assets.list("img")) {
val b = BitmapFactory.decodeStream(resources.assets.open(path)) val path = "img/${a}"
listData.add(MyNsfwBean(0f, 0f, path, b)) val b = BitmapFactory.decodeStream(resources.assets.open(path))
nsfwHelper?.scanBitmap(b) { sfw, nsfw -> listData.add(MyNsfwBean(0f, 0f, path, b))
listData[index].sfw = sfw val nsfwBean = nsfwHelper?.scanBitmap(b)!!
listData[index].nsfw = nsfw listData[index].sfw = nsfwBean.sfw
mainAdapter?.addData(listData[index]) listData[index].nsfw = nsfwBean.nsfw
mainAdapter?.notifyItemInserted(index)
rv.scrollToPosition(index)
index++ index++
} }
runOnUiThread {
mainAdapter?.setNewData(listData)
mainAdapter?.notifyDataSetChanged()
progressDialog?.dismiss()
// rv.scrollToPosition(index)
}
} }
} }
override fun onBackPressed() { override fun onBackPressed() {
@ -136,6 +153,6 @@ class MainActivity : AppCompatActivity(), View.OnClickListener {
override fun onDestroy() { override fun onDestroy() {
super.onDestroy() super.onDestroy()
nsfwHelper?.destroy() nsfwHelper?.destroyFactory()
} }
} }

@ -1,8 +1,5 @@
package com.example.open_nsfw_android package com.example.open_nsfw_android
import android.graphics.Bitmap import android.graphics.Bitmap
import com.zwy.nsfw.api.NsfwBean
data class MyNsfwBean(var sfw: Float,var nsfw: Float, val path: String,val bitmap:Bitmap) { data class MyNsfwBean(var sfw: Float, var nsfw: Float, val path: String, val bitmap: Bitmap)
}

@ -1,14 +1,14 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules. // Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript { buildscript {
ext.kotlin_version = '1.3.31' ext.kotlin_version = '1.3.41'
repositories { repositories {
google() google()
jcenter() jcenter()
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:3.4.1' classpath 'com.android.tools.build:gradle:3.4.2'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
// NOTE: Do not place your application dependencies here; they belong // NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files // in the individual module build.gradle files

@ -13,3 +13,4 @@ org.gradle.jvmargs=-Xmx1536m
# org.gradle.parallel=true # org.gradle.parallel=true
# Kotlin code style for this project: "official" or "obsolete": # Kotlin code style for this project: "official" or "obsolete":
kotlin.code.style=official kotlin.code.style=official
kotlin.coroutines=enable

@ -1,4 +1,6 @@
apply plugin: 'com.android.library' apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
android { android {
compileSdkVersion 28 compileSdkVersion 28
@ -6,17 +8,22 @@ android {
defaultConfig { defaultConfig {
minSdkVersion 15 minSdkVersion 15
targetSdkVersion 28 targetSdkVersion 28
versionCode 1 versionCode 5
versionName "1.0" versionName "1.2.8"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
} }
buildTypes { buildTypes {
debug{ debug {
minifyEnabled false //® minifyEnabled false //®
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
} }
} }
kotlin {
experimental {
coroutines 'enable'
}
}
} }
dependencies { dependencies {
@ -27,4 +34,8 @@ dependencies {
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
implementation 'org.tensorflow:tensorflow-lite:+' implementation 'org.tensorflow:tensorflow-lite:+'
implementation 'org.tensorflow:tensorflow-lite-gpu:+' implementation 'org.tensorflow:tensorflow-lite-gpu:+'
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
}
repositories {
mavenCentral()
} }

@ -1,210 +0,0 @@
package com.zwy.nsfw;
import android.app.Activity;
import android.content.res.AssetFileDescriptor;
import android.graphics.*;
import android.os.Environment;
import android.os.SystemClock;
import android.util.Log;
import com.zwy.nsfw.api.NsfwBean;
import org.tensorflow.lite.Interpreter;
import org.tensorflow.lite.Tensor;
import org.tensorflow.lite.gpu.GpuDelegate;
import java.io.*;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.util.ArrayList;
import java.util.List;
import static java.lang.Math.max;
public class Classifier {
public static final String TAG = "open_nsfw_android";
/**
* tensor input img size
*/
private final int INPUT_WIDTH = 224;
private final int INPUT_HEIGHT = 224;
/**
* BytesPerChannel
*/
private final int BYTES_PER_CHANNEL_NUM = 4;
/**
* 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[INPUT_WIDTH * INPUT_HEIGHT];
List<Integer> list = new ArrayList<>();
/**
* The loaded TensorFlow Lite model.
*/
private MappedByteBuffer tfliteModel;
/**
* Optional GPU delegate for accleration.
*/
private GpuDelegate gpuDelegate = null;
/**
* An instance of the driver class to run model inference with Tensorflow Lite.
*/
private Interpreter tflite;
/**
* A ByteBuffer to hold image data, to be feed into Tensorflow Lite as inputs.
*/
private ByteBuffer imgData;
/**
* Creates a classifier with the provided configuration.
*
* @param activity The current Activity.
* @param isAddGpuDelegate Add gpu delegate
* @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 Classifier(activity, isAddGpuDelegate, numThreads);
}
private Classifier(Activity activity, Boolean isGPU, int numThreads) throws IOException {
tfliteModel = loadModelFile(activity);
Interpreter.Options tfliteOptions = new Interpreter.Options();
if (isGPU) {
gpuDelegate = new GpuDelegate();
tfliteOptions.addDelegate(gpuDelegate);
}
tfliteOptions.setNumThreads(numThreads);
tflite = new Interpreter(tfliteModel, tfliteOptions);
Tensor tensor = tflite.getInputTensor(tflite.getInputIndex("input"));
String stringBuilder = " \n"
+ "dataType : " +
tensor.dataType() +
"\n" +
"numBytes : " +
tensor.numBytes() +
"\n" +
"numDimensions : " +
tensor.numDimensions() +
"\n" +
"numElements : " +
tensor.numElements() +
"\n" +
"shape : " +
tensor.shape().length;
Log.d(TAG, stringBuilder);
imgData =
ByteBuffer.allocateDirect(
DIM_BATCH_SIZE
* INPUT_WIDTH
* INPUT_HEIGHT
* DIM_PIXEL_SIZE
* BYTES_PER_CHANNEL_NUM);
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("nsfw.tflite");
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;
}
imgData.rewind();
int W = bitmap_.getWidth();
int H = bitmap_.getHeight();
int w_off = max((W - INPUT_WIDTH) / 2, 0);
int h_off = max((H - INPUT_HEIGHT) / 2, 0);
//把每个像素的颜色值转为int 存入intValues
bitmap_.getPixels(intValues, 0, INPUT_WIDTH, h_off, w_off, INPUT_WIDTH, INPUT_HEIGHT);
// Convert the image to floating point.
long startTime = SystemClock.uptimeMillis();
for (final int color : intValues) {
int r1 = Color.red(color);
int g1 = Color.green(color);
int b1 = Color.blue(color);
int rr1 = r1 - 123;
int gg1 = g1 - 117;
int bb1 = b1 - 104;
imgData.putFloat(bb1);
imgData.putFloat(gg1);
imgData.putFloat(rr1);
}
long endTime = SystemClock.uptimeMillis();
Log.d(TAG, "Timecost to put values into ByteBuffer: " + (endTime - startTime) + "ms");
}
public NsfwBean run(Bitmap bitmap) {
Bitmap bitmap_256 = Bitmap.createScaledBitmap(bitmap, 256, 256,true);
//Writes image data into byteBuffer
convertBitmapToByteBuffer(bitmap_256);
long startTime = SystemClock.uptimeMillis();
// out
float[][] outArray = new float[1][2];
tflite.run(imgData, outArray);
long endTime = SystemClock.uptimeMillis();
Log.d(TAG, "SFW score :" + outArray[0][0] + ",NSFW score :" + outArray[0][1]);
Log.d(TAG, "Timecost to run model inference: " + (endTime - startTime) + "ms");
return new NsfwBean(outArray[0][0], outArray[0][1]);
}
/**
* Closes the interpreter and model to release resources.
*/
public void close() {
if (tflite != null) {
tflite.close();
tflite = null;
Log.d(TAG, "Tensorflow Lite Image Classifier close.");
}
if (gpuDelegate != null) {
gpuDelegate.close();
Log.d(TAG, "Tensorflow Lite Image gpuDelegate close.");
gpuDelegate = null;
}
tfliteModel = null;
Log.d(TAG, "Tensorflow Lite destroyed.");
}
}

@ -0,0 +1,42 @@
package com.zwy.nsfw.api
import android.graphics.Bitmap
import com.zwy.nsfw.core.Classifier
import com.zwy.nsfw.core.NSFWConfig
import com.zwy.nsfw.core.NsfwBean
object NSFWHelper {
private lateinit var classifier: Classifier
private var isInit = false
@Synchronized
fun init(nsfwConfig: NSFWConfig): NSFWHelper {
classifier = Classifier.create(nsfwConfig.assetMannager, nsfwConfig.userGPU, 10)
isInit = true
return this
}
/**
* 同步扫描图片(建议使用者自己放在线程中处理)
*/
@Synchronized
fun scanBitmap(bitmap: Bitmap): NsfwBean {
if (!isInit) {
throw ExceptionInInitializerError("Initialize the scanned object 'NSFWHelper.init(nsfwConfig: NSFWConfig)' by calling the function first.")
}
return classifier.run(bitmap)
}
/**
* 不会销毁该单利和配置的参数建议页面处理完成后调用该代码对扫描器进行关闭,关闭后下次扫描不需要再次调用init方法
*/
fun destroyFactory() {
classifier.close()
}
}

@ -1,27 +0,0 @@
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 +
'}';
}
}

@ -1,90 +0,0 @@
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;
/**
* Creates a classifier with the provided configuration.
*
* @param activity The current Activity.
* @param isAddGpuDelegate Add gpu delegate
* @param numThreads The number of threads to use for classification.
* @return A classifier with the desired configuration.
*/
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);
}
}
/**
* 同步扫描
*
* @param bitmap
* @return nsfw/sfw
*/
public NsfwBean scanBitmapSyn(Bitmap bitmap) {
synchronized (NsfwHelper.class) {
return classifier.run(bitmap);
}
}
/**
* 异步扫描
*
* @param bitmap
* @param onScanBitmapListener void onSuccess(float sfw, float nsfw);
*/
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 interface OnScanBitmapListener {
void onSuccess(float sfw, float nsfw);
}
}

@ -0,0 +1,197 @@
package com.zwy.nsfw.core
import android.content.res.AssetManager
import android.graphics.Bitmap
import android.graphics.Color
import android.os.SystemClock
import android.util.Log
import org.tensorflow.lite.Interpreter
import org.tensorflow.lite.gpu.GpuDelegate
import java.io.FileInputStream
import java.lang.Math.max
import java.nio.ByteBuffer
import java.nio.ByteOrder
import java.nio.MappedByteBuffer
import java.nio.channels.FileChannel
class Classifier
private constructor(assetManager: AssetManager, isGPU: Boolean?, numThreads: Int) {
/**
* tensor input img size
*/
private val INPUT_WIDTH = 224
private val INPUT_HEIGHT = 224
/**
* BytesPerChannel
*/
private val BYTES_PER_CHANNEL_NUM = 4
/**
* Preallocated buffers for storing image data in.
*/
private val intValues = IntArray(INPUT_WIDTH * INPUT_HEIGHT)
/**
* The loaded TensorFlow Lite model.
*/
private var tfliteModel: MappedByteBuffer? = null
/**
* Optional GPU delegate for accleration.
*/
private var gpuDelegate: GpuDelegate? = null
/**
* An instance of the driver class to run model inference with Tensorflow Lite.
*/
private var tflite: Interpreter? = null
/**
* A ByteBuffer to hold image data, to be feed into Tensorflow Lite as inputs.
*/
private val imgData: ByteBuffer?
init {
tfliteModel = loadModelFile(assetManager)
val tfliteOptions = Interpreter.Options()
if (isGPU == true) {
gpuDelegate = GpuDelegate()
tfliteOptions.addDelegate(gpuDelegate)
}
tfliteOptions.setNumThreads(numThreads)
tflite = Interpreter(tfliteModel!!, tfliteOptions)
val tensor = tflite!!.getInputTensor(tflite!!.getInputIndex("input"))
val stringBuilder = (" \n"
+ "dataType : " +
tensor.dataType() +
"\n" +
"numBytes : " +
tensor.numBytes() +
"\n" +
"numDimensions : " +
tensor.numDimensions() +
"\n" +
"numElements : " +
tensor.numElements() +
"\n" +
"shape : " +
tensor.shape().size)
Log.d(TAG, stringBuilder)
imgData = ByteBuffer.allocateDirect(
DIM_BATCH_SIZE
* INPUT_WIDTH
* INPUT_HEIGHT
* DIM_PIXEL_SIZE
* BYTES_PER_CHANNEL_NUM
)
imgData!!.order(ByteOrder.LITTLE_ENDIAN)
Log.d(TAG, "Tensorflow Lite Image Classifier Initialization Success.")
}
/**
* Memory-map the model file in Assets.
*/
private fun loadModelFile(assetManager: AssetManager): MappedByteBuffer {
val fileDescriptor = assetManager.openFd("nsfw.tflite")
val inputStream = FileInputStream(fileDescriptor.fileDescriptor)
val fileChannel = inputStream.channel
val startOffset = fileDescriptor.startOffset
val declaredLength = fileDescriptor.declaredLength
return fileChannel.map(FileChannel.MapMode.READ_ONLY, startOffset, declaredLength)
}
/**
* Writes Image data into a `ByteBuffer`.
*/
private fun convertBitmapToByteBuffer(bitmap_: Bitmap) {
if (imgData == null || bitmap_ == null) {
return
}
imgData.rewind()
val W = bitmap_.width
val H = bitmap_.height
val w_off = max((W - INPUT_WIDTH) / 2, 0)
val h_off = max((H - INPUT_HEIGHT) / 2, 0)
//把每个像素的颜色值转为int 存入intValues
bitmap_.getPixels(intValues, 0, INPUT_WIDTH, h_off, w_off, INPUT_WIDTH, INPUT_HEIGHT)
// Convert the image to floating point.
val startTime = SystemClock.uptimeMillis()
for (color in intValues) {
val r1 = Color.red(color)
val g1 = Color.green(color)
val b1 = Color.blue(color)
val rr1 = r1 - 123
val gg1 = g1 - 117
val bb1 = b1 - 104
imgData.putFloat(bb1.toFloat())
imgData.putFloat(gg1.toFloat())
imgData.putFloat(rr1.toFloat())
}
val endTime = SystemClock.uptimeMillis()
Log.d(TAG, "Timecost to put values into ByteBuffer: " + (endTime - startTime) + "ms")
}
fun run(bitmap: Bitmap): NsfwBean {
val bitmap_256 = Bitmap.createScaledBitmap(bitmap, 256, 256, true)
//Writes image data into byteBuffer
convertBitmapToByteBuffer(bitmap_256)
val startTime = SystemClock.uptimeMillis()
// out
val outArray = Array(1) { FloatArray(2) }
tflite!!.run(imgData, outArray)
val endTime = SystemClock.uptimeMillis()
Log.d(TAG, "SFW score :" + outArray[0][0] + ",NSFW score :" + outArray[0][1])
Log.d(TAG, "Timecost to run model inference: " + (endTime - startTime) + "ms")
return NsfwBean(outArray[0][0], outArray[0][1])
}
/**
* Closes the interpreter and model to release resources.
*/
fun close() {
if (tflite != null) {
tflite!!.close()
tflite = null
Log.d(TAG, "Tensorflow Lite Image Classifier close.")
}
if (gpuDelegate != null) {
gpuDelegate!!.close()
Log.d(TAG, "Tensorflow Lite Image gpuDelegate close.")
gpuDelegate = null
}
tfliteModel = null
Log.d(TAG, "Tensorflow Lite destroyed.")
}
companion object {
val TAG = "open_nsfw_android"
/**
* Dimensions of inputs.
*/
private val DIM_BATCH_SIZE = 1
private val DIM_PIXEL_SIZE = 3
fun create(assetManager: AssetManager, isAddGpuDelegate: Boolean?, numThreads: Int): Classifier {
return Classifier(assetManager, isAddGpuDelegate!!, numThreads)
}
}
}

@ -0,0 +1,10 @@
package com.zwy.nsfw.core
import android.content.res.AssetManager
/**
* 配置扫描参数
* [assetMannager]资源管理器用于读取lib下的tfLite文件
* [userGPU]是否开启GPU加速 默认关闭部分手机开启后会有奔溃
*/
data class NSFWConfig(val assetMannager: AssetManager, val userGPU: Boolean = false)

@ -0,0 +1,3 @@
package com.zwy.nsfw.core
class NsfwBean(val sfw: Float, val nsfw: Float)
Loading…
Cancel
Save