-
Notifications
You must be signed in to change notification settings - Fork 0
/
submission.py
130 lines (99 loc) · 3.79 KB
/
submission.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
# DO NOT RENAME THIS FILE
# This file enables automated judging
# This file should stay named as `submission.py`
#
# TAMU Datathon Challenge: Puzzle Solver
#
# Author: Cihat Kececi
#
# Import Python Libraries
import os
import numpy as np
from glob import glob
from PIL import Image
from itertools import permutations
# Import helper functions from utils.py
import utils
class Predictor:
"""
DO NOT RENAME THIS CLASS
This class enables automated judging
This class should stay named as `Predictor`
"""
def __init__(self):
"""
Initializes any variables to be used when making predictions
"""
self.all_permutations = list(permutations(range(0, 4)))
def make_prediction(self, img_path):
"""
DO NOT RENAME THIS FUNCTION
This function enables automated judging
This function should stay named as `make_prediction(self, img_path)`
INPUT:
img_path:
A string representing the path to an RGB image with dimensions 128x128
example: `example_images/1.png`
OUTPUT:
A 4-character string representing how to re-arrange the input image to solve the puzzle
example: `3120`
"""
# Load the image
img = Image.open(img_path)
img_array = np.asarray(img, dtype=np.float32)
split_img = utils.get_uniform_rectangular_split(img_array, 2, 2)
def vec_dist(x, y):
"""
This function is used for comparing the edges of the four pieces
"""
std = np.mean(np.std(x, axis=(0, 1)) + np.std(y, axis=(0, 1)))
if std < 4:
return np.inf
return np.mean(np.abs(x - y)) / std
def distance(top_left, top_right, bottom_left, bottom_right):
""""
Calculate the sum of the distances for a given permutation.
"""
return vec_dist(top_left[:, -1, :], top_right[:, 0, :]) \
+ vec_dist(top_left[-1, :, :], bottom_left[0, :, :]) \
+ vec_dist(top_right[-1, :, :], bottom_right[0, :, :]) \
+ vec_dist(bottom_left[:, -1, :], bottom_right[:, 0, :])
prediction = np.zeros(24)
# Calculate the distances for each permutation
for i, perm in enumerate(self.all_permutations):
src = np.argsort(perm)
prediction[i] = distance(*[split_img[j] for j in src])
# Select the permutation with the least distance
result = self.all_permutations[np.argmin(prediction)]
return ''.join(str(x) for x in result)
def main():
# It assumes that the train dataset is located at `./train`
# Local imports since they are optional dependencies
import multiprocessing as mp
PARALLEL_RUN = True
true_count, false_count = 0, 0
predictor = Predictor()
folders = os.listdir('train')
for i, folder_name in enumerate(folders):
print(f'Evaluating folder {i + 1}/{len(folders)}')
if PARALLEL_RUN:
pool = mp.Pool()
preds = pool.map(predictor.make_prediction, glob(f'train/{folder_name}/*'))
for pred in preds:
if pred == folder_name:
true_count += 1
else:
false_count += 1
else:
for img_name in glob(f'train/{folder_name}/*'):
prediction = predictor.make_prediction(img_name)
# print(f'{prediction} <=> {folder_name}')
if prediction == folder_name:
true_count += 1
else:
false_count += 1
print(f'True : {true_count}')
print(f'False: {false_count}')
print(f'Acc: {true_count / (true_count + false_count):.4f}')
if __name__ == '__main__':
main()