优化模型支持GPU加速

pull/34/head
Jason 5 years ago
parent 1d7895b32b
commit fc81c4c09d
  1. 2
      .idea/gradle.xml
  2. BIN
      OpenNSFW/src/main/assets/nsfw.tflite
  3. 71
      OpenNSFW/src/main/java/com/zwy/opennsfw/core/Classifier.kt
  4. 3
      OpenNSFW/src/main/java/com/zwy/opennsfw/utils/kt.kt
  5. 3
      demo/build.gradle
  6. BIN
      demo/src/main/assets/img/58167612-e474c880-7cbe-11e9-8f33-fdce7e23ffcc.png
  7. BIN
      demo/src/main/assets/img/test_img1.jpeg
  8. BIN
      demo/src/main/assets/img/截屏2020-06-09 10.17.56.png
  9. 4
      demo/src/main/java/com/zwy/demo/NSFWApplication.kt
  10. 4
      gradle.properties
  11. 4
      settings.gradle

@ -12,7 +12,7 @@
<set>
<option value="$PROJECT_DIR$" />
<option value="$PROJECT_DIR$/OpenNSFW" />
<option value="$PROJECT_DIR$/app" />
<option value="$PROJECT_DIR$/demo" />
</set>
</option>
<option name="resolveModulePerSourceSet" value="false" />

