优化
This commit is contained in:
@@ -1,6 +1,5 @@
|
|||||||
package com.sw.inbound.activity
|
package com.sw.inbound.activity
|
||||||
|
|
||||||
import android.graphics.Color
|
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.text.SpannableStringBuilder
|
import android.text.SpannableStringBuilder
|
||||||
@@ -62,7 +61,7 @@ abstract class GoodsListActivity : ComponentActivity() {
|
|||||||
public const val IS_NEW_RECEIPT = "isNewReceipt"
|
public const val IS_NEW_RECEIPT = "isNewReceipt"
|
||||||
|
|
||||||
const val PAGE_SIZE = 10
|
const val PAGE_SIZE = 10
|
||||||
private const val DELAY_TIME = 10 * 1000
|
private const val DELAY_TIME = 5 * 1000
|
||||||
}
|
}
|
||||||
|
|
||||||
var isReceiptPage: Boolean = false
|
var isReceiptPage: Boolean = false
|
||||||
@@ -127,11 +126,7 @@ abstract class GoodsListActivity : ComponentActivity() {
|
|||||||
dropdownPopup?.showAsDropDown(v)
|
dropdownPopup?.showAsDropDown(v)
|
||||||
}
|
}
|
||||||
binding.tvPurchaseNo.setOnClickListener {
|
binding.tvPurchaseNo.setOnClickListener {
|
||||||
handleGoodsResult(
|
handleGoodsResult()
|
||||||
goodsList = mutableListOf(),
|
|
||||||
file = null,
|
|
||||||
name = null
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
binding.tvUserName.text = GlobalData.deviceId
|
binding.tvUserName.text = GlobalData.deviceId
|
||||||
binding.tvUserName.gone()
|
binding.tvUserName.gone()
|
||||||
@@ -202,6 +197,9 @@ abstract class GoodsListActivity : ComponentActivity() {
|
|||||||
|
|
||||||
fun recognizeFood() {
|
fun recognizeFood() {
|
||||||
if (isShowRecognizeDialog.not()) {
|
if (isShowRecognizeDialog.not()) {
|
||||||
|
if(goodsRecognizeDialog == null) {
|
||||||
|
isShowRecognizeDialog = true
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
Timber.tag(TAG).d("recognizeFood,takePhoto")
|
Timber.tag(TAG).d("recognizeFood,takePhoto")
|
||||||
@@ -213,6 +211,7 @@ abstract class GoodsListActivity : ComponentActivity() {
|
|||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Timber.tag(TAG).d("recognizeFood,takePhoto异常:${e.message}")
|
Timber.tag(TAG).d("recognizeFood,takePhoto异常:${e.message}")
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
|
handleGoodsResult()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -241,21 +240,21 @@ abstract class GoodsListActivity : ComponentActivity() {
|
|||||||
"IMG_CROP_${System.currentTimeMillis()}.jpg"
|
"IMG_CROP_${System.currentTimeMillis()}.jpg"
|
||||||
)
|
)
|
||||||
Timber.d("${this.javaClass.simpleName}-searchFood-裁剪bitmap保存文件路径:${file?.absolutePath}")
|
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 {
|
runOnUiThread {
|
||||||
if (foodNameList.isEmpty()) {
|
if (foodList.isEmpty()) {
|
||||||
handleGoodsResult(
|
handleGoodsResult()
|
||||||
goodsList = mutableListOf(),
|
|
||||||
file = file,
|
|
||||||
name = null
|
|
||||||
)
|
|
||||||
return@runOnUiThread
|
return@runOnUiThread
|
||||||
}
|
}
|
||||||
viewModel.recognizeGoodsList(foodNameList) { goodsList ->
|
viewModel.recognizeGoodsList(
|
||||||
|
//nameList = foodList,
|
||||||
|
scoreList = foodList,
|
||||||
|
) { goodsList ->
|
||||||
handleGoodsResult(
|
handleGoodsResult(
|
||||||
goodsList = if (goodsList.size == foodNameList.size) goodsList else mutableListOf(),
|
goodsList = if (goodsList.size == foodList.size) goodsList else mutableListOf(),
|
||||||
file = file,
|
file = file,
|
||||||
name = foodNameList.joinToString(",")
|
name = foodList.joinToString(",")
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -269,9 +268,9 @@ abstract class GoodsListActivity : ComponentActivity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun handleGoodsResult(
|
private fun handleGoodsResult(
|
||||||
goodsList: List<SearchGoodsInfo.Record>,
|
goodsList: List<SearchGoodsInfo.Record> = mutableListOf(),
|
||||||
file: File?,
|
file: File?=null,
|
||||||
name: String?
|
name: String?=null,
|
||||||
) {
|
) {
|
||||||
runOnUiThread {
|
runOnUiThread {
|
||||||
if (goodsRecognizeDialog?.isShowing == true) {
|
if (goodsRecognizeDialog?.isShowing == true) {
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import coil.load
|
|||||||
import com.chad.library.adapter4.BaseQuickAdapter
|
import com.chad.library.adapter4.BaseQuickAdapter
|
||||||
import com.chad.library.adapter4.viewholder.QuickViewHolder
|
import com.chad.library.adapter4.viewholder.QuickViewHolder
|
||||||
import com.sw.inbound.databinding.ListItemRecognizeBinding
|
import com.sw.inbound.databinding.ListItemRecognizeBinding
|
||||||
|
import com.sw.inbound.ext.toFormattedString
|
||||||
import com.sw.inbound.model.response.SearchGoodsInfo
|
import com.sw.inbound.model.response.SearchGoodsInfo
|
||||||
import com.sw.inbound.utils.ext.dp
|
import com.sw.inbound.utils.ext.dp
|
||||||
import com.sw.inbound.utils.ext.setShapeDrawable
|
import com.sw.inbound.utils.ext.setShapeDrawable
|
||||||
@@ -26,7 +27,7 @@ class RecognizeAdapter(var list: MutableList<SearchGoodsInfo.Record>) :
|
|||||||
override fun onBindViewHolder(holder: VH, position: Int, item: SearchGoodsInfo.Record?) {
|
override fun onBindViewHolder(holder: VH, position: Int, item: SearchGoodsInfo.Record?) {
|
||||||
holder.binding.let {
|
holder.binding.let {
|
||||||
it.tvRecName.run {
|
it.tvRecName.run {
|
||||||
text = item?.goodsName
|
text = if (item!!.score == 0) item.goodsName else "${item.goodsName}\n${((item.score)/100.0).toFormattedString(2)}%"
|
||||||
setShapeDrawable2(
|
setShapeDrawable2(
|
||||||
solidColor = "#DEF5F5F5",
|
solidColor = "#DEF5F5F5",
|
||||||
bottomRightRadius = 12.dp,
|
bottomRightRadius = 12.dp,
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ import com.sw.inbound.utils.ext.hideKeyboard
|
|||||||
abstract class BaseDialog(
|
abstract class BaseDialog(
|
||||||
context: Context,
|
context: Context,
|
||||||
var defWidth: Int = 1500.dp,
|
var defWidth: Int = 1500.dp,
|
||||||
var defHeight: Int = 900.dp
|
var defHeight: Int = 800.dp
|
||||||
) : Dialog(context, R.style.DialogTheme) {
|
) : Dialog(context, R.style.DialogTheme) {
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
|||||||
@@ -102,12 +102,4 @@ class GoodsRecognizeDialog(
|
|||||||
recognizeAdapter.stateView = it
|
recognizeAdapter.stateView = it
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun dismiss() {
|
|
||||||
super.dismiss()
|
|
||||||
activity?.run {
|
|
||||||
isShowRecognizeDialog = true
|
|
||||||
goodsRecognizeDialog = null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -80,7 +80,8 @@ data class SearchGoodsInfo(
|
|||||||
val unitVoList: List<UnitVo>? = listOf(),
|
val unitVoList: List<UnitVo>? = listOf(),
|
||||||
@SerializedName("zjmCode")
|
@SerializedName("zjmCode")
|
||||||
val zjmCode: String? = "",
|
val zjmCode: String? = "",
|
||||||
var isSelected: Boolean = false
|
var isSelected: Boolean = false,
|
||||||
|
var score: Int = 0
|
||||||
) : Parcelable {
|
) : Parcelable {
|
||||||
val goodsNameStr: String
|
val goodsNameStr: String
|
||||||
get() {
|
get() {
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ 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 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
|
||||||
@@ -60,7 +61,11 @@ object FoodModule {
|
|||||||
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 outputTensor.dataAsFloatArray
|
val floatArray = outputTensor.dataAsFloatArray
|
||||||
|
if (bitmap.isRecycled.not()) {
|
||||||
|
bitmap.recycle()
|
||||||
|
}
|
||||||
|
return floatArray
|
||||||
}
|
}
|
||||||
|
|
||||||
fun queryFood(uri: Uri, queryCount: Int = 15): List<String>? {
|
fun queryFood(uri: Uri, queryCount: Int = 15): List<String>? {
|
||||||
@@ -69,40 +74,63 @@ object FoodModule {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回识别物品名称列表
|
||||||
|
*/
|
||||||
fun queryFood(bitmap: Bitmap, queryCount: Int = 15): List<String> {
|
fun queryFood(bitmap: Bitmap, queryCount: Int = 15): List<String> {
|
||||||
val inputTensor =
|
val floatArray = bitmap2FloatArray(bitmap)
|
||||||
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()
|
|
||||||
}
|
|
||||||
return queryFood(floatArray, queryCount)
|
return queryFood(floatArray, queryCount)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun queryFood(floatArray: FloatArray, queryCount: Int = 15): List<String> {
|
/**
|
||||||
|
* 返回识别物品IdNameScore对象列表
|
||||||
|
*/
|
||||||
|
fun queryFoodNameScore(bitmap: Bitmap, queryCount: Int = 15): List<IdNameScore> {
|
||||||
|
val floatArray = bitmap2FloatArray(bitmap)
|
||||||
|
return queryFoodNameScore(floatArray, queryCount)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun queryFoodNameScore(floatArray: FloatArray, queryCount: Int = 15): List<IdNameScore> {
|
||||||
val query: Query<Food> =
|
val query: Query<Food> =
|
||||||
box.query(Food_.foodVector.nearestNeighbors(floatArray, queryCount)).build()
|
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 tempList = query.findWithScores().sortedBy { it.score }.map { "${it.get().name}|${it.get().foodIdx}|${it.score}" }
|
||||||
val map = mutableMapOf<String, Int>()
|
|
||||||
val idScoreList = query.findIdsWithScores()
|
val idScoreList = query.findIdsWithScores()
|
||||||
val nameScoreList = mutableListOf<IdNameScore>()
|
val nameScoreList = mutableListOf<IdNameScore>()
|
||||||
idScoreList.forEach {
|
idScoreList.forEach {
|
||||||
nameScoreList.add(IdNameScore(id = it.id, name = box.get(it.id).name?:"", score = it.score))
|
nameScoreList.add(IdNameScore(id = it.id, name = box.get(it.id).name?:"", score = it.score))
|
||||||
}
|
}
|
||||||
Timber.tag("FoodModule").d("queryFood数据:${nameScoreList.toJsonString()}")
|
Timber.tag("FoodModule").d("queryFood数据:${nameScoreList.toJsonString()}")
|
||||||
idScoreList.filter { it.score < 0.05 }.forEach {
|
|
||||||
val food = box.get(it.id)
|
return nameScoreList
|
||||||
//FoodQueryResult(id = it.id, name = food.name, foodIdx = food.foodIdx, score = it.score)
|
}
|
||||||
food.name?.let { key ->
|
fun getFoodScoreList(bitmap: Bitmap, queryCount: Int = 15): List<IdNameScore> {
|
||||||
|
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<String, Int>()
|
||||||
|
nameScoreList.forEach {
|
||||||
|
val key = it.name
|
||||||
val count = map[key] ?: 0
|
val count = map[key] ?: 0
|
||||||
map[key] = count + 1
|
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<String> {
|
||||||
|
val map = mutableMapOf<String, Int>()
|
||||||
|
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 }
|
val list = map.entries.sortedByDescending { it.value }.map { it.key }
|
||||||
return list
|
return list
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import com.sw.inbound.model.response.EquipmentInfo
|
|||||||
import com.sw.inbound.model.response.GoodsInfo
|
import com.sw.inbound.model.response.GoodsInfo
|
||||||
import com.sw.inbound.model.response.SearchGoodsInfo
|
import com.sw.inbound.model.response.SearchGoodsInfo
|
||||||
import com.sw.inbound.network.LoadingState
|
import com.sw.inbound.network.LoadingState
|
||||||
|
import com.sw.inbound.objbox.FoodModule
|
||||||
import com.sw.inbound.repository.RemoteRepository
|
import com.sw.inbound.repository.RemoteRepository
|
||||||
import com.sw.inbound.utils.ToastUtils
|
import com.sw.inbound.utils.ToastUtils
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
@@ -197,12 +198,18 @@ abstract class BaseViewModel(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun recognizeGoodsList(
|
fun recognizeGoodsList(
|
||||||
nameList: List<String>,
|
nameList: List<String>?=null,
|
||||||
|
scoreList: List<FoodModule.IdNameScore>?=null,
|
||||||
callback: (List<SearchGoodsInfo.Record>) -> Unit
|
callback: (List<SearchGoodsInfo.Record>) -> Unit
|
||||||
) {
|
) {
|
||||||
launch {
|
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()
|
val list = if (parseResponse(response)) response.data?:listOf() else listOf()
|
||||||
|
scoreList?.forEachIndexed { index, item ->
|
||||||
|
list[index].score = ((1 - item.score) * 10000).toInt()
|
||||||
|
}
|
||||||
callback(list)
|
callback(list)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,14 +18,14 @@
|
|||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/tvRecName"
|
android:id="@+id/tvRecName"
|
||||||
android:layout_width="340dp"
|
android:layout_width="340dp"
|
||||||
android:layout_height="85dp"
|
android:layout_height="86dp"
|
||||||
android:layout_gravity="bottom"
|
android:layout_gravity="bottom"
|
||||||
android:background="#DEF5F5F5"
|
android:background="#DEF5F5F5"
|
||||||
android:textStyle="bold"
|
android:textStyle="bold"
|
||||||
android:textColor="@color/black"
|
android:textColor="@color/black"
|
||||||
android:textSize="30sp"
|
android:textSize="30sp"
|
||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
android:maxLines="1"
|
android:maxLines="2"
|
||||||
android:ellipsize="end"
|
android:ellipsize="end"
|
||||||
tools:text="大白菜"/>
|
tools:text="大白菜"/>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user