特征标注
导入必要的库
import tkinter as tk
: 导入Tkinter库,并将其重命名为tk。
from tkinter import filedialog
: 从Tkinter中导入文件对话框功能。
import cv2
: 导入OpenCV库。
from PIL import Image, ImageTk
: 从PIL库导入Image和ImageTk模块,用于处理图像。
from tkinter import messagebox
: 从Tkinter中导入消息框功能,用于显示消息提示框。
import dlib
:导入dlib库,这个库用于人脸检测和关键点标注。
python
import tkinter as tk
from tkinter import filedialog
import cv2
from PIL import Image, ImageTk
from tkinter import messagebox
import dlib
创建窗口
创建了一个Tkinter窗口实例win,设置窗口标题为"特征标注",大小为800x650像素。
python
win = tk.Tk()
win.title("特征标注")
win.geometry("800x650")
显示原始图片和标注后的图片
创建了两个空的标签控件
image_label_original
和image_label_landmarks
,用于显示原始图像和带有标注的图像。
python
mage_label_original = tk.Label(win)
image_label_landmarks = tk.Label(win)
存储用户选择的图片路径
初始化一个变量
selected_image_path
,用于存储用户选择的图片路径。
python
selected_image_path = None
字体样式和大小
定义了字体样式
my_font
,使用Times New Roman
字体,大小为20。
python
my_font = ("Times New Roman", 20)
定义了select_image函数
global selected_image_path
: 声明selected_image_path为全局变量,以便在函数内部修改其值。
selected_image_path = filedialog.askopenfilename()
:使用文件对话框让用户选择一个图像文件,并将选择的文件路径存储在selected_image_path变量中。
if selected_image_path:
: 检查是否成功选择了图像文件。
img = cv2.imread(selected_image_path)
:使用OpenCV的imread()函数读取选择的图像文件,并将其存储在img变量中。
img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
:将BGR格式的图像转换为RGB格式,便于后续处理。
img_pil = Image.fromarray(img_rgb)
: 将RGB格式的图像数据转换为PIL图像对象。
img_pil = img_pil.resize((300, 300), Image.Resampling.LANCZOS)
:调整图像大小为300x300像素,使用LANCZOS插值方法保持图像质量。
img_tk = ImageTk.PhotoImage(image=img_pil)
:将PIL图像对象转换为Tkinter图像对象,用于在GUI中显示。
image_label_original.config(image=img_tk)
: 在原始图像标签控件上配置显示图像。
image_label_original.image = img_tk
: 更新原始图像标签的图像数据,确保图像正确显示在GUI中。
python
def select_image():
global selected_image_path
selected_image_path = filedialog.askopenfilename()
if selected_image_path:
img = cv2.imread(selected_image_path)
img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
img_pil = Image.fromarray(img_rgb)
img_pil = img_pil.resize((300, 300), Image.Resampling.LANCZOS)
img_tk = ImageTk.PhotoImage(image=img_pil)
image_label_original.config(image=img_tk)
image_label_original.image = img_tk
定义了annotate_landmarks()函数
if selected_image_path:
: 检查是否已选择图像文件。
img = cv2.imread(selected_image_path)
:使用OpenCV的imread()函数读取已选择的图像文件。
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
: 将图像转换为灰度图像,便于人脸检测。
detector = dlib.get_frontal_face_detector()
: 创建一个人脸检测器对象。
predictor = dlib.shape_predictor("shape_predictor_68_face_landmarks.dat")
:创建一个人脸关键点预测器对象,加载已经训练好的68个关键点模型。
faces = detector(gray, 1)
: 使用人脸检测器检测输入图像中的人脸,并将结果存储在faces中。
if len(faces) > 0:
: 检查是否检测到了人脸。
landmarks = predictor(gray, faces[0])
: 使用关键点预测器获取第一个检测到的人脸的关键点坐标。
for n in range(0, 68):
: 遍历68个人脸关键点。
x = landmarks.part(n).x
: 获取第n个关键点的x坐标。
y = landmarks.part(n).y
: 获取第n个关键点的y坐标。
cv2.circle(img, (x, y), 1, (255, 0, 0), -1)
: 在图像上绘制一个圆圈表示关键点的位置。
img_rgb_landmarks = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
:将图像从BGR颜色空间转换为RGB颜色空间,这是因为OpenCV读取图像时默认使用BGR颜色通道。
img_pil_landmarks = Image.fromarray(img_rgb_landmarks)
:使用PIL的Image.fromarray()函数将OpenCV格式的图像转换为PIL图像对象。
img_pil_landmarks = img_pil_landmarks.resize((300, 300), Image.Resampling.LANCZOS)
: 调整PIL图像的大小为(300,300),使用LANCZOS插值方法来保持图像质量。
img_tk_landmarks = ImageTk.PhotoImage(image=img_pil_landmarks)
:将PIL图像转换为Tkinter GUI所需的PhotoImage对象。
image_label_landmarks.config(image=img_tk_landmarks)
:在image_label_landmarks标签控件上配置显示标注后的图像。
image_label_landmarks.image = img_tk_landmarks
:更新image_label_landmarks标签控件的图像数据,确保图像正确显示在GUI中。
messagebox.showinfo("提示", "未检测到人脸")
: 如果未检测到人脸,则在弹出窗口中显示相应提示信息。
messagebox.showinfo("提示", "请先选择一张图片")
: 如果未选择图像,则在弹出窗口中显示相应提示信息。
python
def annotate_landmarks():
if selected_image_path:
img = cv2.imread(selected_image_path)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
detector = dlib.get_frontal_face_detector()
predictor = dlib.shape_predictor("shape_predictor_68_face_landmarks.dat")
faces = detector(gray, 1)
if len(faces) > 0:
landmarks = predictor(gray, faces[0])
for n in range(0, 68):
x = landmarks.part(n).x
y = landmarks.part(n).y
cv2.circle(img, (x, y), 1, (255, 0, 0), -1)
img_rgb_landmarks = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
img_pil_landmarks = Image.fromarray(img_rgb_landmarks)
img_pil_landmarks = img_pil_landmarks.resize((300, 300), Image.Resampling.LANCZOS)
img_tk_landmarks = ImageTk.PhotoImage(image=img_pil_landmarks)
image_label_landmarks.config(image=img_tk_landmarks)
image_label_landmarks.image = img_tk_landmarks
else:
messagebox.showinfo("提示", "未检测到人脸")
else:
messagebox.showinfo("提示", "请先选择一张图片")
设置按钮
button_select = tk.Button(win, text="选择图片", font=my_font, command=select_image, fg='black')
:创建一个名为button_select的按钮,显示文本为"选择图片",应用自定义字体my_font,点击按钮时执行select_image函数,文本颜色为黑色。
button_detect = tk.Button(win, text="标注人脸", font=my_font, command=annotate_landmarks, fg='black')
:创建一个名为button_detect的按钮,显示文本为"标注人脸",应用自定义字体my_font,点击按钮时执行annotate_landmarks函数,文本颜色为黑色。
python
button_select = tk.Button(win, text="选择图片", font=my_font, command=select_image, fg='black')
button_detect = tk.Button(win, text="标注人脸", font=my_font, command=annotate_landmarks, fg='black')
调整图片标签的位置
button_height = 40
: 定义一个名为button_height的变量,值为40,表示按钮的高度为40像素。
button_select.place(x=150, y=50)
:将button_select按钮放置在窗口中x坐标为150像素,y坐标为50像素的位置。
button_detect.place(x=450, y=50)
:将button_detect按钮放置在窗口中x坐标为450像素,y坐标为50像素的位置。
python
# 计算按钮的高度,并据此调整图片标签的位置
button_height = 40 # 假设按钮的高度为40像素
button_select.place(x=150, y=50)
button_detect.place(x=450, y=50)
设置图片位置
image_label_original.place(x=50, y=100)
:将原始图片标签image_label_original放置在窗口中x坐标为50像素,y坐标为100像素的位置。
image_label_landmarks.place(x=420, y=100)
:将人脸标记图片标签image_label_landmarks放置在窗口中x坐标为420像素,y坐标为100像素的位置。
python
# 将图片标签放置在按钮下方
image_label_original.place(x=50, y=100)
image_label_landmarks.place(x=420, y=100)
主事件循环
win.mainloop()
:进入主事件循环,显示窗口并等待用户操作。
python
win.mainloop()
运行显示:
全部代码
python
import tkinter as tk
from tkinter import filedialog
import cv2
from PIL import Image, ImageTk
from tkinter import messagebox
import dlib
win = tk.Tk()
win.title("特征标注")
win.geometry("800x650")
image_label_original = tk.Label(win)
image_label_landmarks = tk.Label(win)
selected_image_path = None
my_font = ("Times New Roman", 20)
def select_image():
global selected_image_path
selected_image_path = filedialog.askopenfilename()
if selected_image_path:
img = cv2.imread(selected_image_path)
img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
img_pil = Image.fromarray(img_rgb)
img_pil = img_pil.resize((300, 300), Image.Resampling.LANCZOS)
img_tk = ImageTk.PhotoImage(image=img_pil)
image_label_original.config(image=img_tk)
image_label_original.image = img_tk
def annotate_landmarks():
if selected_image_path:
img = cv2.imread(selected_image_path)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
detector = dlib.get_frontal_face_detector()
predictor = dlib.shape_predictor("shape_predictor_68_face_landmarks.dat")
faces = detector(gray, 1)
if len(faces) > 0:
landmarks = predictor(gray, faces[0])
for n in range(0, 68):
x = landmarks.part(n).x
y = landmarks.part(n).y
cv2.circle(img, (x, y), 1, (255, 0, 0), -1)
img_rgb_landmarks = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
img_pil_landmarks = Image.fromarray(img_rgb_landmarks)
img_pil_landmarks = img_pil_landmarks.resize((300, 300), Image.Resampling.LANCZOS)
img_tk_landmarks = ImageTk.PhotoImage(image=img_pil_landmarks)
image_label_landmarks.config(image=img_tk_landmarks)
image_label_landmarks.image = img_tk_landmarks
else:
messagebox.showinfo("提示", "未检测到人脸")
else:
messagebox.showinfo("提示", "请先选择一张图片")
button_select = tk.Button(win, text="选择图片", font=my_font, command=select_image, fg='black')
button_detect = tk.Button(win, text="标注人脸", font=my_font, command=annotate_landmarks, fg='black')
# 计算按钮的高度,并据此调整图片标签的位置
button_height = 40 # 假设按钮的高度为40像素
button_select.place(x=150, y=50)
button_detect.place(x=450, y=50)
# 将图片标签放置在按钮下方
image_label_original.place(x=50, y=100)
image_label_landmarks.place(x=420, y=100)
win.mainloop()