65.9K
CodeProject 正在变化。 阅读更多。
Home

条形码的替代方案

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.53/5 (9投票s)

Mar 23, 2008

BSD

3分钟阅读

viewsIcon

45424

downloadIcon

1009

一种将数字表示为页面上打印的方块的二进制字符串的方法。

引言

dnnScanFree 项目(一个用于创建免费且廉价的测试程序的开源项目)面临的挑战是创建一种指示参加考试的学生的 方法。预先在试卷上打印学生编号将允许评分程序对试卷进行评分,并自动更新学生的记录。这将节省大量时间。

自然而然地,考虑了条形码,并且 CodeProject 上提供了一个示例代码。但是,如果扫描不直,条形码将无法正确读取。 CodeProject 上有一篇关于图像去斜的文章;然而,它非常占用资源。

dnnScanFree 项目决定使用一种简单的方法:将数字转换为页面上打印的方块字符串。然后,将使用与用于对考试进行评分相同的技术读取这些方块。

程序

启动程序,从菜单中选择“BinaryCode”,然后选择“创建”。

弹出一个框,允许您输入一个七位数字。

该数字将出现在屏幕上,以及它的二进制表示形式和右上角的一个方块,该方块将用于在读取图像扫描时检测起始点。

选择“文件”,然后选择“打印”,将允许您打印图像并对其进行扫描

扫描图像后,可以通过使用“文件”,然后选择“打开”来打开图像进行读取。红色方框将绘制在每个识别区域周围(基于检测到的起始点)。一个弹出框将指示识别出的数字。

处理倾斜

图像扫描或打印完全笔直的情况很少见。以下显示了当图像向右倾斜时如何检测相同的图像

如果图像向左倾斜,则检测起始点的代码会意识到存在错误,因为它会计算一个 20 像素方块中的黑色像素。如果计数不匹配,它会假设左倾斜并在 X 轴上减去像素。

// Count the pixels in the starting point to see if the scan will be readable
int intPixels = CountBlackPixelsInRectangle(m_Bitmap, StartingPoint.X, 
                                            StartingPoint.Y, 20, 20);
if (intPixels < 120)
{
 DrawARedRectangle(m_Bitmap, StartingPoint);
 StartingPoint.X = StartingPoint.X - 25;
 MessageBox.Show(String.Format("Scan is skewed. Only {0} pixels found " + 
                 "at the starting point. StartingPoint will be reset to {1}", 
                 intPixels.ToString(), StartingPoint.X.ToString()));
}

图像在倾斜时仍然可读。

修复倾斜

虽然方框可以在页面左侧倾斜时被读取,但倾斜在页面右侧变得更糟。 有必要实现一种对图像进行去斜的方法。 关于图像去斜的 CodeProject 文章不起作用,因为它的算法对图像感到困惑。 它也不适用于所有图像类型,并且速度显着降低。

然而,它确实提供了一个非常有用的RotateImage方法。 这非常有帮助,因为Bitmap通常只能使用枚举以 90 度增量旋转。 该项目需要更精细的旋转。

对图像进行去斜的过程很简单。 检测到FirstStartingPointSecondStartingPoint,并且这些值与Bitmap图像一起传递给StraightenImage方法,该方法将图像拉直。

Point FirstStartingPoint = FindStartingPoint1(m_Bitmap, BoxSize);
if (FirstStartingPoint.IsEmpty)
{
 MessageBox.Show("Scan is unreadable");
 return;
}

Point SecondStartingPoint = FindStartingPoint2(m_Bitmap, 
                            FirstStartingPoint, 2158, BoxSize);
if (SecondStartingPoint.IsEmpty)
{
 MessageBox.Show("Scan is unreadable");
 return;
}

// Straighten Image
m_Bitmap = StraightenImage(m_Bitmap, FirstStartingPoint, SecondStartingPoint, BoxSize);

StraightenImage方法的代码是

