从 LED 面板中提取文本

问题描述 投票:0回答:1

我需要提取下图中显示的千克(kg)值:

enter image description here

我手动裁剪图像以隔离文本部分,并应用了多种图像处理技术,例如灰度转换、阈值处理、高斯模糊和膨胀。然而,结果并没有我想象的那么清晰,Tesseract OCR 无法读取它们。以下是一些处理后的图像:

enter image description here enter image description here enter image description here

我目前正在使用 EmguCV 和 Tesseract,并尝试了各种 tesseract 模型,包括

tessdata_best
(英文)、
lets
letsgodigital
。不幸的是,这些尝试都没有成功。

使用的具体语言或库并不重要,因为我计划将解决方案转换为 C#。最终的实现将是使用 Xamarin.Forms 的移动应用程序。

以下是我使用但没有成功的示例方法:

public static void Apply()
        {
            var folderName = "letsgodigital";
            var dataname = "letsgodigital";

            string tesseractPath = @$"./{folderName}";
            string imagePath = @"img.jpg";

            Mat image = CvInvoke.Imread(imagePath, ImreadModes.Color);


            Mat blurredImg = new Mat();
            CvInvoke.Blur(image, blurredImg, new Size(9, 9), new Point(-1, -1));

            Mat grayImg = new Mat();
            CvInvoke.CvtColor(blurredImg, grayImg, ColorConversion.Bgr2Gray);

            Mat binaryImg = new Mat();
            CvInvoke.Threshold(grayImg, binaryImg, 122, 255, ThresholdType.Binary);

            binaryImg.Save("full_pannel_bw.png");

            using (var engine = new TesseractEngine(tesseractPath, dataname, EngineMode.Default))
            {
                engine.DefaultPageSegMode = PageSegMode.SingleLine;

                using (var img = Pix.LoadFromFile("full_pannel_bw.png"))
                {
                    using (var page = engine.Process(img))
                    {
                        string text = page.GetText();
                        Console.WriteLine("tesseract got: \"{0}\"", text.Trim());
                    }
                }
            }
        }

编辑我的最终流程 但 tesseract 无法读取它。我得到空文本。现在我正在尝试使图像上的文字变暗

enter image description here

static void loggg()
        {
            Mat img = CvInvoke.Imread("5.jpg", ImreadModes.Color);

            VectorOfMat channels = new VectorOfMat();
            CvInvoke.Split(img, channels);

            Mat redChannel = new Mat();
            CvInvoke.Subtract(channels[2], channels[1], redChannel);
            CvInvoke.Subtract(redChannel, channels[0], redChannel);

            CvInvoke.Threshold(redChannel, redChannel, 40, 255, ThresholdType.Binary);


            Mat invertedRedChannel = new Mat();
            CvInvoke.BitwiseNot(redChannel, invertedRedChannel);
            Mat morphKernel = CvInvoke.GetStructuringElement(ElementShape.Rectangle, new Size(2, 2), new Point(-1, -1));
            CvInvoke.MorphologyEx(invertedRedChannel, invertedRedChannel, MorphOp.Close, morphKernel, new Point(-1, -1), 1, BorderType.Constant, new MCvScalar(255));

             Mat dilateKernel = CvInvoke.GetStructuringElement(ElementShape.Rectangle, new Size(1, 1), new Point(-1, -1));
            CvInvoke.Dilate(invertedRedChannel, invertedRedChannel, dilateKernel, new Point(-1, -1), 1, BorderType.Constant, new MCvScalar(0));

            invertedRedChannel.Save("darker_red_text.jpg");

            img.Dispose();
            redChannel.Dispose();
            invertedRedChannel.Dispose();
            channels.Dispose();
        }
c# image-processing ocr tesseract image-preprocessing
1个回答
0
投票

首先对图片进行一些评论:

  • 它是升级版。数字分辨率为 2496 x 3328 像素,但光学分辨率大约低 3.3 倍。上采样的像素清晰可辨。
  • 它是饱和。 LED 面板中一些最亮的“红色”像素并不是真正的红色,而是白色。该机制很复杂,可能有多种效应在起作用。光线渗入相邻像素(其他颜色),当光线充足时,这种现象会变得明显。

建议:

  • 不要使用“数码变焦”。反正不是这个类型。它对你没有任何好处。
  • 调低物理曝光,即缩短曝光时间和/或缩小光圈。数字滤波器(增益、事后调整亮度/对比度)不会起任何作用。您应该注意 LED 面板是否在动态范围内,即不要使传感器饱和。如果做得正确,所有周围环境看起来都是黑暗的,包括 LED 面板的黑暗部分。

ROI

显示的文字明亮且呈红色。您可以利用这两个属性。所以你会选择图片的红色通道,然后选择阈值。

这只是红色通道:

red channel

我将应用“伽玛”映射,它是非线性的。这是一种可以尝试的事情,如果结果更好的话就保留它。如果它是线性的,它不会做太多事情,无论如何都会达到一个阈值(稍后出现)。

red ** (1/0.45)

面板的深色 LED 看起来仍然相当亮(水平约为 0.25),但不如以前那么亮(约为 0.5)。人们可以应用替代或附加映射来使面板的黑暗部分变得更暗。

这已经构成了某种阈值......手动选取的值。

more adjustment

现在您还可以看到 LED 以及字母中它们之间的空格。我将应用低通滤波器来平滑它。这将有助于阈值处理,因为这些“异常值”的字母内部和外部不会有“噪音”。

lowpassed

对于阈值处理,尝试 Otsu 等自动算法通常是个好主意。在解决这个问题时,大津经常给我一些导致字母连接的阈值,所以我大部分时间都使用手动选择的阈值。通过额外的对比度拉伸,所有字母之间实际上只留下黑色(见最后一张图片),大津“工作”得足够好。

mask from Otsu

我认为即使对于简单的旧 Tesseract OCR 来说,这看起来也足够好了。如果需要反转,就反转即可。

inverted mask


这里有一些 Python,使用 OpenCV 函数,即使在第三方 C# 绑定中,这些函数也应该是等效的。

我立即转换为浮点数。如果我超出“通常”值范围(即值可以低于 0 并超过 255/1.0),这可以防止数字被剪切或环绕。这对于一些数学计算也很方便。

imshow()
将浮点数解释为从 0.0 到 1.0 的范围,但
imwrite()
只是转换为整数,因此您必须缩小范围。

im = cv.imread("QsvdNqcn.jpg")

# convert to float32 and scale to 0.0 .. 1.0
im = im * np.float32(1/255) # Mat::convertTo() with rtype=CV_32F and alpha=1.0/255.0

# getting a region
(x,y,w,h) = 763, 1281, 1167, 388
im = im[y:y+h, x:x+w] # Mat::operator()(cv::Rect)

(blue, green, red) = cv.split(im)

red_linear = red ** (1/0.45) # cv::pow()

# more contrast stretching to make "dark" parts darker
vmin, vmax = 0.7, 1.0
red_linear = (red_linear - vmin) / (vmax - vmin) # cv::Mat in C++ supports such expressions too

lowpassed = cv.GaussianBlur(red_linear, None, sigmaX=4.0)

(th, mask) = cv.threshold(lowpassed, 0.25, 1.0, cv.THRESH_BINARY)
# with Otsu, that'd take converting back to uint8 ranged 0..255
# (th, mask) = cv.threshold(np.clip(lowpassed * 255, 0, 255).astype(np.uint8), 128, 255, cv.THRESH_BINARY | cv.THRESH_OTSU)
© www.soinside.com 2019 - 2024. All rights reserved.