Click here to Skip to main content
Click here to Skip to main content

An Alternative to Barcodes

By , 26 Apr 2008
 

Introduction

The dnnScanFree project (an Open Source project to enable the creation of free and inexpensive testing programs) was faced with the challenge of creating a method to indicate the student taking the test. Pre-printing the test forms with the student number will allow the grading program to grade the test and automatically update the student's record. This will provide significant time savings.

Naturally, bar codes were considered, and an example code was available on CodeProject. However, if the scan is not straight the bar codes will not be read correctly. There is a CodeProject article on de-skewing an image; however, it is resource intensive.

The dnnScanFree project decided to use a simple method: a number would be converted to a string of boxes printed on the page. The boxes would then be read using the same technology that is being used to grade the tests.

The program

Start the program, and select BinaryCode from the menu, then Create.

A popup box will allow you to enter a seven digit number.

The number will appear on the screen along with its binary representation and a block in the upper right-hand corner that will be used to detect the starting point when the scan of the image is read.

Selecting File, then Print, will allow you to print out the image and scan it

After scanning the image, it can be read by opening the image using File, then Open. Red boxes will be drawn around each recognition zone (based on the detected starting point). A popup box will indicate the number that is recognized.

Dealing with skew

It is rare for an image to scan or print completely straight. The following shows how the same image is detected when the image is skewed to the right:

If the image is skewed to the left, the code that detects the starting point realizes there is an error because it counts the black pixels in a 20 pixel square. If the count does not match, it assumes a left skew and subtracts pixels on the X axis.

// 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()));
}

The image is still readable with the skew.

Fixing the skew

While the boxes can be read with a skew on the left hand side of the page, the skew gets worse on the right-hand side of the page. It was necessary to implement a method to de-skew the image. The CodeProject article on de-skewing an image did not work because its algorithm was confused by the image. It also does not work with all image types, and is significantly slower.

It did, however, provide a very useful RotateImage method. This was very helpful because a Bitmap normally can only be rotated in 90 degree increments using an enumeration. This project required a more granular rotation.

The process to de-skew the image is simple. FirstStartingPoint and SecondStartingPoint are detected, and those values along with the Bitmap image are passed to the StraightenImage method which straightens the image.

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);

The code for the StraightenImage method is:

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;
}

Reading the numbers

Creating the boxes is a straightforward process. A number is entered into the popup box and converted to a string of 0's and 1's.

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));
}

The GetBinaryNumber method is called by the ConvertToBinaryNumber method to create the binary string.

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;
}

When the image is read, the process is reversed.

// 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);

Why not just use bar codes?

Barcodes are a great technology, however, they were designed to be read with a special bar code reader. They were also designed to be printed clearly, not read from possibly reprinted, faxed, and scanned images. While they may be readable under these conditions, it is an attempt to make a technology work under conditions it was not designed for.

This project presents an alternative technology that is designed to be read under adverse conditions.

License

This article, along with any associated source code and files, is licensed under The BSD License

About the Author

defwebserver
Software Developer (Senior) http://ADefWebserver.com
United States United States
Member
Michael Washington is a Microsoft Silverlight MVP. He is a Silverlight developer and an ASP.NET, C#, and Visual Basic programmer.
 
He is a DotNetNuke Core member and has been involved with DotNetNuke for over 4 years. He is the Co-Author of Building Websites with DotNetNuke (4 and 5).
 
He is one of the founding members of The Open Light Group (http://openlightgroup.net).
 
He is the founder of http://LightSwitchHelpWebsite.com
 
He has a son, Zachary and resides in Los Angeles with his wife Valerie.

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
GeneralAnother great image rotation optioneditorSmitha Vijayan23 Jul '08 - 6:06 
This is another great image rotation code available here in CodeProject:
Image Rotation in .NET by James T Johnson[^]
 
Thought you might find it useful!
 
Cheers
Smitha
 
Are you an aspiring author? Read how to submit articles to CodeProject: Article Submission Guidelines[^]
 
More questions? Ask an editor here...

GeneralDon't fear barcodes!memberyrodrigu23 Mar '08 - 12:33 
I like what you're doing here. It's a neat exercise in both encoding and fuzzy logic and I'm sure you learned a lot from this.
 
At the same time, I've worked with barcodes for a long time in industry, and there's a reason they're everywhere. They're easy to print, using even crude dot matrix impact printers; they're very compact (e.g. Code128) and more recently with 2-D barcodes you can put a lot of information in a very small space (e.g. 1kB in 1 sq in); they have redundancy built so when the inevitable coffee is spilled in the sheet you`re scanning it can still be read. Skewing is no more a problem with barcodes than with your system, other than it is an issue that has to be addressed.
 
This reminds me a lot of a system Xerox tried to push a few years back called PrintFlow. It was basically some glyphs that print in the corner of a human readable sheet and was suited for handwritten forms etc. http://www.xerox.com/digital-printing/print-solutions/enus.html[^]
 
Good work!
GeneralRe: Don't fear barcodes!memberdefwebserver23 Mar '08 - 15:24 
Thanks for the feedback. I agree that barcodes are the best technology if you can afford the libraries. I just found that the open source options were limited. I'm not sure if the skewing is the same. With my idea the image does not have to be deskewed. My understanding is you have to deskew a skewed barcode.

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web02 | 2.6.130523.1 | Last Updated 26 Apr 2008
Article Copyright 2008 by defwebserver
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid