Skip to content
Snippets Groups Projects
Commit 98a3fe5c authored by André Anjos's avatar André Anjos :speech_balloon:
Browse files

Initial commit

parents
Branches
Tags
No related merge requests found
*~
*.swp
*.pyc
bin
eggs
parts
.installed.cfg
.mr.developer.cfg
*.egg-info
src
develop-eggs
This diff is collapsed.
include README.rst bootstrap-buildout.py buildout.cfg requirements.txt version.txt
recursive-include doc *.py *.rst *.png *.ico
recursive-include bob/ip/annotator/data *.jpg
Image Key Point Annotation Tool
===============================
A small TkInter-based keypoint annotation tool for images written in Python.
Installation
------------
Follow our `binary installation
<https://github.com/idiap/bob/wiki/Binary-Installation>`_ instructions. Then,
using the Python interpreter inside that distribution, bootstrap and buildout
this package::
$ python bootstrap-buildout.py
$ ./bin/buildout
Annotation format
-----------------
The annotation file, result of annotating an image, contains points in the
``(y,x)`` (Bob-style) format. Every line corresponds to one point annotated on
the original image.
Usage
-----
To start annotating, just pass a single image to the ``./bin/annotate.py``
program. A window will open and allow you to annotate a variable number of
points in it. The output of the annotation will be placed on the current
directory, with the same filename as the original input file::
$ bin/annotate.py example/image.jpg
After a successful annotation session, the file ``image.txt`` should be
available with all the points you have touched during the annotation process.
Available keyboard shortcuts
----------------------------
During the annotation process, on the image window, you can use the following
keyboard shortcuts to optimise your work.
* ``?``: this help message
* ``x``: places keypoint under mouse cursor
* ``d``: deletes the last annotation
* ``s``: saves current annotations
* ``q``: quits the application, saving annotations
* ``D``: deletes annotations for the current image
* ``<Esc>`` Quits the application, does not save anything
Mouse
-----
You can use the mouse to either drag-and-drop keypoints or move the closest
keypoint to the clicked location.
# see https://docs.python.org/3/library/pkgutil.html
from pkgutil import extend_path
__path__ = extend_path(__path__, __name__)
# see https://docs.python.org/3/library/pkgutil.html
from pkgutil import extend_path
__path__ = extend_path(__path__, __name__)
bob/ip/annotator/data/lena.jpg

48.3 KiB

