优化算法以提高速度 - 从文件扫描图像

4

我的代码能够运行,但处理较大文件时需要花费更多时间。我想知道是否有人能够检查我的代码并提出任何改进建议,以使其更快。

目的:

这是为了扫描PDF文件并搜索QR码的位图图像,并返回其代码(解码)。

private void ScanQRPdf(string imagePath)
    {
        foreach (var item in Directory.GetFiles(imagePath))
        {
            if (Path.GetExtension(item).ToLower() == ".png")
            {
                Bitmap b = new Bitmap(imagePath);
                try
                {
                    QRCodeDecoder decoder = new QRCodeDecoder();
                    String decodedString = decoder.decode(new QRCodeBitmapImage(b));
                    rtbpdfData.Text += decodedString + "\n";
                }
                catch (Exception ex)
                {
                }
            }
        }
    }

 static void AddQRTag(PdfSharp.Drawing.XGraphics gfx, int xPosition, int yPosition, string QRdata, string HRdata)
    {
        gfx.DrawRectangle(XBrushes.White, xPosition, yPosition, xPosition + 55, yPosition + 85);

        PdfSharp.Drawing.XImage xImage =
            PdfSharp.Drawing.XImage.FromGdiPlusImage(BuildQR(QRdata.ToUpper(), 3,
                                            QRCodeEncoder.ENCODE_MODE.ALPHA_NUMERIC, 2, QRCodeEncoder.ERROR_CORRECTION.M));
        gfx.DrawImage(xImage, xPosition + 5, yPosition + 5, xImage.PixelWidth * .8, xImage.PixelWidth * .8);


        XFont font = new XFont("OCR B", 6);
        XTextFormatter tf = new XTextFormatter(gfx);
        tf.Alignment = XParagraphAlignment.Left;


        XRect layout = new XRect(xPosition + 5, yPosition + 55, 55, 30);
        tf.DrawString(HRdata.ToUpper(), font, XBrushes.Black, layout, XStringFormats.TopLeft);
    }

3
慢速代码可能位于“QRCodeDecoder”类中。从这里你无法做太多事情。 - Rawling
1
有些问题需要问自己:解码器是否处理了你的位图? - Grant Thomas
1
建议:移除 catch{} - H H
你能发布QRCodeDecoder源代码的链接吗?(我假设你正在使用操作系统库) - vgru
3
获取性能分析器,它会告诉你瓶颈在哪里。猜测通常是浪费时间的。 - Eric Lippert
4个回答

3
在代码中,你所贴的一切都是正常的。问题必须出在QRCodeDecoder.decode函数上。如果你通过Bitmap.GetPixel函数一个像素一个像素地扫描图像,将会浪费很多时间。更好的方式是使用不安全的代码并将位图转换为BitmapData。

如果这是一个开源库,那么GetPixel可能是OP最好的选择(除非他/她想设计更好的算法)。+1 - vgru
谢谢提供信息 - 有没有办法只扫描/检查文件的角落是否有位图? - BB987
顺便提一下, rgbValues[counter] - 代表红色; rgbValues[counter+1] - 代表绿色; rgbValues[counter+2] - 代表蓝色; - Vahagn Nahapetyan
@BB987 如果是这样,您可能想要使用Bitmap.Clone(Rectangle, PixelFormat)方法将图像的部分克隆到4个单独的位图中,然后逐个处理它们。顺便说一句,不要忘记处理您的初始位图和所有随后创建的位图,因为它们通常会消耗大量内存。 - vgru
@BB987:就像我所写的那样,你应该像你已经在做的那样将整个文件加载到一个“Bitmap”中,然后使用“b.Clone”来提取单独的“Bitmap”。 - vgru
显示剩余5条评论

2

尝试以下几点:

  1. 按照Carra的建议,通过文件扩展名过滤文件
  2. 只声明和实例化一次QRCodeDecoder
  3. 使用StringBuilder追加文本并仅分配一次

大概是这样的:

private void ScanQRPdf(string imagePath)
{
    var files = Directory.GetFiles ( path, "*.png", SearchOption.AllDirectories );

    QRCodeDecoder decoder = new QRCodeDecoder();

    StringBuilder sb = new StringBuilder();

    foreach (var item in files)
    {

            Bitmap b = new Bitmap(imagePath);
            try
            {
                String decodedString = decoder.decode(new QRCodeBitmapImage(b));

                sb.AppendLine(decodedString);
            }
            catch (Exception ex)
            {
            }

    }

    rtbpdfData.Text = sb.ToString();
}

但我认为这并不能解决你的问题,这只是一些小的增强,而你的延迟肯定是在QRCodeDecoderQRCodeBitmapImage类中某个地方,具体来说是在decode方法中,你应该尽力理解它们,并找出内部实际执行的操作,以此来改进你的代码。


1
我认为在没有进行分析的情况下不应该做出这样的建议。在这种情况下,我非常确定decode方法占据了至少95%的执行时间。 - vgru
我刚试了一下,还是有点慢。我在想如果只扫描/检查页面的一个角落上的QR码,是否能够节省时间。QR码总是在左上角,如果找不到它,我会旋转页面并进行检查。这听起来可行吗? - BB987
是的,这是可行的,但你需要找出如何切分PDF文件。 - RMalke

2

根据您的评论,如果您只需要处理图像的左上角,则可以使用 Bitmap.Clone 提取图像的该部分。

在这种情况下,我会将您的代码重构为以下内容:

private void ScanQRPdf(string imagePath)
{
    foreach (var decodedString in DecodeAllImagesInFolder(imagePath))
    {
        rtbpdfData.Text += decodedString + "\n";
    }
}

private static IEnumerable<string> DecodeAllImagesInFolder(string imagePath)
{
    foreach (var item in Directory.GetFiles(imagePath, "*.png"))
    {
        using (Bitmap b = new Bitmap(imagePath))
        {
            yield return DecodeTopLeftCorner(b);
        }
    }
}

private static string DecodeTopLeftCorner(Bitmap bitmap)
{
    var rect = new Rectangle(0, 0, 100, 100);
    using (var topLeft = bitmap.Clone(rect, bitmap.PixelFormat))
    {
        return new QRCodeDecoder().decode(new QRCodeBitmapImage(topLeft));
    }
}

我只是在尝试这个,但是出现了“检测到无效的查找器模式数量”错误,我正在调试以查看源代码。 - BB987
@BB987:请注意,我选择的矩形大小完全是任意的。你应该检查它是否裁剪了整个代码。而且,正如@Henk在评论中提到的那样,你之前一直在吞噬异常,这通常是一个不好的做法。可能decode方法在找不到代码时会抛出此异常,但我没有查看其源代码就无法确定。 - vgru
我看到了,我正在进行一些修改和查看。同时,我想到了一个问题。由于我只查看用户传入的单个文件,那么我是否仍然需要一个目录来获取所有文件呢?我不能只使用那个单个文件吗? - BB987
@BB987:当然,为什么不呢?如果你已经知道文件名,就没有必要使用Directory.GetFiles了。 - vgru
那我把foreach循环移除,将imagepath插入到using语句中,这样做是安全的吗?using(Bitmap b = new Bitmap(imagePath) - BB987
@BB987:是的,那就是你应该做的。 - vgru

1
您可以使用带有类型的 GetFiles:
string[ ] files = Directory.GetFiles ( path, "*.png", SearchOption.AllDirectories ); 

我怀疑这不是瓶颈所在。 - vgru
你说得对,这可能只是一个小改进,取决于文件数量。但瓶颈可能在解码器中,我们看不到那部分。 - Carra

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接