Skip to content

Commit

Permalink
Merge branch 'proper_scantron_module'
Browse files Browse the repository at this point in the history
This branch breaks scantron.py into several files under the new scantron/
folder.
  • Loading branch information
philsc committed May 19, 2013
2 parents fef0490 + f8fc621 commit fdd4614
Show file tree
Hide file tree
Showing 7 changed files with 114 additions and 101 deletions.
32 changes: 16 additions & 16 deletions data_2013.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
#!/usr/bin/env python2

from scantron import Field
from scantron import ScantronField

data = [
Field('auton_high', 'Auton High', int),
Field('auton_mid', 'Auton Mid', int),
Field('auton_low', 'Auton Low', int),
Field('high', 'High', int),
Field('mid', 'Mid', int),
Field('low', 'Low', int),
Field('pyramid', 'Pyramid', int),
Field('missed', 'Missed', int),
Field('fouls', 'Fouls', int),
Field('tech_fouls', 'Tech fouls', int),
Field('defense', 'Defense', bool),
Field('pickup', 'Pickup', bool),
Field('noshow', 'Noshow', bool),
Field('brokedown', 'Brokedown', bool),
Field('dq', 'DQ', bool),
ScantronField('auton_high', 'Auton High', int),
ScantronField('auton_mid', 'Auton Mid', int),
ScantronField('auton_low', 'Auton Low', int),
ScantronField('high', 'High', int),
ScantronField('mid', 'Mid', int),
ScantronField('low', 'Low', int),
ScantronField('pyramid', 'Pyramid', int),
ScantronField('missed', 'Missed', int),
ScantronField('fouls', 'Fouls', int),
ScantronField('tech_fouls', 'Tech fouls', int),
ScantronField('defense', 'Defense', bool),
ScantronField('pickup', 'Pickup', bool),
ScantronField('noshow', 'Noshow', bool),
ScantronField('brokedown', 'Brokedown', bool),
ScantronField('dq', 'DQ', bool),
]
4 changes: 2 additions & 2 deletions generate_pdf.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import os
import sys
from argparse import ArgumentParser
from scantron import *
from scantron import ScantronGenerator, inch

# Make this file easier to use by adding nice arguments
parser = ArgumentParser(description='Generate scantron PDFs.')
Expand Down Expand Up @@ -47,7 +47,7 @@
quit(1)

# If everything went well, proceed to generate the PDF
st = Scantron(args.output, spacing=0.3*inch)
st = ScantronGenerator(args.output, spacing=0.3*inch)
st.set_box_sizes(box_size=0.2*inch, box_spacing=0.3*inch)
st.populate(data, matches=args.num_matches, collate=args.collate)
st.save()
4 changes: 4 additions & 0 deletions scantron/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
from generator import ScantronGenerator
from parser import ScantronParser
from data import ScantronField
from reportlab.lib.units import inch
8 changes: 8 additions & 0 deletions scantron/data.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#!/usr/bin/env python2


class ScantronField:
def __init__(self, name, label, fieldType):
self.name = name
self.label = label
self.fieldType = fieldType
68 changes: 1 addition & 67 deletions scantron.py → scantron/generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,67 +5,8 @@
from reportlab.pdfgen import canvas
from qrcode import *

import numpy as np
import scipy as sp
from scipy import ndimage
from PIL import Image, ImageDraw
import math


class ScantronParser:
def __init__(self):
pass


def scan(self, data, path):
img = Image.open(path).convert('RGB')
im = sp.misc.fromimage(img, flatten=True)
im = np.where(im > 128, 0, 1)
label_im, num = ndimage.label(im, structure=np.ones((3, 3)).tolist())
centroids = ndimage.measurements.center_of_mass(im, label_im, xrange(1,
num+1))
slices = ndimage.find_objects(label_im)

squares = []

for i in range(len(slices)):
sub_img = np.where(label_im[slices[i]] == i + 1, 1, 0)
num_ones = np.sum(sub_img)
num_all = sub_img.size
shape = sub_img.shape

ratio = float(shape[0]) / float(shape[1])
darkness = float(num_ones)/float(num_all)

if darkness > 0.95 and abs(ratio - 1.0) < 0.1 and shape[0] > 14:
x1, x2 = slices[i][1].start, slices[i][1].stop
y1, y2 = slices[i][0].start, slices[i][0].stop

draw = ImageDraw.Draw(img)
draw.rectangle([x1, y1, x2, y2], outline='blue')
del draw

squares.append(i)

if len(squares) != 3:
print('Could not uniquely identify the three page markers.')
raise Exception

squares = zip(squares, map(lambda s: sum(centroids[s]), squares))
squares = sorted(squares, key=lambda x: x[1])

