条形码的替代方案






3.53/5 (9投票s)
一种将数字表示为页面上打印的方块的二进制字符串的方法。
引言
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 度增量旋转。 该项目需要更精细的旋转。
对图像进行去斜的过程很简单。 检测到FirstStartingPoint
和SecondStartingPoint
,并且这些值与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);
为什么不直接使用条形码?
条形码是一项伟大的技术,但是,它们被设计为使用特殊的条形码阅读器进行读取。 它们也被设计为清晰地打印,而不是从可能重新打印、传真和扫描的图像中读取。 虽然在这些条件下它们可能是可读的,但这是试图使一项技术在设计上不适用的条件下工作。
该项目提出了一种备选技术,该技术旨在在不利条件下读取。