OMR for Camera Based Images using MATLAB

Viewed 243

I need help with a computer vision-related assignment, I have to build an OMR in MATLAB using the specified instructions but I am not able to do so.

This is the assignment:

You are provided with images of sample answer sheets. Using morphological image processing operators, you are required to develop a system in Matlab which extracts the answers provided by the student for each question.

Hints:

  • Binarize the image – The initial Binarization may be a crude one.
  • Apply dilation with a horizontal structuring element to merge components in lines.
  • Extract each line using CC labeling.
  • If required, again binarize each line separately.
  • For each line, use CC labeling to find different components. Use the area information to distinguish filled and non-filled bubbles. _ For each question, find the correct answer of the student. Input: Camera-based image of answer sheet
    Output: A vector of correct answers provided by the students.
    Assumptions: There are no lines with multiple answers and answers for all questions are provided.
    Useful MATLAB functions
    bwlabel, regionprops, imdilate, imerode, strel

This is the code I have written but it's not bringing me the results, I need some more code or preferably some module which will help me extract the correct answers from the filled answer sheet, attached is the code and input sheet

enter image description here

%reading the images

[fn, pn]=uigetfile('.');
    
InputImage = im2bw(imread([pn fn]));

 figure, imshow(InputImage ), title('Original Binary Image');
 
 % Removing Noise Pixels
 
% Rnp = bwareaopen(InputImage,45);
%figure,imshow(Rnp),title('Removed Noise Pixels');

% Strcuturing element
% se = strel('rectangle',[1,1]);
se = strel('rectangle', [15,1]);

% Erosion

%img_eroded = imerode(InputImage,se);
% figure,imshow(img_eroded), title('Eroded Image');

%Dilation
 img_dilated = imdilate(InputImage, se);
figure, imshow(img_dilated), title('Dilated Image')
% figure,imshow(InputImage);

% Calculate the connected components

CC = bwconncomp(img_dilated);

% Create a label matrix

L = labelmatrix(CC);

% Find the maximum value of the label matrix, this value indicates the number of detected objects

numObjects = max(L(:))

% Display the label matrix

figure, imshow(L,[]);


%subplot (1,3,1), imshow(InputImage ), title('Orignal Binary Image');
%subplot (1,3,2), imshow(img_eroded), title('Eroded Image');
%subplot (1,3,3), imshow(img_dilated), title('Dilated Image');


% To make it easier to differentiate the different connected components, display the label matrix as an RGB Image

 figure, imshow(label2rgb(L,'jet', 'k', 'shuffle'));
2 Answers

Here are problems with your code:

  1. You need to specify a level for im2bw since the paper is not completely white. So when calling im2bw with 0.5 as level (which is the default value), some parts of the background become black in the black and white image. enter image description here
  2. Note that bwconncomp finds white bodies of pixels in a black background. So you need to invert the binarized image before you go on. enter image description here
  3. It's better to remove salt and pepper noise by calling medfilt2. enter image description here
  4. As expected in the assignment, you need to dilate the image horizontally to connect components of a line, so you need a horizontal rectangle when calling strel to construct structuring element. enter image description here When you fix these, you will get the correct connected components:

enter image description here

The following are the steps you need to take from the point you got the correct labeled image:

  1. Find the area, centroid, and bounding box of each component by calling regionprops.
  2. Separate the components that correspond to the answers by specifying a threshold value for the area of ​​the components.
  3. Also, separate the components of the left and right columns by setting another threshold value for the x of centroid of the components.
  4. For each of the columns:
    1. Arrange the rows of each column in the order of y of their centroids.
    2. For each of the rows of the column:
      1. Calculate the question number based on the row and column index.
      2. Extract the contents inside the component boundingbox from the binarized image (not the image containing the labels nor the dilated image) and keep it in a separate image.
      3. Call the regionprops for the segmented image and find the area of ​​the components inside it.
      4. Find index of the component with the largest area, and since the regionprops sorts the components from left to right, return this index as the selected answer.

answers =

 1     1
 2     3
 3     1
 4     5
 5     2
 6     2
 7     4
 8     1
 9     3
10     1
11     4
12     3
13     2
14     4
15     2
16     3
17     2
18     5
19     1
20     3
21     2
22     4
23     2
24     3
25     2
26     4
27     3
28     5
29     2
30     5

