parent
0123fd9243
commit
de513b60e3
@ -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,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…
Reference in new issue