Omr
Omr
INTRODUCTION
To build a document scanner, contour sorting and perspective image filtering
techniques using canning filter for edge detection and Gaussian filter for
blurring the image(OMR answer key) .This all is done using python in sublime
software. The OMR response of the student is taken and is compared with the
answer key and thus percentage is estimated. This evaluated OMR sheet is
displayed on the screen after executing the code.
The most notable bubble sheet test you experienced (at least in the United
States) were taking the SATs during high school, prior to filling out college
admission applications.
I believe that the SATs use the software provided by Scantron to perform OMR
and grade student exams, but I could easily be wrong there. I only make note
of this because Scantron is used in over 98% of all US school districts.
In short, what I’m trying to say is that there is a massive market for Optical
Mark Recognition and the ability to grade and interpret human-marked forms
and exams.
1
FIG.1.1
1.3 Prerequisites:
Python 3
OpenCV 3.4.3 or later
NumPy
imutils
SciPy (Windows only)
2
$ pip3 install imutils
3
Python is derived from many other languages, including ABC, Modula-3,
C, C++, Algol-68, SmallTalk, and Unix shell and other scripting languages.
Python is copyrighted. Like Perl, Python source code is now available
under the GNU General Public License (GPL).
Python is now maintained by a core development team at the institute,
although Guido van Rossum still holds a vital role in directing its
progress.
Python 1.0 was released in November 1994. In 2000, Python 2.0 was
released. Python 2.7.11 is the latest edition of Python 2.
Meanwhile, Python 3.0 was released in 2008. Python 3 is not backward
compatible with Python 2. The emphasis in Python 3 had been on the
removal of duplicate programming constructs and modules so that
"There should be one -- and preferably only one -- obvious way to do
it." Python 3.5.1 is the latest version of Python 3.
1.7 Python Features
Python's features include −
Easy-to-learn − Python has few keywords, simple structure, and a
clearly defined syntax. This allows a student to pick up the language
quickly.
Easy-to-read − Python code is more clearly defined and visible to the
eyes.
Easy-to-maintain − Python's source code is fairly easy-to-maintain.
A broad standard library − Python's bulk of the library is very portable
and cross-platform compatible on UNIX, Windows, and Macintosh.
Interactive Mode − Python has support for an interactive mode which
allows interactive testing and debugging of snippets of code.
Portable − Python can run on a wide variety of hardware platforms and
has the same interface on all platforms.
Extendable − You can add low-level modules to the Python interpreter.
These modules enable programmers to add to or customize their tools
to be more efficient.
Databases − Python provides interfaces to all major commercial
databases.
4
GUI Programming − Python supports GUI applications that can be
created and ported to many system calls, libraries and windows
systems, such as Windows MFC, Macintosh, and the X Window system
of Unix.
Scalable − Python provides a better structure and support for large
programs than shell scripting.
Apart from the above-mentioned features, Python has a big list of good
features. A, few are listed below −
It supports functional and structured programming methods as well as
OOP.
It can be used as a scripting language or can be compiled to byte-code
for building large applications.
It provides very high-level dynamic data types and supports dynamic
type checking.
It supports automatic garbage collection.
It can be easily integrated with C, C++, COM, ActiveX, CORBA, and Java.
OpenCV has a modular structure, which means that the package includes
several shared or static libraries. The following modules are available:
5
affine and perspective warping, generic table-based remapping), color
space conversion, histograms, and so on.
video - a video analysis module that includes motion estimation,
background subtraction, and object tracking algorithms.
calib3d - basic multiple-view geometry algorithms, single and stereo
camera calibration, object pose estimation, stereo correspondence
algorithms, and elements of 3D reconstruction.
features2d - salient feature detectors, descriptors, and descriptor
matchers.
objdetect - detection of objects and instances of the predefined classes
(for example, faces, eyes, mugs, people, cars, and so on).
highgui - an easy-to-use interface to simple UI capabilities.
Video I/O - an easy-to-use interface to video capturing and video
codecs.
gpu - GPU-accelerated algorithms from different OpenCV modules.
... some other helper modules, such as FLANN and Google test wrappers,
Python bindings, and others.
FIG. 1.2
6
1.9.2 Features
The following is a list of features of Sublime Text:[4]
FIG 1.3
CHAPTER -2
2.1 Implementing a bubble sheet scanner and grader using OMR, Python,
and OpenCV
7
We will use this image of bubble sheet for executing the code:
FIG 2.1
8
To get started, open up a new file, name it test_grader.py
FIG 2.2
FIG 2.3
Installing Numpy
Installing argparse
Installing OpenCV
9
Step 1: Install Visual Studio
FIG 2.4
10
FIG 2.5
FIG 2.6
Depending upon where you have kept opencv-3.3.1 folder, this path would be
different.
FIG 2.7
11
FIG 2.8
FIG 2.9
12
FIG 2.10
Click finish and in the next window keep the default parameters checked.
Click finish. Now CMake will look in the system directories and generate the
makefiles.
13
Step 5.2 : Add Python paths for both Python2 and Python3
14
If CMake is able to configure without any errors it should say “Configuring
done”.
Click generate.
Now that we have compiled OpenCV we will find out how to test a OpenCV
project using CMake.
First of all we will add OpenCV dll files’ path to our system PATH. Press
Windows Super key, search for “environment variables”
15
Click Environment Variables in System Properties window
16
Click New, and give path to OPENCV_PATH\build\install\x64\vc14\bin and click
Ok. Depending upon where you have kept opencv-3.3.1 folder and what
version of Visual Studio you used to compile OpenCV, this path would be
different. In my case full path is:
C:\Users\Documents\opencv-3.3.1\build\install\x64\vc14\bin
17
Now click Ok to save.
Lines 10-12 parse our command line arguments. We only need a single
switch here, --image , which is the path to the input bubble sheet test
image that we are going to grade for correctness.
Line 17 then defines our ANSWER_KEY .
As the name of the variable suggests, the ANSWER_KEY provides integer
mappings of the question numbers to the index of the correct bubble.
In this case, a key of 0 indicates the first question, while a value
of 1 signifies “B” as the correct answer (since “B” is the index 1 in the
string “ABCDE”). As a second example, consider a key of 1 that maps to a value
of 4 — this would indicate that the answer to the second question is “E”.
As a matter of convenience, I have written the entire answer key in plain
english here:
Question #1: B
Question #2: E
Question #3: A
18
Question #4: D
Question #5: B
Next, let’s preprocess our input image:
Below I have included a screenshot of our exam after applying edge detection:
19
Notice how the edges of the document are clearly defined, with all
four vertices of the exambeing present in the image.
Obtaining this silhouette of the document is extremely important in our next
step as we will use it as a marker to apply a perspective transform to the exam,
obtaining a top-down, birds-eye-view of the document:
We make the assumption that our exam will be the main focal point of
the image, and thus be larger than other objects in the image. This
20
assumption allows us to “filter” our contours, simply by investigating
their area and knowing that the contour that corresponds to the exam
should be near the front of the list.
However, contour area and size is not enough — we should also check
the number of vertices on the contour.
To do, this, we loop over each of our (sorted) contours on Line 40. For
each of them, we approximate the contour, which in essence means
we simplify the number of points in the contour, making it a “more
basic” geometric shape.
21
An example of drawing the contour associated with the exam on our
original image, indicating that we have successfully found the exam.
Now that we have used contours to find the outline of the exam, we
can apply a perspective transform to obtain a top-down, birds-eye-
view of the document:
22
Obtaining a top-down, birds-eye view of both the original image (left) along
with the grayscale version (right).
After applying Otsu’s thresholding method, our exam is now a binary image:
23
Using Otsu’s thresholding allows us to segment the foreground from the
background of the image.
24
Lines 64-67 handle finding contours on our thresh binary image,
followed by initializingquestionCnts , a list of contours that correspond
to the questions/bubbles on the exam.
To determine which regions of the image are bubbles, we first loop over
each of the individual contours (Line 70).
For each of these contours, we compute the bounding box (Line 73),
which also allows us to compute the aspect ratio, or more simply, the
ratio of the width to the height (Line 74).
In order for a contour area to be considered a bubble, the region should:
1. Be sufficiently wide and tall (in this case, at least 20 pixels in both dimensions).
2. Have an aspect ratio that is approximately equal to 1.
As long as these checks hold, we can update our questionCnts list and mark
the region as a bubble.
Below I have included a screenshot that has drawn the output
of questionCnts on our image:
Using contour filtering allows us to find all the question bubbles in our bubble
sheet exam recognition software.
25
Notice how only the question regions of the exam are highlighted and nothing
else.
First, we must sort our questionCnts from top-to-bottom. This will ensure that
rows of questions that are closer to the top of the exam will appear first in the
sorted list.
We also initialize a bookkeeper variable to keep track of the number of correct
answers.
On Line 90 we start looping over our questions. Since each question has
5 possible answers, we’ll apply NumPy array slicing and contour sorting
to to sort the current set of contours from left to right.
The reason this methodology works is because we have already sorted
our contours from top-to-bottom. We know that the 5 bubbles for each
question will appear sequentially in our list — but we do not
know whether these bubbles will be sorted from left-to-right. The sort
contour call on Line 94 takes care of this issue and ensures each row
of contours are sorted into rows, from left-to-right.
26
By sorting our contours from top-to-bottom, followed by left-to-right, we can
extract each row of bubbles. Therefore, each row is equal to the bubbles for
one question.
Given a row of bubbles, the next step is to determine which bubble is filled in.
We can accomplish this by using our thresh image and counting the number of
non-zero pixels (i.e., foreground pixels) in each bubble region:
27
Line 98 handles looping over each of the sorted bubbles in the row.
We then construct a mask for the current bubble on Line 101 and then
count the number of non-zero pixels in the masked region (Lines 107
and 108). The more non-zero pixels we count, then the more foreground
pixels there are, and therefore the bubble with the maximum non-zero
count is the index of the bubble that the the test taker has bubbled in
(Line 113 and 114).
Below I have included an example of creating and applying a mask to
each bubble associated with a question:
28
An example of constructing a mask for each bubble in a row.
Clearly, the bubble associated with “B” has the most thresholded pixels,
and is therefore the bubble that the user has marked on their exam.
29
Based on whether the test taker was correct or incorrect yields which
color is drawn on the exam. If the test taker is correct, we’ll highlight
their answer in green. However, if the test taker made a mistake and
marked an incorrect answer, we’ll let them know by highlighting
the correct answer in red:
Finally, our last code block handles scoring the exam and displaying the results
to our screen:
30
Below you can see the output of our fully graded example image:
31
CHAPTER 3
3.1 Why not use circle detection?
Hough circles don’t handle deformations in their outlines very well — your
circle detection would totally fail in that case.
Instead, the cv2.findContours function will return a set of blobs to you, which
will be the foreground regions in your image. You can then take these regions
process and filter them to find your questions (as we did in this tutorial), and
go about your way.
We’ve already seen test_01.png as our example earlier in this post, so let’s try
test_02.png :
32
We get the following result:
33
3.3 Extending the OMR and test scanner
1. What happens if a user does not bubble in an answer for a particular question?
2. What if the user is nefarious and marks multiple bubbles as “correct” in the
same row?
Luckily, detecting and handling of these issues isn’t terribly challenging,
we just need to insert a bit of logic.
34
For issue #1, if a reader chooses not to bubble in an answer for a
particular row, then we can place a minimum threshold on Line
108 where we compute cv2.countNonZero :
If this value is sufficiently large, then we can mark the bubble as “filled
in”. Conversely, if total is too small, then we can skip that particular
bubble. If at the end of the row there are no bubbles with sufficiently
large threshold counts, we can mark the question as “skipped” by the
test taker.
A similar set of steps can be applied to issue #2, where a user
marks multiple bubbles as correct for a single question:
35
CHAPTER 4
4.1 Summary
In this report , we demonstrated how to build a bubble sheet scanner
and test grader using computer vision and image processing techniques.
36
37