Merge pull request #1 from bnewcomb/Py3Compat

Fixes for Py3
This commit is contained in:
Ben Newcomb 2019-09-16 11:19:52 -05:00 committed by GitHub
commit e4183b986a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 175 additions and 5 deletions

170
README.md Normal file
View File

@ -0,0 +1,170 @@
# Photo Ranking With Python
![Screenshot](screenshot.jpg)
## What is this?
This is a tool that uses the [`Elo Ranking System`](http://en.wikipedia.org/wiki/Elo_rating_system) written in Python using:
1. [Matplotlib](https://matplotlib.org/)
2. [Numpy](https://www.numpy.org/)
3. [Pillow](https://python-pillow.org/)
4. [exifread](https://pypi.python.org/pypi/ExifRead)
### Features:
- Auto image rotation that the camera recored in the EXIF meta data
- Persistent state from execution to execution so you can pickup where you left off
- New photos that are added to the photo dir after initial ranking are picked up
## Install dependencies
Use your system's package manager to install Matplotlib & Numpy if you don't
already have them installed.
`$ pip install Matplotlib [--user]` <---------------------This should also install [Numpy](https://www.numpy.org/)
Next, you can use pip to install the EXIF image reader package [exifread](https://pypi.python.org/pypi/ExifRead).
`$ pip install exifread [--user]` - The --user flag is optional and it depends on your environment.
> Note for Windows Users - You may need to install [Pillow](https://python-pillow.org/) for jpg support.
> `$ pip install Pillow [--user]`
## How to rank photos
Once you have to dependencies installed, run `rank_photos.py` on the command
line passing it the directory of photos.
```
$ ./rank_photos.py -h
usage: rank_photos.py [-h] [-r N_ROUNDS] [-f FIGSIZE FIGSIZE] photo_dir
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
photo. After n-rounds, the results are reported.
positional arguments:
photo_dir The photo directory to scan for .jpg images
optional arguments:
-h, --help show this help message and exit
-r N_ROUNDS, --n-rounds N_ROUNDS
Specifies the number of rounds to pass through the
photo set (3)
-f FIGSIZE FIGSIZE, --figsize FIGSIZE FIGSIZE
Specifies width and height of the Matplotlib figsize
(20, 12)
```
For example, iterate over all photos three times:
```bash
$ ./rank_photos.py -r 3 ~/Desktop/example/
```
After the number of rounds complete, **`ranked.txt`** is written into the photo dir.
## Ranking work is cached
After completing N rounds of ranking, a file called `ranking_table.json` is
written into the photo dir. The next time `rank_photos.py` is executed with
the photo dir, this table is read in and ranking can continue where you left
off.
You can also add new photos the the directory and they will get added to the
ranked list even though they weren't included previously.
## Example
Suppose there is a dir containing some photos:
```bash
$ ls -1 ~/Desktop/example/
20160102_164732.jpg
20160109_151557.jpg
20160109_151607.jpg
20160109_152318.jpg
20160109_152400.jpg
20160109_152414.jpg
20160109_153443.jpg
```
The photos haven't been ranked yet. Let's rank them in 1 round, shall we?
<pre>$ ./rank_photos.py <b>-r 1</b> ~/Desktop/example/</pre>
Once the number of rounds is complete, the ranked list is dumped to the console:
```bash
Final Ranking:
Rank Score Matches Win % Filename
1 1433 2 100.00 20160109_152414.jpg
2 1414 3 66.67 20160109_151557.jpg
3 1401 2 50.00 20160109_153443.jpg
4 1400 2 50.00 20160102_164732.jpg
5 1387 3 33.33 20160109_151607.jpg
6 1383 3 33.33 20160109_152318.jpg
7 1382 3 33.33 20160109_152400.jpg
```
The ranked list is also written to the file **`ranked.txt`**. The raw data is
cached to the file **`ranking_table.json`**:
```bash
$ cat ~/Desktop/example/ranking_table.json
{
"photos" : [
{
"matches" : 2,
"wins" : 2,
"score" : 1432.736306793522,
"filename" : "20160109_152414.jpg"
},
{
"matches" : 3,
"wins" : 2,
"score" : 1413.760501639972,
"filename" : "20160109_151557.jpg"
},
{
"matches" : 2,
"wins" : 1,
"score" : 1400.736306793522,
"filename" : "20160109_153443.jpg"
},
{
"matches" : 2,
"wins" : 1,
"score" : 1400.0336900375303,
"filename" : "20160102_164732.jpg"
},
{
"matches" : 3,
"wins" : 1,
"score" : 1387.00607880615,
"filename" : "20160109_151607.jpg"
},
{
"matches" : 3,
"wins" : 1,
"score" : 1383.263693206478,
"filename" : "20160109_152318.jpg"
},
{
"matches" : 3,
"wins" : 1,
"score" : 1382.4634227228255,
"filename" : "20160109_152400.jpg"
}
]
}
```
> If you run the program again, the cached data is loaded and new match-ups can
> be continued using the cached data. If new photos were added, they also get
> added to the table data and are included in match-ups.

View File

@ -303,15 +303,15 @@ class EloTable:
n_photos = len(self._photos) n_photos = len(self._photos)
keys = self._photos.keys() keys = list(self._photos.keys())
for i in xrange(n_iterations): for i in range(n_iterations):
np.random.shuffle(keys) np.random.shuffle(keys)
n_matchups = n_photos / 2 n_matchups = n_photos / 2
for j in xrange(0, n_photos - 1, 2): for j in range(0, n_photos - 1, 2):
match_up = j / 2 match_up = j / 2
@ -482,12 +482,12 @@ better photo.
#-------------------------------------------------------------------------- #--------------------------------------------------------------------------
# dump ranked list to screen # dump ranked list to screen
print "Final Ranking:" print("Final Ranking:")
with open(ranked_txt, 'r') as fd: with open(ranked_txt, 'r') as fd:
text = fd.read() text = fd.read()
print text print(text)