private Bitmap StraightenImage(Bitmap b, Point FirstStartingPoint, 
               Point SecondStartingPoint, int BoxSize)
{
    int intCompletelyBlackSquare = (BoxSize * 85);
    int intTmpPixels = CountBlackPixelsInRectangle(b, SecondStartingPoint, BoxSize, BoxSize);

    // Possibly rotate the image
    if (intTmpPixels < (intCompletelyBlackSquare - (BoxSize * 2)))
    {
        // The difference between a completely black square and the number of pixels found
        int intPixelsOff = intCompletelyBlackSquare - intTmpPixels;

        // Make a reading from a higher point to determine
        // if the scan is skewed to the left or the right
        Point tmpStartingPoint = new Point(SecondStartingPoint.X, 
                                           SecondStartingPoint.Y + 10);
        // Count the pixels in the new point
        int intTmpPixels2 = CountBlackPixelsInRectangle(b, tmpStartingPoint, 
                                                        BoxSize, BoxSize);

        // Determine if the scan is skewed to the left or the right
        float fOfsetPercentage;
        int intPixelSkew = intTmpPixels - intTmpPixels2;
        if (intPixelSkew > 0)
        {
            // intPixelSkew is a negative number so the image is skewed to the left
            fOfsetPercentage = ((float)intPixelsOff / (float)intCompletelyBlackSquare);
        }
        else
        {
            // intPixelSkew is a negative number so the image is skewed to the right
            fOfsetPercentage = ((float)intPixelsOff / 
                                (float)intCompletelyBlackSquare) * (float)-1;
        }

        // Rotate the scan based on the number of pixels and the direction ofthe skew
        b = RotateImage(b, fOfsetPercentage);

    }
    return b;
}

读取数字

创建方框是一个简单的过程。 在弹出框中输入一个数字,然后将其转换为 0 和 1 的字符串。

private void createToolStripMenuItem_Click(object sender, EventArgs e)
{
 //Create Blank Image
 m_Bitmap = new Bitmap(800, 600);

 //Create starting marker
 StartingPoint = new Point(10, 30);
 DrawARectangle(m_Bitmap, StartingPoint, true);
 string strNumber = "5234567";

 strNumber = Microsoft.VisualBasic.Interaction.InputBox("Enter a seven digit number", 
                                   "Enter Number", strNumber, 100, 100);
 string strBinaryNumber = ConvertToBinaryNumber(strNumber);
 int intWidth = 50;

 for (int i = 0; i <= 27; i++)
 {
  intWidth = intWidth + 10;
  StartingPoint = new Point(intWidth, 50);
  DrawARectangle(m_Bitmap, StartingPoint, (strBinaryNumber.Substring(i, 1) == "1"));
 }
 StartingPoint = new Point(50, 100);
 DrawText(m_Bitmap, StartingPoint, String.Format("Number: {0}", strNumber));
}

ConvertToBinaryNumber方法调用GetBinaryNumber方法来创建二进制字符串。

private string GetBinaryNumber(string p)
{
 string strBinaryNumber = "";
 switch (p)
 {
  case "0":
    strBinaryNumber = "0000";
    break;
  case "1":
    strBinaryNumber = "0001";
    break;
  case "2":
    strBinaryNumber = "0010";
    break;
  case "3":
    strBinaryNumber = "0011";
    break;
  case "4":
    strBinaryNumber = "0100";
    break;
  case "5":
    strBinaryNumber = "0101";
    break;
  case "6":
    strBinaryNumber = "0110";
    break;
  case "7":
    strBinaryNumber = "0111";
    break;
  case "8":
    strBinaryNumber = "1000";
    break;
  case "9":
    strBinaryNumber = "1001";
    break;
  }
 return strBinaryNumber;
}

读取图像时,该过程会反过来进行。

// Use StartingPoint to read each block and count the pixels
// and determine if the block is marked or not
string[] Answers = ReadBlocks(StartingPoint);
// Read the string of 0's and 1's and convert to decimal
string strNumber = ConvertAnswersToBinary(Answers);
MessageBox.Show(strNumber);

为什么不直接使用条形码?

条形码是一项伟大的技术,但是,它们被设计为使用特殊的条形码阅读器进行读取。 它们也被设计为清晰地打印,而不是从可能重新打印、传真和扫描的图像中读取。 虽然在这些条件下它们可能是可读的,但这是试图使一项技术在设计上不适用的条件下工作。

该项目提出了一种备选技术,该技术旨在在不利条件下读取。

© . All rights reserved.