diff --git a/app/src/main/java/com/sw/inbound/activity/GoodsListActivity.kt b/app/src/main/java/com/sw/inbound/activity/GoodsListActivity.kt index 8abbd98..a76ea32 100644 --- a/app/src/main/java/com/sw/inbound/activity/GoodsListActivity.kt +++ b/app/src/main/java/com/sw/inbound/activity/GoodsListActivity.kt @@ -1,6 +1,5 @@ package com.sw.inbound.activity -import android.graphics.Color import android.net.Uri import android.os.Bundle import android.text.SpannableStringBuilder @@ -62,7 +61,7 @@ abstract class GoodsListActivity : ComponentActivity() { public const val IS_NEW_RECEIPT = "isNewReceipt" const val PAGE_SIZE = 10 - private const val DELAY_TIME = 10 * 1000 + private const val DELAY_TIME = 5 * 1000 } var isReceiptPage: Boolean = false @@ -127,11 +126,7 @@ abstract class GoodsListActivity : ComponentActivity() { dropdownPopup?.showAsDropDown(v) } binding.tvPurchaseNo.setOnClickListener { - handleGoodsResult( - goodsList = mutableListOf(), - file = null, - name = null - ) + handleGoodsResult() } binding.tvUserName.text = GlobalData.deviceId binding.tvUserName.gone() @@ -202,6 +197,9 @@ abstract class GoodsListActivity : ComponentActivity() { fun recognizeFood() { if (isShowRecognizeDialog.not()) { + if(goodsRecognizeDialog == null) { + isShowRecognizeDialog = true + } return } Timber.tag(TAG).d("recognizeFood,takePhoto") @@ -213,6 +211,7 @@ abstract class GoodsListActivity : ComponentActivity() { } catch (e: Exception) { Timber.tag(TAG).d("recognizeFood,takePhoto异常:${e.message}") e.printStackTrace() + handleGoodsResult() } } @@ -241,21 +240,21 @@ abstract class GoodsListActivity : ComponentActivity() { "IMG_CROP_${System.currentTimeMillis()}.jpg" ) Timber.d("${this.javaClass.simpleName}-searchFood-裁剪bitmap保存文件路径:${file?.absolutePath}") - val foodNameList = FoodModule.queryFood(newBmp) + //val foodList = FoodModule.queryFood(newBmp) + val foodList = FoodModule.getFoodScoreList(newBmp) runOnUiThread { - if (foodNameList.isEmpty()) { - handleGoodsResult( - goodsList = mutableListOf(), - file = file, - name = null - ) + if (foodList.isEmpty()) { + handleGoodsResult() return@runOnUiThread } - viewModel.recognizeGoodsList(foodNameList) { goodsList -> + viewModel.recognizeGoodsList( + //nameList = foodList, + scoreList = foodList, + ) { goodsList -> handleGoodsResult( - goodsList = if (goodsList.size == foodNameList.size) goodsList else mutableListOf(), + goodsList = if (goodsList.size == foodList.size) goodsList else mutableListOf(), file = file, - name = foodNameList.joinToString(",") + name = foodList.joinToString(",") ) } } @@ -269,9 +268,9 @@ abstract class GoodsListActivity : ComponentActivity() { } private fun handleGoodsResult( - goodsList: List, - file: File?, - name: String? + goodsList: List = mutableListOf(), + file: File?=null, + name: String?=null, ) { runOnUiThread { if (goodsRecognizeDialog?.isShowing == true) { diff --git a/app/src/main/java/com/sw/inbound/adapter/RecognizeAdapter.kt b/app/src/main/java/com/sw/inbound/adapter/RecognizeAdapter.kt index 62fd280..247a183 100644 --- a/app/src/main/java/com/sw/inbound/adapter/RecognizeAdapter.kt +++ b/app/src/main/java/com/sw/inbound/adapter/RecognizeAdapter.kt @@ -7,6 +7,7 @@ import coil.load import com.chad.library.adapter4.BaseQuickAdapter import com.chad.library.adapter4.viewholder.QuickViewHolder import com.sw.inbound.databinding.ListItemRecognizeBinding +import com.sw.inbound.ext.toFormattedString import com.sw.inbound.model.response.SearchGoodsInfo import com.sw.inbound.utils.ext.dp import com.sw.inbound.utils.ext.setShapeDrawable @@ -26,7 +27,7 @@ class RecognizeAdapter(var list: MutableList) : override fun onBindViewHolder(holder: VH, position: Int, item: SearchGoodsInfo.Record?) { holder.binding.let { it.tvRecName.run { - text = item?.goodsName + text = if (item!!.score == 0) item.goodsName else "${item.goodsName}\n${((item.score)/100.0).toFormattedString(2)}%" setShapeDrawable2( solidColor = "#DEF5F5F5", bottomRightRadius = 12.dp, diff --git a/app/src/main/java/com/sw/inbound/dialog/BaseDialog.kt b/app/src/main/java/com/sw/inbound/dialog/BaseDialog.kt index ac24391..753a633 100644 --- a/app/src/main/java/com/sw/inbound/dialog/BaseDialog.kt +++ b/app/src/main/java/com/sw/inbound/dialog/BaseDialog.kt @@ -16,7 +16,7 @@ import com.sw.inbound.utils.ext.hideKeyboard abstract class BaseDialog( context: Context, var defWidth: Int = 1500.dp, - var defHeight: Int = 900.dp + var defHeight: Int = 800.dp ) : Dialog(context, R.style.DialogTheme) { override fun onCreate(savedInstanceState: Bundle?) { diff --git a/app/src/main/java/com/sw/inbound/dialog/GoodsRecognizeDialog.kt b/app/src/main/java/com/sw/inbound/dialog/GoodsRecognizeDialog.kt index 9420728..944c330 100644 --- a/app/src/main/java/com/sw/inbound/dialog/GoodsRecognizeDialog.kt +++ b/app/src/main/java/com/sw/inbound/dialog/GoodsRecognizeDialog.kt @@ -102,12 +102,4 @@ class GoodsRecognizeDialog( recognizeAdapter.stateView = it } } - - override fun dismiss() { - super.dismiss() - activity?.run { - isShowRecognizeDialog = true - goodsRecognizeDialog = null - } - } } \ No newline at end of file diff --git a/app/src/main/java/com/sw/inbound/model/response/SearchGoodsInfo.kt b/app/src/main/java/com/sw/inbound/model/response/SearchGoodsInfo.kt index 9c2a6bb..3a938db 100644 --- a/app/src/main/java/com/sw/inbound/model/response/SearchGoodsInfo.kt +++ b/app/src/main/java/com/sw/inbound/model/response/SearchGoodsInfo.kt @@ -80,7 +80,8 @@ data class SearchGoodsInfo( val unitVoList: List? = listOf(), @SerializedName("zjmCode") val zjmCode: String? = "", - var isSelected: Boolean = false + var isSelected: Boolean = false, + var score: Int = 0 ) : Parcelable { val goodsNameStr: String get() { diff --git a/app/src/main/java/com/sw/inbound/objbox/FoodModule.kt b/app/src/main/java/com/sw/inbound/objbox/FoodModule.kt index 2dc7744..960b5ac 100644 --- a/app/src/main/java/com/sw/inbound/objbox/FoodModule.kt +++ b/app/src/main/java/com/sw/inbound/objbox/FoodModule.kt @@ -3,6 +3,7 @@ package com.sw.inbound.objbox import android.content.Context import android.graphics.Bitmap import android.net.Uri +import android.util.SparseLongArray import com.google.gson.Gson import com.google.gson.reflect.TypeToken import com.sw.inbound.MyApp @@ -60,7 +61,11 @@ object FoodModule { NO_STD_RGB // [0.229, 0.224, 0.225] TORCHVISION_NORM_STD_RGB ) val outputTensor = module_mobile.forward(IValue.from(inputTensor)).toTensor() - return outputTensor.dataAsFloatArray + val floatArray = outputTensor.dataAsFloatArray + if (bitmap.isRecycled.not()) { + bitmap.recycle() + } + return floatArray } fun queryFood(uri: Uri, queryCount: Int = 15): List? { @@ -69,40 +74,63 @@ object FoodModule { } } + /** + * 返回识别物品名称列表 + */ fun queryFood(bitmap: Bitmap, queryCount: Int = 15): List { - val inputTensor = - TensorImageUtils.bitmapToFloat32Tensor( - bitmap, - 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 - ) - val outputTensor = module_mobile.forward(IValue.from(inputTensor)).toTensor() - val floatArray = outputTensor.dataAsFloatArray - if (bitmap.isRecycled.not()) { - bitmap.recycle() - } + val floatArray = bitmap2FloatArray(bitmap) return queryFood(floatArray, queryCount) } - fun queryFood(floatArray: FloatArray, queryCount: Int = 15): List { + /** + * 返回识别物品IdNameScore对象列表 + */ + fun queryFoodNameScore(bitmap: Bitmap, queryCount: Int = 15): List { + val floatArray = bitmap2FloatArray(bitmap) + return queryFoodNameScore(floatArray, queryCount) + } + + fun queryFoodNameScore(floatArray: FloatArray, queryCount: Int = 15): List { val query: Query = box.query(Food_.foodVector.nearestNeighbors(floatArray, queryCount)).build() //查询比较分数 // val tempList = query.findWithScores().sortedBy { it.score }.map { "${it.get().name}|${it.get().foodIdx}|${it.score}" } - val map = mutableMapOf() val idScoreList = query.findIdsWithScores() val nameScoreList = mutableListOf() idScoreList.forEach { nameScoreList.add(IdNameScore(id = it.id, name = box.get(it.id).name?:"", score = it.score)) } Timber.tag("FoodModule").d("queryFood数据:${nameScoreList.toJsonString()}") - idScoreList.filter { it.score < 0.05 }.forEach { - val food = box.get(it.id) - //FoodQueryResult(id = it.id, name = food.name, foodIdx = food.foodIdx, score = it.score) - food.name?.let { key -> - val count = map[key] ?: 0 - map[key] = count + 1 - } + + return nameScoreList + } + fun getFoodScoreList(bitmap: Bitmap, queryCount: Int = 15): List { + val floatArray = bitmap2FloatArray(bitmap) + val nameScoreList = queryFoodNameScore(floatArray, queryCount) + val maxScoreList = nameScoreList.groupBy { it.name }.map { (_, value) -> value.maxByOrNull { it.score }!! }.toMutableList() + val map = mutableMapOf() + nameScoreList.forEach { + val key = it.name + val count = map[key] ?: 0 + map[key] = count + 1 + } + val orderList = map.entries.sortedByDescending { it.value }.map { it.key }.toMutableList() + val firstFood = nameScoreList[0].name + orderList.remove(firstFood) + orderList.add(0, firstFood) + + val sortedScoreList = maxScoreList.sortedWith(compareBy { + orderList.indexOf(it.name) + }) + Timber.tag("FoodModule").d("getFoodScoreList数据:${sortedScoreList.toJsonString()}") + return sortedScoreList + } + fun queryFood(floatArray: FloatArray, queryCount: Int = 15): List { + val map = mutableMapOf() + val nameScoreList = queryFoodNameScore(floatArray, queryCount) + nameScoreList.filter { it.score < 0.05 }.forEach { + val count = map[it.name] ?: 0 + map[it.name] = count + 1 } val list = map.entries.sortedByDescending { it.value }.map { it.key } return list diff --git a/app/src/main/java/com/sw/inbound/viewmodel/BaseViewModel.kt b/app/src/main/java/com/sw/inbound/viewmodel/BaseViewModel.kt index 784a86b..e51e40e 100644 --- a/app/src/main/java/com/sw/inbound/viewmodel/BaseViewModel.kt +++ b/app/src/main/java/com/sw/inbound/viewmodel/BaseViewModel.kt @@ -10,6 +10,7 @@ import com.sw.inbound.model.response.EquipmentInfo import com.sw.inbound.model.response.GoodsInfo import com.sw.inbound.model.response.SearchGoodsInfo import com.sw.inbound.network.LoadingState +import com.sw.inbound.objbox.FoodModule import com.sw.inbound.repository.RemoteRepository import com.sw.inbound.utils.ToastUtils import kotlinx.coroutines.flow.MutableStateFlow @@ -197,12 +198,18 @@ abstract class BaseViewModel( } fun recognizeGoodsList( - nameList: List, + nameList: List?=null, + scoreList: List?=null, callback: (List) -> Unit ) { launch { - val response = repository.recognizeGoodsList(nameList) + if (nameList.isNullOrEmpty() && scoreList.isNullOrEmpty()) return@launch + val paramList = if (nameList.isNullOrEmpty()) scoreList!!.map { it.name } else nameList + val response = repository.recognizeGoodsList(paramList) val list = if (parseResponse(response)) response.data?:listOf() else listOf() + scoreList?.forEachIndexed { index, item -> + list[index].score = ((1 - item.score) * 10000).toInt() + } callback(list) } } diff --git a/app/src/main/res/layout/list_item_recognize.xml b/app/src/main/res/layout/list_item_recognize.xml index dc0191c..6c13723 100644 --- a/app/src/main/res/layout/list_item_recognize.xml +++ b/app/src/main/res/layout/list_item_recognize.xml @@ -18,14 +18,14 @@