Build a Fast Offline Image Annotation Tool with Python and LabelMe
I Built a Custom Image Annotation Tool with Python That Works Offline and Feels Instant When working with AI image datasets, annotation can quickly become a bottleneck. Web-based tools often suffer from latency issues, are overly complex for straightforward tasks, or fail to function effectively offline. Frustrated with the inefficiency of existing solutions, I decided to create a lightweight, offline-capable, and speedy image labeling tool using Python. This desktop application leverages the simplicity of labelme for annotation and the flexibility of Tkinter for the user interface, allowing for seamless and efficient labeling with keyboard shortcuts. How to Build Your Own Custom Annotation Utility in Python Here’s a step-by-step guide to creating your own local image annotation tool: 1. Setup: Install LabelMe and Tkinter First, you need to install labelme, a powerful annotation tool known for its simplicity and ease of use. You can install it via pip: sh pip install labelme Next, ensure you have Tkinter installed. It usually comes pre-packaged with Python, but if your system lacks it, you can install it with: sh sudo apt-get install python3-tk 2. Create the Basic Application Structure Start by setting up the basic structure of your application. This involves importing the necessary libraries and initializing the main window: ```python import tkinter as tk from tkinter import filedialog, messagebox import os import labelme Initialize the main application window root = tk.Tk() root.title("Custom Image Annotator") root.geometry("800x600") Define the directory containing the images image_dir = "" Function to open a directory dialog and set the image directory def open_directory(): global image_dir image_dir = filedialog.askdirectory() if image_dir: load_images() Function to load images from the selected directory def load_images(): # Get all image files in the directory images = [f for f in os.listdir(image_dir) if f.endswith('.jpg') or f.endswith('.png')] if images: display_image(images[0]) Function to display an image def display_image(image_name): image_path = os.path.join(image_dir, image_name) labelme.MainWindow(file=image_path) Create a button to open a directory open_dir_button = tk.Button(root, text="Open Image Directory", command=open_directory) open_dir_button.pack(pady=20) Run the application root.mainloop() ``` 3. Implement Keyboard Shortcuts To enhance productivity, add keyboard shortcuts for common operations such as opening a new image, saving annotations, and navigating through images: ```python Function to handle keyboard shortcuts def handle_key(event): if event.keysym == 'Return': # Press Enter to save the annotation save_annotation() elif event.keysym == 'Right': # Press Right Arrow to load the next image load_next_image() elif event.keysym == 'Left': # Press Left Arrow to load the previous image load_previous_image() Function to save the current annotation def save_annotation(): labelme.MainWindow().saveFile() Function to load the next image def load_next_image(): current_index = images.index(current_image) if current_index < len(images) - 1: display_image(images[current_index + 1]) else: messagebox.showinfo("End of Images", "You have reached the last image.") Function to load the previous image def load_previous_image(): current_index = images.index(current_image) if current_index > 0: display_image(images[current_index - 1]) else: messagebox.showinfo("First Image", "You have reached the first image.") Bind the keyboard shortcuts to the application root.bind('', handle_key) ``` 4. Enhance the User Interface To make the tool more user-friendly, enhance the UI with additional elements such as a status bar to display the current image and a progress indicator: ```python Add a status bar status_bar = tk.Label(root, text="", bd=1, relief=tk.SUNKEN, anchor=tk.W) status_bar.pack(side=tk.BOTTOM, fill=tk.X) Function to update the status bar def update_status_bar(image_name): status_bar.config(text=f"Current Image: {image_name}") Update the status bar when an image is displayed def display_image(image_name): image_path = os.path.join(image_dir, image_name) labelme.MainWindow(file=image_path) update_status_bar(image_name) Variable to keep track of the current image current_image = images[0] if images else "" ``` 5. Optimize for Performance To ensure the tool feels instant and responsive, optimize the code for performance. For instance, pre-load image files into memory if possible to reduce loading times: ```python Pre-load images into memory images = [] image_objects = [] def load_images(): global images, image_objects images = [f for f in os.listdir(image_dir) if f.endswith('.jpg') or f.endswith('.png')] if images: image_objects = [labelme.Image(image_dir, f) for f in images] display_image(images[0]) Update display_image to use pre-loaded image objects def display_image(image_name): image_path = os.path.join(image_dir, image_name) current_image_obj = next((img for img in image_objects if img.filename == image_name), None) if current_image_obj: labelme.MainWindow(file=image_path, image_data=current_image_obj.data) update_status_bar(image_name) ``` Conclusion By combining labelme and Tkinter, you can create a custom image annotation tool that is lightweight, functional, and tailored to your specific needs. This tool eliminates the lag and complexity associated with web-based solutions, making your AI dataset annotation process faster and more efficient. Whether you are tagging objects for YOLO, drawing polygons for segmentation, or classifying images, this desktop utility can help streamline your workflow and boost productivity. Give it a try and see the difference for yourself!