gazesim/code/recording/util/markers/marker.py

201 lines
6.2 KiB
Python

import os, sys
import Image
from random import sample
'''
IMPORTANT:
- When printing images generated with this code you HAVE to ignore page margins! (use GIMP)
- Markers on a board must be separated by white borders or ArUco won't detect them
'''
ARUCO_MARKER_CREATOR = '/home/mmbrian/temp/aruco-1.3.0/build/utils/aruco_create_marker '
DESTINATION_DIR = '/home/mmbrian/HiWi/etra2016_mohsen/code/recording/util/markers/'
DEFAULT_MARKER_SIZE_IN_PIXELS = 64
DEFAULT_MARKER_BORDER_IN_PIXELS = 7 # only applies for random boards
DEFAULT_MARKER_SIZE_IN_MM = 40
A4_PADDING_IN_PIXELS = 50
def createMarker(marker_id, size_in_pixels, format = 'png'):
assert marker_id >= 0 and marker_id < 1024, 'Invalid Marker ID. Must be in Range 0-1023'
cmd = ARUCO_MARKER_CREATOR + '{0} ' + DESTINATION_DIR + '{0}.' + format + ' {1} 0'
os.popen(cmd.format(marker_id, size_in_pixels))
def handleMarkers():
'''
Usage:
marker.py num_of_markers size_in_pixels
marker.py -i marker_id size_in_pixels
'''
if len(sys.argv) > 3:
try:
createMarker(int(sys.argv[2]), int(sys.argv[3]))
except:
print 'Usage: marker.py -i marker_id size_in_pixels'
else:
try:
num = int(sys.argv[1])
assert num>0 and num<=1024
except:
print 'Invalid number of markers. please specify a number between 1 and 1024'
return
size = DEFAULT_MARKER_SIZE_IN_PIXELS
try:
size = int(sys.argv[2])
print 'Marker size set to', size, 'px'
except:
print 'Marker size set to default size', size, 'px'
print 'Creating', num, 'random markers...'
generateRandomMarkers(num, size)
def generateRandomMarkers(num, size):
# for i in range(num):
for i, _id in enumerate(sorted(sample(range(1024), num))):
createMarker(_id, size)
print i+1, '/', num
print 'Finished.'
def computeDimensions():
A4W, A4H = 210, 297 # A4 dimension in millimeters
MM2IN = 25.4 # millimeters per inch
dpi = -1 # Must be given (use xdpyinfo under linux)
marker_width = -1 # Arbitrary value which must be given by user (in millimeters)
try:
dpi = int(sys.argv[2])
assert dpi>0
except:
print sys.argv
print 'Invalid dpi.'
return
marker_width = DEFAULT_MARKER_SIZE_IN_MM
try:
marker_width = int(sys.argv[3])
print 'Marker size set to', marker_width, 'mm'
except:
print 'Marker size set to default size', marker_width, 'mm'
# Width and Height of A4 image in pixels
xWidth = A4W / MM2IN * dpi
yWidth = A4H / MM2IN * dpi
# Marker width in pixels
mWidth = marker_width / MM2IN * dpi
e = mWidth - int(mWidth) # subpixel error in marker width
eMM = e / dpi * MM2IN # subpixel error in millimeters (report to user)
print 'Marker size finally set to', marker_width - eMM, 'mm'
# Converting pixel values to real integers
xWidth, yWidth, mWidth = int(xWidth), int(yWidth), int(mWidth)
return xWidth, yWidth, mWidth
def removeMarkers():
print 'Removing old markers...'
os.popen('rm ' + DESTINATION_DIR + '*.png') # Removing previous markers
def generateGrid():
'''
Generates a picture of a 9-point calibration grid using random markers, the size of which is
calculated such that after printing the marker size is of a specified length in millimeters
Usage: marker.py dpi marker_size_in_mm
'''
xWidth, yWidth, mWidth = computeDimensions()
# Create markers with this pixel size
removeMarkers()
print 'Generating new markers...'
generateRandomMarkers(9, mWidth)
# Stitch markers and generate grid
print 'Creating grid...'
markers = []
for f in os.listdir(DESTINATION_DIR):
if f.endswith('.png'):
markers.append(Image.open(f))
grid = Image.new("RGB", (xWidth, yWidth), (255, 255, 255))
for row in range(3):
if row == 0:
y = A4_PADDING_IN_PIXELS
elif row == 1:
y = (yWidth - mWidth) / 2
else:
y = yWidth - A4_PADDING_IN_PIXELS - mWidth
for col in range(3):
if col == 0:
x = A4_PADDING_IN_PIXELS
elif col == 1:
x = (xWidth - mWidth) / 2
else:
x = xWidth - A4_PADDING_IN_PIXELS - mWidth
grid.paste(markers[row*3+col], (x, y))
grid.save(DESTINATION_DIR + 'grid_board/grid.jpg')
print 'Finished.'
def generateRandomBoard():
xWidth, yWidth, mWidth = computeDimensions()
# Get number of random markers
try:
n = int(sys.argv[4])
assert n>0 and n<150 # no more than 150 markers allowed
except:
print 'Please choose between 1 to 20 markers'
return
# Create markers with this pixel size
removeMarkers()
print 'Generating new markers...'
generateRandomMarkers(n, mWidth)
# Stitch markers and generate board
print 'Creating board...'
markers = []
for f in os.listdir(DESTINATION_DIR):
if f.endswith('.png'):
markers.append(Image.open(f))
board = Image.new("RGB", (xWidth, yWidth), (255, 255, 255))
# Now we have to randomly append markers to board in a way that they do not collide
# To simplify this, we gridify the image based on marker size and choose a random sample
# of grids (not entirely random but in case markers are small enough it gives a good sample)
w = (xWidth - 2*A4_PADDING_IN_PIXELS) / mWidth
h = (yWidth - 2*A4_PADDING_IN_PIXELS) / mWidth
w, h = int(w), int(h) # w and h are maximum row and column size for the grid
# Now markers would take w*mWidth pixels in a row with no borders, applying border size:
w = ((xWidth - 2*A4_PADDING_IN_PIXELS) - (w-1)*DEFAULT_MARKER_BORDER_IN_PIXELS) / mWidth
h = ((yWidth - 2*A4_PADDING_IN_PIXELS) - (h-1)*DEFAULT_MARKER_BORDER_IN_PIXELS) / mWidth
w, h = int(w), int(h) # w and h now also count for marker borders
xOffset = A4_PADDING_IN_PIXELS + ((xWidth - 2*A4_PADDING_IN_PIXELS) - w*mWidth - (w-1)*DEFAULT_MARKER_BORDER_IN_PIXELS)/2
yOffset = A4_PADDING_IN_PIXELS + ((yWidth - 2*A4_PADDING_IN_PIXELS) - h*mWidth - (h-1)*DEFAULT_MARKER_BORDER_IN_PIXELS)/2
grids = [(xOffset + c*mWidth + c*DEFAULT_MARKER_BORDER_IN_PIXELS,
yOffset + r*mWidth + r*DEFAULT_MARKER_BORDER_IN_PIXELS) for r in xrange(h) for c in xrange(w)]
print len(grids), 'possible positions exist for markers...'
n = min(n, len(grids))
for i, pos in enumerate(sample(grids, n)):
board.paste(markers[i], pos)
board.save(DESTINATION_DIR + 'random_board/board.jpg')
print 'Finished.'
def main():
assert len(sys.argv) > 1, 'Invalid number of arguments.'
if sys.argv[1] == 'grid':
generateGrid()
elif sys.argv[1] == 'rand':
generateRandomBoard()
else:
handleMarkers()
if __name__ == '__main__':
main()