-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathvoteCounter.py
164 lines (134 loc) · 4.54 KB
/
voteCounter.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
import numpy as np
import cv2
from glob import glob
import csv
from warpImage import warpImage, autoWarpImage
from collections import Counter
import csv
def binaryROIImage(image, rectangles, threshold=10):
"""
Creates a binary image with black background and white
BLOBS within each ROI indicating marks on the ballot.
"""
h, w = image.shape[:2]
# Grayscale image.
grayscale = image.copy()
if len(grayscale.shape) == 3:
grayscale = cv2.cvtColor(grayscale, cv2.COLOR_BGR2GRAY)
# Create negative imgage for thresholding
inv = cv2.bitwise_not(grayscale)
# Create a binary image.
_, thres = cv2.threshold(inv, threshold, 255, cv2.THRESH_BINARY)
zeroed = np.zeros((h,w), dtype=np.uint8)
for points in rectangles:
x,y,w,h = cv2.boundingRect(np.array(points, dtype=np.int32))
w-=1
h-=1
roi = thres[y:y+h, x:x+w]
zeroed[y:y+h, x:x+w] = roi
return zeroed
def detectMark(image, rectangles, threshold=10):
"""
Finding largest BLOB area and returns the centroid.
Threshold is for creation og binary ROI image.
"""
areas = []
centroids = []
image = binaryROIImage(image, rectangles, threshold)
# Find blobs in the input image.
_, contours, hierarchy = cv2.findContours(image, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
if len(contours) != 0:
for contour in contours:
area = cv2.contourArea(contour)
centroid = calcCentroid(contour)
areas.append(area)
centroids.append(centroid)
idx = areas.index(max(areas))
point = centroids[idx]
return point
else:
print("Didn't find any marks")
def pointWithinRectangle(point, rectangle):
"""
Return true if point is within a rectangle. rectangles is
defined as two diagonal corner points e.g. bottom left, top right
"""
px = point[1]
py = point[0]
x_vals = [x for (y, x) in rectangle]
y_vals = [y for (y, x) in rectangle]
x_min = min(x_vals)
x_max = max(x_vals)
y_min = min(y_vals)
y_max = max(y_vals)
if (x_min < px < x_max and y_min < py < y_max):
return True
else:
return False
def calcCentroid(contour):
"""
Calculates the centroid of the contour. Moments up to the third
order of a polygon or rasterized shape.
"""
moments = cv2.moments(contour)
centroid = (-1, -1)
if moments["m00"] != 0:
centroid = (int(round(moments["m10"] / moments["m00"])),
int(round(moments["m01"] / moments["m00"])))
return centroid
def getVote(point, data):
"""
Looping though ROIs on a ballot and checking if the point is within on of the rectangles.
Returns corrosponding ROI name (politician) if condition is meet.
"""
for i in data:
if pointWithinRectangle(point, i["rect"]):
return i["name"]
def loadImagesFromPath(glob_path):
"""
Loads images from folder into list. Glob patterns can be used to select
specicific files e.g. path/to/*.jpg. This function might not be appropriate
when loading thousands of images since it is stored in memory.
"""
images = []
paths = glob(glob_path)
for path in paths:
img = cv2.imread(path)
images.append(img)
return images
def voteCounter(ballots, empty_ballot, data):
"""
Counting votes on ballots and returning list of votes and
dictionary of politicians and corrosponding counts. Inputs
is list of images of filled ballots, image of empty ballot
and data about ROIs and corrosponding names
"""
votes = []
rects = [i['rect'] for i in data]
for ballot in ballots:
warped = autoWarpImage(empty_ballot, ballot)
point = detectMark(warped, rects)
vote = getVote(point, data)
print(vote)
votes.append(vote)
counts = dict(Counter(votes))
return votes, counts
def countsToCSV(csv_path, counts):
"""
Saves vote counts to CSV
"""
with open(csv_path, 'w', newline='') as f:
w = csv.writer(f)
w.writerows(counts.items())
if __name__ == '__main__':
empty_path = "images/stemboks/stem_back.jpg"
empty = cv2.imread(empty_path)
data = np.load('data/stemboks_corrected.npy')
csv_path = 'temp/reuslt.csv'
ballots = loadImagesFromPath('images/stemboks/test/*.jpeg')
votes, counts = voteCounter(ballots,empty, data)
print(counts)
csv_path = "temp/test2.csv"
countsToCSV(csv_path, counts)
# cv2.waitKey(0)
# cv2.destroyAllWindows()