Skip to content

Commit 981b014

Browse files
authored
Merge pull request #6 from hlgirard/dev/cliexec
Version 0.1.3
2 parents bde79cf + a07fe50 commit 981b014

File tree

9 files changed

+324
-149
lines changed

9 files changed

+324
-149
lines changed

README.md

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -38,15 +38,17 @@ pip install .
3838

3939
### Quick start
4040

41-
Pass the labels and image directory on the command line to start labelling. Use the on-screen buttons to select a label for the current image and advance to the next one. Number keys correspond to labels and can be used instead.
41+
Simplabel can be started from the command line without any argument:
42+
```
43+
simplabel
44+
```
45+
You will be prompted to select a directory containing images to label. Add labels with the '+' button and start labeling. Number keys correspond to labels and can be used instead.
4246

47+
The target directory and/or labels can also be passed directly from the command line:
4348
```
4449
simplabel --labels dog cat bird --directory path/to/image/directory
4550
```
4651

47-
Note that simplabel can now be called without any arguments as follows: `simplabel`.
48-
The user will be prompted to select an image directory and labels can be added with the + button at the bottom of the screen.
49-
5052
After the first use, labels are stored in `labels.pkl` and the `--labels` argument is ignored.
5153

5254
### Command line arguments
@@ -56,9 +58,9 @@ After the first use, labels are stored in `labels.pkl` and the `--labels` argume
5658
- `-u, --user <USERNAME>` sets the username. Defaults to the OS login name if none is passed.
5759
- `-r, --redundant` does not display other labelers selections for independent labelling. Reconciliation and Make Master are unavailable in this mode.
5860
- `-v, --verbose` increases the verbosity level.
59-
- `--remove-label` tries to safely remove a label from the list saved in `labels.pkl`.
61+
- `--remove-label <LABEL>` tries to safely remove a label from the list saved in `labels.pkl` (must also pass `-d`)
6062
- `--reset-lock` overrides the lock preventing the same username from being used multiple times simultaneously.
61-
- `--delete-all` removes all files created by simplabel in the directory
63+
- `--delete-all` removes all files created by simplabel in the directory (must also pass `-d`)
6264

6365
### Multiuser
6466

@@ -82,7 +84,7 @@ with open("labeled_user1.pkl","rb") as f:
8284
Once you are done labelling, use the flow_to_directory tool to copy images to distinct directories by label
8385

8486
```
85-
flow_to_directory --rawDirectory data/raw --outDirectory data/labeled
87+
flow_to_directory --input-directory data/labeled --output-directory data/sorted
8688
```
8789

8890
### Python object

bin/flow_to_directory

Lines changed: 0 additions & 20 deletions
This file was deleted.

bin/simplabel

Lines changed: 0 additions & 99 deletions
This file was deleted.

setup.py

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,22 @@
44
long_description = f.read()
55

66
setup(name='simplabel',
7-
version='0.1.2',
7+
version='0.1.3',
88
description='Simple tool to manually label images in disctinct categories to build training datasets.',
99
long_description=long_description,
1010
long_description_content_type="text/markdown",
1111
url='https://github.com/hlgirard/Simplabel',
1212
author='Henri-Louis Girard',
1313
author_email='[email protected]',
1414
license='GPLv3',
15-
packages=find_packages(),
15+
packages=find_packages(exclude=["tests.*", "tests"]),
1616
install_requires=[
1717
'pillow',
1818
],
19-
scripts=[
20-
'bin/simplabel',
21-
'bin/flow_to_directory'
22-
],
23-
zip_safe=False)
19+
entry_points={
20+
'console_scripts': [
21+
'simplabel = simplabel.simplabel:main',
22+
'flow_to_directory = simplabel.flow_to_dict:main',
23+
],
24+
},
25+
zip_safe=False)

simplabel/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
from .simplabel import *
22

3-
__version__ = '0.1.0'
3+
__version__ = '0.1.3'