@ -6,9 +6,11 @@ import android.graphics.Bitmap
import android.graphics.Color
import android.os.SystemClock
import d
import e
import mClassifier
import org.tensorflow.lite.Interpreter
import org.tensorflow.lite.gpu.GpuDelegate
import java.io.ByteArrayOutputStream
import java.io.FileInputStream
import java.lang.Math.max
import java.nio.ByteBuffer
@ -16,6 +18,7 @@ import java.nio.ByteOrder
import java.nio.MappedByteBuffer
import java.nio.channels.FileChannel
class Classifier private constructor(config: Config) {
/**
@ -52,12 +55,12 @@ class Classifier private constructor(config: Config) {
/**
* Tensorflow Lite
*/
private lateinit var tflite: Interpreter
private var tflite: Interpreter
/**
* 喂入模型的最终数据源
*/
private lateinit var imgData: ByteBuffer
private var imgData: ByteBuffer
companion object {
private var config = Config(context = null)
@ -104,11 +107,14 @@ class Classifier private constructor(config: Config) {
}
init {
val tfliteOptions = Interpreter.Options()
if (config.isOpenGPU) tfliteOptions.addDelegate(GpuDelegate())
tfliteOptions.setNumThreads(config.numThreads)
tflite = Interpreter(loadModelFile(config.context!!), tfliteOptions)
try {
tflite =
Interpreter(loadModelFile(config.context!!), getTfLiteOptions(config.isOpenGPU))
if (config.isOpenGPU) "开启GPU加速成功".d()
} catch (e: Exception) {
"不支持GPU加速".e()
tflite = Interpreter(loadModelFile(config.context!!), getTfLiteOptions(false))
}
imgData = ByteBuffer.allocateDirect(
DIM_BATCH_SIZE
@ -121,6 +127,15 @@ class Classifier private constructor(config: Config) {
imgData.order(ByteOrder.LITTLE_ENDIAN)
}
private fun getTfLiteOptions(isOpenGPU: Boolean = true): Interpreter.Options {
return Interpreter.Options().apply {
if (isOpenGPU) this.addDelegate(GpuDelegate())
this.setNumThreads(config.numThreads)
this.setAllowBufferHandleOutput(true)
this.setAllowFp16PrecisionForFp32(true)
}
}
private fun loadModelFile(context: Context): MappedByteBuffer {
val fileDescriptor = context.assets.openFd("nsfw.tflite")
val inputStream = FileInputStream(fileDescriptor.fileDescriptor)
@ -160,12 +175,28 @@ class Classifier private constructor(config: Config) {
"数据装载成功,耗时:${(endTime - startTime)} ms".d()
}
// # 根据路径获取图片 Image.open(path)
// # 判断图片的像素格式是否为RGB,如果不是RGB则转换为RGB(24位彩色图像,每个像素用24个bit表示,分别表示红色、绿色和蓝色三个通道)
// # 重制图片大小为256*256并使用 官方解释:若要调整大小,请对所有可能影响输出值的像素使用线性插值计算输出像素值。对于其他变换,使用输入图像中2x2环境上的线性插值。
// # 对resize的结果转换为io流并保存为JPEG格式
// # Convert to 64-bit floating point.asType float32 定义变量存储转换后的32位float图片数据
// # 获取图片的宽高,截取 224*224大小 x从16位开始,取到16+224位置 y亦是如此
// # 将取的数值转换位float32
// # 将每一个颜色值*255
// # 将每一个颜色减去一定的阈值 104....
// # [[[127.64.-18]]] 转换为 [[[[127.64.-18]]]]
// # 使用index关键字喂入模型
// # 删除所有单维度的条目
// # 输出扫描结果
fun run(bitmap: Bitmap): NsfwBean {
//缩放位图时是否应使用双线性过滤。如果这是正确的,则在缩放时将使用双线性滤波,从而以较差的性能为代价具有更好的图像质量。如果这是错误的,则使用最近邻居缩放,这将使图像质量较差但速度更快。推荐的默认设置是将滤镜设置为“ true”,因为双线性滤镜的成本通常很小,并且改善的图像质量非常重要
val stream = ByteArrayOutputStream()
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, stream)
stream.close()
val bitmap_256 = Bitmap.createScaledBitmap(bitmap, 256, 256, true)
convertBitmapToByteBuffer(bitmap_256)
//
val startTime = SystemClock.uptimeMillis()
// out
val outArray = Array(1) { FloatArray(2) }
@ -173,8 +204,26 @@ class Classifier private constructor(config: Config) {
tflite.run(imgData, outArray)
val endTime = SystemClock.uptimeMillis()
val nsfw = NsfwBean(outArray[0][0], outArray[0][1])
"扫描完成[${nsfw}]耗时: ${(endTime - startTime)} ms".d()
return nsfw
}
"扫描完成,耗时: ${(endTime - startTime)} ms".d()
return NsfwBean(outArray[0][0], outArray[0][1])
fun bitmap2RGB(bitmap: Bitmap): ByteArray? {
val bytes = bitmap.byteCount //返回可用于储存此位图像素的最小字节数
val buffer =
ByteBuffer.allocate(bytes) // 使用allocate()静态方法创建字节缓冲区
bitmap.copyPixelsToBuffer(buffer) // 将位图的像素复制到指定的缓冲区
val rgba = buffer.array()
val pixels = ByteArray(rgba.size / 4 * 3)
val count = rgba.size / 4
//Bitmap像素点的色彩通道排列顺序是RGBA
for (i in 0 until count) {
pixels[i * 3] = rgba[i * 4] //R
pixels[i * 3 + 1] = rgba[i * 4 + 1] //G
pixels[i * 3 + 2] = rgba[i * 4 + 2] //B
}
return pixels
}
}

@ -18,7 +18,8 @@ class NsfwBean(val sfw: Float, val nsfw: Float) {
override fun toString(): String {
val floatFormat = DecimalFormat("0.0000")
floatFormat.roundingMode = RoundingMode.HALF_UP
return "适宜度(非色情程度):${floatFormat.format(sfw * 100)}%,不适宜度(涉黄程度):${floatFormat.format(nsfw * 100)}%"
// return "适宜度(非色情程度):${floatFormat.format(sfw * 100)}%,不适宜度(涉黄程度):${floatFormat.format(nsfw * 100)}%"
return "sfw:$sfw,nsfw:$nsfw%"
}
}

@ -83,7 +83,8 @@ dependencies {
implementation "org.jetbrains.anko:anko:0.10.5"
//NSFW鉴黄库
implementation 'com.github.devzwy:open_nsfw_android:1.3.0'
// implementation 'com.github.devzwy:open_nsfw_android:1.3.0'
implementation project(path: ':OpenNSFW')
implementation 'pub.devrel:easypermissions:3.0.0'
implementation 'com.github.LuckSiege.PictureSelector:picture_library:2.2.5'

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 MiB

@ -28,8 +28,8 @@ class NSFWApplication : MultiDexApplication() {
//初始化鉴黄库NSFW
Classifier.Build()
.context(this) //必须调用 否则会有异常抛出
// .isOpenGPU(true)//默认不开启GPU加速 部分机型开启会奔溃,自行选择,默认false
// .numThreads(1) //分配的线程数 根据手机配置设置,默认1
.isOpenGPU(true)//默认不开启GPU加速 部分机型开启会奔溃,自行选择,默认false
.numThreads(100) //分配的线程数 根据手机配置设置,默认1
.build()
//全局注入对象
startKoin {

@ -1,5 +1,5 @@
org.gradle.jvmargs=-Xmx1536m
kotlin.code.style=official
kotlin.coroutines=enable
#android.useAndroidX=true
#android.enableJetifier=true
android.useAndroidX=true
android.enableJetifier=true

@ -1,3 +1,3 @@
//include ':demo' //demo必须注释 include ':app' gradle.properties中 demo使用了androidx与app模块冲突 demo模块完成Assets目录识别
include ':app'
include ':demo' //demo必须注释 include ':app' gradle.properties中 demo使用了androidx与app模块冲突 demo模块完成Assets目录识别
//include ':app'
include ':OpenNSFW'

Loading…
Cancel
Save