File size: 3,082 Bytes
6e2dee6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
from config import *
from time import time
from copy import copy

import functools, os, re
import sys, cv2, math, numpy as np
na = np.array

################################################################################

rows, columns = os.popen('stty size', 'r').read().split()
__strip_ansi_re = re.compile(r"""
    \x1b     # literal ESC
    \[       # literal [
    [;\d]*   # zero or more digits or semicolons
    [A-Za-z] # a letter
    """, re.VERBOSE).sub
def __strip_ansi(s):
    return __strip_ansi_re("", s)

################################################################################

def clock():
	global NC_CLOCK; return "(%8s)s" % round((time() - NC_CLOCK), 3)
def reset(): global NC_CLOCK; NC_CLOCK = time()

def warn(msg): print("\x1b[0;33;40m warn: \x1b[4;33;40m" + msg + "\x1b[0m")
def errn(msg): print("\n\x1b[0;37;41m errn: " + msg + "\x1b[0m\n"); sys.exit(1)

def head(msg): return "\x1b[5;30;43m " + msg + " \x1b[0m"
def call(msg): return "--> \x1b[5;31;40m@" + msg + "\x1b[0m"

def ribb(*msg, sep='-'):
	msg = ' '.join(msg)
	return msg + sep * int(int(columns) - len(__strip_ansi(msg)))

################################################################################

def image_scale(pts, scale):
	"""scale to original image size"""
	def __loop(x, y): return [x[0] * y, x[1] * y]
	return list(map(functools.partial(__loop, y=1/scale), pts))

def image_resize(img, height=500):
	"""resize image to same normalized area (height**2)"""
	pixels = height * height; shape = list(np.shape(img))
	scale = math.sqrt(float(pixels)/float(shape[0]*shape[1]))
	shape[0] *= scale; shape[1] *= scale
	img = cv2.resize(img, (int(shape[1]), int(shape[0])))
	img_shape = np.shape(img)
	return img, img_shape, scale

def image_transform(img, points, square_length=150):
	"""crop original image using perspective warp"""
	board_length = square_length * 8
	def __dis(a, b): return np.linalg.norm(na(a)-na(b))
	def __shi(seq, n=0): return seq[-(n % len(seq)):] + seq[:-(n % len(seq))]
	best_idx, best_val = 0, 10**6
	for idx, val in enumerate(points):
		val = __dis(val, [0, 0])
		if val < best_val:
			best_idx, best_val = idx, val
	pts1 = np.float32(__shi(points, 4 - best_idx))
	pts2 = np.float32([[0, 0], [board_length, 0], \
			[board_length, board_length], [0, board_length]])
	M = cv2.getPerspectiveTransform(pts1, pts2)
	W = cv2.warpPerspective(img, M, (board_length, board_length))
	return W

class ImageObject(object):
	images = {}; scale = 1; shape = (0, 0)

	def __init__(self, img):
		"""save and prepare image array"""
		self.images['orig'] = img
		self.images['main'], self.shape, self.scale = \
				image_resize(img) # downscale for speed
		self.images['test'] = copy(self.images['main'])

	def __getitem__(self, attr):
		"""return image as array"""
		return self.images[attr]

	def __setitem__(self, attr, val):
		"""save image to object"""
		self.images[attr] = val

	def crop(self, pts):
		"""crop using 4 points transform"""
		pts_orig = image_scale(pts, self.scale)
		img_crop = image_transform(self.images['orig'], pts_orig)
		self.__init__(img_crop)