基于OpenCV实现动态绘制矩形与多边形并自动保存坐标
本文详解如何利用Python与OpenCV在图像窗口中动态绘制矩形和多边形。通过自定义鼠标回调与键盘指令切换标注模式,脚本自动按屏幕分辨率适配窗口大小,实时记录顶点位置并导出至TXT文件,同时内置坐标清洗逻辑,适用于定制化图像数据标注场景。
环境与依赖准备
运行该脚本需确保本地已安装以下组件:
- Python 3.7+
- 核心库:
opencv-python、imutils - Windows API支持:
pywin32(用于获取桌面物理分辨率以实现图像自适应缩放)
配置文件设置
程序启动前需准备一个JSON文件指定待处理图像的绝对路径,例如命名为drawPath.json:
{"path": "D:\\ALLBuffers\\Pycharm\\OpencvRun\\DataSet\\smpj.jpg"}
核心功能实现逻辑
代码主要包含图像分辨率自适应检测、鼠标事件监听绑定、几何图形实时绘制与坐标持久化四大模块。优化后的交互逻辑默认直接进入多边形标注状态,支持运行时切换矩形模式,并在退出时统一清理无效数据并保存结果。
import json
import cv2
import os
import imutils
import win32gui, win32print, win32con
WIN_NAME = 'draw_rect'
def get_list0(path):
if not os.path.exists(path):
print("记录该型号标准位置的文件缺失或输入文件名不一致")
return []
with open(path, 'r', encoding='utf-8') as f:
lines = f.readlines()
zb0 = []
for line in lines:
line = line.strip()
if line != '(pt1,pt2):':
zb0.append(int(line))
list0 = []
for i in range(0, len(zb0), 4):
x0, y0, x1, y1 = zb0[i:i + 4]
if y1 <= y0:
y0, y1 = y1, y0
list0.append([x0, y0, x1, y1])
return list0
def set_ratio(image):
if image is None:
return 0, 0
img_h, img_w = image.shape[:2]
hDC = win32gui.GetDC(0)
screen_w = win32print.GetDeviceCaps(hDC, win32con.DESKTOPHORZRES)
screen_h = win32print.GetDeviceCaps(hDC, win32con.DESKTOPVERTRES)
num_wh = 1
if img_w * img_h > 1.9e7:
num_wh = 4
elif img_w * img_h > 1.0e7:
num_wh = 3
elif min(img_w, img_h) >= min(screen_w, screen_h) or \\
max(img_w, img_h) >= max(screen_w, screen_h):
num_wh = 2
return int(img_h / num_wh), int(img_w / num_wh)
POLY_MODE = False
def define_start(img_name, img_path):
pts = []
def draw_roi(event, x, y, flags, param):
nonlocal POLY_MODE
img2 = img.copy()
if event == cv2.EVENT_LBUTTONDOWN:
pts.append((x, y))
cv2.circle(img2, pts[-1], 3, (0, 255, 0), -1)
elif event == cv2.EVENT_RBUTTONDOWN:
if len(pts) > 0:
pts.pop()
elif event == cv2.EVENT_MBUTTONDOWN:
POLY_MODE = True
if len(pts) > 0:
cv2.circle(img2, pts[-1], 3, (0, 255, 0), -1)
if len(pts) > 1:
for i in range(len(pts) - 1):
cv2.circle(img2, pts[i], 5, (0, 255, 0), -1)
cv2.line(img2, pts[i], pts[i + 1], color=(0, 0, 255), thickness=2)
cv2.line(img2, pts[0], pts[-1], color=(0, 0, 255), thickness=2)
cv2.imshow(WIN_NAME, img2)
if not img_path.lower().endswith(('.jpg', '.png')):
print("输入图片类型错误!请输入JPG/PNG格式的图片!")
return
image = cv2.imread(img_path)
ratio_h, ratio_w = set_ratio(image)
if ratio_h == 0 or ratio_w == 0:
print("无法获取有效图像或分辨率数据")
return
txt_path = f"./DrawRect/biaozhun/labels/{img_name}.txt"
os.makedirs(os.path.dirname(txt_path), exist_ok=True)
f = open(txt_path, mode='w', encoding='utf-8')
txt_save = []
img = imutils.resize(image, width=ratio_w)
cv2.namedWindow(WIN_NAME, cv2.WINDOW_NORMAL)
cv2.resizeWindow(WIN_NAME, ratio_w, ratio_h)
cv2.imshow(WIN_NAME, img)
cv2.setMouseCallback(WIN_NAME, draw_roi)
while True:
w_key = cv2.waitKey(1)
if w_key == 13 or cv2.getWindowProperty(WIN_NAME, 0) == -1:
for i, pt in enumerate(pts):
if i == 0:
txt_save.append("(pt1,pt2):")
txt_save.append(str(pt[0]))
txt_save.append(str(pt[1]))
while '(pt1,pt2):' in txt_save:
idx = txt_save.index('(pt1,pt2):')
del txt_save[idx:idx+6]
for val in txt_save:
f.write(str(val) + '\n')
print("坐标数据已输出:", txt_save)
f.close()
break
if POLY_MODE or w_key == 32:
roi = cv2.selectROI(windowName=WIN_NAME, img=img, showCrosshair=False, fromCenter=False)
x, y, w, h = roi
cv2.rectangle(img, (x, y), (x + w, y + h), color=(0, 0, 255), thickness=2)
cv2.imshow(WIN_NAME, img)
txt_save.extend(["(pt1,pt2):", str(x), str(y), str(x + w), str(y + h)])
points_list = get_list0(txt_path)
new_points_list = []
for coords in points_list:
x0, y0, x1, y1 = coords
if abs(x1 - x0) > 5 and abs(y1 - y0) > 5:
new_points_list.extend(["(pt1,pt2):", x0, y0, x1, y1])
file2 = open(txt_path, 'w', encoding='utf-8')
for item in new_points_list:
file2.write(str(item) + '\n')
file2.close()
break
cv2.destroyAllWindows()
if __name__ == '__main__':
path_file = open('./DataSet/drawPath.json', 'r', encoding='utf-8')
path_dic = json.load(path_file)
path_file.close()
img_path = path_dic['path']
img_name = os.path.splitext(os.path.basename(img_path))[0]
define_start(img_name, img_path)
交互操作说明
第1步:初始化窗口与图像加载
运行主程序后,脚本依据set_ratio函数对比当前图像像素数与系统桌面分辨率。当图像尺寸较大时自动计算缩放因子(通常为1/2或1/4),调用imutils.resize保持宽高比,防止高维图像在普通屏幕上显示不全。
第2步:多边形点位绘制
窗口弹出后默认进入多边形模式。鼠标左键单击图像任意位置追加顶点,右键点击撤销最近一个点。算法会实时绘制红绿双色连线预览,绿点表示已确认节点,红线勾勒闭合轨迹。
第3步:矩形框选与数据导出
按下空格键或鼠标中键可临时激活矩形模式,此时界面将接管控制权并弹出cv2.selectROI拖拽框。选定形状后按下Enter键结束程序。框架会在退出前遍历坐标数组,剔除水平或垂直跨度小于5像素的噪点边界,最终按行追加写入对应名称的.txt标注文件。
关键细节与排错提示
输出的TXT文件采用纯文本逐行列出数值,每四列一组对应矩形左上角与右下角坐标,多边形则连续存放各顶点XY值。若运行时报错No module named 'win32',请执行pip install pywin32补全系统API依赖。原草稿中存在f.close()置于break之后导致语句不可达的问题,本版已修正资源释放顺序。若发现标注坐标与实际画面错位,请确认操作系统未开启超清模式下的Display Scaling(缩放比例),或直接在原生分辨率显示器下运行。
以上就是基于OpenCV实现动态绘制矩形与多边形并自动保存坐标的详细内容,更多关于Python图像处理与自定义标注工具的资料请关注本站其它相关文章!
本站声明
本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。













