优化异常
This commit is contained in:
@@ -371,13 +371,9 @@ class FoodCollectionActivity : ComponentActivity() {
|
|||||||
// }
|
// }
|
||||||
val goods = searchGoodsList[clickIndex]
|
val goods = searchGoodsList[clickIndex]
|
||||||
val saveName = goods.goodsName + goods.goodsCode
|
val saveName = goods.goodsName + goods.goodsCode
|
||||||
box?.put(
|
ObjectBox.boxStore.runInTx {
|
||||||
Food(
|
box?.put(Food(name = saveName, foodIdx = 0, foodVector = imageVector))
|
||||||
name = saveName,
|
}
|
||||||
foodIdx = 0,
|
|
||||||
foodVector = imageVector
|
|
||||||
)
|
|
||||||
)
|
|
||||||
collectList[position].let {
|
collectList[position].let {
|
||||||
// it.imageVector = imageVector
|
// it.imageVector = imageVector
|
||||||
it.isFinish = true
|
it.isFinish = true
|
||||||
|
|||||||
@@ -158,7 +158,9 @@ class CollectSearchDialog(
|
|||||||
}
|
}
|
||||||
val name = list[position].goodsName
|
val name = list[position].goodsName
|
||||||
val filterIdList = box?.all?.filter { it.name == name }?.map { it.id }
|
val filterIdList = box?.all?.filter { it.name == name }?.map { it.id }
|
||||||
|
ObjectBox.boxStore.runInTx {
|
||||||
box?.removeByIds(filterIdList)
|
box?.removeByIds(filterIdList)
|
||||||
|
}
|
||||||
activity?.runOnUiThread {
|
activity?.runOnUiThread {
|
||||||
Loading.dismiss()
|
Loading.dismiss()
|
||||||
//list.remove(list[position])
|
//list.remove(list[position])
|
||||||
@@ -209,24 +211,24 @@ class CollectSearchDialog(
|
|||||||
binding.root.hideKeyboard()
|
binding.root.hideKeyboard()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun test() {
|
// fun test() {
|
||||||
val goodsName = "多宝鱼,黑鱼(整条鱼),鸡腿肉"
|
// val goodsName = "多宝鱼,黑鱼(整条鱼),鸡腿肉"
|
||||||
activity?.searchGoodsInfoList(goodsName = goodsName) { items->
|
// activity?.searchGoodsInfoList(goodsName = goodsName) { items->
|
||||||
items.forEach { goods ->
|
// items.forEach { goods ->
|
||||||
box?.all?.filter {
|
// box?.all?.filter {
|
||||||
it.name?.contains(goods.goodsName?:"-") == true
|
// it.name?.contains(goods.goodsName?:"-") == true
|
||||||
}?.forEach {
|
// }?.forEach {
|
||||||
activity?.runOnUiThread {
|
|
||||||
activity?.toast(goods.goodsName)
|
|
||||||
}
|
|
||||||
it.name = goods.goodsName + goods.goodsCode
|
|
||||||
box?.put(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// activity?.runOnUiThread {
|
// activity?.runOnUiThread {
|
||||||
// activity?.toast("已保存")
|
// activity?.toast(goods.goodsName)
|
||||||
|
// }
|
||||||
|
// it.name = goods.goodsName + goods.goodsCode
|
||||||
|
// box?.put(it)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//// activity?.runOnUiThread {
|
||||||
|
//// activity?.toast("已保存")
|
||||||
|
//// }
|
||||||
|
// }
|
||||||
// }
|
// }
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -454,11 +454,11 @@ class GoodsStoreDialog(
|
|||||||
Thread {
|
Thread {
|
||||||
try {
|
try {
|
||||||
val imageVector = FoodModule.bitmap2FloatArray(bitmap)
|
val imageVector = FoodModule.bitmap2FloatArray(bitmap)
|
||||||
box?.put(
|
if (imageVector != null) {
|
||||||
Food(
|
ObjectBox.boxStore.runInTx {
|
||||||
name = goods.goodsName + goods.goodsCode, foodIdx = 0, foodVector = imageVector
|
box?.put(Food(name = goods.goodsName + goods.goodsCode, foodIdx = 0, foodVector = imageVector))
|
||||||
)
|
}
|
||||||
)
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,8 @@ package com.sw.inbound.objbox
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.graphics.Bitmap
|
import android.graphics.Bitmap
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.util.SparseLongArray
|
import android.renderscript.Element.DataType
|
||||||
|
import androidx.core.graphics.scale
|
||||||
import com.google.gson.Gson
|
import com.google.gson.Gson
|
||||||
import com.google.gson.reflect.TypeToken
|
import com.google.gson.reflect.TypeToken
|
||||||
import com.sw.inbound.MyApp
|
import com.sw.inbound.MyApp
|
||||||
@@ -22,6 +23,7 @@ import java.io.FileOutputStream
|
|||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
|
|
||||||
|
|
||||||
object FoodModule {
|
object FoodModule {
|
||||||
|
|
||||||
private lateinit var module_mobile: Module
|
private lateinit var module_mobile: Module
|
||||||
@@ -31,6 +33,9 @@ object FoodModule {
|
|||||||
private lateinit var classInfo: FoodClassInfo
|
private lateinit var classInfo: FoodClassInfo
|
||||||
private val NO_MEAN_RGB = floatArrayOf(0.0f, 0.0f, 0.0f)
|
private val NO_MEAN_RGB = floatArrayOf(0.0f, 0.0f, 0.0f)
|
||||||
private val NO_STD_RGB = floatArrayOf(1.0f, 1.0f, 1.0f)
|
private val NO_STD_RGB = floatArrayOf(1.0f, 1.0f, 1.0f)
|
||||||
|
// 1. 定义你的模型固定输入尺寸 (根据你的tflite模型修改,比如224x224)
|
||||||
|
private const val MODEL_INPUT_WIDTH = 300
|
||||||
|
private const val MODEL_INPUT_HEIGHT = 300
|
||||||
const val DEFAULT_FOOD_INDEX = -1
|
const val DEFAULT_FOOD_INDEX = -1
|
||||||
|
|
||||||
const val BAG_RATE = 0.05
|
const val BAG_RATE = 0.05
|
||||||
@@ -48,49 +53,64 @@ object FoodModule {
|
|||||||
}.start()
|
}.start()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun uri2FloatArray(uri: Uri): FloatArray? {
|
// fun uri2FloatArray(uri: Uri): FloatArray? {
|
||||||
return MyApp.instance?.let { context ->
|
// return MyApp.instance?.let { context ->
|
||||||
ImageUtil.uriToBitmap(context, uri)?.let {
|
// ImageUtil.uriToBitmap(context, uri)?.let {
|
||||||
bitmap2FloatArray(it)
|
// bitmap2FloatArray(it)
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
fun bitmap2FloatArray(bitmap: Bitmap): FloatArray {
|
fun bitmap2FloatArray(originBitmap: Bitmap): FloatArray? {
|
||||||
|
var rgb565Bitmap: Bitmap?=null
|
||||||
|
try {
|
||||||
|
val scaledBitmap = originBitmap.scale(MODEL_INPUT_WIDTH, MODEL_INPUT_HEIGHT)
|
||||||
|
originBitmap.recycle()
|
||||||
|
rgb565Bitmap = scaledBitmap.copy(Bitmap.Config.RGB_565, false)
|
||||||
|
scaledBitmap.recycle()
|
||||||
val inputTensor = TensorImageUtils.bitmapToFloat32Tensor(
|
val inputTensor = TensorImageUtils.bitmapToFloat32Tensor(
|
||||||
bitmap,
|
rgb565Bitmap,
|
||||||
NO_MEAN_RGB, // [0.485, 0.456, 0.406] TORCHVISION_NORM_MEAN_RGB
|
NO_MEAN_RGB, // [0.485, 0.456, 0.406] TORCHVISION_NORM_MEAN_RGB
|
||||||
NO_STD_RGB // [0.229, 0.224, 0.225] TORCHVISION_NORM_STD_RGB
|
NO_STD_RGB // [0.229, 0.224, 0.225] TORCHVISION_NORM_STD_RGB
|
||||||
)
|
)
|
||||||
val outputTensor = module_mobile.forward(IValue.from(inputTensor)).toTensor()
|
val outputTensor = module_mobile.forward(IValue.from(inputTensor)).toTensor()
|
||||||
// return floatArrayOf()
|
return outputTensor.dataAsFloatArray
|
||||||
val floatArray = outputTensor.dataAsFloatArray
|
} catch (e: OutOfMemoryError) {
|
||||||
return floatArray
|
e.printStackTrace()
|
||||||
|
} finally {
|
||||||
|
if (rgb565Bitmap != null && rgb565Bitmap.isRecycled.not()) {
|
||||||
|
rgb565Bitmap.recycle()
|
||||||
|
}
|
||||||
|
//System.gc()
|
||||||
|
//System.runFinalization()
|
||||||
|
}
|
||||||
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
fun queryFood(uri: Uri, queryCount: Int = 15): List<String>? {
|
// fun queryFood(uri: Uri, queryCount: Int = 15): List<String>? {
|
||||||
return uri2FloatArray(uri)?.let {
|
// return uri2FloatArray(uri)?.let {
|
||||||
queryFood(it, queryCount)
|
// queryFood(it, queryCount)
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
/**
|
// /**
|
||||||
* 返回识别物品名称列表
|
// * 返回识别物品名称列表
|
||||||
*/
|
// */
|
||||||
fun queryFood(bitmap: Bitmap, queryCount: Int = 15): List<String> {
|
// fun queryFood(bitmap: Bitmap, queryCount: Int = 15): List<String> {
|
||||||
val floatArray = bitmap2FloatArray(bitmap)
|
// val floatArray = bitmap2FloatArray(bitmap)
|
||||||
return queryFood(floatArray, queryCount)
|
// return queryFood(floatArray, queryCount)
|
||||||
}
|
// }
|
||||||
|
|
||||||
/**
|
// /**
|
||||||
* 返回识别物品IdNameScore对象列表
|
// * 返回识别物品IdNameScore对象列表
|
||||||
*/
|
// */
|
||||||
fun queryFoodNameScore(bitmap: Bitmap, queryCount: Int = 15): List<IdNameScore> {
|
// fun queryFoodNameScore(bitmap: Bitmap, queryCount: Int = 15): List<IdNameScore> {
|
||||||
val floatArray = bitmap2FloatArray(bitmap)
|
// val floatArray = bitmap2FloatArray(bitmap)
|
||||||
return queryFoodNameScore(floatArray, queryCount)
|
// return queryFoodNameScore(floatArray, queryCount)
|
||||||
}
|
// }
|
||||||
|
|
||||||
fun queryFoodNameScore(floatArray: FloatArray, queryCount: Int = 15): List<IdNameScore> {
|
fun queryFoodNameScore(floatArray: FloatArray?, queryCount: Int = 15): List<IdNameScore> {
|
||||||
|
if (floatArray == null) return emptyList()
|
||||||
val query: Query<Food> =
|
val query: Query<Food> =
|
||||||
box.query(Food_.foodVector.nearestNeighbors(floatArray, queryCount)).build()
|
box.query(Food_.foodVector.nearestNeighbors(floatArray, queryCount)).build()
|
||||||
//查询比较分数
|
//查询比较分数
|
||||||
@@ -106,7 +126,7 @@ object FoodModule {
|
|||||||
}
|
}
|
||||||
fun getFoodScoreList(bitmap: Bitmap, queryCount: Int = 15): List<IdNameScore> {
|
fun getFoodScoreList(bitmap: Bitmap, queryCount: Int = 15): List<IdNameScore> {
|
||||||
val floatArray = bitmap2FloatArray(bitmap)
|
val floatArray = bitmap2FloatArray(bitmap)
|
||||||
// return emptyList()
|
if (floatArray == null) return emptyList()
|
||||||
val nameScoreList = queryFoodNameScore(floatArray, queryCount)
|
val nameScoreList = queryFoodNameScore(floatArray, queryCount)
|
||||||
if (nameScoreList.isEmpty()) {
|
if (nameScoreList.isEmpty()) {
|
||||||
return emptyList()
|
return emptyList()
|
||||||
@@ -170,8 +190,10 @@ object FoodModule {
|
|||||||
val classIdx = labelsList[index]
|
val classIdx = labelsList[index]
|
||||||
val foodName = foodMap["$classIdx"]
|
val foodName = foodMap["$classIdx"]
|
||||||
val array = floatList.toFloatArray()
|
val array = floatList.toFloatArray()
|
||||||
|
ObjectBox.boxStore.runInTx {
|
||||||
box.put(Food(name = foodName, foodVector = array, foodIdx = DEFAULT_FOOD_INDEX))
|
box.put(Food(name = foodName, foodVector = array, foodIdx = DEFAULT_FOOD_INDEX))
|
||||||
}
|
}
|
||||||
|
}
|
||||||
action()
|
action()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -184,47 +206,75 @@ object FoodModule {
|
|||||||
*
|
*
|
||||||
* 不可能反正SD
|
* 不可能反正SD
|
||||||
*/
|
*/
|
||||||
|
// fun copyAssetToCache(context: Context, fileName: String): String? {
|
||||||
|
// // 此app的缓存目录 --> 会默认在 cache目录...,可以自己去看看哦
|
||||||
|
// val cacheDir = context.getCacheDir()
|
||||||
|
// if (!cacheDir.exists()) {
|
||||||
|
// cacheDir.mkdirs() // TODO 如果没有缓存目录,就创建
|
||||||
|
// }
|
||||||
|
// val outPath = File(cacheDir, fileName) // TODO 创建输出的文件位置
|
||||||
|
// if (outPath.exists()) {
|
||||||
|
// outPath.delete() // TODO 如果该文件已经存在,就删掉
|
||||||
|
// }
|
||||||
|
// var `is`: InputStream? = null // 读取
|
||||||
|
// var fos: FileOutputStream? = null // 写入
|
||||||
|
// try {
|
||||||
|
// // 创建文件,如果创建成功,就返回true
|
||||||
|
// val res = outPath.createNewFile()
|
||||||
|
// if (res) {
|
||||||
|
// `is` = context.getAssets().open(fileName) // 拿到main/assets目录的输入流,用于读取字节
|
||||||
|
// fos = FileOutputStream(outPath) // 读取出来的字节最终写到outPath
|
||||||
|
// val buf = ByteArray(`is`.available()) // 缓存区
|
||||||
|
// var byteCount: Int
|
||||||
|
//
|
||||||
|
// // 开始循环读取
|
||||||
|
// while ((`is`.read(buf).also { byteCount = it }) != -1) {
|
||||||
|
// fos.write(buf, 0, byteCount)
|
||||||
|
// }
|
||||||
|
// return outPath.getAbsolutePath()
|
||||||
|
// }
|
||||||
|
// } catch (e: IOException) {
|
||||||
|
// e.printStackTrace()
|
||||||
|
// } finally {
|
||||||
|
// try {
|
||||||
|
// // TODO 一定要记得关闭资源,为了不去性能的磨损
|
||||||
|
// fos?.flush()
|
||||||
|
// `is`?.close()
|
||||||
|
// fos?.close()
|
||||||
|
// } catch (e: IOException) {
|
||||||
|
// e.printStackTrace()
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// return null
|
||||||
|
// }
|
||||||
|
|
||||||
fun copyAssetToCache(context: Context, fileName: String): String? {
|
fun copyAssetToCache(context: Context, fileName: String): String? {
|
||||||
// 此app的缓存目录 --> 会默认在 cache目录...,可以自己去看看哦
|
val cacheFile = File(context.cacheDir, fileName)
|
||||||
val cacheDir = context.getCacheDir()
|
val buffer = ByteArray(8 * 1024)
|
||||||
if (!cacheDir.exists()) {
|
var inputStream: InputStream? = null
|
||||||
cacheDir.mkdirs() // TODO 如果没有缓存目录,就创建
|
var outputStream: FileOutputStream? = null
|
||||||
}
|
|
||||||
val outPath = File(cacheDir, fileName) // TODO 创建输出的文件位置
|
|
||||||
if (outPath.exists()) {
|
|
||||||
outPath.delete() // TODO 如果该文件已经存在,就删掉
|
|
||||||
}
|
|
||||||
var `is`: InputStream? = null // 读取
|
|
||||||
var fos: FileOutputStream? = null // 写入
|
|
||||||
try {
|
|
||||||
// 创建文件,如果创建成功,就返回true
|
|
||||||
val res = outPath.createNewFile()
|
|
||||||
if (res) {
|
|
||||||
`is` = context.getAssets().open(fileName) // 拿到main/assets目录的输入流,用于读取字节
|
|
||||||
fos = FileOutputStream(outPath) // 读取出来的字节最终写到outPath
|
|
||||||
val buf = ByteArray(`is`.available()) // 缓存区
|
|
||||||
var byteCount: Int
|
|
||||||
|
|
||||||
// 开始循环读取
|
|
||||||
while ((`is`.read(buf).also { byteCount = it }) != -1) {
|
|
||||||
fos.write(buf, 0, byteCount)
|
|
||||||
}
|
|
||||||
return outPath.getAbsolutePath()
|
|
||||||
}
|
|
||||||
} catch (e: IOException) {
|
|
||||||
e.printStackTrace()
|
|
||||||
} finally {
|
|
||||||
try {
|
try {
|
||||||
// TODO 一定要记得关闭资源,为了不去性能的磨损
|
inputStream = context.assets.open(fileName)
|
||||||
fos?.flush()
|
outputStream = FileOutputStream(cacheFile)
|
||||||
`is`?.close()
|
var byteCount: Int
|
||||||
fos?.close()
|
while (inputStream.read(buffer).also { byteCount = it } != -1) {
|
||||||
|
outputStream.write(buffer, 0, byteCount)
|
||||||
|
}
|
||||||
|
outputStream.channel.force(true) // 强制物理落盘,比flush更彻底
|
||||||
|
return cacheFile.absolutePath
|
||||||
} catch (e: IOException) {
|
} catch (e: IOException) {
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
|
Timber.tag("FoodModule").d("文件拷贝失败 fileName=$fileName, error=${e.message}")
|
||||||
|
// 拷贝失败时删除残缺文件,避免下次读取到损坏文件
|
||||||
|
if (cacheFile.exists()) {
|
||||||
|
cacheFile.delete()
|
||||||
}
|
}
|
||||||
|
} finally {
|
||||||
|
outputStream?.close()
|
||||||
|
inputStream?.close()
|
||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -55,13 +55,15 @@ object ObjectBox {
|
|||||||
private set
|
private set
|
||||||
|
|
||||||
fun init(context: Context) {
|
fun init(context: Context) {
|
||||||
|
// 优化:调大事务回收超时时间
|
||||||
|
System.setProperty("objectbox.finalizerTimeout", "30000");
|
||||||
// On Android make sure to pass a Context when building the Store.
|
// On Android make sure to pass a Context when building the Store.
|
||||||
boxStore = try {
|
boxStore = try {
|
||||||
MyObjectBox.builder()
|
MyObjectBox.builder()
|
||||||
.androidContext(context.applicationContext)
|
.androidContext(context.applicationContext)
|
||||||
.build()
|
.build()
|
||||||
} catch (e: DbException) {
|
} catch (e: DbException) {
|
||||||
if (e.javaClass.equals(DbException::class.java) || e is FileCorruptException) {
|
if (e.javaClass == DbException::class.java || e is FileCorruptException) {
|
||||||
// Failed to build BoxStore due to database file issue, store message;
|
// Failed to build BoxStore due to database file issue, store message;
|
||||||
// checked in NoteListActivity to notify user.
|
// checked in NoteListActivity to notify user.
|
||||||
dbExceptionMessage = e.toString()
|
dbExceptionMessage = e.toString()
|
||||||
|
|||||||
Reference in New Issue
Block a user