#!/usr/bin/env python
# vim: set fileencoding=utf-8 :
HELP_MESSAGE = """A TkInter-based keypoint annotation tool for images
This tool treats any number of image inputs, one after the other. As images are
loaded, annotations for the previous image are saved on a text file, following
a simple format (y,x) keeping the order in which they where inserted.
Keyboard shortcuts
------------------
?: this help message
a: places new annotation under mouse cursor
d: deletes the last annotation
D: deletes all annotations
n: moves to the next image
p: moves to the previous image
s: saves current annotations
q: quits the application, saving annotations for the current image
<Esc>: Quits the application, does not save anything
Annotation displacement
-----------------------
h | Left: move last annotation by 1 pixel to the left
l | Right: move last annotation by 1 pixel to the right
k | Up: move last annotation by 1 pixel up
j | Down: move last annotation by 1 pixel down
Shift + movement: move last annotation by 5 pixels on that direction
"""
import os
import sys
import time
from operator import itemgetter
import Tkinter as tkinter
from PIL import Image, ImageTk
import numpy.linalg
import bob.io.base
import bob.io.image
from . import io as annotator_io
import logging
logger = logging.getLogger()
COLOR_ACTIVE = "yellow"
COLOR_INACTIVE = "white"
SHIFT = 0x0001
def _uniq(seq, idfun=None):
'''Order preserving uniq for lists
See: https://www.peterbe.com/plog/uniqifiers-benchmark
'''
if idfun is None:
def idfun(x): return x
seen = {}
result = []
for item in seq:
marker = idfun(item)
if marker in seen: continue
seen[marker] = 1
result.append(item)
return result
class HelpDialog(tkinter.Toplevel):
def __init__(self, parent, message):
tkinter.Toplevel.__init__(self, parent)
self.transient(parent)
self.title('help')
self.parent = parent
self.result = None
body = tkinter.Frame(self, width=100, height=400)
# Now build the dialog geometry
buttonbox = tkinter.Frame(body, height=20)
w = tkinter.Button(buttonbox, text="Dismiss", command=self.on_dismiss,
default=tkinter.ACTIVE)
w.pack(side=tkinter.RIGHT)
self.bind("<Return>", self.on_dismiss)
self.bind("<Escape>", self.on_dismiss)
buttonbox.pack(side=tkinter.BOTTOM)
textbox = tkinter.Frame(body, height=380)
self.initial_focus = t = tkinter.Text(textbox)
t.insert(tkinter.INSERT, message) #fill in contents
scrollbar = tkinter.Scrollbar(textbox)
scrollbar.pack(side=tkinter.RIGHT, fill=tkinter.Y)
t.config(yscrollcommand=scrollbar.set)
scrollbar.config(command=t.yview)
t.config(state=tkinter.DISABLED)
t.pack(side=tkinter.TOP)
textbox.pack(side=tkinter.TOP)
body.pack(padx=5, pady=5)
self.grab_set()
if not self.initial_focus: self.initial_focus = self
self.protocol("WM_DELETE_WINDOW", self.on_dismiss)
self.geometry("+%d+%d" % (parent.winfo_rootx()+50,
parent.winfo_rooty()+50))
self.initial_focus.focus_set()
self.wait_window(self)
def on_dismiss(self, event=None):
# put focus back to the parent window
self.parent.focus_set()
self.destroy()
class AnnotatorApp(tkinter.Tk):
"""A wrapper for the annotation application
Parameters:
filelist (list): A list of paths to images that will be annotated
outputdir (str): A path that will be used as the base directory for the
recording the annotations. If this directory does not exist, it will be
created.
zoom (float, Optional): The zoom level for the image. In case it is greater
than 1.0, then the image will be zoomed in. Otherwise, it will be zoomed
out. Annotations will be take relatively to the original image.
marker_radius (int, Optional): The number of pixels in the original image
each annotation marker will occupy.
pixel_skip (int, Optional): The number of pixels skipped every time the
user uses a motion key with the Shift key pressed.
"""
def __init__(self, filelist, outputdir, zoom=1.0, marker_radius=5,
pixel_skip=5, *args, **kwargs):
tkinter.Tk.__init__(self, *args, **kwargs)
self.title("annotate")
# setup parameters
filelist = _uniq(filelist)
if len(filelist) == 1:
self.basedir = os.path.dirname(filelist[0])
else:
self.basedir = os.path.commonprefix(filelist)
self.filelist = filelist
self.outputdir = outputdir
self.zoom = zoom
self.marker_radius = marker_radius
self.skip_factor = pixel_skip
# setup internal variables
self.curr_pos = 0 #the current position in the file list we're treating
self.curr_image = None #the actual pixels of the image in a PIL Image
self.curr_photo = None #the Tk widget containing the image
self.annotations = [] #points to the current frame
self.annotation_widgets = [] #the widgets related to the annotations
self.canvas = None #the main canvas
self.update_image()
def zoom_compensated(self):
"""Returns zoom-compensated annotations"""
def rounded(x, y, z):
return (int(round(float(x)/z)), int(round(float(y)/z)))
return [rounded(x,y,self.zoom) for k in self.annotations for x,y in k]
def update_image(self):
"""Updates the image displayed"""
# loads the current image, creates the image
self.image = Image.fromarray(bob.io.load(self.file_list[self.curr_pos]))
if self.canvas is None:
# creates the image canvas
self.canvas = tkinter.Canvas(self, width=self.image.width,
height=self.image.height)
self.canvas.pack(side=tkinter.TOP)
# creates the status bar - bellow the image canvas
self.bottom_frame = tkinter.Frame(self)
self.bottom_frame.pack(side=tkinter.BOTTOM, fill=tkinter.BOTH)
self.text_status = tkinter.StringVar()
self.label_status = tkinter.Label(self.bottom_frame,
textvariable=self.text_status)
self.label_status.pack(side=tkinter.LEFT)
else:
# resizes the canvas
self.canvas.resize(width=self.image.width,
height=self.image.height)
# set or replace the current frame image
self.curr_photo = ImageTk.PhotoImage(self.image, Image.ANTIALIAS)
self.canvas.itemconfig(self.curr_frame, image=self.curr_photo)
# show keypoints if they exist
candidate_annotation = os.path.join(self.outputdir,
self.filelist[self.curr_pos])
if os.path.exists(candidate_annotation):
self.annotations = annotator_io.load(candidate_annotation)
self.create_annotation_widgets()
else:
self.annotations = []
# resize all dialog boxes by default to be 200px wide
self.option_add("*Dialog.msg.wrapLength", "200p")
# some keyboard and mouse bindings
self.add_keyboard_bindings()
# Capture closing the app -> use to save the file
self.protocol("WM_DELETE_WINDOW", self.on_quit)
self.bind("q", self.on_quit)
self.bind("<Escape>", self.on_quit_no_saving)
self.bind("s", self.save)
self.bind("D", self.remove_all_annotations)
self.update_status_bar()
def update_status_bar(self):
# updates the status bar
if not self.annotations:
annotated = '(no annotations)'
else:
annotated = '(%d annotations loaded)' % len(self.annotations)
self.text_status.set('%s (%03d/%03d) - %s - press ? for help' % \
(path, self.curr_pos+1, len(self.filelist), annotated))
def cross(y, x, text):
"""Defines a cross + number in terms of a center and a radius"""
#points = (x, y-r, x, y+r, x, y, x-r, y, x+r, y, x, y)
w = self.marker_radius
w3 = 3*w;
points = (
x-w, y-w3,
x+w, y-w3,
x+w, y-w,
x+w3, y-w,
x+w3, y+w,
x+w, y+w,
x+w, y+w3,
x-w, y+w3,
x-w, y+w,
x-w3, y+w,
x-w3, y-w,
x-w, y-w,
)
# text - not modifiable for the color
t = self.canvas.create_text((x-w3, y-w3), anchor=tkinter.SE,
fill='black', tags="keypoint", state=tkinter.NORMAL,
justify=tkinter.RIGHT, text=text)
bbox = self.canvas.bbox(t)
self.canvas.itemconfig(t, state=tkinter.HIDDEN)
# background drop shadow
s = self.canvas.create_rectangle(bbox, fill=COLOR_INACTIVE,
tags="annotation", state=tkinter.HIDDEN)
# text on the top of the drop shadow
self.canvas.tag_raise(t)
poly = self.canvas.create_polygon(points, outline='black',
fill=COLOR_INACTIVE, tags="keypoint", width=1.0, state=tkinter.HIDDEN)
# [0]: (polygon) activate-able (not hidden);
# [1]: (shadow) activate-able (hidden);
# [2]: (text) not activate-able (hidden);
return (poly, s, t)
def create_annotation_widgets(self):
"""Creates the annotation widgets (displays them as well)"""
self.annotation_widgets = []
for i, (y, x) in enumerate(self.annotations):
self.annotation_widgets.append(cross(y, x, str(i)))
def save(self, *args, **kwargs):
"""Action executed when we need to save the current annotations"""
stem = os.path.relpath(self.filelist[self.curr_pos], self.basedir)
output_path = os.path.join(self.outputdir, stem)
output_dir = os.path.dirname(output_path)
if not os.path.exists(output_dir):
os.makedirs(output_dir)
if self.annotations:
file_save(self.zoom_compensated(), self.output_path, backup=True)
self.text_status.set('Saved to `%s\'' % output_path)
else:
self.text_status.set('No annotations to save')
def on_quit_no_saving(self, *args, **kwargs):
"""On quit we either dump the output to screen or to a file."""
if self.annotations:
logger.warn("Lost annotations for %s", )
self.destroy()
def on_quit(self, *args, **kwargs):
"""On quit we either dump the output to screen or to a file."""
self.save(*args, **kwargs)
self.on_quit_no_saving(*args, **kwargs)
def on_help(self, event):
"""Creates a help dialog box with the currently enabled commands"""
dialog = HelpDialog(self, HELP_MESSAGE)
def change_frame(self, event):
"""Advances to the next or rewinds to the previous frame"""
move = 0
if event.keysym in ('n', 'N') or event.num in (4,):
self.curr_pos += 1
if self.curr_pos >= len(self.filelist):
self.curr_pos = len(self.filelist) - 1
self.text_status.set('[warning] cannot go beyond end')
return
if event.keysym in ('p', 'P') or event.num in (5,):
self.curr_pos -= 1
if self.curr_pos < 0:
self.text_status.set('[warning] cannot go beyond first image')
self.curr_pos = 0
return
self.update_image()
def add_annotation(self, event):
"""Adds the given annotation position immediately"""
self.annotations.append((event.y, event.x))
self.annotation_widgets.append(self.cross(event.y, event.x,
str(len(self.annotations))))
self.update_status_bar()
def remove_last_annotation(self, event):
"""Removes the last annotation"""
self.annotations.pop()
for k in self.annotation_widgets.pop(): k.destroy()
def remove_all_annotations(self, event):
"""Delete current frame annotations and reset the view"""
while self.annotations: self.remove_last_annotation()
def on_show_labels(self, event):
"""Shows labels"""
for widgets in self.annotation_widgets:
for k in widgets[1:]:
self.canvas.itemconfig(k, state=tkinter.NORMAL)
def on_hide_labels(self, event):
"""Hide labels"""
for widgets in self.annotation_widgets:
for k in widgets[1:]:
self.canvas.itemconfig(o, state=tkinter.HIDDEN)
def move_last_annotation(self, event):
"""Moves the last annotated keypoint using the keyboard"""
# move the object the appropriate amount
dx, dy = (0, 0)
if event.keysym in ('Right', 'l', 'L'): dx = 1
elif event.keysym in ('Left', 'h', 'H'): dx = -1
elif event.keysym in ('Up', 'k', 'K'): dy = -1
elif event.keysym in ('Down', 'j', 'J'): dy = 1
if event.state & SHIFT: dx *= self.skip_factor; dy *= self.skip_factor
for k in self.annotation_widgets[-1]: self.canvas.move(k, dx, dy)
self.annotations[-1][1] += dy
self.annotations[-1][0] += dx
self.update_status_bar()
def add_keyboard_bindings(self):
"""Adds mouse bindings to the given widget"""
# add a given annotation (marked in white)
self.bind("a", self.add_annotation)
self.bind("d", self.remove_annotation)
self.bind("D", self.remove_all_annotations)
self.bind("n", self.change_frame)
self.bind("p", self.change_frame)
self.bind("N", self.change_frame)
self.bind("P", self.change_frame)
# motion keys - move frame or keypoint depending on keypoint focus
self.bind("<Right>", self.move_last_annotation)
self.bind("<Shift-Right>", self.move_last_annotation)
self.bind("<Left>", self.move_last_annotation)
self.bind("<Shift-Left>", self.move_last_annotation)
self.bind("<Up>", self.move_last_annotation)
self.bind("<Shift-Up>", self.move_last_annotation)
self.bind("<Down>", self.move_last_annotation)
self.bind("<Shift-Down>", self.move_last_annotation)
self.bind("h", self.move_last_annotation)
self.bind("H", self.move_last_annotation)
self.bind("l", self.move_last_annotation)
self.bind("L", self.move_last_annotation)
self.bind("k", self.move_last_annotation)
self.bind("K", self.move_last_annotation)
self.bind("j", self.move_last_annotation)
self.bind("J", self.move_last_annotation)
# show text labels with Alt pressed
self.bind("<KeyPress-Alt_L>", self.on_show_labels)
self.bind("<KeyRelease-Alt_L>", self.on_hide_labels)
self.bind("?", self.on_help)
#!/usr/bin/env python
# vim: set fileencoding=utf-8 :
# Andre Anjos <andre.anjos@idiap.ch>
# Fri 29 Jun 2012 13:42:57 CEST
"""A set of utilities and library functions to handle keypoint annotations."""
import os
import six
def save(data, fp, backup=False):
"""Saves a given data set to a file
Parameters:
data (numpy.ndarray): A dictionary where the keys are frame numbers and the
values are lists of tuples indicating each of the keypoints in (x, y)
fp (File, str): The name of a file, with full path, to be used for recording
the data or an already opened file-like object, that accepts the "write()"
call.
backup (boolean, Optional): If set, backs-up a possibly existing file path
before overriding it. Note this is not valid in case 'fp' above points to
an opened file.
"""
if isinstance(fp, six.string_types):
if backup and os.path.exists(fp):
bname = fp + '~'
if os.path.exists(bname): os.unlink(bname)
os.rename(fp, bname)
fp = open(fp, 'wt')
return numpy.savetxt(fp, data, fmt='%d')
def load(fp):
"""Loads a given data set from file
Parameters:
fp (File, str): The name of a file, with full path, to be used for
reading the data or an already opened file-like object, that accepts
the "read()" call.
Returns:
numpy.ndarray: Containing the matrix of loaded annotations, one per row, in
the format (y, x). That is Bob's style.
"""
if isinstance(fp, six.string_types):
fp = open(fp, 'rt')
return numpy.loadtxt(fp)
#!/usr/bin/env python
# vim: set fileencoding=utf-8 :
"""A TkInter-based keypoint annotation tool for images (%(version)s)
Usage:
%(prog)s [-v ...] [-o=<path>] <image>...
Arguments:
<image> Paths to the images to be annotated. The paths given will be made
absolute and duplicates will be removed. The common directory
between all images is saved as a base directory for the application.
The output directory will contain the annotations taking into
consideration the path structure of input images starting from that
base directory. For example, if input images are "A/B/img1.png" and
"A/C/img2.png", then the output annotations will be available in
"<output-path>/B/img1.txt" and "<output-path>/C/img2.txt"
Options:
-h, --help Shows this help message and exit
-V, --version Shows program's version number and exit
-o, --output=<path> The output directory where annotations will be saved
[default: .]
-v, --verbose If set, increases the verbosity level - may be given
twice, to enable debug output
Examples:
Annotate two images, results in the directory `./tmp':
$ %(prog)s -v --output=./tmp A/B/img1.png A/C/img2.png
Annotate all images, results in the directory `./tmp':
$ %(prog)s -v --output=./tmp A/*/*.png
Annotate the image of Lena, get the result in `lena.txt`:
$ %(prog)s -v bob/ip/annotator/data/lena.jpg
"""
import logging
logger = logging.getRootLogger()
from docopt import docopt
from ..gui import AnnotatorApp
def main(user_input=None):
# Parse the command-line arguments
if user_input is not None:
arguments = user_input
else:
arguments = sys.argv[1:]
completions = dict(
prog = os.path.basename(sys.argv[0]),
version = pkg_resources.require('bob.ip.annotator')[0].version,
)
args = docopt(
__doc__ % completions,
argv=arguments,
options_first=True,
version=completions['version'],
)
# If the user wants more verbosity, lower the level
if args['--verbose'] == 1: logger.setLevel(logging.INFO)
elif args['--verbose'] >= 2: logger.setLevel(logging.DEBUG)
args['--output'] = os.path.abspath(os.path.expanduser(args['--output']))
args['<image>'] = [os.path.abspath(k) for k in args['<image>']]
app = AnnotatorApp(args.filelist, args['--output'])
app.mainloop()
##############################################################################
#
# Copyright (c) 2006 Zope Foundation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""Bootstrap a buildout-based project
Simply run this script in a directory containing a buildout.cfg.
The script accepts buildout command-line options, so you can
use the -c option to specify an alternate configuration file.
"""
import os
import shutil
import sys
import tempfile
from optparse import OptionParser
__version__ = '2015-07-01'
# See zc.buildout's changelog if this version is up to date.
tmpeggs = tempfile.mkdtemp(prefix='bootstrap-')
usage = '''\
[DESIRED PYTHON FOR BUILDOUT] bootstrap.py [options]
Bootstraps a buildout-based project.
Simply run this script in a directory containing a buildout.cfg, using the
Python that you want bin/buildout to use.
Note that by using --find-links to point to local resources, you can keep
this script from going over the network.
'''
parser = OptionParser(usage=usage)
parser.add_option("--version",
action="store_true", default=False,
help=("Return bootstrap.py version."))
parser.add_option("-t", "--accept-buildout-test-releases",
dest='accept_buildout_test_releases',
action="store_true", default=False,
help=("Normally, if you do not specify a --buildout-version, "
"the bootstrap script and buildout gets the newest "
"*final* versions of zc.buildout and its recipes and "
"extensions for you. If you use this flag, "
"bootstrap and buildout will get the newest releases "
"even if they are alphas or betas."))
parser.add_option("-c", "--config-file",
help=("Specify the path to the buildout configuration "
"file to be used."))
parser.add_option("-f", "--find-links",
help=("Specify a URL to search for buildout releases"))
parser.add_option("--allow-site-packages",
action="store_true", default=False,
help=("Let bootstrap.py use existing site packages"))
parser.add_option("--buildout-version",
help="Use a specific zc.buildout version")
parser.add_option("--setuptools-version",
help="Use a specific setuptools version")
parser.add_option("--setuptools-to-dir",
help=("Allow for re-use of existing directory of "
"setuptools versions"))
options, args = parser.parse_args()
if options.version:
print("bootstrap.py version %s" % __version__)
sys.exit(0)
######################################################################
# load/install setuptools
try:
from urllib.request import urlopen
except ImportError:
from urllib2 import urlopen
ez = {}
if os.path.exists('ez_setup.py'):
exec(open('ez_setup.py').read(), ez)
else:
exec(urlopen('https://bootstrap.pypa.io/ez_setup.py').read(), ez)
if not options.allow_site_packages:
# ez_setup imports site, which adds site packages
# this will remove them from the path to ensure that incompatible versions
# of setuptools are not in the path
import site
# inside a virtualenv, there is no 'getsitepackages'.
# We can't remove these reliably
if hasattr(site, 'getsitepackages'):
for sitepackage_path in site.getsitepackages():
# Strip all site-packages directories from sys.path that
# are not sys.prefix; this is because on Windows
# sys.prefix is a site-package directory.
if sitepackage_path != sys.prefix:
sys.path[:] = [x for x in sys.path
if sitepackage_path not in x]
setup_args = dict(to_dir=tmpeggs, download_delay=0)
if options.setuptools_version is not None:
setup_args['version'] = options.setuptools_version
if options.setuptools_to_dir is not None:
setup_args['to_dir'] = options.setuptools_to_dir
ez['use_setuptools'](**setup_args)
import setuptools
import pkg_resources
# This does not (always?) update the default working set. We will
# do it.
for path in sys.path:
if path not in pkg_resources.working_set.entries:
pkg_resources.working_set.add_entry(path)
######################################################################
# Install buildout
ws = pkg_resources.working_set
setuptools_path = ws.find(
pkg_resources.Requirement.parse('setuptools')).location
# Fix sys.path here as easy_install.pth added before PYTHONPATH
cmd = [sys.executable, '-c',
'import sys; sys.path[0:0] = [%r]; ' % setuptools_path +
'from setuptools.command.easy_install import main; main()',
'-mZqNxd', tmpeggs]
find_links = os.environ.get(
'bootstrap-testing-find-links',
options.find_links or
('http://downloads.buildout.org/'
if options.accept_buildout_test_releases else None)
)
if find_links:
cmd.extend(['-f', find_links])
requirement = 'zc.buildout'
version = options.buildout_version
if version is None and not options.accept_buildout_test_releases:
# Figure out the most recent final version of zc.buildout.
import setuptools.package_index
_final_parts = '*final-', '*final'
def _final_version(parsed_version):
try:
return not parsed_version.is_prerelease
except AttributeError:
# Older setuptools
for part in parsed_version:
if (part[:1] == '*') and (part not in _final_parts):
return False
return True
index = setuptools.package_index.PackageIndex(
search_path=[setuptools_path])
if find_links:
index.add_find_links((find_links,))
req = pkg_resources.Requirement.parse(requirement)
if index.obtain(req) is not None:
best = []
bestv = None
for dist in index[req.project_name]:
distv = dist.parsed_version
if _final_version(distv):
if bestv is None or distv > bestv:
best = [dist]
bestv = distv
elif distv == bestv:
best.append(dist)
if best:
best.sort()
version = best[-1].version
if version:
requirement = '=='.join((requirement, version))
cmd.append(requirement)
import subprocess
if subprocess.call(cmd) != 0:
raise Exception(
"Failed to execute command:\n%s" % repr(cmd)[1:-1])
######################################################################
# Import and run buildout
ws.add_entry(tmpeggs)
ws.require(requirement)
import zc.buildout.buildout
if not [a for a in args if '=' not in a]:
args.append('bootstrap')
# if -c was provided, we push it back into args for buildout' main function
if options.config_file is not None:
args[0:0] = ['-c', options.config_file]
zc.buildout.buildout.main(args)
shutil.rmtree(tmpeggs)
[buildout]
parts = scripts
extensions = bob.buildout
eggs = bob.ip.annotator
develop = .
newest = false
[scripts]
recipe = bob.buildout:scripts
setup.py 0 → 100644
#!/usr/bin/env python
# vim: set fileencoding=utf-8 :
from setuptools import setup, dist
dist.Distribution(dict(setup_requires=['bob.extension']))
from bob.extension.utils import load_requirements, find_packages
install_requires = load_requirements()
setup(
name='bob.ip.annotator',
version=open("version.txt").read().rstrip(),
description='A TkInter-based keypoint annotation tool for images',
url='http://gitlab.idiap.ch/biometric/bob.ip.annotator',
license='GPLv3',
author='Andre Anjos',
author_email='andre.anjos@idiap.ch',
# This line is required for any distutils based packaging.
packages=find_packages(),
long_description=open('README.rst').read(),
packages=find_packages(),
include_package_data=True,
zip_safe = False,
install_requires=install_requires,
entry_points={
'console_scripts': [
'annotate.py = bob.ip.annotator.script.annotate:main',
],
},
# Classifiers are important if you plan to distribute this package through
# PyPI. You can find the complete list of classifiers that are valid and
# useful here (http://pypi.python.org/pypi?%3Aaction=list_classifiers).
classifiers = [
'Framework :: Bob',
'Development Status :: 4 - Beta',
'Intended Audience :: Developers',
'License :: OSI Approved :: GNU General Public License v3 (GPLv3)',
'Natural Language :: English',
'Programming Language :: Python',
'Programming Language :: Python :: 3',
'Topic :: Software Development :: Libraries :: Python Modules',
],
'License :: OSI Approved :: BSD License',
)
1.0.0a0
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment