Add data chaching and downsampling, speeds up the display. Added LEFT or RIGHT arrow keys to pick better photo
This commit is contained in:
parent
91f81a691a
commit
470559c392
157
rank_photos.py
157
rank_photos.py
|
@ -41,12 +41,11 @@ class Photo:
|
||||||
self._wins = wins
|
self._wins = wins
|
||||||
self._matches = matches
|
self._matches = matches
|
||||||
|
|
||||||
# read orientation with exifread
|
self._read_and_downsample()
|
||||||
|
|
||||||
with open(filename, 'rb') as fd:
|
|
||||||
tags = exifread.process_file(fd)
|
|
||||||
|
|
||||||
self._rotation = str(tags['Image Orientation'])
|
def data(self):
|
||||||
|
return self._data
|
||||||
|
|
||||||
|
|
||||||
def filename(self):
|
def filename(self):
|
||||||
|
@ -57,10 +56,6 @@ class Photo:
|
||||||
return self._matches
|
return self._matches
|
||||||
|
|
||||||
|
|
||||||
def rotation(self):
|
|
||||||
return self._rotation
|
|
||||||
|
|
||||||
|
|
||||||
def score(self, s = None, is_winner = None):
|
def score(self, s = None, is_winner = None):
|
||||||
|
|
||||||
if s is None:
|
if s is None:
|
||||||
|
@ -94,10 +89,77 @@ class Photo:
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def _read_and_downsample(self):
|
||||||
|
"""
|
||||||
|
Reads the image, performs rotation, and downsamples.
|
||||||
|
"""
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
# read image
|
||||||
|
|
||||||
|
f = self._filename
|
||||||
|
|
||||||
|
data = mpimg.imread(f)
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
# downsample
|
||||||
|
|
||||||
|
# the point of downsampling is so the images can be redrawn by the
|
||||||
|
# display as fast as possible, this is so one can iterate though the
|
||||||
|
# image set as quickly as possible. No one want's to wait around for
|
||||||
|
# the fat images to be loaded over and over.
|
||||||
|
|
||||||
|
# dump downsample, just discard columns-n-rows
|
||||||
|
|
||||||
|
M, N = data.shape[0:2]
|
||||||
|
|
||||||
|
MN = max([M,N])
|
||||||
|
|
||||||
|
step = int(MN / 800)
|
||||||
|
if step == 0: m_step = 1
|
||||||
|
|
||||||
|
data = data[ 0:M:step, 0:N:step, :]
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
# rotate
|
||||||
|
|
||||||
|
# read orientation with exifread
|
||||||
|
|
||||||
|
with open(f, 'rb') as fd:
|
||||||
|
tags = exifread.process_file(fd)
|
||||||
|
|
||||||
|
r = str(tags['Image Orientation'])
|
||||||
|
|
||||||
|
# rotate as necessary
|
||||||
|
|
||||||
|
if r == 'Horizontal (normal)':
|
||||||
|
pass
|
||||||
|
|
||||||
|
elif r == 'Rotated 90 CW':
|
||||||
|
|
||||||
|
data = np.rot90(data, 3)
|
||||||
|
|
||||||
|
elif r == 'Rotated 90 CCW':
|
||||||
|
|
||||||
|
data = np.rot90(data, 1)
|
||||||
|
|
||||||
|
else:
|
||||||
|
raise RuntimeError('Unhandled rotation "%s"' % r)
|
||||||
|
|
||||||
|
self._data = data
|
||||||
|
|
||||||
|
|
||||||
class Display(object):
|
class Display(object):
|
||||||
"""
|
"""
|
||||||
Given two photos, displays them with Matplotlib and provides a graphical
|
Given two photos, displays them with Matplotlib and provides a graphical
|
||||||
means of choosing the better photo.
|
means of choosing the better photo.
|
||||||
|
|
||||||
|
Click on the select button to pick the better photo.
|
||||||
|
|
||||||
|
~OR~
|
||||||
|
|
||||||
|
Press the left or right arrow key to pick the better photo.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
@ -139,11 +201,8 @@ class Display(object):
|
||||||
hspace = 0,
|
hspace = 0,
|
||||||
)
|
)
|
||||||
|
|
||||||
left = self._read(f1)
|
ax11.imshow(f1.data())
|
||||||
right = self._read(f2)
|
ax12.imshow(f2.data())
|
||||||
|
|
||||||
ax11.imshow(left)
|
|
||||||
ax12.imshow(right)
|
|
||||||
|
|
||||||
for ax in [ax11, ax12, ax21, ax22]:
|
for ax in [ax11, ax12, ax21, ax22]:
|
||||||
ax.set_xticklabels([])
|
ax.set_xticklabels([])
|
||||||
|
@ -160,36 +219,6 @@ class Display(object):
|
||||||
plt.show()
|
plt.show()
|
||||||
|
|
||||||
|
|
||||||
def _read(self, photo):
|
|
||||||
|
|
||||||
data = mpimg.imread(photo.filename())
|
|
||||||
|
|
||||||
r = photo.rotation()
|
|
||||||
|
|
||||||
#~ print "Rotation: ", r
|
|
||||||
#~ print " data.shape = ", data.shape
|
|
||||||
|
|
||||||
# rotate as necessary
|
|
||||||
|
|
||||||
if r == 'Horizontal (normal)':
|
|
||||||
pass
|
|
||||||
|
|
||||||
elif r == 'Rotated 90 CW':
|
|
||||||
|
|
||||||
data = np.rot90(data, 3)
|
|
||||||
|
|
||||||
elif r == 'Rotated 90 CCW':
|
|
||||||
|
|
||||||
data = np.rot90(data, 1)
|
|
||||||
|
|
||||||
else:
|
|
||||||
raise RuntimeError('Unhandled rotation "%s"' % r)
|
|
||||||
|
|
||||||
#~ print " data.shape = ", data.shape
|
|
||||||
|
|
||||||
return data
|
|
||||||
|
|
||||||
|
|
||||||
def _on_click(self, event):
|
def _on_click(self, event):
|
||||||
|
|
||||||
if event.inaxes == self._ax_select_left:
|
if event.inaxes == self._ax_select_left:
|
||||||
|
@ -201,8 +230,20 @@ class Display(object):
|
||||||
plt.close(self._fig)
|
plt.close(self._fig)
|
||||||
|
|
||||||
|
|
||||||
|
def _on_key_press(self, event):
|
||||||
|
|
||||||
|
if event.key == 'left':
|
||||||
|
self._choice = Photo.LEFT
|
||||||
|
plt.close(self._fig)
|
||||||
|
|
||||||
|
elif event.key == 'right':
|
||||||
|
self._choice = Photo.RIGHT
|
||||||
|
plt.close(self._fig)
|
||||||
|
|
||||||
|
|
||||||
def _attach_callbacks(self):
|
def _attach_callbacks(self):
|
||||||
self._fig.canvas.mpl_connect('button_press_event', self._on_click)
|
self._fig.canvas.mpl_connect('button_press_event', self._on_click)
|
||||||
|
self._fig.canvas.mpl_connect('key_press_event', self._on_key_press)
|
||||||
|
|
||||||
|
|
||||||
class EloTable:
|
class EloTable:
|
||||||
|
@ -214,12 +255,21 @@ class EloTable:
|
||||||
self._shuffled_keys = []
|
self._shuffled_keys = []
|
||||||
|
|
||||||
|
|
||||||
def add_photo(self, photo):
|
def add_photo(self, filename_or_photo):
|
||||||
|
|
||||||
filename = photo.filename()
|
if isinstance(filename_or_photo, str):
|
||||||
|
|
||||||
|
filename = filename_or_photo
|
||||||
|
|
||||||
if filename not in self._photos:
|
if filename not in self._photos:
|
||||||
self._photos[filename] = photo
|
self._photos[filename] = Photo(filename)
|
||||||
|
|
||||||
|
elif isinstance(filename_or_photo, Photo):
|
||||||
|
|
||||||
|
photo = filename_or_photo
|
||||||
|
|
||||||
|
if photo.filename() not in self._photos:
|
||||||
|
self._photos[photo.filename()] = photo
|
||||||
|
|
||||||
|
|
||||||
def get_ranked_list(self):
|
def get_ranked_list(self):
|
||||||
|
@ -308,6 +358,10 @@ def main():
|
||||||
Uses the Elo ranking algorithm to sort your images by rank. The program globs
|
Uses the Elo ranking algorithm to sort your images by rank. The program globs
|
||||||
for .jpg images to present to you in random order, then you select the better
|
for .jpg images to present to you in random order, then you select the better
|
||||||
photo. After n-rounds, the results are reported.
|
photo. After n-rounds, the results are reported.
|
||||||
|
|
||||||
|
Click on the "Select" button or press the LEFT or RIGHT arrow to pick the
|
||||||
|
better photo.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
parser = argparse.ArgumentParser(description = description)
|
parser = argparse.ArgumentParser(description = description)
|
||||||
|
|
||||||
|
@ -349,11 +403,14 @@ photo. After n-rounds, the results are reported.
|
||||||
#--------------------------------------------------------------------------
|
#--------------------------------------------------------------------------
|
||||||
# Read in table .json if present
|
# Read in table .json if present
|
||||||
|
|
||||||
|
sys.stdout.write("Reading in photos and downsampling ...")
|
||||||
|
sys.stdout.flush()
|
||||||
|
|
||||||
if os.path.isfile(ranking_table_json):
|
if os.path.isfile(ranking_table_json):
|
||||||
with open(ranking_table_json, 'r') as fd:
|
with open(ranking_table_json, 'r') as fd:
|
||||||
d = json.load(fd)
|
d = json.load(fd)
|
||||||
|
|
||||||
# create photos and add to table
|
# read photos and add to table
|
||||||
|
|
||||||
for p in d['photos']:
|
for p in d['photos']:
|
||||||
|
|
||||||
|
@ -366,10 +423,10 @@ photo. After n-rounds, the results are reported.
|
||||||
|
|
||||||
filelist = glob.glob('*.jpg')
|
filelist = glob.glob('*.jpg')
|
||||||
|
|
||||||
photos = [Photo(x) for x in filelist]
|
for f in filelist:
|
||||||
|
table.add_photo(f)
|
||||||
|
|
||||||
for p in photos:
|
print(" done!")
|
||||||
table.add_photo(p)
|
|
||||||
|
|
||||||
#--------------------------------------------------------------------------
|
#--------------------------------------------------------------------------
|
||||||
# Rank the photos!
|
# Rank the photos!
|
||||||
|
|
Loading…
Reference in New Issue