草庐IT

android - 改进 Tesseract 的阈值结果

coder 2023-12-29 原文

我有点被这个问题困住了,我知道有很多关于堆栈溢出的问题,但就我而言。没有给出预期的结果。

上下文:

我正在使用 Android OpenCV 和 Tesseract,所以我可以读取护照中的 MRZ 区域。当相机启动时,我将输入帧传递给 AsyncTask,处理该帧,成功提取 MRZ 区域,我将提取的 MRZ 区域传递给函数 prepareForOCR(inputImage),该函数将 MRZ 区域作为灰色 Mat 并输出我将传递给 Tesseract 的带有阈值图像的位图。

问题:

问题是在对图像进行阈值处理时,我使用 blockSize = 13 和 C = 15 的自适应阈值处理,但给出的结果并不总是相同,具体取决于图像的照明和帧的一般条件采取。

我尝试过的:

首先,我将图像调整为特定大小 (871,108),以便输入图像始终相同,而不依赖于所使用的手机。 调整大小后,我尝试使用不同的 BlockSize 和 C 值

//toOcr contains the extracted MRZ area
Bitmap toOCRBitmap = Bitmap.createBitmap(bitmap);
Mat inputFrame = new Mat();
Mat toOcr = new Mat();
Utils.bitmapToMat(toOCRBitmap, inputFrame);
Imgproc.cvtColor(inputFrame, inputFrame, Imgproc.COLOR_BGR2GRAY);
TesseractResult lastResult = null;
for (int B = 11; B < 70; B++) {
    for (int C = 11; C < 70; C++){
        if (IsPrime(B) && IsPrime(C)){
            Imgproc.adaptiveThreshold(inputFrame, toOcr, 255, Imgproc.ADAPTIVE_THRESH_GAUSSIAN_C, Imgproc.THRESH_BINARY, B ,C);
            Bitmap toOcrBitmap = OpenCVHelper.getBitmap(toOcr);
            TesseractResult result = TesseractInstance.extractFrame(toOcrBitmap, "ocrba");
            if (result.getMeanConfidence()> 70) {
                if (MrzParser.tryParse(result.getText())){
                    Log.d("Main2Activity", "Best result with " + B + " : " + C);
                    return result;
                }
            }
        }
    }
}

使用下面的代码,阈值结果图像是一张黑白图像,置信度大于 70,出于隐私原因,我不能真正发布整个图像,但这里有一个剪裁的图像和一个虚拟密码图像。

使用 MrzParser.tryParse 函数添加对字符位置及其在 MRZ 内有效性的检查,我能够更正一些出现的情况,例如包含 8 而不是 B 的名称,并获得良好的结果,但需要花费很多时间,这是正常的,因为我在循环中对近 255 张图像进行了阈值处理,并添加了 Tesseract 调用。

我已经尝试获取出现次数最多的 C 和 B 值列表,但结果不同。

问题:

有没有一种方法可以定义 C 和 blocksize 值,以便它始终给出相同的结果,也许添加更多的 OpenCV 调用以便输入图像增加对比度等,我在网上搜索了 2 周,现在我可以'找到可行的解决方案,这是唯一提供准确结果的解决方案

最佳答案

您可以使用聚类算法根据颜色对像素进行聚类。字符是黑色的,并且在 MRZ 区域中有很好的对比度,因此如果将聚类方法应用于 MRZ 区域,它很可能会为您提供良好的分割。

在这里,我使用从 Internet 上可以找到的示例图像获得的 MRZ 区域进行演示。

我使用彩色图像,应用一些平滑处理,转换为 Lab 颜色空间,然后使用 kmeans (k=2) 对 a、b channel 数据进行聚类。代码在 python 中,但您可以轻松地将其改编为 java。由于 kmeans 算法的随机性,分割后的字符将具有标签 0 或 1。您可以通过检查 cluster centers 轻松地将其分类。字符对应的聚类中心在您使用的颜色空间中应该有一个 dark 值。 我刚刚在这里使用了 Lab 色彩空间。您可以使用 RGBHSV 甚至 GRAY,看看哪一个更适合您。

