Android项目集成Google ML OCR Tesseract出现识别乱码问题求助
Tesseract OCR识别乱码问题排查方案
我在Android项目中集成Google ML OCR Tesseract时,拍摄图片后识别出乱码文本(如m”;-.v__m-~_;;;;;;§'F"WWW If?),以下是实现代码,请求排查问题所在:
class MainActivity : ComponentActivity() { private lateinit var tessBaseAPI: TessBaseAPI private lateinit var imageView: ImageView private lateinit var textView: TextView private val requestImageCapture = 1 private val takePicture = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result -> if (result.resultCode == RESULT_OK) { val resultInfo = result.data?.extras?.get("data") val imageBitmap = result.data?.extras?.get("data") as Bitmap? imageBitmap?.let { imageView.setImageBitmap(it) recognizeText(it) } } } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) imageView = findViewById(R.id.imageView) textView = findViewById(R.id.textView) val captureButton: Button = findViewById(R.id.captureButton) copyLanguageDataToInternalStorage() initTesseract() captureButton.setOnClickListener { dispatchTakePictureIntent() } } private fun initTesseract() { val datapath = "${filesDir}/tesseract/" val dir = File(datapath) if (!dir.exists()) { dir.mkdirs() } val datafilepath = "${datapath}/tessdata/" val filesDir = File(datafilepath) if (!filesDir.exists()) { filesDir.mkdirs() } Log.d("dataPath", "data: $datapath") tessBaseAPI = TessBaseAPI() tessBaseAPI.init(datapath, "eng") } private fun copyLanguageDataToInternalStorage() { Log.d("test", "testing") try { val assetManager = assets val inputStream = assetManager.open("tesseract/tessdata/eng.traineddata") val outputStream = FileOutputStream(File(filesDir, "tesseract/tessdata/eng.traineddata")) inputStream.use { input -> outputStream.use { output -> input.copyTo(output) } } } catch (e: IOException) { e.printStackTrace() } } private fun dispatchTakePictureIntent() { val takePictureIntent = Intent(MediaStore.ACTION_IMAGE_CAPTURE) if (takePictureIntent.resolveActivity(packageManager) != null) { takePicture.launch(takePictureIntent) } } private fun recognizeText(bitmap: Bitmap) { tessBaseAPI.setImage(bitmap) val recognizedText = tessBaseAPI.getUTF8Text() textView.text = recognizedText } override fun onDestroy() { super.onDestroy() tessBaseAPI.end() }
核心问题排查与修复方案
拍摄图片分辨率过低
MediaStore.ACTION_IMAGE_CAPTURE返回的"data"字段只是缩略图,分辨率极低,Tesseract无法从中提取有效文字。必须获取完整尺寸的图片:- 先创建临时文件保存拍摄的图片
- 通过
MediaStore.EXTRA_OUTPUT指定图片保存路径 - 拍摄完成后从该路径加载完整Bitmap
修改后的
dispatchTakePictureIntent及相关代码示例:private lateinit var photoFile: File private fun dispatchTakePictureIntent() { val takePictureIntent = Intent(MediaStore.ACTION_IMAGE_CAPTURE) photoFile = createImageFile() photoFile?.also { val photoURI = FileProvider.getUriForFile( this, "${packageName}.fileprovider", it ) takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI) takePictureIntent.resolveActivity(packageManager)?.let { takePicture.launch(takePictureIntent) } } } private fun createImageFile(): File { val timeStamp: String = SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(Date()) val storageDir: File? = getExternalFilesDir(Environment.DIRECTORY_PICTURES) return File.createTempFile( "JPEG_${timeStamp}_", ".jpg", storageDir ) } // 修改回调逻辑加载完整图片 private val takePicture = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result -> if (result.resultCode == RESULT_OK) { val fullBitmap = BitmapFactory.decodeFile(photoFile.absolutePath) imageView.setImageBitmap(fullBitmap) recognizeText(fullBitmap) } }图片未做预处理
Tesseract对输入图片要求较高,彩色图、低对比度图片都会导致识别失败。需要将Bitmap转为灰度图并做二值化处理:private fun preprocessBitmap(bitmap: Bitmap): Bitmap { // 转为灰度图 val grayBitmap = Bitmap.createBitmap(bitmap.width, bitmap.height, Bitmap.Config.ARGB_8888) val canvas = Canvas(grayBitmap) val paint = Paint() val colorMatrix = ColorMatrix() colorMatrix.setSaturation(0f) val filter = ColorMatrixColorFilter(colorMatrix) paint.colorFilter = filter canvas.drawBitmap(bitmap, 0f, 0f, paint) // 二值化处理 val threshold = 127 val pixels = IntArray(grayBitmap.width * grayBitmap.height) grayBitmap.getPixels(pixels, 0, grayBitmap.width, 0, 0, grayBitmap.width, grayBitmap.height) for (i in pixels.indices) { val gray = (pixels[i] and 0xff) pixels[i] = if (gray > threshold) 0xffffffff.toInt() else 0xff000000.toInt() } grayBitmap.setPixels(pixels, 0, grayBitmap.width, 0, 0, grayBitmap.width, grayBitmap.height) return grayBitmap }在
recognizeText中调用预处理:private fun recognizeText(bitmap: Bitmap) { val processedBitmap = preprocessBitmap(bitmap) tessBaseAPI.setImage(processedBitmap) val recognizedText = tessBaseAPI.getUTF8Text() textView.text = recognizedText processedBitmap.recycle() // 释放资源 }语言包复制与初始化问题
- 检查
eng.traineddata是否正确放置在assets/tesseract/tessdata/目录下 - 复制完成后校验文件大小,避免重复复制或复制不完整:
private fun copyLanguageDataToInternalStorage() { try { val assetManager = assets val destFile = File(filesDir, "tesseract/tessdata/eng.traineddata") // 如果文件已存在且大小一致,跳过复制 if (destFile.exists()) { val assetFd = assetManager.openFd("tesseract/tessdata/eng.traineddata") if (destFile.length() == assetFd.length) { assetFd.close() return } assetFd.close() } val inputStream = assetManager.open("tesseract/tessdata/eng.traineddata") val outputStream = FileOutputStream(destFile) inputStream.use { input -> outputStream.use { output -> input.copyTo(output) } } } catch (e: IOException) { e.printStackTrace() } } - 确认Tesseract初始化成功,添加日志校验:
private fun initTesseract() { val datapath = "${filesDir}/tesseract/" val tessDataFile = File("${datapath}/tessdata/eng.traineddata") if (!tessDataFile.exists()) { Log.e("Tesseract", "语言包文件不存在") return } val dir = File(datapath) if (!dir.exists()) dir.mkdirs() val filesDir = File("${datapath}/tessdata/") if (!filesDir.exists()) filesDir.mkdirs() tessBaseAPI = TessBaseAPI() val initSuccess = tessBaseAPI.init(datapath, "eng") Log.d("Tesseract", "初始化结果: $initSuccess") }
- 检查
内容的提问来源于stack exchange,提问作者Vijay Chandan Kandadai
相关产品推荐
相关产品推荐

