手动标注程序

通过AI生成的一个手动标注程序。
功能如下:
1、鼠标点击,划定特定区域。能够划定多个闭合区域。
2、保存为mask图像。

操作流程:
  1. 程序启动后,会弹出一个文件选择框,请选择一张图片。
  2. 在图片窗口中,用鼠标左键单击来放置多边形的顶点。
  3. 按 'f' 键完成当前多边形,之后可以立刻开始绘制下一个。

键盘快捷键:
  - 'f' 键: 完成当前多边形的绘制,并准备开始下一个。
  - 's' 键: 保存所有已完成的多边形到同一个Mask图像中。
  - 'z' 键: 撤销 (Undo),清除当前正在绘制的多边形的上一个点。
  - 'c' 键: 清除 (Clear),清除当前图片上的所有标注。
  - 'q' 键: 退出程序。

界面如下

保存mask如下:

代码如下:

import cv2
import numpy as np
import tkinter as tk
from tkinter import filedialog
import os

# --- 全局变量 ---
current_points = []         # 存储当前正在绘制的多边形的顶点
completed_polygons = []     # 存储所有已完成的多边形列表 [poly1, poly2, ...]
original_image = None       # 存储原始、未被修改的图像
display_image = None        # 用于在屏幕上显示的图像,包含了所有绘制内容
WINDOW_NAME = "多区域Mask标注工具 (中文路径支持)"

# --- 核心功能函数 ---

def imread_zh(file_path):
    """替代 cv2.imread,以正确处理包含中文字符的路径。"""
    try:
        img_buffer = np.fromfile(file_path, dtype=np.uint8)
        img_decode = cv2.imdecode(img_buffer, cv2.IMREAD_COLOR)
        return img_decode
    except Exception as e:
        print(f"读取文件时发生错误: {e}")
        return None

def imwrite_zh(file_path, image):
    """替代 cv2.imwrite,以正确处理包含中文字符的路径。"""
    try:
        ext = os.path.splitext(file_path)[1]
        result, buffer = cv2.imencode(ext, image)
        if result:
            with open(file_path, 'wb') as f:
                f.write(buffer)
            return True
        else:
            print(f"图像编码失败: {file_path}")
            return False
    except Exception as e:
        print(f"写入文件时发生错误: {e}")
        return False


def redraw_canvas():
    """
    重绘整个画布。
    此函数负责在干净的原图上绘制所有已完成和正在进行中的标注。
    """
    global display_image
    display_image = original_image.copy()

    # 1. 绘制所有已完成的多边形 (绿色边框)
    if completed_polygons:
        cv2.polylines(display_image, [np.array(p) for p in completed_polygons], isClosed=True, color=(0, 255, 0), thickness=2)

    # 2. 绘制当前正在进行的多边形的顶点 (红色圆点) 和连线 (黄色)
    if current_points:
        for point in current_points:
            cv2.circle(display_image, point, 5, (0, 0, 255), -1)
        if len(current_points) > 1:
            cv2.polylines(display_image, [np.array(current_points)], isClosed=False, color=(0, 255, 255), thickness=2)

    cv2.imshow(WINDOW_NAME, display_image)

def mouse_events(event, x, y, flags, param):
    """处理用户的点击事件"""
    if event == cv2.EVENT_LBUTTONDOWN:
        current_points.append((x, y))
        print(f"添加点: ({x}, {y})")
        redraw_canvas()

def display_instructions():
    """在控制台打印操作说明"""
    print("\n" + "="*50)
    print("      多区域图像掩码(Mask)标注工具")
    print("="*50)
    print("操作流程:")
    print("  1. 程序启动后,会弹出一个文件选择框,请选择一张图片。")
    print("  2. 在图片窗口中,用鼠标左键单击来放置多边形的顶点。")
    print("  3. 按 'f' 键完成当前多边形,之后可以立刻开始绘制下一个。")
    print("\n键盘快捷键:")
    print("  - 'f' 键: 完成当前多边形的绘制,并准备开始下一个。")
    print("  - 's' 键: 保存所有已完成的多边形到同一个Mask图像中。")
    print("  - 'z' 键: 撤销 (Undo),清除当前正在绘制的多边形的上一个点。")
    print("  - 'c' 键: 清除 (Clear),清除当前图片上的所有标注。")
    print("  - 'q' 键: 退出程序。")
    print("="*50 + "\n")

# --- 主程序入口 ---
if __name__ == "__main__":
    
    display_instructions()

    root = tk.Tk()
    root.withdraw()
    image_path = filedialog.askopenfilename(
        title="请选择一张需要标注的图片 (支持中文路径)",
        filetypes=[("Image Files", "*.jpg *.jpeg *.png *.bmp *.tiff")]
    )

    if not image_path:
        print("没有选择文件,程序退出。")
        exit()

    original_image = imread_zh(image_path)
    if original_image is None:
        print(f"错误:无法加载图片文件 {image_path}")
        exit()
    
    display_image = original_image.copy()
    cv2.namedWindow(WINDOW_NAME, cv2.WINDOW_AUTOSIZE)
    cv2.setMouseCallback(WINDOW_NAME, mouse_events)
    cv2.imshow(WINDOW_NAME, display_image)

    while True:
        key = cv2.waitKey(1) & 0xFF

        if key == ord('q'):
            break
        elif key == ord('z'):
            if current_points:
                current_points.pop()
                print("已撤销上一个点。")
                redraw_canvas()
            else:
                print("没有正在绘制的点可以撤销。")
        elif key == ord('c'):
            current_points.clear()
            completed_polygons.clear()
            print("已清除所有标注。")
            redraw_canvas()
        elif key == ord('f'):
            if len(current_points) > 2:
                # 将当前点列表作为一个完成的多边形存入列表
                completed_polygons.append(list(current_points))
                # 清空当前点列表,为下一个多边形做准备
                current_points.clear()
                print(f"已完成第 {len(completed_polygons)} 个多边形。可以开始绘制下一个。")
                redraw_canvas()
            else:
                print("错误:至少需要3个点才能形成一个多边形。")
        elif key == ord('s'):
            if not completed_polygons:
                print("错误:没有已完成的多边形可以保存。请先按 'f' 键完成绘制。")
                continue
            if current_points:
                print("警告:您有正在绘制的多边形尚未完成。它将不会被保存。请按 'f' 完成它或按 'c' 清除。")


            save_path = filedialog.asksaveasfilename(
                title="请选择保存Mask图像的位置 (支持中文路径)",
                defaultextension=".png",
                filetypes=[("PNG Image", "*.png"), ("All Files", "*.*")]
            )

            if save_path:
                # 创建一个与原图大小相同,且所有像素值为1的背景
                mask = np.ones(original_image.shape[:2], dtype=np.uint8)

                # 将所有已完成的多边形顶点列表转换为NumPy数组格式
                polygons_to_draw = [np.array(p, dtype=np.int32) for p in completed_polygons]

                # 在背景上,将所有多边形内部区域填充为255
                cv2.fillPoly(mask, polygons_to_draw, 255)

                success = imwrite_zh(save_path, mask)
                if success:
                    print(f"所有 {len(completed_polygons)} 个区域已成功保存到同一个Mask: {save_path}")
                else:
                    print(f"Mask图像保存失败!")

    cv2.destroyAllWindows()

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值