PS: Although it is explicitly stated in the assignment that the answers to all the questions have been provided, the answer to question 10 is not specified in the image you posted. The results of the above algorithm for such questions will be random. But you can use deviation of area of components to detect if all of them are blank, as @Adriaan suggested in the comments.

See Here Full Code

 close , clear all;
P=0;
Q=0;
I2=imread('132446254287421150.jpg');
G2=rgb2gray(I2);
H2=~(im2bw(G2,graythresh(I2)));
BW2=bwareaopen(H2,1000);
cc2=bwconncomp(BW2);
L= bwlabel(BW2);
figure,imshow(BW2);title('Marked Options');
regionprop = regionprops(BW2,'Area', 'BoundingBox', 'Eccentricity', 'MajorAxisLength', 'MinorAxisLength',
'Orientation', 'Perimeter','Centroid');
coords = vertcat(regionprop.Centroid); % 2-by-18 matrix of centroid data
[~, ~, coords(:, 2)] = histcounts(coords(:, 2), 3); % Bin the "y" data
[~, sortIndex] = sortrows(coords, [2 1]); % Sort by "y" ascending, then "x" ascending
s = regionprop(sortIndex); % Apply sort index to s
figure, imshow(G2)
hold on;
for k = 1:numel(s)
 c = s(k).Centroid;
 text(c(1), c(2), sprintf('%d', k), ...
 'HorizontalAlignment', 'center', ...
 'VerticalAlignment', 'middle', 'color', 'r');
fprintf('Question No: = %d \n Marked Answer: = %d \nT', k,c(1));

end
hold off;
for k = 1:length(sortIndex)
 if(sortIndex(k)==1)
fprintf('\nQ1 filled circle is: A');
 end
 if(sortIndex(k)==2)
fprintf('\nQ3 filled circle is: A');
 end
 if(sortIndex(k)==3)
fprintf('\nQ2 filled circle is: C');
 end
 if(sortIndex(k)==4)
fprintf('\nQ4 filled circle is: E');
 end
 if(sortIndex(k)==5)
fprintf('\nQ19 filled circle is: A');
 end
 if(sortIndex(k)==6)
fprintf('\nQ17 filled circle is: B');
 end
 if(sortIndex(k)==7)
fprintf('\nQ16 filled circle is: C');
 end
 if(sortIndex(k)==8)
fprintf('\nQ18 filled circle is: E');
Mat lab Session:
 end
 if(sortIndex(k)==9)
fprintf('\nQ8 filled circle is: A');
 end
 if(sortIndex(k)==10)
fprintf('\nQ6 filled circle is: B');
 end
 if(sortIndex(k)==11)
fprintf('\nQ5 filled circle is: B');
 end
 if(sortIndex(k)==12)
fprintf('\nQ9 filled circle is: C');
 end
 if(sortIndex(k)==13)
fprintf('\nQ7 filled circle is: D');
 end
 if(sortIndex(k)==14)
fprintf('\nQ25 filled circle is: B');
 end
 if(sortIndex(k)==15)
fprintf('\nQ23 filled circle is: B');
 end
 if(sortIndex(k)==16)
fprintf('\nQ21 filled circle is: B');
 end
 if(sortIndex(k)==17)
fprintf('\nQ24 filled circle is: C');
 end
 if(sortIndex(k)==18)
fprintf('\nQ20 filled circle is: C');
 end
 if(sortIndex(k)==19)
fprintf('\nQ22 filled circle is: D');
 end
 if(sortIndex(k)==20)
fprintf('\nQ15 filled circle is: B');
 end
 if(sortIndex(k)==21)
fprintf('\nQ13 filled circle is: B');
 end
 if(sortIndex(k)==22)
fprintf('\nQ12 filled circle is: C');
 end
 if(sortIndex(k)==23)
fprintf('\nQ14 filled circle is: D');
 end
 if(sortIndex(k)==24)
fprintf('\nQ11 filled circle is: D');
 end
 if(sortIndex(k)==25)
fprintf('\nQ29 filled circle is: B');
 end
 if(sortIndex(k)==26)
fprintf('\nQ27 filled circle is: C');
 end
 if(sortIndex(k)==27)
fprintf('\nQ26 filled circle is: D');
end
 if(sortIndex(k)==28)
fprintf('\nQ30 filled circle is: E');
 end
 if(sortIndex(k)==29)
fprintf('\nQ1 filled circle is: A');
 end
 end
Related