运行后,拖动鼠标画框,按q取消框,按c截取框中内容并保存,按f选择下一张图片,按e退出程序
import cv2
import os
import tkinter as tk
from tkinter import filedialog
class Cropper:
def __init__(self, image):
self.image = image
self.clone = image.copy()
self.temp_clone = image.copy()
self.startX, self.startY, self.endX, self.endY = 0, 0, 0, 0
self.cropping = False
self.rectangles = []
self.rect_size_text = "" # 用于保存框的尺寸文本
def display_rectangle_size(self, key_pressed):
if self.cropping or (key_pressed == ord("c") or key_pressed == ord("q")):
rect_height = abs(self.endY - self.startY)
rect_width = abs(self.endX - self.startX)
self.rect_size_text = f"{rect_height} x {rect_width}"
cv2.putText(self.temp_clone, self.rect_size_text, (self.startX, self.startY - 10),
cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 2)
else:
self.rect_size_text = ""
def click_and_crop(self, event, x, y, flags, param):
self.temp_clone = self.clone.copy() # 在任何鼠标事件发生时更新临时克隆图像
# 添加水平和垂直定位线
cv2.line(self.temp_clone, (x, 0), (x, self.temp_clone.shape[0]), (0, 0, 255), 1)
cv2.line(self.temp_clone, (0, y), (self.temp_clone.shape[1], y), (0, 0, 255), 1)
if event == cv2.EVENT_LBUTTONDOWN:
self.startX, self.startY = x, y
self.endX, self.endY = x, y
self.cropping = True
elif event == cv2.EVENT_MOUSEMOVE:
if self.cropping:
self.endX, self.endY = x, y
self.display_rectangle_size(cv2.EVENT_MOUSEMOVE) # 显示框的尺寸
cv2.rectangle(self.temp_clone, (self.startX, self.startY), (self.endX, self.endY), (0, 255, 0), 2)
elif event == cv2.EVENT_LBUTTONUP:
if self.cropping:
self.endX, self.endY = x, y
self.cropping = False
cv2.rectangle(self.clone, (self.startX, self.startY), (self.endX, self.endY), (0, 255, 0), 2)
self.rectangles.append(((self.startX, self.startY), (self.endX, self.endY)))
# 绘制尺寸信息
if self.cropping or self.rect_size_text != "":
cv2.putText(self.temp_clone, self.rect_size_text, (self.startX, self.startY - 10),cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 2)
cv2.imshow('Original Image', self.temp_clone)
def main():
cropper = None
image = None
save_counter = 1
cropped_image_window = None # 用于显示剪裁后的图片的窗口
while True:
key = cv2.waitKey(1) & 0xFF
if key == ord("f") or image is None:
root = tk.Tk()
root.withdraw() # 隐藏主窗口,只显示文件对话框
image_path = filedialog.askopenfilename(title="选择图片",
filetypes=[("Image files", "*.jpg *.jpeg *.png *.bmp")])
root.destroy() # 关闭临时的 Tkinter 窗口
if image_path:
if cropper is not None:
cv2.destroyWindow('Original Image') # 关闭之前的窗口
if cropped_image_window is not None:
if cv2.getWindowProperty('Cropped Image', cv2.WND_PROP_VISIBLE) > 0:
cv2.destroyWindow('Cropped Image') # 仅在窗口可见时销毁
cropper.rectangles = [] # 清除之前的框
save_counter = 1 # 重置剪裁图片计数器
image = cv2.imread(image_path)
cv2.namedWindow('Original Image', cv2.WINDOW_NORMAL) # 设置为可调整窗口大小
cv2.imshow('Original Image', image)
print("点击鼠标左键确定第一个点,按住鼠标左键拖动鼠标,放开鼠标左键确定框的第二个点。"
"按下 'c' 键确认剪裁,按下 'q' 键取消剪裁,按下 'e' 键退出程序。")
cropper = Cropper(image)
cv2.setMouseCallback('Original Image', cropper.click_and_crop)
elif key == ord("c"):
if cropper is not None and len(cropper.rectangles) > 0:
startX, startY = cropper.rectangles[-1][0]
endX, endY = cropper.rectangles[-1][1]
# 确保起始点在左上角,结束点在右下角
if startX > endX:
startX, endX = endX, startX
if startY > endY:
startY, endY = endY, startY
cropped_image = image[startY:endY, startX:endX]
# 获取原始图片的文件名(不带路径)
image_filename = os.path.basename(image_path)
# 构造新的文件名
cropped_image_filename = f"{os.path.splitext(image_filename)[0]}_cropped_image_{save_counter}.jpg"
cv2.imshow("Cropped Image", cropped_image)
# 在剪裁后的图片上显示框的尺寸
rect_height = abs(endY - startY)
rect_width = abs(endX - startX)
cropper.display_rectangle_size(cv2.EVENT_LBUTTONUP)
size_text = cropper.rect_size_text
cv2.putText(cropped_image, size_text, (10, 30),
cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2)
cv2.imwrite(cropped_image_filename, cropped_image)
print(f"剪裁后的图片 {cropped_image_filename} 已保存")
save_counter += 1
# 清除上一次的框
cropper.clone = image.copy()
cv2.imshow('Original Image', cropper.clone)
# 在新的窗口中显示剪裁后的图片
if cropped_image_window is not None:
cv2.destroyWindow('Cropped Image')
cv2.namedWindow('Cropped Image', cv2.WINDOW_NORMAL)
cv2.imshow('Cropped Image', cropped_image)
cropped_image_window = True
elif key == ord("q"):
if cropper is not None:
# 清除当前绘制的框和尺寸文本
cropper.clone = image.copy()
cropper.display_rectangle_size(key)
cv2.imshow('Original Image', cropper.clone)
# 清除尺寸文本
cropper.rect_size_text = ""
elif key == ord("e"):
break
cv2.destroyAllWindows()
if __name__ == "__main__":
main()