3

I need to make a OMR detection system using Python for my High School Vacation Project(which potentially might be used by the school to some extent if it is reliable enough) , I have done quite a bit of research on it , and have tried out everything from contours to template matching, I feel template matching works fine but it can only detect one circle out of many in the OMR sheet, can someone help me in figuring out how I can detect multiple(all) circles(irrespective of whether they are bubbled or not), in the omr sheet and their respective coordinates , that would be enough for me. OMR Sheet

What I have tried:

import numpy as np
import cv2

img = cv2.resize(cv2.imread('assets/omr_match1.jpg', 0), (0, 0), fx=0.2, fy=0.5)
template = cv2.resize(cv2.imread('assets/circle.jpg', 0), (0, 0), fx=0.2, fy=0.5)
h, w = template.shape

methods = [cv2.TM_CCOEFF, cv2.TM_CCOEFF_NORMED, cv2.TM_CCORR,
            cv2.TM_CCORR_NORMED, cv2.TM_SQDIFF, cv2.TM_SQDIFF_NORMED]

methods=methods[0] 
# This is one among the above which works perfectly

for method in methods:
    img2 = img.copy()

    result = cv2.matchTemplate(img2, template, method)
    min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result)
    if method in [cv2.TM_SQDIFF, cv2.TM_SQDIFF_NORMED]:
        location = min_loc
    else:
        location = max_loc

    bottom_right = (location[0] + w, location[1] + h)
    cv2.rectangle(img2, location,bottom_right, 0, 1)
    cv2.imshow('Match', img2)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

Circle.jpg # circle.jpg

Result

See above, only one random circle is marked and not all circles.

Christoph Rackwitz
  • 11,317
  • 4
  • 27
  • 36
Manav Sengupta
  • 101
  • 1
  • 10
  • 2
    Possibly post some code or sudo-code to describe what you have tried. If you can find one and you know your width of the circle in pixels you should be able to fine everyone by making a grid. Are you converting to grayscale and masking to leave only the black circles? https://stackoverflow.com/questions/63730808/golf-ball-tracking-in-python-opencv-with-different-color-balls – Cary H Oct 08 '21 at 16:58
  • @Cary H Ok Thanks a lot, that may work, I will try and revert if I was able to work it out, Thanks a lot – Manav Sengupta Oct 08 '21 at 17:01
  • 1
    There are several ways to try to solve this: Pattern/template matching, watershed transformation, edge detection, geometry/ellipse correction (to make sure that only circular or circular-ish objects are detected) ... However, having well transformed grayscale or inverted-color images is a hard requirement before applying any of the beforementioned algorithms. – albert Oct 08 '21 at 17:02
  • 1
    @albert Unfortunately I am very new to opencv , and I have not heard any of the aforementioned algorithms except template matching, May be consider suggesting something simpler to understand like template matching/contour etc I will try the solution of #Cary H first and if that doesn't work , I will dig deeper into the algorithms you suggested, I will revert to this post , If I was able to find a solution, I highly appreciate your time to help , Thanks a lot – Manav Sengupta Oct 08 '21 at 17:07
  • 1
    https://www.pyimagesearch.com/2014/10/20/finding-shapes-images-using-python-opencv/ – Cary H Oct 08 '21 at 17:20
  • @Cary H I have added my code and images – Manav Sengupta Oct 08 '21 at 17:35
  • 3
    usually, it's a sheet of paper with **registration marks** (easy to detect and localize) and some barcode (or sth.) to identify the form. given those marks, you align the scan to the "model", and then you "blindly" apply a mask to the rectified scan... and sample the areas where the circle is *supposed to be*. you could even try feature matching of the whole scanned page against a whole model page (blank, clean, an image rasterized from a PDF) – Christoph Rackwitz Oct 08 '21 at 19:15
  • 2
    You are just getting one match - the one with the best score. You are not getting all the matches. See for example https://www.pyimagesearch.com/2021/03/29/multi-template-matching-with-opencv/ on how to get multiple matches. – fmw42 Oct 08 '21 at 20:15
  • 2
    An alternate method would be to use cv2.HoughCircles to get all the circles. – fmw42 Oct 08 '21 at 20:16
  • 2
    if someone "fills" a circle vigorously, you won't be able to detect it. *looking for* individual marks anywhere on the page is a bad idea, honestly. – Christoph Rackwitz Oct 08 '21 at 20:45
  • @ChristophRackwitz Yes your approach is the first thing that came to my mind, but unfortunately there is a subtle bit of problem both of us are ignoring that is two images may have different topology due to the way they were captured by the student , like the student may have captured the sheet when it was folded slightly in thus changing the distance(say) between some points when we see the image. I tried it, but I got unreliable results, sometimes it work , sometimes it doesn't, thats why I went with the circle approach, but anyway thanks a lot ,I really appreciate your time to help me out. – Manav Sengupta Oct 09 '21 at 03:35
  • 3
    then they need to be told to uncrumple the sheet and lay it as flat as possible. -- if that is a problem, you can put a lot of registration markers all over the sheet and unwarp it piecewise. -- I think you can use the page itself instead of special markers. the unwarping will be difficult. OpenCV comes with no single function that does mesh warping. you'd have to write that yourself. -- a perspective distortion from taking a photo is not an issue. that can be corrected. – Christoph Rackwitz Oct 09 '21 at 12:28
  • 1
    @ManavSengupta With software there's this principle called "garbage in, garbage out". In my experience, this is even more true for CV systems. If you skimp on the image acquisition (be it bad lighting, improper camera/lense choice, or even improper geometry of the system), at best you can expect a nightmare. – Dan Mašek Oct 10 '21 at 00:33
  • @DanMašek I 100% agree with you, problem is, at best I can request the students to properly capture the image with good lighting,etc, but given large number of students,causalities are bound to happen,that's why preparing for the worst situation and thats why i went with that circle method which is inefficient but it is working with almost every OMR Sheet even when it has some problems like its geometry and stuff,but definitely what you said is absolutely reasonable that the input image quality should be taken care of, and I will keep it in my mind,Thanks a lot. – Manav Sengupta Oct 10 '21 at 05:00

1 Answers1

3

Here we go:

import cv2
import numpy as np
from matplotlib import pyplot as plt


img = cv2.imread('/path/tabela_circle.jpg', 0)

template = cv2.imread('/path/circle.jpg', 0)
h, w = template.shape

res = cv2.matchTemplate(img,template,cv2.TM_CCOEFF_NORMED)
threshold = 0.8
loc = np.where( res >= threshold)
for pt in zip(*loc[::-1]):
    cv2.rectangle(img, pt, (pt[0] + w, pt[1] + h), (0,0,255), 2)

cv2.imwrite('res.png',img)

Solution

Also works to detect only answers:

Solution 2

razimbres
  • 4,715
  • 5
  • 23
  • 50
  • hi,nice work,i am trying to apply OMR for tiked box detection and localization,your approach didn't work for me,do you know how to solve this issue? : https://stackoverflow.com/questions/69706170/detect-and-possibly-localize-ticked-boxes-in-an-image-optical-mark-recognition thanks – Mobassir Hossen Oct 25 '21 at 11:07