我的 kotlin 应用程序因 ML 图像处理(文本识别)而崩溃
My kotlin app crashes because of ML image processing (text recognition)
我正在编写一个用于在 android 智能手机上搜索模因的应用程序。我已经有了从存储中获取所有图像并将它们显示在屏幕上的代码,它工作得很好。现在,我正在尝试使用 google ML 文本识别处理每张获取的图像,并将该文本添加到存储在数组列表中的图像对象(每次处理新图像时,我都会将该列表保存在 sharedpreferences 中)。通常,我的代码可以工作,但不知何故,当有大量图像要处理时,应用程序会崩溃。我从日志中几乎看不到任何东西,没有错误。我有一次并发修改错误,但是当我添加newList时,它不再显示了。
但是,我可以再次启动该应用程序,它会继续加载图像,直到下一次崩溃。好像处理了几百张图片就崩溃了
这是从设备存储中获取和处理图像的完整函数(抱歉代码乱七八糟,我还不是专业人士):
//Function for fetching all the data and creating Image objects, which are returned in a list.
//I will pass them to the main activity and show on the screen
@SuppressLint("SetTextI18n")
@RequiresApi(Build.VERSION_CODES.Q)
private fun queryImageStorage() {
var imageNumber = 0
var percentageloaded = 0
val list = readListFromPref(this, R.string.preference_file_key.toString()).toList()
Log.d(TAG, "queryImageStorage: ROZMIAR ${list.size}")
val newList = readListFromPref(this, R.string.preference_file_key.toString())
val imageProjection = arrayOf(
MediaStore.Images.Media.DISPLAY_NAME,
MediaStore.Images.Media.SIZE,
MediaStore.Images.Media.DATE_TAKEN,
MediaStore.Images.Media._ID,
)
val imageSortOrder = "${MediaStore.Images.Media.DATE_TAKEN} DESC"
val cursor = contentResolver.query(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
imageProjection,
null,
null,
imageSortOrder
)
cursor.use { it ->
val imagesAmount = cursor?.count
//Log.d(TAG, "queryImageStorage: $x")
it?.let { it ->
val idColumn = it.getColumnIndexOrThrow(MediaStore.Images.Media._ID)
val nameColumn = it.getColumnIndexOrThrow(MediaStore.Images.Media.DISPLAY_NAME)
val sizeColumn = it.getColumnIndexOrThrow(MediaStore.Images.Media.SIZE)
val dateColumn = it.getColumnIndexOrThrow(MediaStore.Images.Media.DATE_TAKEN)
while (it.moveToNext()) {
val id = it.getLong(idColumn)
val name = it.getString(nameColumn)
val size = it.getString(sizeColumn)
val date = it.getString(dateColumn)
val contentUri = ContentUris.withAppendedId(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
id
).toString()
// make image object and add to list if it's not there already.
//TODO: Dodac skanowanie obrazów z unused stuff
if (!list.any { Image -> Image.id == id }) {
//process the image
val inputImage =
contentUri.let { InputImage.fromFilePath(this, it.toUri()) }
val recognizer = TextRecognition.getClient(TextRecognizerOptions.DEFAULT_OPTIONS)
val result = recognizer.process(inputImage)
result.addOnSuccessListener { visionText ->
// Task completed successfully
if (visionText.toString().isNotEmpty()){
val image = Image(id, name, size, date, contentUri, visionText.text)
newList.add(image)
Log.d(TAG, "queryImageStorage: SUCCESS ${visionText.text}")
Log.d(TAG, "queryImageStorage: NEW LIST $newList")
writeListToPref(this, newList, R.string.preference_file_key.toString())
}
}
result.addOnFailureListener { e ->
Log.d(TAG, "queryImageStorage: FAILURE $e")
// Task failed with an exception
}
}
imageNumber += 1
//Updating textview with percentage
if (imagesAmount != null) {
percentageloaded = (imageNumber *100/ imagesAmount)
//Log.d(TAG, "queryImageStorage: PROCENTY $imageNumber $imagesAmount")
}
Handler(Looper.getMainLooper()).post(Runnable {
loadingTextView.text = "Loading images: $percentageloaded %"
})
try {
Thread.sleep(0,1)
} catch (e: InterruptedException) {
e.printStackTrace()
}
//Log.d(TAG, "queryImageStorage: $imageNumber")
//TODO: Działa sharedpreferences. Przy 1 uruchomienu laduje wszystko, przy kolejnych tylko nowe zdjecia. Do zrobienia Listener zeby działało płynnie.
}
}
} ?: kotlin.run {
Log.e("TAG", "Cursor is null!")
}
val intent = Intent(applicationContext, MainActivity::class.java)
startActivity(intent)
finish()
}
当我删除负责处理图像的代码并创建没有文本的图像对象时,应用程序运行正常。
图像数据class:
data class Image(val id: Long, val name: String?, val size: String?, val date: String?, val uri: String?, val text: String? = null) {
}
感谢您提供的所有提示和帮助。毕竟,我可以在它崩溃时自动重启我的应用程序,但我认为这不是一个好的解决方案:)
编辑:实际上,应用程序并没有崩溃,它只是冻结了一秒钟并且正在最小化。当我再次打开它时,应用程序继续加载图片。可能是某种内存问题?
调用 val result = recognizer.process(inputImage)
是一个异步调用。如果将太多图片放入队列中,可能会导致内存问题。
您可以启动工作线程并在发送新图像之前等待工作线程中的结果。使用 Tasks.await(task)
我正在编写一个用于在 android 智能手机上搜索模因的应用程序。我已经有了从存储中获取所有图像并将它们显示在屏幕上的代码,它工作得很好。现在,我正在尝试使用 google ML 文本识别处理每张获取的图像,并将该文本添加到存储在数组列表中的图像对象(每次处理新图像时,我都会将该列表保存在 sharedpreferences 中)。通常,我的代码可以工作,但不知何故,当有大量图像要处理时,应用程序会崩溃。我从日志中几乎看不到任何东西,没有错误。我有一次并发修改错误,但是当我添加newList时,它不再显示了。
但是,我可以再次启动该应用程序,它会继续加载图像,直到下一次崩溃。好像处理了几百张图片就崩溃了
这是从设备存储中获取和处理图像的完整函数(抱歉代码乱七八糟,我还不是专业人士):
//Function for fetching all the data and creating Image objects, which are returned in a list.
//I will pass them to the main activity and show on the screen
@SuppressLint("SetTextI18n")
@RequiresApi(Build.VERSION_CODES.Q)
private fun queryImageStorage() {
var imageNumber = 0
var percentageloaded = 0
val list = readListFromPref(this, R.string.preference_file_key.toString()).toList()
Log.d(TAG, "queryImageStorage: ROZMIAR ${list.size}")
val newList = readListFromPref(this, R.string.preference_file_key.toString())
val imageProjection = arrayOf(
MediaStore.Images.Media.DISPLAY_NAME,
MediaStore.Images.Media.SIZE,
MediaStore.Images.Media.DATE_TAKEN,
MediaStore.Images.Media._ID,
)
val imageSortOrder = "${MediaStore.Images.Media.DATE_TAKEN} DESC"
val cursor = contentResolver.query(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
imageProjection,
null,
null,
imageSortOrder
)
cursor.use { it ->
val imagesAmount = cursor?.count
//Log.d(TAG, "queryImageStorage: $x")
it?.let { it ->
val idColumn = it.getColumnIndexOrThrow(MediaStore.Images.Media._ID)
val nameColumn = it.getColumnIndexOrThrow(MediaStore.Images.Media.DISPLAY_NAME)
val sizeColumn = it.getColumnIndexOrThrow(MediaStore.Images.Media.SIZE)
val dateColumn = it.getColumnIndexOrThrow(MediaStore.Images.Media.DATE_TAKEN)
while (it.moveToNext()) {
val id = it.getLong(idColumn)
val name = it.getString(nameColumn)
val size = it.getString(sizeColumn)
val date = it.getString(dateColumn)
val contentUri = ContentUris.withAppendedId(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
id
).toString()
// make image object and add to list if it's not there already.
//TODO: Dodac skanowanie obrazów z unused stuff
if (!list.any { Image -> Image.id == id }) {
//process the image
val inputImage =
contentUri.let { InputImage.fromFilePath(this, it.toUri()) }
val recognizer = TextRecognition.getClient(TextRecognizerOptions.DEFAULT_OPTIONS)
val result = recognizer.process(inputImage)
result.addOnSuccessListener { visionText ->
// Task completed successfully
if (visionText.toString().isNotEmpty()){
val image = Image(id, name, size, date, contentUri, visionText.text)
newList.add(image)
Log.d(TAG, "queryImageStorage: SUCCESS ${visionText.text}")
Log.d(TAG, "queryImageStorage: NEW LIST $newList")
writeListToPref(this, newList, R.string.preference_file_key.toString())
}
}
result.addOnFailureListener { e ->
Log.d(TAG, "queryImageStorage: FAILURE $e")
// Task failed with an exception
}
}
imageNumber += 1
//Updating textview with percentage
if (imagesAmount != null) {
percentageloaded = (imageNumber *100/ imagesAmount)
//Log.d(TAG, "queryImageStorage: PROCENTY $imageNumber $imagesAmount")
}
Handler(Looper.getMainLooper()).post(Runnable {
loadingTextView.text = "Loading images: $percentageloaded %"
})
try {
Thread.sleep(0,1)
} catch (e: InterruptedException) {
e.printStackTrace()
}
//Log.d(TAG, "queryImageStorage: $imageNumber")
//TODO: Działa sharedpreferences. Przy 1 uruchomienu laduje wszystko, przy kolejnych tylko nowe zdjecia. Do zrobienia Listener zeby działało płynnie.
}
}
} ?: kotlin.run {
Log.e("TAG", "Cursor is null!")
}
val intent = Intent(applicationContext, MainActivity::class.java)
startActivity(intent)
finish()
}
当我删除负责处理图像的代码并创建没有文本的图像对象时,应用程序运行正常。
图像数据class:
data class Image(val id: Long, val name: String?, val size: String?, val date: String?, val uri: String?, val text: String? = null) {
}
感谢您提供的所有提示和帮助。毕竟,我可以在它崩溃时自动重启我的应用程序,但我认为这不是一个好的解决方案:)
编辑:实际上,应用程序并没有崩溃,它只是冻结了一秒钟并且正在最小化。当我再次打开它时,应用程序继续加载图片。可能是某种内存问题?
调用 val result = recognizer.process(inputImage)
是一个异步调用。如果将太多图片放入队列中,可能会导致内存问题。
您可以启动工作线程并在发送新图像之前等待工作线程中的结果。使用 Tasks.await(task)