You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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无法从中提取有效文字。必须获取完整尺寸的图片:

    1. 先创建临时文件保存拍摄的图片
    2. 通过MediaStore.EXTRA_OUTPUT指定图片保存路径
    3. 拍摄完成后从该路径加载完整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() // 释放资源
    }
    
  • 语言包复制与初始化问题

    1. 检查eng.traineddata是否正确放置在assets/tesseract/tessdata/目录下
    2. 复制完成后校验文件大小,避免重复复制或复制不完整:
      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()
          }
      }
      
    3. 确认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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 12:59:14