jhOMR-webInterface 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- jhomr_webinterface-0.1.0/PKG-INFO +21 -0
- jhomr_webinterface-0.1.0/jhOMR_webInterface/OMRutils_v4.py +787 -0
- jhomr_webinterface-0.1.0/jhOMR_webInterface/__init__.py +3 -0
- jhomr_webinterface-0.1.0/jhOMR_webInterface.egg-info/PKG-INFO +21 -0
- jhomr_webinterface-0.1.0/jhOMR_webInterface.egg-info/SOURCES.txt +8 -0
- jhomr_webinterface-0.1.0/jhOMR_webInterface.egg-info/dependency_links.txt +1 -0
- jhomr_webinterface-0.1.0/jhOMR_webInterface.egg-info/requires.txt +9 -0
- jhomr_webinterface-0.1.0/jhOMR_webInterface.egg-info/top_level.txt +1 -0
- jhomr_webinterface-0.1.0/setup.cfg +4 -0
- jhomr_webinterface-0.1.0/setup.py +24 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
Metadata-Version: 2.2
|
|
2
|
+
Name: jhOMR_webInterface
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: OMR format to be used in KUET. This program supports using of Web Interface making use of fastAPI
|
|
5
|
+
Author: Md Nur Kutubul Alam
|
|
6
|
+
Author-email: alamjhilam@gmail.com
|
|
7
|
+
Requires-Python: >=3.7
|
|
8
|
+
Requires-Dist: numpy
|
|
9
|
+
Requires-Dist: pandas
|
|
10
|
+
Requires-Dist: opencv-python
|
|
11
|
+
Requires-Dist: openpyxl
|
|
12
|
+
Requires-Dist: fastapi
|
|
13
|
+
Requires-Dist: uvicorn
|
|
14
|
+
Requires-Dist: requests
|
|
15
|
+
Requires-Dist: Jinja2
|
|
16
|
+
Requires-Dist: python-multipart
|
|
17
|
+
Dynamic: author
|
|
18
|
+
Dynamic: author-email
|
|
19
|
+
Dynamic: requires-dist
|
|
20
|
+
Dynamic: requires-python
|
|
21
|
+
Dynamic: summary
|
|
@@ -0,0 +1,787 @@
|
|
|
1
|
+
import cv2
|
|
2
|
+
import pandas as pd
|
|
3
|
+
import numpy as np
|
|
4
|
+
widthImg=800
|
|
5
|
+
heightImg=800
|
|
6
|
+
RectangleScore_dict={}
|
|
7
|
+
DisplayLocation={'Roll':1,'TF':2,'QA':3}
|
|
8
|
+
DisplayLocation_3img={'Roll':0,'TF':1,'QA':2}
|
|
9
|
+
|
|
10
|
+
#Rectangle_Shape_Dict={'QA':(14,8),'TF':(5,10),'Roll':(8,10)}
|
|
11
|
+
#Rectangle_Shape_Dict={'QA':(14,9),'TF':(11,4),'Roll':(11,7)}
|
|
12
|
+
Rectangle_Shape_Dict={'QA':(14,9),'TF':(12,4),'Roll':(11,7)}
|
|
13
|
+
|
|
14
|
+
T='T';F='F';
|
|
15
|
+
a, b, c, d, e, f = 'a', 'b', 'c', 'd', 'e', 'f'
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
Rectangle_no_dict={'QA':0,'Roll':1,'TF':2}
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def imgPreProcess(img, widthImg, heightImg,rectContours,TakeRectangle,threshold_value=0.5):
|
|
22
|
+
#imgCanny = getImgCanny(img)
|
|
23
|
+
# imgBlank = np.zeros_like(img)
|
|
24
|
+
# contours, hierarchy = cv2.findContours(imgCanny, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
|
|
25
|
+
#rectContours, imgContours = getRectangles_fromImage(img, widthImg, heightImg)
|
|
26
|
+
|
|
27
|
+
rectangle_no=Rectangle_no_dict[TakeRectangle]
|
|
28
|
+
selectedContour = getCornerPoints(rectContours[rectangle_no])
|
|
29
|
+
|
|
30
|
+
imgWarpColored = getWarpPerspective(img, selectedContour, widthImg, heightImg)
|
|
31
|
+
imgWarpGray = cv2.cvtColor(imgWarpColored, cv2.COLOR_BGR2GRAY)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
# if threshold_value is None:
|
|
35
|
+
# threshold_value=(int(imgWarpGray.min())+int(imgWarpGray.max()))/2
|
|
36
|
+
# else:
|
|
37
|
+
threshold_value=int(imgWarpGray.min()*threshold_value)+int(imgWarpGray.max()*(1-threshold_value))
|
|
38
|
+
|
|
39
|
+
imgThreshold = cv2.threshold(imgWarpGray, threshold_value, 255, cv2.THRESH_BINARY_INV)[1]
|
|
40
|
+
return imgThreshold,imgWarpColored
|
|
41
|
+
|
|
42
|
+
def getWarpPerspective(img,selectedContour,widthImg,heightImg):
|
|
43
|
+
selectedContour = reorder(selectedContour)
|
|
44
|
+
pt1 = np.float32(selectedContour)
|
|
45
|
+
pt2 = np.float32([[0, 0], [widthImg, 0], [0, heightImg], [widthImg, heightImg]])
|
|
46
|
+
matrix = cv2.getPerspectiveTransform(pt1, pt2)
|
|
47
|
+
# Transform the image into bird-eye-view using the warp transformation matrix
|
|
48
|
+
imgWarpColored = cv2.warpPerspective(img, matrix, (widthImg, heightImg))
|
|
49
|
+
return imgWarpColored
|
|
50
|
+
|
|
51
|
+
def getRectangles_fromImage(img,widthImg, heightImg):
|
|
52
|
+
img = cv2.resize(img, (widthImg, heightImg))
|
|
53
|
+
imgContours = img.copy()
|
|
54
|
+
|
|
55
|
+
imgGray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
|
|
56
|
+
imgBlur = cv2.GaussianBlur(imgGray, (5, 5), 1)
|
|
57
|
+
imgCanny = cv2.Canny(imgBlur, 10, 50) # Canny is edge detector. Here we are able to detect the edge of the rectengles
|
|
58
|
+
|
|
59
|
+
##### FINDING ALL CONTOURS
|
|
60
|
+
contours, hierarchy = cv2.findContours(imgCanny, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
|
|
61
|
+
cv2.drawContours(imgContours, contours, -1, (0, 255, 0), 10) # Draw all the contours with green line
|
|
62
|
+
|
|
63
|
+
#### FIND RECTANGLES
|
|
64
|
+
rectContours = rectContour(contours) # Find the rectangular contours and sort them according to their area
|
|
65
|
+
return rectContours,imgContours
|
|
66
|
+
|
|
67
|
+
def rectContour(contours):
|
|
68
|
+
rectCon=[]
|
|
69
|
+
for i in contours:
|
|
70
|
+
area=cv2.contourArea(i)
|
|
71
|
+
#print(area)
|
|
72
|
+
if area>50:
|
|
73
|
+
perimeter=cv2.arcLength(i,True) #Closed perimeter=True
|
|
74
|
+
approx=cv2.approxPolyDP(i,0.02*perimeter,True) #Finds the number of corner points
|
|
75
|
+
#print("Corner Points",len(approx))
|
|
76
|
+
if len(approx)==4: #taking the rectangles only
|
|
77
|
+
rectCon.append(i)
|
|
78
|
+
rectCon=sorted(rectCon,key=cv2.contourArea,reverse=True)
|
|
79
|
+
return rectCon
|
|
80
|
+
|
|
81
|
+
def reorder(myPoints):
|
|
82
|
+
#This function takes the corner points of a rectangle as input.
|
|
83
|
+
# Then x+y=minimu, its the origin. If x+y=maximum, it is (w,h)
|
|
84
|
+
#If x-y is minimum, it is (0,h). if x-y is maximu, it is the remaining point (h,0)
|
|
85
|
+
|
|
86
|
+
myPoints=myPoints.reshape((4,2))
|
|
87
|
+
myPointsNew_likeVideo=np.zeros((4,1,2),np.int32)
|
|
88
|
+
add=myPoints.sum(1) #
|
|
89
|
+
|
|
90
|
+
myPointsNew_likeVideo[0]=myPoints[np.argmin(add)] #[0,0]
|
|
91
|
+
myPointsNew_likeVideo[3]=myPoints[np.argmax(add)] #[w,h]
|
|
92
|
+
diff=np.diff(myPoints,axis=1)
|
|
93
|
+
myPointsNew_likeVideo[1]=myPoints[np.argmin(diff)] #[w,0]
|
|
94
|
+
myPointsNew_likeVideo[2]=myPoints[np.argmax(diff)] #[0,h]
|
|
95
|
+
return myPointsNew_likeVideo
|
|
96
|
+
def getCornerPoints(contour):
|
|
97
|
+
perimeter = cv2.arcLength(contour, True) # Closed perimeter=True
|
|
98
|
+
approx = cv2.approxPolyDP(contour, 0.02 * perimeter,True) # Finds the number of corner points
|
|
99
|
+
return approx
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def getImgCanny(img):
|
|
103
|
+
imgGray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
|
|
104
|
+
imgBlur = cv2.GaussianBlur(imgGray, (5, 5), 1)
|
|
105
|
+
imgCanny = cv2.Canny(imgBlur, 10,50) # Canny is edge detector. Here we are able to detect the edge of the rectengles
|
|
106
|
+
return imgCanny
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def fillFraction(box):
|
|
110
|
+
n_pixel=cv2.countNonZero(box)
|
|
111
|
+
total_pixel=np.shape(box)[0]*np.shape(box)[1]
|
|
112
|
+
return round(n_pixel / total_pixel,4)
|
|
113
|
+
|
|
114
|
+
def get_Fillups(imgThreshold,n_rows,n_cols,n_skipPixels=30,w=0.5):
|
|
115
|
+
# This function splits a rectangle into individual boxes. Then calculates box fill fraction
|
|
116
|
+
# Note that sometimes box can be significantly larger than the OMR circle. In that case we skip few pixels along
|
|
117
|
+
# the horizontal direction. In other words, we crop the image horizontally
|
|
118
|
+
imgThreshold_modified = cv2.resize(imgThreshold, (n_cols * 100, n_rows * 100))
|
|
119
|
+
rows = np.vsplit(imgThreshold_modified, n_rows)
|
|
120
|
+
############################
|
|
121
|
+
boxes=[]
|
|
122
|
+
rgbBoxes=[]
|
|
123
|
+
boxFillFractions=[]
|
|
124
|
+
for row in rows:
|
|
125
|
+
cols=np.hsplit(row, n_cols)
|
|
126
|
+
for box in cols:
|
|
127
|
+
grayBox = box.copy()
|
|
128
|
+
rgb_box = cv2.cvtColor(grayBox, cv2.COLOR_GRAY2RGB)
|
|
129
|
+
|
|
130
|
+
box=box[:,n_skipPixels:100-n_skipPixels]
|
|
131
|
+
boxes.append(box)
|
|
132
|
+
fill = fillFraction(box)
|
|
133
|
+
boxFillFractions.append(fill)
|
|
134
|
+
#Mark the cropped region with "Blue gray"
|
|
135
|
+
rgb_box[:, 0:n_skipPixels, 1] = 54;rgb_box[:, -n_skipPixels:, 1] = 54
|
|
136
|
+
rgb_box[:, 0:n_skipPixels, 1] = 147;rgb_box[:, -n_skipPixels:, 1] = 147
|
|
137
|
+
rgb_box[:, 0:n_skipPixels, 2] = 179;rgb_box[:, -n_skipPixels:, 2] = 179
|
|
138
|
+
rgbBoxes.append(rgb_box)
|
|
139
|
+
|
|
140
|
+
boxes = np.reshape(boxes, (n_rows, n_cols, np.shape(boxes)[1], np.shape(boxes)[2]))
|
|
141
|
+
rgbBoxes=np.reshape(rgbBoxes,(n_rows, n_cols, np.shape(rgbBoxes)[1], np.shape(rgbBoxes)[2],np.shape(rgbBoxes)[3]))
|
|
142
|
+
boxFillFractions = np.reshape(boxFillFractions, (n_rows, n_cols))
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
# threshold_row=boxFillFractions[0,:]
|
|
146
|
+
# threshold=w*threshold_row.max()+(1-w)*threshold_row.min()
|
|
147
|
+
threshold=w*boxFillFractions.max()+(1-w)*boxFillFractions.min()
|
|
148
|
+
|
|
149
|
+
ind=np.where(boxFillFractions>threshold)
|
|
150
|
+
fillupMatrix=np.zeros(np.shape(boxFillFractions))
|
|
151
|
+
fillupMatrix[ind]=1
|
|
152
|
+
|
|
153
|
+
return boxes,rgbBoxes,boxFillFractions,fillupMatrix
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
#answer_loc_dict={'a':1, 'b':2, 'c':4, 'd':5, 'e':7, 'f':8}
|
|
160
|
+
|
|
161
|
+
answer_loc_dict={'a':1, 'b':2, 'c':4, 'd':5, 'e':7, 'f':8,'T':1,'F':3}
|
|
162
|
+
|
|
163
|
+
def GenerateCorrectAnswerMatrix(Correct_answer_dict,Rectangle_Shape,ignore_nrows=2):
|
|
164
|
+
|
|
165
|
+
correct_answer_matrix = np.zeros(Rectangle_Shape)
|
|
166
|
+
for key in Correct_answer_dict:
|
|
167
|
+
correct_answer=Correct_answer_dict[key] #a, b, c etc.
|
|
168
|
+
column=answer_loc_dict[correct_answer]
|
|
169
|
+
#print(key,column)
|
|
170
|
+
correct_answer_matrix[int(key)+int(ignore_nrows-1), column] = 1
|
|
171
|
+
return correct_answer_matrix
|
|
172
|
+
|
|
173
|
+
# def GenerateCorrectTF_Matrix(Correct_TrueFalse_indices,Rectangle_Shape):
|
|
174
|
+
# correct_TF_matrix = np.zeros(Rectangle_Shape)
|
|
175
|
+
# for key in Correct_TrueFalse_indices:
|
|
176
|
+
# if Correct_TrueFalse_indices[key]=='T':
|
|
177
|
+
# correct_TF_matrix[ key,1] = 1
|
|
178
|
+
# elif Correct_TrueFalse_indices[key]=='F':
|
|
179
|
+
# correct_TF_matrix[ key,3] = 1
|
|
180
|
+
# else:
|
|
181
|
+
# print("Correct_TrueFalse_indices is not correctly defined")
|
|
182
|
+
# return correct_TF_matrix
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
def RotateClockwise(*Matrices):
|
|
189
|
+
retList=[]
|
|
190
|
+
for matrix in Matrices:
|
|
191
|
+
matrix=cv2.rotate(matrix, cv2.ROTATE_90_CLOCKWISE)
|
|
192
|
+
retList.append(matrix)
|
|
193
|
+
return retList
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
def Nullify_DoubleAnswers(fillups,TakeRectangle):
|
|
199
|
+
def ApplyNullification(fillups_innder):
|
|
200
|
+
fillupNullified = fillups_innder.copy()
|
|
201
|
+
for nn in range(0, np.shape(fillups)[0]):
|
|
202
|
+
ind = np.where(fillups[nn,] == 1)
|
|
203
|
+
if len(ind[0])> 1:
|
|
204
|
+
fillupNullified[nn,ind]='Nan'
|
|
205
|
+
return fillupNullified
|
|
206
|
+
|
|
207
|
+
fillups[0, :] = 0
|
|
208
|
+
if TakeRectangle=='TF':
|
|
209
|
+
fillupProcessed=ApplyNullification(fillups)
|
|
210
|
+
elif TakeRectangle=='QA':
|
|
211
|
+
if np.shape(fillups)[0]<np.shape(fillups)[1]:
|
|
212
|
+
print("Nullify_DoubleAnswers: Wrong fillup matrix supplied!!! Expecting for 'QA' box...")
|
|
213
|
+
#print(colored("Nullify_DoubleAnswers: Wrong fillup matrix supplied!!! Expecting for 'QA' box..."),'red')
|
|
214
|
+
fillupProcessed = ApplyNullification(fillups)
|
|
215
|
+
else:
|
|
216
|
+
fillupProcessed = ApplyNullification(fillups)
|
|
217
|
+
# fillups[0, :] = 'Nan'
|
|
218
|
+
# fillupProcessed=fillups
|
|
219
|
+
return fillupProcessed
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
def computeRoll(fillupMatrix,TakeRectangle):
|
|
224
|
+
fillupMatrix=fillupMatrix[1:,:] #Skipping 1st row
|
|
225
|
+
|
|
226
|
+
if TakeRectangle=='Roll':
|
|
227
|
+
roll=0
|
|
228
|
+
for nn in range(0,7):
|
|
229
|
+
col=fillupMatrix[:,nn]
|
|
230
|
+
ind=np.where(col==1)[0]
|
|
231
|
+
if len(ind)==0:
|
|
232
|
+
print("Roll: No fillup in col ",nn,'\t',col)
|
|
233
|
+
return -1
|
|
234
|
+
elif len(ind)>1:
|
|
235
|
+
print("Roll: Multiple fillup in col ",nn,'\t',col)
|
|
236
|
+
return -1
|
|
237
|
+
else:
|
|
238
|
+
roll=roll+10**(6-nn)*ind[0]
|
|
239
|
+
else:
|
|
240
|
+
roll=-1
|
|
241
|
+
return '{:07.0f}'.format(roll)
|
|
242
|
+
|
|
243
|
+
def computeRoll_v2(boxFillFractions,TakeRectangle,ignore_nrows=2,w=0.5):
|
|
244
|
+
n_cols=np.shape(boxFillFractions)[1]
|
|
245
|
+
fillupMatrix=np.zeros(np.shape(boxFillFractions))
|
|
246
|
+
|
|
247
|
+
threshold=w*boxFillFractions[0,:].max()+(1-w)*boxFillFractions[0,:].min()
|
|
248
|
+
ind=np.where(boxFillFractions[0,:]>threshold)
|
|
249
|
+
fillupMatrix[0,ind]=1
|
|
250
|
+
|
|
251
|
+
for nn in range(n_cols):
|
|
252
|
+
ind=np.where(boxFillFractions[ignore_nrows:,nn]==max(boxFillFractions[ignore_nrows:,nn]))[0]
|
|
253
|
+
fillupMatrix[ind+ignore_nrows,nn]=1 #Skipping 1st "ignore_nrows"
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
if TakeRectangle=='Roll':
|
|
257
|
+
roll=0
|
|
258
|
+
for nn in range(0,7):
|
|
259
|
+
col=fillupMatrix[ignore_nrows:,nn]
|
|
260
|
+
ind=np.where(col==1)[0]
|
|
261
|
+
if len(ind)==0:
|
|
262
|
+
print("Roll: No fillup in col ",nn,'\t',col)
|
|
263
|
+
return -1
|
|
264
|
+
elif len(ind)>1:
|
|
265
|
+
print("Roll: Multiple fillup in col ",nn,'\t',col)
|
|
266
|
+
return -1
|
|
267
|
+
else:
|
|
268
|
+
roll=roll+10**(6-nn)*ind[0]
|
|
269
|
+
else:
|
|
270
|
+
roll=-1
|
|
271
|
+
|
|
272
|
+
return fillupMatrix,'{:07.0f}'.format(roll)
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
import itertools
|
|
277
|
+
|
|
278
|
+
def showRoll(imgWarpColored, fillupMatrix_doubleAns_nullified):
|
|
279
|
+
imgAnswers = imgWarpColored.copy()
|
|
280
|
+
|
|
281
|
+
n_rows = np.shape(fillupMatrix_doubleAns_nullified)[0]
|
|
282
|
+
n_cols = np.shape(fillupMatrix_doubleAns_nullified)[1]
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
secW = int(imgAnswers.shape[1] / n_cols)
|
|
286
|
+
secH = int(imgAnswers.shape[0] / n_rows)
|
|
287
|
+
thickness = 8
|
|
288
|
+
|
|
289
|
+
for row, col in itertools.product(range(1,n_rows), range(n_cols)):
|
|
290
|
+
if np.isnan(fillupMatrix_doubleAns_nullified[row,]).any(): # If row contains a 'nan' (multiple answer),continue
|
|
291
|
+
cX=int(secW / 2 + 0 * secW)
|
|
292
|
+
cY = int(secH / 2 + row * secH)
|
|
293
|
+
cv2.rectangle(imgAnswers, (int(cX - secW / 2), int(cY+ secH / 2)),
|
|
294
|
+
(int(cX+n_rows* secW + secW / 2), int(cY - secH / 2)), (94, 48, 201), thickness)
|
|
295
|
+
continue
|
|
296
|
+
|
|
297
|
+
Entry_Student_answerMatrix = fillupMatrix_doubleAns_nullified[row, col]
|
|
298
|
+
|
|
299
|
+
if Entry_Student_answerMatrix: #Mark the answer only if student answered it
|
|
300
|
+
cX_student = int(secW / 2 + col * secW)
|
|
301
|
+
cY_student = int(secH / 2 + row * secH)
|
|
302
|
+
|
|
303
|
+
cv2.rectangle(imgAnswers, (int(cX_student-secW / 2),int(cY_student+secH/2)), (int(cX_student+secW / 2),int(cY_student-secH/2)), (255, 255, 0), thickness)
|
|
304
|
+
return imgAnswers
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
def showAnswers(imgWarpColored, correct_answer_matrix,fillupMatrix_doubleAns_nullified,TakeRectangle,Mark_dict,ignore_nrows=2):
|
|
308
|
+
text_x_offset_dict={'TF':2,'QA':4}
|
|
309
|
+
mark_x_offset_dict={'TF':0.6,'QA':3}
|
|
310
|
+
|
|
311
|
+
Mark_correctAnswer_dict,Mark_wrongAnswer_dict=Mark_dict[TakeRectangle]
|
|
312
|
+
text_x_offset=text_x_offset_dict[TakeRectangle]
|
|
313
|
+
mark_x_offset_dict=mark_x_offset_dict[TakeRectangle]
|
|
314
|
+
|
|
315
|
+
dictLen=np.shape(correct_answer_matrix)[0]
|
|
316
|
+
Mark_correctAnswer_dict, Mark_wrongAnswer_dict = Convert_integer_mark_into_dictionary(Mark_correctAnswer_dict,Mark_wrongAnswer_dict,dictLen)
|
|
317
|
+
|
|
318
|
+
imgAnswers = imgWarpColored.copy()
|
|
319
|
+
n_rows = np.shape(correct_answer_matrix)[0]
|
|
320
|
+
n_cols = np.shape(correct_answer_matrix)[1]
|
|
321
|
+
|
|
322
|
+
|
|
323
|
+
secW = int(imgAnswers.shape[1] / n_cols)
|
|
324
|
+
secH = int(imgAnswers.shape[0] / n_rows)
|
|
325
|
+
|
|
326
|
+
angle = 0;
|
|
327
|
+
startAngle = 0;
|
|
328
|
+
endAngle = 360;
|
|
329
|
+
axesLength = (int(secW / 2.5), int(secH / 2));
|
|
330
|
+
thickness = 8
|
|
331
|
+
|
|
332
|
+
# for row, col in itertools.product(range(n_rows), range(n_cols)):
|
|
333
|
+
for row, col in itertools.product(range(ignore_nrows,n_rows), range(n_cols)):
|
|
334
|
+
Dict_key=row-(ignore_nrows-1)
|
|
335
|
+
|
|
336
|
+
cX_student = int(secW / 2 + col * secW)
|
|
337
|
+
cY_student = int(secH / 2 + row * secH)
|
|
338
|
+
center_coordinates = (cX_student, cY_student);
|
|
339
|
+
Mark_text_coordinate=(int(secW * mark_x_offset_dict), cY_student)
|
|
340
|
+
# Mark_text_coordinate=(int(secW / 2), cY_student)
|
|
341
|
+
text_coordinate = (int(secW * text_x_offset), cY_student)
|
|
342
|
+
|
|
343
|
+
|
|
344
|
+
if np.isnan(fillupMatrix_doubleAns_nullified[row,]).any(): # If row contains a 'nan' (multiple answer),continue
|
|
345
|
+
cX=int(secW / 2 + 0 * secW)
|
|
346
|
+
cY = int(secH / 2 + row * secH)
|
|
347
|
+
cv2.rectangle(imgAnswers, (int(cX - secW / 2), int(cY+ secH / 2)),
|
|
348
|
+
(int(cX+n_rows* secW + secW / 2), int(cY - secH / 2)), (94, 48, 201), thickness)
|
|
349
|
+
cv2.putText(imgAnswers, "Null", text_coordinate, cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255),
|
|
350
|
+
4, cv2.LINE_AA)
|
|
351
|
+
continue
|
|
352
|
+
|
|
353
|
+
if Dict_key not in Mark_correctAnswer_dict and Dict_key not in Mark_wrongAnswer_dict:
|
|
354
|
+
cv2.putText(imgAnswers, "CA & WA Mark Not provided", text_coordinate, cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 0, 255), 4,
|
|
355
|
+
cv2.LINE_AA)
|
|
356
|
+
elif Dict_key not in Mark_correctAnswer_dict:
|
|
357
|
+
cv2.putText(imgAnswers, "CA Mark Not provided", text_coordinate, cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 0, 255), 4,
|
|
358
|
+
cv2.LINE_AA)
|
|
359
|
+
elif Dict_key not in Mark_wrongAnswer_dict:
|
|
360
|
+
cv2.putText(imgAnswers, "WA Mark Not provided", text_coordinate, cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 0, 255), 4,
|
|
361
|
+
cv2.LINE_AA)
|
|
362
|
+
|
|
363
|
+
|
|
364
|
+
Entry_Student_answerMatrix = fillupMatrix_doubleAns_nullified[row, col]
|
|
365
|
+
Entry_corr_answerMatrix = correct_answer_matrix[row, col]
|
|
366
|
+
|
|
367
|
+
if Entry_Student_answerMatrix: #Mark the answer only if student answered it
|
|
368
|
+
#Correct answer for this question is not provided, though student answered it.
|
|
369
|
+
#In that case, mark the answer with a square.
|
|
370
|
+
if not np.any(correct_answer_matrix[row,]): # If there is no "correct answer" of a given question
|
|
371
|
+
cv2.rectangle(imgAnswers, (int(cX_student-secW / 2),int(cY_student+secH/2)), (int(cX_student+secW / 2),int(cY_student-secH/2)), (255, 255, 0), thickness)
|
|
372
|
+
cv2.putText(imgAnswers,"Ans. Not Provided",text_coordinate,cv2.FONT_HERSHEY_SIMPLEX,1,(0,0,255),4,cv2.LINE_AA)
|
|
373
|
+
continue
|
|
374
|
+
|
|
375
|
+
#Answer is correct
|
|
376
|
+
elif Entry_corr_answerMatrix == Entry_Student_answerMatrix:
|
|
377
|
+
cv2.ellipse(imgAnswers, center_coordinates, axesLength, angle, startAngle, endAngle, (0, 255, 0),
|
|
378
|
+
thickness)
|
|
379
|
+
isKeyPresent = Dict_key in Mark_correctAnswer_dict
|
|
380
|
+
if isKeyPresent:
|
|
381
|
+
Mark_CA=Mark_correctAnswer_dict[row-(ignore_nrows-1)]
|
|
382
|
+
cv2.putText(imgAnswers,str(Mark_CA),Mark_text_coordinate,cv2.FONT_HERSHEY_SIMPLEX,1,(0,0,255),4,cv2.LINE_AA)
|
|
383
|
+
|
|
384
|
+
else:
|
|
385
|
+
cv2.ellipse(imgAnswers, center_coordinates, axesLength, angle, startAngle, endAngle, (0, 0, 255),
|
|
386
|
+
thickness)
|
|
387
|
+
col = np.where(correct_answer_matrix[row,] == 1)[0]
|
|
388
|
+
|
|
389
|
+
cX_correct = int(secW / 2 + col * secW)
|
|
390
|
+
cY_correct = int(secH / 2 + row * secH)
|
|
391
|
+
cv2.ellipse(imgAnswers, (cX_correct, cY_correct), axesLength, angle, startAngle, endAngle,
|
|
392
|
+
(255, 255, 0), thickness)
|
|
393
|
+
|
|
394
|
+
isKeyPresent = Dict_key in Mark_wrongAnswer_dict
|
|
395
|
+
if isKeyPresent:
|
|
396
|
+
Mark_WA=Mark_wrongAnswer_dict[row-(ignore_nrows-1)]
|
|
397
|
+
cv2.putText(imgAnswers,str(Mark_WA),Mark_text_coordinate,cv2.FONT_HERSHEY_SIMPLEX,1,(0,0,255),4,cv2.LINE_AA)
|
|
398
|
+
|
|
399
|
+
return imgAnswers
|
|
400
|
+
|
|
401
|
+
|
|
402
|
+
|
|
403
|
+
|
|
404
|
+
|
|
405
|
+
def Convert_integer_mark_into_dictionary(Mark_correctAns_dict,Mark_wrongAns_dict,dictLen):
|
|
406
|
+
if type(Mark_correctAns_dict) is not dict:
|
|
407
|
+
tmp_Mark_correctAnswer = {}
|
|
408
|
+
tmr_Mark_wrongAnswer = {}
|
|
409
|
+
for row in range(0, dictLen):
|
|
410
|
+
tmp_Mark_correctAnswer[row] = Mark_correctAns_dict
|
|
411
|
+
tmr_Mark_wrongAnswer[row] = Mark_wrongAns_dict
|
|
412
|
+
Mark_correctAns_dict = tmp_Mark_correctAnswer
|
|
413
|
+
Mark_wrongAns_dict = tmr_Mark_wrongAnswer
|
|
414
|
+
return Mark_correctAns_dict,Mark_wrongAns_dict
|
|
415
|
+
|
|
416
|
+
|
|
417
|
+
def GenerateScore(TakeRectangle,Mark_dict,correct_answer_matrix,fillupMatrix_doubleAns_nullified,imgWarpColored,ignore_nrows=2):
|
|
418
|
+
def _GenerateScore(correct_answer_matrix,fillupMatrix_doubleAns_nullified,Mark_correctAnswer_dict,Mark_wrongAnswer_dict,imgWarpColored,ignore_nrows):
|
|
419
|
+
n_rows = np.shape(correct_answer_matrix)[0]
|
|
420
|
+
n_cols = np.shape(correct_answer_matrix)[1]
|
|
421
|
+
|
|
422
|
+
text_x_offset_dict = {'TF': 2, 'QA': 4}
|
|
423
|
+
mark_x_offset_dict = {'TF': 0.6, 'QA': 3}
|
|
424
|
+
text_x_offset = text_x_offset_dict[TakeRectangle]
|
|
425
|
+
mark_x_offset_dict = mark_x_offset_dict[TakeRectangle]
|
|
426
|
+
imgAnswers = imgWarpColored.copy()
|
|
427
|
+
secW = int(imgAnswers.shape[1] / n_cols)
|
|
428
|
+
secH = int(imgAnswers.shape[0] / n_rows)
|
|
429
|
+
angle = 0;
|
|
430
|
+
startAngle = 0;
|
|
431
|
+
endAngle = 360;
|
|
432
|
+
axesLength = (int(secW / 2.5), int(secH / 2));
|
|
433
|
+
thickness = 8
|
|
434
|
+
|
|
435
|
+
score_positive = 0
|
|
436
|
+
score_negative = 0
|
|
437
|
+
|
|
438
|
+
for row in range(ignore_nrows, n_rows):
|
|
439
|
+
# if Mark_correctAnswer is None or Mark_wrongAnswer is None: #If the mark is not given
|
|
440
|
+
# continue
|
|
441
|
+
|
|
442
|
+
Row_studentAnswerMatrix = fillupMatrix_doubleAns_nullified[row, :]
|
|
443
|
+
Row_CorrectAnswerMatrix = correct_answer_matrix[row, :]
|
|
444
|
+
Dict_key = row - (ignore_nrows - 1)
|
|
445
|
+
|
|
446
|
+
if not np.any(Row_studentAnswerMatrix): #Student did not answer this question.
|
|
447
|
+
continue
|
|
448
|
+
if np.isnan(Row_studentAnswerMatrix).any(): # Multiple answering nullifies the answer.
|
|
449
|
+
cX = int(secW / 2 + 0 * secW)
|
|
450
|
+
cY = int(secH / 2 + row * secH)
|
|
451
|
+
cv2.rectangle(imgAnswers, (int(cX - secW / 2), int(cY + secH / 2)),(int(cX + n_rows * secW + secW / 2), int(cY - secH / 2)), (94, 48, 201), thickness)
|
|
452
|
+
text_coordinate = (int(secW * text_x_offset), cY)
|
|
453
|
+
cv2.putText(imgAnswers, "Null", text_coordinate, cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 4, cv2.LINE_AA)
|
|
454
|
+
continue
|
|
455
|
+
|
|
456
|
+
|
|
457
|
+
studentAnsIndex = np.where(Row_studentAnswerMatrix == 1)[0]
|
|
458
|
+
correctAnsIndex = np.where(Row_CorrectAnswerMatrix == 1)[0]
|
|
459
|
+
|
|
460
|
+
cX_student = int(secW / 2 + studentAnsIndex * secW)
|
|
461
|
+
cY_student = int(secH / 2 + row * secH)
|
|
462
|
+
student_fillup_coordinate = (cX_student, cY_student);
|
|
463
|
+
Mark_text_coordinate = (int(secW * mark_x_offset_dict), cY_student)
|
|
464
|
+
# Mark_text_coordinate=(int(secW / 2), cY_student)
|
|
465
|
+
text_coordinate = (int(secW * text_x_offset), cY_student)
|
|
466
|
+
|
|
467
|
+
|
|
468
|
+
if not np.any(Row_CorrectAnswerMatrix): # If there is no "correct answer" of a given question, though student answered it.
|
|
469
|
+
# In that case, assume this question does not exist, and continue.
|
|
470
|
+
cv2.rectangle(imgAnswers, (int(cX_student-secW / 2),int(cY_student+secH/2)), (int(cX_student+secW / 2),int(cY_student-secH/2)), (255, 255, 0), thickness)
|
|
471
|
+
cv2.putText(imgAnswers,"Ans. Not Provided",text_coordinate,cv2.FONT_HERSHEY_SIMPLEX,1,(0,0,255),4,cv2.LINE_AA)
|
|
472
|
+
continue
|
|
473
|
+
|
|
474
|
+
|
|
475
|
+
|
|
476
|
+
elif studentAnsIndex == correctAnsIndex: #Student filled the correct circle
|
|
477
|
+
cv2.ellipse(imgAnswers, student_fillup_coordinate, axesLength, angle, startAngle, endAngle, (0, 255, 0),thickness)
|
|
478
|
+
if Mark_correctAnswer_dict.get(Dict_key) is not None:
|
|
479
|
+
Mark_correctAnswer = Mark_correctAnswer_dict.get(Dict_key)
|
|
480
|
+
score_positive = score_positive + Mark_correctAnswer
|
|
481
|
+
cv2.putText(imgAnswers,str(Mark_correctAnswer),Mark_text_coordinate,cv2.FONT_HERSHEY_SIMPLEX,1,(0,0,255),4,cv2.LINE_AA)
|
|
482
|
+
else:
|
|
483
|
+
cv2.putText(imgAnswers, "CA Mark Not provided", text_coordinate, cv2.FONT_HERSHEY_SIMPLEX, 1,(255, 0, 255), 4,cv2.LINE_AA)
|
|
484
|
+
|
|
485
|
+
|
|
486
|
+
else: #student filled a wrong circle
|
|
487
|
+
cv2.ellipse(imgAnswers, student_fillup_coordinate, axesLength, angle, startAngle, endAngle, (0, 0, 255),thickness)
|
|
488
|
+
col = np.where(correct_answer_matrix[row,] == 1)[0]
|
|
489
|
+
cX_correct = int(secW / 2 + col * secW); cY_correct = int(secH / 2 + row * secH)
|
|
490
|
+
cv2.ellipse(imgAnswers, (cX_correct, cY_correct), axesLength, angle, startAngle, endAngle, (255, 255, 0), thickness)
|
|
491
|
+
|
|
492
|
+
if Mark_wrongAnswer_dict.get(Dict_key) is not None :
|
|
493
|
+
Mark_wrongAnswer = Mark_wrongAnswer_dict.get(Dict_key)
|
|
494
|
+
score_negative = score_negative + Mark_wrongAnswer
|
|
495
|
+
cv2.putText(imgAnswers,str(Mark_wrongAnswer),Mark_text_coordinate,cv2.FONT_HERSHEY_SIMPLEX,1,(0,0,255),4,cv2.LINE_AA)
|
|
496
|
+
else:
|
|
497
|
+
cv2.putText(imgAnswers, "WA Mark Not provided", text_coordinate, cv2.FONT_HERSHEY_SIMPLEX, 1,(255, 0, 255), 4, cv2.LINE_AA)
|
|
498
|
+
|
|
499
|
+
return score_positive,score_negative,imgAnswers
|
|
500
|
+
################################## showAnswers works from Here ###############################################
|
|
501
|
+
|
|
502
|
+
|
|
503
|
+
Mark_correctAnswer_dict,Mark_wrongAnswer_dict=Mark_dict[TakeRectangle]
|
|
504
|
+
dictLen=np.shape(correct_answer_matrix)[0]
|
|
505
|
+
Mark_correctAnswer_dict, Mark_wrongAnswer_dict = Convert_integer_mark_into_dictionary(Mark_correctAnswer_dict,Mark_wrongAnswer_dict,dictLen)
|
|
506
|
+
|
|
507
|
+
score_positive,score_negative,imgAnswers=_GenerateScore(correct_answer_matrix,fillupMatrix_doubleAns_nullified,Mark_correctAnswer_dict, Mark_wrongAnswer_dict,imgWarpColored,ignore_nrows)
|
|
508
|
+
|
|
509
|
+
return score_positive,score_negative,imgAnswers
|
|
510
|
+
|
|
511
|
+
# def GenerateScore(TakeRectangle,Mark_dict,correct_answer_matrix,fillupMatrix_doubleAns_nullified,ignore_nrows=2):
|
|
512
|
+
# def _GenerateScore(correct_answer_matrix,fillupMatrix_doubleAns_nullified,Mark_correctAnswer_dict,Mark_wrongAnswer_dict,ignore_nrows):
|
|
513
|
+
# n_rows = np.shape(correct_answer_matrix)[0]
|
|
514
|
+
# score = 0
|
|
515
|
+
#
|
|
516
|
+
# for row in range(0, n_rows):
|
|
517
|
+
#
|
|
518
|
+
# # if Mark_correctAnswer is None or Mark_wrongAnswer is None: #If the mark is not given
|
|
519
|
+
# # continue
|
|
520
|
+
#
|
|
521
|
+
# Row_studentAnswerMatrix = fillupMatrix_doubleAns_nullified[row, :]
|
|
522
|
+
# Row_CorrectAnswerMatrix = correct_answer_matrix[row, :]
|
|
523
|
+
# Dict_key = row - (ignore_nrows - 1)
|
|
524
|
+
#
|
|
525
|
+
# # Correct answer for this question is not provided, though student answered it.
|
|
526
|
+
# # In that case, assume this question does not exist, and continue.
|
|
527
|
+
# if not np.any(Row_CorrectAnswerMatrix): # If there is no "correct answer" of a given question
|
|
528
|
+
# continue
|
|
529
|
+
# if not np.any(Row_studentAnswerMatrix): #Student did not answer this question.
|
|
530
|
+
# continue
|
|
531
|
+
#
|
|
532
|
+
# studentAnsIndex = np.where(Row_studentAnswerMatrix == 1)[0]
|
|
533
|
+
# correctAnsIndex = np.where(Row_CorrectAnswerMatrix == 1)[0]
|
|
534
|
+
#
|
|
535
|
+
# if np.isnan(Row_studentAnswerMatrix).any(): # Multiple answering nullifies the answer.
|
|
536
|
+
# continue
|
|
537
|
+
# elif studentAnsIndex == correctAnsIndex and Mark_correctAnswer_dict.get(Dict_key) is not None:
|
|
538
|
+
# Mark_correctAnswer = Mark_correctAnswer_dict.get(Dict_key)
|
|
539
|
+
# score = score + Mark_correctAnswer
|
|
540
|
+
# elif Mark_wrongAnswer_dict.get(Dict_key) is not None :
|
|
541
|
+
# Mark_wrongAnswer = Mark_wrongAnswer_dict.get(Dict_key)
|
|
542
|
+
# score = score + Mark_wrongAnswer
|
|
543
|
+
#
|
|
544
|
+
# return score
|
|
545
|
+
# ################################## showAnswers works from Here ###############################################
|
|
546
|
+
#
|
|
547
|
+
#
|
|
548
|
+
# Mark_correctAnswer_dict,Mark_wrongAnswer_dict=Mark_dict[TakeRectangle]
|
|
549
|
+
# dictLen=np.shape(correct_answer_matrix)[0]
|
|
550
|
+
# Mark_correctAnswer_dict, Mark_wrongAnswer_dict = Convert_integer_mark_into_dictionary(Mark_correctAnswer_dict,Mark_wrongAnswer_dict,dictLen)
|
|
551
|
+
#
|
|
552
|
+
#
|
|
553
|
+
# score=_GenerateScore(correct_answer_matrix,fillupMatrix_doubleAns_nullified,Mark_correctAnswer_dict, Mark_wrongAnswer_dict,ignore_nrows)
|
|
554
|
+
#
|
|
555
|
+
# return score
|
|
556
|
+
|
|
557
|
+
|
|
558
|
+
|
|
559
|
+
# def GenerateScore(TakeRectangle,Mark_dict,correct_answer_matrix,fillupMatrix_doubleAns_nullified):
|
|
560
|
+
# def _GenerateScore(correct_answer_matrix,fillupMatrix_doubleAns_nullified,Mark_correctAnswer_dict,Mark_wrongAnswer_dict):
|
|
561
|
+
# n_rows = np.shape(correct_answer_matrix)[0]
|
|
562
|
+
# score = 0
|
|
563
|
+
#
|
|
564
|
+
# for row in range(0, n_rows):
|
|
565
|
+
#
|
|
566
|
+
# Mark_correctAnswer = Mark_correctAnswer_dict.get(row)
|
|
567
|
+
# Mark_wrongAnswer=Mark_wrongAnswer_dict.get(row)
|
|
568
|
+
#
|
|
569
|
+
# if Mark_correctAnswer is None or Mark_wrongAnswer is None: #If the mark is not given
|
|
570
|
+
# continue
|
|
571
|
+
#
|
|
572
|
+
# Row_studentAnswerMatrix = fillupMatrix_doubleAns_nullified[row, :]
|
|
573
|
+
# Row_CorrectAnswerMatrix = correct_answer_matrix[row, :]
|
|
574
|
+
#
|
|
575
|
+
# # Correct answer for this question is not provided, though student answered it.
|
|
576
|
+
# # In that case, assume this question does not exist, and continue.
|
|
577
|
+
# if not np.any(Row_CorrectAnswerMatrix): # If there is no "correct answer" of a given question
|
|
578
|
+
# continue
|
|
579
|
+
# if not np.any(Row_studentAnswerMatrix): #Student did not answer this question.
|
|
580
|
+
# continue
|
|
581
|
+
#
|
|
582
|
+
# studentAnsIndex = np.where(Row_studentAnswerMatrix == 1)[0]
|
|
583
|
+
# correctAnsIndex = np.where(Row_CorrectAnswerMatrix == 1)[0]
|
|
584
|
+
#
|
|
585
|
+
# if np.isnan(Row_studentAnswerMatrix).any(): # Multiple answering nullifies the answer.
|
|
586
|
+
# continue
|
|
587
|
+
# elif studentAnsIndex == correctAnsIndex:
|
|
588
|
+
# score = score + Mark_correctAnswer
|
|
589
|
+
# else:
|
|
590
|
+
# score = score + Mark_wrongAnswer
|
|
591
|
+
# return score
|
|
592
|
+
# ################################## showAnswers works from Here ###############################################
|
|
593
|
+
#
|
|
594
|
+
#
|
|
595
|
+
# Mark_correctAnswer_dict,Mark_wrongAnswer_dict=Mark_dict[TakeRectangle]
|
|
596
|
+
# dictLen=np.shape(correct_answer_matrix)[0]
|
|
597
|
+
# Mark_correctAnswer_dict, Mark_wrongAnswer_dict = Convert_integer_mark_into_dictionary(Mark_correctAnswer_dict,Mark_wrongAnswer_dict,dictLen)
|
|
598
|
+
#
|
|
599
|
+
#
|
|
600
|
+
# score=_GenerateScore(correct_answer_matrix,fillupMatrix_doubleAns_nullified,Mark_correctAnswer_dict, Mark_wrongAnswer_dict)
|
|
601
|
+
#
|
|
602
|
+
# return score
|
|
603
|
+
|
|
604
|
+
def Conv_FirstRow_into_Binary(a):
|
|
605
|
+
b = 0
|
|
606
|
+
for i in range(len(a)):
|
|
607
|
+
b=b+10**i*a[len(a)-i-1]
|
|
608
|
+
return int(b)
|
|
609
|
+
|
|
610
|
+
def ReadCorrectAnswer(xlsxFN, nrows=12, usecols='A:Q', Print=False):
|
|
611
|
+
print("\n\t\tReading "+str(xlsxFN)+'...')
|
|
612
|
+
AllAnswer_df = pd.read_excel(xlsxFN, nrows=nrows, usecols=usecols)
|
|
613
|
+
|
|
614
|
+
TF_df = AllAnswer_df[
|
|
615
|
+
['True/False no', 'T/F Set 1', 'T/F Set 2', 'T/F Set 3', 'Correct T/F mark', 'Wrong T/F mark']].copy()
|
|
616
|
+
QA_df = AllAnswer_df[
|
|
617
|
+
['Question no', 'Q/A Set 1', 'Q/A Set 2', 'Q/A Set 3', 'Correct Q/A mark', 'Wrong Q/A mark']].copy()
|
|
618
|
+
|
|
619
|
+
Course = list(AllAnswer_df['Course'])[0]
|
|
620
|
+
|
|
621
|
+
binaryCode=AllAnswer_df['Binary code'].dropna()
|
|
622
|
+
binaryCode=list(binaryCode.astype(int))
|
|
623
|
+
equivalentSetCode=AllAnswer_df['Equivalent Set Code'].dropna()
|
|
624
|
+
equivalentSetCode =list(equivalentSetCode.astype(int))
|
|
625
|
+
SetCode = pd.Series(equivalentSetCode,index=binaryCode)
|
|
626
|
+
GetSetCode_from_Binary=SetCode.to_dict()
|
|
627
|
+
|
|
628
|
+
|
|
629
|
+
TF_df = TF_df.dropna(subset=['T/F Set 1', 'T/F Set 2', 'T/F Set 3', 'Correct T/F mark', 'Wrong T/F mark'])
|
|
630
|
+
QA_df = QA_df.dropna(subset=['Q/A Set 1', 'Q/A Set 2', 'Q/A Set 3', 'Correct Q/A mark', 'Wrong Q/A mark'])
|
|
631
|
+
|
|
632
|
+
if Print:
|
|
633
|
+
print('\n\t Verify the content of '+str(xlsxFN)+':\n')
|
|
634
|
+
print('\t\tCourse= ',Course)
|
|
635
|
+
for key in GetSetCode_from_Binary:
|
|
636
|
+
print("\t\tBinary:", key, '\t\tSet Code:', GetSetCode_from_Binary[key])
|
|
637
|
+
print('\n')
|
|
638
|
+
print(TF_df.to_string(index=False), '\n', QA_df.to_string(index=False))
|
|
639
|
+
|
|
640
|
+
TF_question_no = TF_df['True/False no']
|
|
641
|
+
TF_set1 = TF_df['T/F Set 1']
|
|
642
|
+
TF_set2 = TF_df['T/F Set 2']
|
|
643
|
+
TF_set3 = TF_df['T/F Set 3']
|
|
644
|
+
C_TF_Mark_Arr = TF_df['Correct T/F mark']
|
|
645
|
+
W_TF_Mark_Arr = TF_df['Wrong T/F mark']
|
|
646
|
+
|
|
647
|
+
QA_question_no = QA_df['Question no']
|
|
648
|
+
QA_set1 = QA_df['Q/A Set 1']
|
|
649
|
+
QA_set2 = QA_df['Q/A Set 2']
|
|
650
|
+
QA_set3 = QA_df['Q/A Set 3']
|
|
651
|
+
C_QA_Mark_Arr = QA_df['Correct Q/A mark']
|
|
652
|
+
W_QA_Mark_Arr = QA_df['Wrong Q/A mark']
|
|
653
|
+
|
|
654
|
+
TF_ind_set1={}
|
|
655
|
+
TF_ind_set2 = {}
|
|
656
|
+
TF_ind_set3 = {}
|
|
657
|
+
Mark_correctAnswer_TF={}
|
|
658
|
+
Mark_wrongAnswer_TF={}
|
|
659
|
+
|
|
660
|
+
|
|
661
|
+
for i in range(len(TF_question_no)):
|
|
662
|
+
TF_ind_set1[TF_question_no[i]] = TF_set1[i].strip()
|
|
663
|
+
TF_ind_set2[TF_question_no[i]] = TF_set2[i].strip()
|
|
664
|
+
TF_ind_set3[TF_question_no[i]] = TF_set3[i].strip()
|
|
665
|
+
Mark_correctAnswer_TF[TF_question_no[i]] = C_TF_Mark_Arr[i]
|
|
666
|
+
Mark_wrongAnswer_TF[TF_question_no[i]] = W_TF_Mark_Arr[i]
|
|
667
|
+
|
|
668
|
+
QA_ind_set1={}
|
|
669
|
+
QA_ind_set2 = {}
|
|
670
|
+
QA_ind_set3 = {}
|
|
671
|
+
Mark_CorrectAnswer_QA={}
|
|
672
|
+
Mark_wrongAnswer_QA={}
|
|
673
|
+
for i in range(len(QA_question_no)):
|
|
674
|
+
QA_ind_set1[QA_question_no[i]] = QA_set1[i].strip()
|
|
675
|
+
QA_ind_set2[QA_question_no[i]] = QA_set2[i].strip()
|
|
676
|
+
QA_ind_set3[QA_question_no[i]] = QA_set3[i].strip()
|
|
677
|
+
Mark_CorrectAnswer_QA[QA_question_no[i]] = C_QA_Mark_Arr[i]
|
|
678
|
+
Mark_wrongAnswer_QA[QA_question_no[i]] = W_QA_Mark_Arr[i]
|
|
679
|
+
|
|
680
|
+
return Course,GetSetCode_from_Binary,TF_ind_set1, TF_ind_set2, TF_ind_set3, QA_ind_set1, QA_ind_set2, QA_ind_set3, Mark_correctAnswer_TF, Mark_wrongAnswer_TF, Mark_CorrectAnswer_QA, Mark_wrongAnswer_QA
|
|
681
|
+
|
|
682
|
+
|
|
683
|
+
def ReadCorrectAnswer_dict_from_webread_df(AllAnswer_df, Print=False):
|
|
684
|
+
|
|
685
|
+
TF_df = AllAnswer_df[
|
|
686
|
+
['True/False no', 'T/F Set 1', 'T/F Set 2', 'T/F Set 3', 'Correct T/F mark', 'Wrong T/F mark']].copy()
|
|
687
|
+
QA_df = AllAnswer_df[
|
|
688
|
+
['Question no', 'Q/A Set 1', 'Q/A Set 2', 'Q/A Set 3', 'Correct Q/A mark', 'Wrong Q/A mark']].copy()
|
|
689
|
+
|
|
690
|
+
Course = list(AllAnswer_df['Course'])[0]
|
|
691
|
+
|
|
692
|
+
binaryCode=AllAnswer_df['Binary code'].dropna()
|
|
693
|
+
binaryCode=list(binaryCode.astype(int))
|
|
694
|
+
equivalentSetCode=AllAnswer_df['Equivalent Set Code'].dropna()
|
|
695
|
+
equivalentSetCode =list(equivalentSetCode.astype(int))
|
|
696
|
+
SetCode = pd.Series(equivalentSetCode,index=binaryCode)
|
|
697
|
+
GetSetCode_from_Binary=SetCode.to_dict()
|
|
698
|
+
|
|
699
|
+
|
|
700
|
+
TF_df = TF_df.dropna(subset=['T/F Set 1', 'T/F Set 2', 'T/F Set 3', 'Correct T/F mark', 'Wrong T/F mark'])
|
|
701
|
+
QA_df = QA_df.dropna(subset=['Q/A Set 1', 'Q/A Set 2', 'Q/A Set 3', 'Correct Q/A mark', 'Wrong Q/A mark'])
|
|
702
|
+
|
|
703
|
+
if Print:
|
|
704
|
+
print('\t\tCourse= ',Course)
|
|
705
|
+
for key in GetSetCode_from_Binary:
|
|
706
|
+
print("\t\tBinary:", key, '\t\tSet Code:', GetSetCode_from_Binary[key])
|
|
707
|
+
print('\n')
|
|
708
|
+
print(TF_df.to_string(index=False), '\n', QA_df.to_string(index=False))
|
|
709
|
+
|
|
710
|
+
TF_question_no = TF_df['True/False no']
|
|
711
|
+
TF_set1 = TF_df['T/F Set 1']
|
|
712
|
+
TF_set2 = TF_df['T/F Set 2']
|
|
713
|
+
TF_set3 = TF_df['T/F Set 3']
|
|
714
|
+
C_TF_Mark_Arr = TF_df['Correct T/F mark']
|
|
715
|
+
W_TF_Mark_Arr = TF_df['Wrong T/F mark']
|
|
716
|
+
|
|
717
|
+
QA_question_no = QA_df['Question no']
|
|
718
|
+
QA_set1 = QA_df['Q/A Set 1']
|
|
719
|
+
QA_set2 = QA_df['Q/A Set 2']
|
|
720
|
+
QA_set3 = QA_df['Q/A Set 3']
|
|
721
|
+
C_QA_Mark_Arr = QA_df['Correct Q/A mark']
|
|
722
|
+
W_QA_Mark_Arr = QA_df['Wrong Q/A mark']
|
|
723
|
+
|
|
724
|
+
TF_ind_set1={}
|
|
725
|
+
TF_ind_set2 = {}
|
|
726
|
+
TF_ind_set3 = {}
|
|
727
|
+
Mark_correctAnswer_TF={}
|
|
728
|
+
Mark_wrongAnswer_TF={}
|
|
729
|
+
|
|
730
|
+
|
|
731
|
+
for i in range(len(TF_question_no)):
|
|
732
|
+
TF_ind_set1[TF_question_no[i]] = TF_set1[i].strip()
|
|
733
|
+
TF_ind_set2[TF_question_no[i]] = TF_set2[i].strip()
|
|
734
|
+
TF_ind_set3[TF_question_no[i]] = TF_set3[i].strip()
|
|
735
|
+
Mark_correctAnswer_TF[TF_question_no[i]] = C_TF_Mark_Arr[i]
|
|
736
|
+
Mark_wrongAnswer_TF[TF_question_no[i]] = W_TF_Mark_Arr[i]
|
|
737
|
+
|
|
738
|
+
QA_ind_set1={}
|
|
739
|
+
QA_ind_set2 = {}
|
|
740
|
+
QA_ind_set3 = {}
|
|
741
|
+
Mark_CorrectAnswer_QA={}
|
|
742
|
+
Mark_wrongAnswer_QA={}
|
|
743
|
+
for i in range(len(QA_question_no)):
|
|
744
|
+
QA_ind_set1[QA_question_no[i]] = QA_set1[i].strip()
|
|
745
|
+
QA_ind_set2[QA_question_no[i]] = QA_set2[i].strip()
|
|
746
|
+
QA_ind_set3[QA_question_no[i]] = QA_set3[i].strip()
|
|
747
|
+
Mark_CorrectAnswer_QA[QA_question_no[i]] = C_QA_Mark_Arr[i]
|
|
748
|
+
Mark_wrongAnswer_QA[QA_question_no[i]] = W_QA_Mark_Arr[i]
|
|
749
|
+
|
|
750
|
+
return Course,GetSetCode_from_Binary,TF_ind_set1, TF_ind_set2, TF_ind_set3, QA_ind_set1, QA_ind_set2, QA_ind_set3, Mark_correctAnswer_TF, Mark_wrongAnswer_TF, Mark_CorrectAnswer_QA, Mark_wrongAnswer_QA
|
|
751
|
+
|
|
752
|
+
|
|
753
|
+
|
|
754
|
+
|
|
755
|
+
def stackImages(imgArray,scale):
|
|
756
|
+
rows = len(imgArray)
|
|
757
|
+
cols = len(imgArray[0])
|
|
758
|
+
|
|
759
|
+
rowsAvailable = isinstance(imgArray[0], (list,np.ndarray))
|
|
760
|
+
width = imgArray[0][0].shape[1]
|
|
761
|
+
height = imgArray[0][0].shape[0]
|
|
762
|
+
|
|
763
|
+
if rowsAvailable:
|
|
764
|
+
for x in range ( 0, rows):
|
|
765
|
+
for y in range(0, cols):
|
|
766
|
+
if imgArray[x][y].shape[:2] == imgArray[0][0].shape [:2]:
|
|
767
|
+
imgArray[x][y] = cv2.resize(imgArray[x][y], (0, 0), None, scale, scale)
|
|
768
|
+
else:
|
|
769
|
+
imgArray[x][y] = cv2.resize(imgArray[x][y], (imgArray[0][0].shape[1], imgArray[0][0].shape[0]), None, scale, scale)
|
|
770
|
+
if len(imgArray[x][y].shape) == 2: imgArray[x][y]= cv2.cvtColor( imgArray[x][y], cv2.COLOR_GRAY2BGR)
|
|
771
|
+
imageBlank = np.zeros((height, width, 3), np.uint8)
|
|
772
|
+
hor = [imageBlank]*rows
|
|
773
|
+
hor_con = [imageBlank]*rows
|
|
774
|
+
for x in range(0, rows):
|
|
775
|
+
hor[x] = np.hstack(imgArray[x])
|
|
776
|
+
ver = np.vstack(hor)
|
|
777
|
+
|
|
778
|
+
else:
|
|
779
|
+
for x in range(0, rows):
|
|
780
|
+
if imgArray[x].shape[:2] == imgArray[0].shape[:2]:
|
|
781
|
+
imgArray[x] = cv2.resize(imgArray[x], (0, 0), None, scale, scale)
|
|
782
|
+
else:
|
|
783
|
+
imgArray[x] = cv2.resize(imgArray[x], (imgArray[0].shape[1], imgArray[0].shape[0]), None,scale, scale)
|
|
784
|
+
if len(imgArray[x].shape) == 2: imgArray[x] = cv2.cvtColor(imgArray[x], cv2.COLOR_GRAY2BGR)
|
|
785
|
+
hor= np.hstack(imgArray)
|
|
786
|
+
ver = hor
|
|
787
|
+
return ver
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
Metadata-Version: 2.2
|
|
2
|
+
Name: jhOMR_webInterface
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: OMR format to be used in KUET. This program supports using of Web Interface making use of fastAPI
|
|
5
|
+
Author: Md Nur Kutubul Alam
|
|
6
|
+
Author-email: alamjhilam@gmail.com
|
|
7
|
+
Requires-Python: >=3.7
|
|
8
|
+
Requires-Dist: numpy
|
|
9
|
+
Requires-Dist: pandas
|
|
10
|
+
Requires-Dist: opencv-python
|
|
11
|
+
Requires-Dist: openpyxl
|
|
12
|
+
Requires-Dist: fastapi
|
|
13
|
+
Requires-Dist: uvicorn
|
|
14
|
+
Requires-Dist: requests
|
|
15
|
+
Requires-Dist: Jinja2
|
|
16
|
+
Requires-Dist: python-multipart
|
|
17
|
+
Dynamic: author
|
|
18
|
+
Dynamic: author-email
|
|
19
|
+
Dynamic: requires-dist
|
|
20
|
+
Dynamic: requires-python
|
|
21
|
+
Dynamic: summary
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
setup.py
|
|
2
|
+
jhOMR_webInterface/OMRutils_v4.py
|
|
3
|
+
jhOMR_webInterface/__init__.py
|
|
4
|
+
jhOMR_webInterface.egg-info/PKG-INFO
|
|
5
|
+
jhOMR_webInterface.egg-info/SOURCES.txt
|
|
6
|
+
jhOMR_webInterface.egg-info/dependency_links.txt
|
|
7
|
+
jhOMR_webInterface.egg-info/requires.txt
|
|
8
|
+
jhOMR_webInterface.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
jhOMR_webInterface
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
from setuptools import setup, find_packages
|
|
2
|
+
|
|
3
|
+
setup(
|
|
4
|
+
name='jhOMR_webInterface',
|
|
5
|
+
version='0.1.0',
|
|
6
|
+
author='Md Nur Kutubul Alam',
|
|
7
|
+
author_email='alamjhilam@gmail.com',
|
|
8
|
+
description='OMR format to be used in KUET. This program supports using of Web Interface making use of fastAPI',
|
|
9
|
+
|
|
10
|
+
packages=find_packages(), # Automatically find the 'jhOMR_webInterface' package
|
|
11
|
+
|
|
12
|
+
install_requires=[
|
|
13
|
+
'numpy',
|
|
14
|
+
'pandas',
|
|
15
|
+
'opencv-python',
|
|
16
|
+
'openpyxl',
|
|
17
|
+
'fastapi',
|
|
18
|
+
'uvicorn',
|
|
19
|
+
'requests',
|
|
20
|
+
'Jinja2',
|
|
21
|
+
'python-multipart'
|
|
22
|
+
],
|
|
23
|
+
python_requires='>=3.7',
|
|
24
|
+
)
|