for s in squares:
print('square ' + str(s))

tl = centroids[squares[0][0]]
bl = centroids[squares[1][0]]
br = centroids[squares[2][0]]

rotation = math.atan2(bl[1] - tl[1], bl[0] - tl[0])
print('rotation: ' + str(rotation))


class Scantron:
class ScantronGenerator:
def __init__(self, filename, spacing=0.3*inch):
self._fontSize = 0.15*inch

Expand Down Expand Up @@ -217,10 +158,3 @@ def populate(self, data, matches=1, collate='on'):

def save(self):
self._canvas.save()


class Field:
def __init__(self, name, label, fieldType):
self.name = name
self.label = label
self.fieldType = fieldType
60 changes: 60 additions & 0 deletions scantron/parser.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
#!/usr/bin/env python2

import numpy as np
import scipy as sp
from scipy import ndimage
from PIL import Image, ImageDraw
import math


class ScantronParser:
def __init__(self):
pass


def scan(self, data, path):
img = Image.open(path).convert('RGB')
im = sp.misc.fromimage(img, flatten=True)
im = np.where(im > 128, 0, 1)
label_im, num = ndimage.label(im, structure=np.ones((3, 3)).tolist())
centroids = ndimage.measurements.center_of_mass(im, label_im, xrange(1,
num+1))
slices = ndimage.find_objects(label_im)

squares = []

for i in range(len(slices)):
sub_img = np.where(label_im[slices[i]] == i + 1, 1, 0)
num_ones = np.sum(sub_img)
num_all = sub_img.size
shape = sub_img.shape

ratio = float(shape[0]) / float(shape[1])
darkness = float(num_ones)/float(num_all)

if darkness > 0.95 and abs(ratio - 1.0) < 0.1 and shape[0] > 14:
x1, x2 = slices[i][1].start, slices[i][1].stop
y1, y2 = slices[i][0].start, slices[i][0].stop

draw = ImageDraw.Draw(img)
draw.rectangle([x1, y1, x2, y2], outline='blue')
del draw

squares.append(i)

if len(squares) != 3:
print('Could not uniquely identify the three page markers.')
raise Exception

squares = zip(squares, map(lambda s: sum(centroids[s]), squares))
squares = sorted(squares, key=lambda x: x[1])

for s in squares:
print('square ' + str(s))

tl = centroids[squares[0][0]]
bl = centroids[squares[1][0]]
br = centroids[squares[2][0]]

rotation = math.atan2(bl[1] - tl[1], bl[0] - tl[0])
print('rotation: ' + str(rotation))
39 changes: 23 additions & 16 deletions testcase.py
Original file line number Diff line number Diff line change
@@ -1,30 +1,30 @@
#!/usr/bin/env python2

from scantron import *
from scantron import ScantronGenerator, ScantronField, inch
import PythonMagick
from pyPdf import PdfFileReader
from PIL import Image
from PIL import Image, ImageOps

data = [
Field('foo', 'Foo foo foo', int),
Field('bar', 'Bar bar bar', int),
Field('baz', 'Baz baz baz', int),
Field('laber', 'Laber laber', bool),
test_data = [
ScantronField('foo', 'Foo foo foo', int),
ScantronField('bar', 'Bar bar bar', int),
ScantronField('baz', 'Baz baz baz', int),
ScantronField('laber', 'Laber laber', bool),
]

# Generate PDF
st = Scantron('test.pdf')
st.set_box_sizes(box_size=0.2*inch, box_spacing=0.3*inch)
st.populate(data, matches=1, collate='no')
st.save()
# Generate scantron PDF
pdf = ScantronGenerator('test.pdf')
pdf.set_box_sizes(box_size=0.2*inch, box_spacing=0.3*inch)
pdf.populate(test_data, matches=1, collate='no')
pdf.save()

# Convert PDF to a series of pictures
pages = []

pdf = PdfFileReader(file('test.pdf', 'rb'))
num_pages = pdf.getNumPages()

for page in range(pdf.getNumPages()):
#page += 1
for page in range(num_pages):
name = 'test_image_%d.png' % page

im = PythonMagick.Image()
Expand All @@ -35,9 +35,16 @@
pages.append(name)

# Create a series of transformations to apply
def rotate(im, deg):
blank = Image.new('L', im.size, 255)
mask = blank.rotate(deg, expand=False)
mask = ImageOps.invert(mask)
rot = im.rotate(deg, expand=False)
return Image.composite(blank, rot, mask)

transformations = [
lambda x: x.rotate(10, expand=False),
lambda x: x.rotate(-10, expand=False),
lambda x: rotate(x, 10),
lambda x: rotate(x, -10),
]

tf = 0
Expand Down

0 comments on commit fdd4614

Please sign in to comment.