Click here to Skip to main content
6,595,854 members and growing! (19,474 online)
Email Password   helpLost your password?
Multimedia » GDI+ » Applications     Intermediate License: The BSD License

ScanFree

By defwebserver

A program that will grade a test from a scanned sheet of paper.
C# (C# 1.0, C# 2.0, C# 3.0), .NET (.NET 3.5), GDI+, Dev
Posted:10 Mar 2008
Views:10,105
Bookmarked:28 times
Announcements
Loading...
 
Search    
Advanced Search
Add to IE Search
printPrint   add Share
      Discuss Discuss   Broken Article?Report  
6 votes for this article.
Popularity: 2.33 Rating: 3.00 out of 5
1 vote, 16.7%
1
1 vote, 16.7%
2

3
1 vote, 16.7%
4
3 votes, 50.0%
5

dnnScanFree_Source

Why ScanFree?

A group of developers started the dnnScanFree project in hopes of creating Open Source software that would allow others to create free and inexpensive testing programs. The idea is that school children can be helped if there are reliable inexpensive methods to measure their progress. The group of developers were alarmed when they discovered that free programs such as this did not exist.

This project is in the early stages, yet enough has been completed so far to be useful to other developers. The program will grade a test from a scanned sheet of paper.

Background

Christian Graus has written a great series of CodeProject articles on image processing. In his articles, he has explained how to process images quickly and reliably. There is no point covering what he so expertly covered, so it is recommended that you read his original articles to understand the basic concepts of image manipulation. He covers important points such as why pointers are used rather than GetPixel and SetPixel.

The Program

First, a method is used to turn each pixel into black or white. Next, the following method is used to determine a starting point. This simply looks for the first two black pixels that are found together.

private Point LocateStartingPoint(Bitmap b)
{
 Point tmpStartingPoint = new Point(0, 0);
 bool boolBreakOut = false; 


 BitmapData bmData = b.LockBits(new Rectangle(0, 0, b.Width, b.Height), 
 ImageLockMode.ReadWrite, b.PixelFormat); 

 int stride = bmData.Stride; 

 System.IntPtr Scan0 = bmData.Scan0; 


 unsafe 
 { 
  byte* p = (byte*)(void*)Scan0; 
  int nOffset = stride - (b.Width) * 3; 
  int nWidth = (b.Width) * 3; 


  for (int y = 0; y < (b.Height); ++y) 
  { 
   for (int x = 0; x < nWidth; ++x) 
   { 
   if ( 
      ((byte)p[0] == (byte)0) 
      & (x > 10 & y > 10) 
      & (x < b.Width + 10 & y < b.Height + 10) 
      ) 
      { 
       // See if the next one is black also 
       ++p; 
       if ((byte)p[0] == (byte)0) 
       { 
        tmpStartingPoint = new Point((x / 3), y); 
        boolBreakOut = true; 
        break; 
       } 

       // It wasn't black so move back 
       --p; 
       } 


       ++p; 
       } 
       if (boolBreakOut) 
       { 
        break; 
       } 
       p += nOffset; 
     } 
   } 


 b.UnlockBits(bmData); 
 return tmpStartingPoint; 

}

Once the starting point is found, it is a simple calculation to find the answer blocks and loop through them. FindAnswerBlocks is the method used to loop through the answer blocks and each answer in each block. CountBlackPixelsInRectangle is a simple method that counts the number of black pixels in each square. If a square has more than 1000 black pixels, then it is considered to be marked as an answer.

private string FindAnswerBlocks()
{

 string strSelectedAnswers = "";
 // Find answer blocks

 int intX = 499;
 int intY = 209;

 for (int i = 1; i <= 8; i++)
 {
  Point AnswerBlock = new Point(StartingPoint.X + intX, StartingPoint.Y + intY);
  DrawARectangle(m_Bitmap, AnswerBlock, 480, 65, Color.Red);


  // Find answers in answer block
  int intX2 = StartingPoint.X + intX;

  for (int ii = 1; ii <= 5; ii++)
  {
   Point Answer = new Point(intX2, AnswerBlock.Y);
   DrawARectangle(m_Bitmap, Answer, 90, 62, Color.Blue);
   int intCount = CountBlackPixelsInRectangle(m_Bitmap, Answer.X, Answer.Y, 90, 
   62);


   if (intCount > 1000)
   {
    strSelectedAnswers = strSelectedAnswers + Environment.NewLine + 
     String.Format("Answer 
    Block: {0} | Answer: {1} ", i, ii);
   }


   intX2 = intX2 + 97;
   }


  intY = intY + 68;
 }

 return strSelectedAnswers;
}

Points of Interest

There is still a lot more work that needs to be done. The boxes are currently too big, and each answer should be compared rather than using a set number of expected pixels. However, the code should provide a starting point for other developers.

License

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

About the Author

defwebserver


Member
Michael Washington is a Website developer and an ASP.NET, C#, and Visual Basic programmer. He has extensive knowledge in process improvement, billing systems, and credit card transaction processing. He is a DotNetNuke Core member and has been involved with DotNetNuke for nearly 4 years. He is the author of numerous DotNetNuke modules and tutorials. He is one of the founding members of the Southern California DotNetNuke Users group (http://socaldug.org). He has a son, Zachary and resides in Los Angeles with his wife Valerie.
Occupation: Web Developer
Location: United States United States

Other popular GDI+ articles:

Article Top
You must Sign In to use this message board.
FAQ FAQ 
 
Noise Tolerance  Layout  Per page   
 Msgs 1 to 2 of 2 (Total in Forum: 2) (Refresh)FirstPrevNext
GeneralHelp PinmemberGrim Re@p3r11:42 10 Apr '08  
GeneralRe: Help Pinmemberdefwebserver14:19 10 Apr '08  

General General    News News    Question Question    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

PermaLink | Privacy | Terms of Use
Last Updated: 10 Mar 2008
Editor: Smitha Vijayan
Copyright 2008 by defwebserver
Everything else Copyright © CodeProject, 1999-2009
Web17 | Advertise on the Code Project