像这样分割后,我认为您甚至可以使用字符笔画宽度的属性为自适应阈值的 B 和 C 找到好的值(如果您认为自适应阈值提供更好质量的输出)。

import cv2
import numpy as np

im = cv2.imread('mrz1.png')
# convert to Lab
lab = cv2.cvtColor(cv2.GaussianBlur(im, (3, 3), 1), cv2.COLOR_BGR2Lab)

im32f = np.array(im[:, :, 1:3], dtype=np.float32)
k = 2 # 2 clusters
term_crit = (cv2.TERM_CRITERIA_EPS, 30, 0.1)
ret, labels, centers = cv2.kmeans(im32f.reshape([im.shape[0]*im.shape[1], -1]), 
                                  k, None, term_crit, 10, 0)
# segmented image
labels = labels.reshape([im.shape[0], im.shape[1]]) * 255

一些结果:

关于android - 改进 Tesseract 的阈值结果,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52992237/

有关android - 改进 Tesseract 的阈值结果的更多相关文章

  1. 报告回顾丨模型进化狂飙,DetectGPT能否识别最新模型生成结果? - 2

    导读语言模型给我们的生产生活带来了极大便利,但同时不少人也利用他们从事作弊工作。如何规避这些难辨真伪的文字所产生的负面影响也成为一大难题。在3月9日智源Live第33期活动「DetectGPT:判断文本是否为机器生成的工具」中,主讲人Eric为我们讲解了DetectGPT工作背后的思路——一种基于概率曲率检测的用于检测模型生成文本的工具,它可以帮助我们更好地分辨文章的来源和可信度,对保护信息真实、防止欺诈等方面具有重要意义。本次报告主要围绕其功能,实现和效果等展开。(文末点击“阅读原文”,查看活动回放。)Ericmitchell斯坦福大学计算机系四年级博士生,由ChelseaFinn和Chri

  2. 安卓apk修改(Android反编译apk) - 2

    最近因为项目需要,需要将Android手机系统自带的某个系统软件反编译并更改里面某个资源,并重新打包,签名生成新的自定义的apk,下面我来介绍一下我的实现过程。APK修改,分为以下几步:反编译解包,修改,重打包,修改签名等步骤。安卓apk修改准备工作1.系统配置好JavaJDK环境变量2.需要root权限的手机(针对系统自带apk,其他软件免root)3.Auto-Sign签名工具4.apktool工具安卓apk修改开始反编译本文拿Android系统里面的Settings.apk做demo,具体如何将apk获取出来在此就不过多介绍了,直接进入主题:按键win+R输入cmd,打开命令窗口,并将路

  3. ruby-on-rails - 我可以用鸭子类型(duck typing)改进这种方法吗? - 2

    希望我没有误解“ducktyping”的含义,但从我读到的内容来看,这意味着我应该根据对象如何响应方法而不是它是什么类型/类来编写代码。代码如下:defconvert_hash(hash)ifhash.keys.all?{|k|k.is_a?(Integer)}returnhashelsifhash.keys.all?{|k|k.is_a?(Property)}new_hash={}hash.each_pair{|k,v|new_hash[k.id]=v}returnnew_hashelseraise"CustomattributekeysshouldbeID'sorPropertyo

  4. ruby - gem 推送结果为 "package metadata is missing" - 2

    我正在尝试将我更新的gem推送到ruby​​gems.com并得到以下结果。~/dev/V2/V2GPTI(master)$gembuildv2gpti.gemspecSuccessfullybuiltRubyGemName:v2gptiVersion:0.2File:v2gpti-0.2-universal-darwin-13.gem~/dev/V2/V2GPTI(master)$gempushv2gpti.gemspecERROR:Whileexecutinggem...(Gem::Package::FormatError)packagemetadataismissinginv2g

  5. ruby - 猴子修补 float 中缀运算符产生意想不到的结果 - 2

    重新定义Float#/似乎没有效果:classFloatdef/(other)"magic!"endendputs10.0/2.0#=>5.0但是当另一个中缀运算符Float#*被重新定义时,Float#/突然采用了新的定义:classFloatdef/(other)"magic!"enddef*(other)"spooky"endendputs10.0/2.0#=>"magic!"我很想知道是否有人可以解释这种行为的来源,以及其他人是否得到相同的结果。ruby:ruby2.0.0p353(2013-11-22)[x64-mingw32]要快速确认错误,请运行thisscript.

  6. ruby-on-rails - 尝试编辑时,Rails form_for 结果为 POST 而不是 PUT - 2

    我正在使用Rails4并遇到以下错误。RoutingErrorNoroutematches[POST]"/logs/1/meals/13/edit我正在使用:meal传递模型对象的form_for,并且编辑页面正确呈现。但是,Rails似乎并没有检查膳食对象是否已经保存,因此它一直尝试将表单发送到#create操作并尝试发出POST请求,而不是将表单发送到更新操作并进行当我点击提交时一个PUT请求。我如何让form_for识别我正在尝试更新现有对象并且需要PUT而不是POST?其他一切正常,我已经运行了所有迁移。我是Rails的新手,几乎一整天都在尝试自己解决这个问题。请帮忙!请注意,

  7. ruby - 为什么会.is_a?和 .class 给出相互矛盾的结果? - 2

    我有三个属于同一个类的对象。一个是通过Item.new创建的,另外两个是从数据库(Mongoid)中提取的。我将这些对象中的一个/任何一个传递给另一个方法,并通过is_a?检查该方法中的类型:definitialize(item,attrs=nil,options=nil)super(attrs,options)raise'invaliditemobject'unlessitem.is_a?(Item)好吧,这次加薪被击中了。所以我在Rails控制台中检查类、is_a和instance_of。我得到相互矛盾的结果。为什么它们有相同的class但只有其中一个是那个class的instan

  8. ruby - 带有位串的意外解包结果 - 2

    为什么当我打开irb并运行时放'A'.unpack("B8")我得到01000001但是当我运行放'A'.unpack("B4B4")我只得到0100而不是[0100,0001]?unpack的分辨率是不是只有一个完整的字节?一点都不差? 最佳答案 让我们做一些测试来理解行为:>'A'.unpack('B8')=>["01000001"]它返回char'A'的8个最高有效位(MSB)>'A'.unpack('B4')=>["0100"]它返回char'A'的4MSBs>'A'.unpack('B16')=>["01000001"]它

  9. ruby - 为什么我会看到这两个几乎相同的 Ruby 正则表达式模式的不同结果,为什么一个匹配我认为不应该匹配的内容? - 2

    使用Ruby1.9.2,我在IRB中有以下Ruby代码:>r1=/^(?=.*[\d])(?=.*[\W]).{8,20}$/i>r2=/^(?=.*\d)(?=.*\W).{8,20}$/i>a=["password","1password","password1","pass1word","password1"]>a.each{|p|puts"r1:#{r1.match(p)?"+":"-"}\"#{p}\"".ljust(25)+"r2:#{r2.match(p)?"+":"-"}\"#{p}\""}这会产生以下输出:r1:-"password"r2:-"password"r1:

  10. ruby - 为什么 Gemfile 语义版本控制运算符 (~>) 会产生与一个数字不一致的结果? - 2

    gemspec语义版本控制运算符~>(又名twiddle-wakka,又名pessimistic运算符)允许限制gem版本但允许进行一些升级。我经常看到它可以读作:"~>3.1"=>"Anyversion3.x,butatleast3.1""~>3.1.1"=>"Anyversion3.1.x,butatleast3.1.1"但是有了一个数字,这条规则就失效了:"~>3"=>"Anyversionx,butatleast3"*NOTTRUE!*"~>3"=>"Anyversion3.x"*True.Butwhy?*如果我想要“任何版本3.x”,我可以只使用“~>3.0”,这是一致的。就

随机推荐