simplabel/utils.py renamed to simplabel/flow_to_directory.py

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
1+
import argparse
12
import os
23
import pickle
34
import shutil
45
import sys
6+
import tkinter as tk
57

6-
def flow_to_dict(rawDirectory, labelledDirectory):
8+
def flow_to_dict(rawDirectory, labelledDirectory=None):
79
'''
810
Copies labelled images to discting directories by label
911
@@ -18,11 +20,15 @@ def flow_to_dict(rawDirectory, labelledDirectory):
1820
# Detected users
1921
users = [f.split('_')[1].split('.')[0] for f in os.listdir(rawDirectory) if (f.endswith('.pkl') and f.startswith('labeled_'))]
2022

23+
if not users:
24+
print("No label files found in directory.")
25+
sys.exit()
26+
2127
# Open the labelled dictionary
2228
if 'master' in users:
2329
dictPath = rawDirectory + '/labeled_master.pkl'
2430
else:
25-
username = input("Enter username to flow [{}]:".format(users))
31+
username = input("Enter username to flow {}:".format(users))
2632
dictPath = rawDirectory + '/labeled_{}.pkl'.format(username)
2733

2834
if os.path.exists(dictPath):
@@ -34,6 +40,9 @@ def flow_to_dict(rawDirectory, labelledDirectory):
3440

3541
# Get all categories that exist in the dictionary
3642
categories = set(labelled_dict.values())
43+
# If no output directory is passed, use the input directory
44+
if not labelledDirectory:
45+
labelledDirectory = rawDirectory
3746
# Check existence of output directory
3847
if not os.path.exists(labelledDirectory):
3948
os.mkdir(labelledDirectory)
@@ -49,9 +58,17 @@ def flow_to_dict(rawDirectory, labelledDirectory):
4958
shutil.copy2(rawDirectory + '/' + image, labelDirect)
5059

5160

61+
def main():
62+
#Setup parser
63+
ap = argparse.ArgumentParser()
64+
ap.add_argument("-i", "--input-directory", default=os.getcwd(), help="Path of the directory containing the raw images and labeled.pkl file. Defaults to current directory")
65+
ap.add_argument("-o", "--output-directory", help="Path of the output directory, will be created if it does not exist")
66+
67+
args = ap.parse_args()
5268

53-
if __name__ == "__main__":
54-
rawDirectory = 'data/raw'
55-
labelledDirectory = 'data/labeled'
69+
# Get the variables from parser
70+
rawDirectory = args.input_directory
71+
outDirectory = args.output_directory
5672

57-
flow_to_dict(rawDirectory, labelledDirectory)
73+
flow_to_dict(rawDirectory, outDirectory)
74+

simplabel/simplabel.py

Lines changed: 95 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import argparse
12
import tkinter as tk
23
from tkinter.messagebox import askquestion, askokcancel, showwarning
34
from tkinter import simpledialog, filedialog
@@ -872,8 +873,8 @@ def keypress_handler(self,e):
872873
self.save()
873874
elif e.char == 'q':
874875
self.exit()
875-
elif e.char == 'd': # FIXME: debug option only
876-
self.debug_prints()
876+
#elif e.char == 'd': # For debug only
877+
# self.debug_prints()
877878
else:
878879
pass
879880

@@ -995,9 +996,97 @@ def is_locked(self):
995996
with open(self.filename, 'r') as f:
996997
return f.read() == 'locked'
997998

998-
if __name__ == "__main__":
999+
def delete_all_files(directory):
1000+
'''Deletes all files created by simplabel in a directory, this resets the labels and all saved data'''
1001+
1002+
save_files = [f for f in os.listdir(directory) if (f.endswith('.pkl') and f.startswith('label'))]
1003+
save_files.extend([f for f in os.listdir(directory) if f.startswith('.') and f.endswith('_lock.txt')])
1004+
if len(save_files) > 0:
1005+
response = input("Are you sure you want to delete all saved files: {}? (y/n)".format(save_files))
1006+
if response == 'y':
1007+
for f in save_files:
1008+
os.remove(os.path.join(directory,f))
1009+
print("Successfully deleted all saved files")
1010+
else:
1011+
print("Cancelled deletion, your files are exactly where you left them ;)")
1012+
else:
1013+
print("No files found in {}".format(directory))
1014+
return
1015+
1016+
def remove_label(directory, labelName):
1017+
'''Removes a label from the label file after verifying it isn't in use'''
1018+
1019+
labelToRemove = labelName.strip().lower().capitalize()
1020+
1021+
# Load the label file to check the presence of the label to remove
1022+
labelFile = directory + '/labels.pkl'
1023+
if os.path.isfile(labelFile):
1024+
with open(labelFile, 'rb') as f:
1025+
labels = pickle.load(f)
1026+
if labelToRemove not in labels:
1027+
print("No such label in labels.pkl")
1028+
return
1029+
else:
1030+
print("No label file found.")
1031+
return
1032+
1033+
1034+
# Get a list of users
1035+
users = [f.split('_')[1].split('.')[0] for f in os.listdir(directory) if (f.endswith('.pkl') and f.startswith('labeled_'))]
1036+
1037+
# Load each user's dictionary and check for the presense of the label to remove
1038+
for user in users:
1039+
dictPath = directory + "/labeled_" + user +".pkl"
1040+
with open(dictPath, "rb") as f:
1041+
userDict = pickle.load(f)
1042+
if labelToRemove in userDict.values():
1043+
print("Label {} is used by {}, cannot remove it from the list".format(labelToRemove, user))
1044+
return
1045+
1046+
# If the check have passed, remove the label from the list and resave the list
1047+
labels.remove(labelToRemove)
1048+
with open(labelFile, 'wb') as f:
1049+
pickle.dump(labels, f)
1050+
1051+
print("Successfully removed label {} from the list".format(labelToRemove))
1052+
return
1053+
1054+
def main():
1055+
1056+
#Setup parser
1057+
ap = argparse.ArgumentParser()
1058+
ap.add_argument("-d", "--directory", default=None, help="Path of the directory")
1059+
ap.add_argument("-l", "--labels", default=None, help="List of labels")
1060+
ap.add_argument("-v", "--verbose", action='count', default=0, help="Enable verbose mode")
1061+
ap.add_argument("-u", "--user", help="Set username for the current session")
1062+
ap.add_argument("-r", "--redundant", action='store_true', help="Redundant mode: do not show other labeler's selections")
1063+
ap.add_argument("--delete-all", action='store_true', help="Deletes all files created by simplabel in a directory, this resets the labels and all saved data")
1064+
ap.add_argument("--reset-lock", action='store_true', help="Overrides the lock in case of incorrect lockout")
1065+
ap.add_argument("--remove-label", help="Remove a label from the list")
1066+
1067+
args = ap.parse_args()
1068+
1069+
# Get the variables from parser
1070+
rawDirectory = args.directory
1071+
categories = args.labels
1072+
verbosity = args.verbose
1073+
username = args.user
1074+
bResetLock = args.reset_lock
1075+
bRedundant = args.redundant
1076+
1077+
# Reset all saved data if requested
1078+
if args.delete_all:
1079+
delete_all_files(rawDirectory)
1080+
sys.exit(0)
1081+
1082+
# Remove label
1083+
if args.remove_label:
1084+
if not rawDirectory:
1085+
print("No directory specified. You must pass the directory containing the label file with -d")
1086+
remove_label(rawDirectory, args.remove_label)
1087+
sys.exit(0)
1088+
1089+
# Launch the app
9991090
root = tk.Tk()
1000-
rawDirectory = "data/raw"
1001-
categories = ['Crystal', 'Clear']
1002-
MyApp = ImageClassifier(root, rawDirectory, categories, 2)
1091+
MyApp = ImageClassifier(root, directory = rawDirectory, categories = categories, verbose = verbosity, username = username, bResetLock = bResetLock, bRedundant = bRedundant)
10031092
tk.mainloop()

0 commit comments

Comments
 (0)