書接上回樹莓派上利用python+opencv+dlib實作嘴唇檢測
我發現卡頓主要原因就是dlib檢測程序,但是實力不夠還改變不了哈哈,那么我就想著要不然做一個界面讓這個卡頓在人們的操作習慣中不那么卡頓,做界面主要使用pygame,
專案的主要目標是加入pygame來定義按鈕,讓檢測操作僅僅發生在點擊按鈕之后,效果如下:


1、安裝pygame
python3 -m pip install -U pygame --user
或者是直接寫后面的也可以
pip install -U pygame --user
2、安裝python3-sdl2
2.1 說明
這個安裝是在樹莓派上面的,在windows上面不需要安裝這個
2.2 原因
在樹莓派上運行,發現pygame中字體檔案也就是ttf檔案讀取不出來,會存在如下錯誤:ImportError: libSDL_ttf-2.0.so.0: cannot open shared object file: No such file or directory
2.3 解決方法
網路上的主要解決方法有三種,大家都可以嘗試一下看看哪一種合適,我的是要安裝python3-sdl2
# 第一種
sudo apt-get install libsdl-ttf2.0-0
# 第二種
sudo apt-get install libsdl2-mixer-2.0-0
# 第三種
sudo apt-get install python3-sdl2
3、代碼
# from gpiozero import LED
from time import sleep
from subprocess import check_call
import cv2 as cv
import numpy as np
import dlib
import threading
import pygame
from pygame.locals import *
import sys
# 宣告界面中按鈕的界面
# 輸入引數
# screen:主界面,按鈕界面將繪制于主界面之上
# font:pygame字體全域變數
# color:字體顏色
# button_size:按鈕形狀,要是元組中包含三個引數則是圓形,四個是矩形
# text:按鈕中的文字
# x:按鈕界面位于主界面中x的相對位置
# y:按鈕界面位于主界面中y的相對位置
class SurfaceButton:
def __init__(self, screen, font, color, button_size, text, x=0, y=0):
self.screen = screen
self.color = color
self.button_size = button_size
self.text = font.render(text, True, (158, 16, 16))
self.surface = pygame.Surface(screen.get_size())
# 配合下面代碼使用,使得創建界面默認的黑色背景最終是透明的
self.surface.set_colorkey((0, 0, 0))
self.surfaceX = x
self.surfaceY = y
# 用于除錯中顯示輸入文字
# self.text_show = text
# 把按鈕界面繪制到主界面上
def draw(self):
# 繪制矩形按鈕
if len(self.button_size) == 4:
pygame.draw.rect(self.surface, self.color, pygame.Rect(self.button_size), width=0)
self.surface.blit(self.text, (
self.button_size[0] - self.text.get_width() // 2, self.button_size[1] - self.text.get_height() // 2))
self.surface.convert_alpha()
self.screen.blit(self.surface, (self.surfaceX, self.surfaceY))
# 繪制圓形按鈕
elif len(self.button_size) == 3:
pygame.draw.circle(self.surface, self.color, self.button_size[:2], self.button_size[2], width=0)
self.surface.blit(self.text, (
self.button_size[0] - self.text.get_width() // 2, self.button_size[1] - self.text.get_height() // 2))
self.surface.convert_alpha()
self.screen.blit(self.surface, (self.surfaceX, self.surfaceY))
# 多執行緒操作
class MyThread(threading.Thread):
def __init__(self, func, args=()):
super(MyThread, self).__init__()
self.func = func
self.args = args
# 在執行函式的同時,把結果賦值給result,然后通過get_result函式獲取回傳的結果
def run(self):
self.result = self.func(*self.args)
def get_result(self):
try:
return self.result
except Exception as e:
return e
# 利用dlib進行人臉和嘴唇檢測并繪制
# 輸入引數
# screen:主界面
# img:用于嘴唇檢測的圖片,格式是opencv的圖片格式(BGR)
def lip_detector(screen, img):
landmarks_lip = []
gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
rects = detector(gray, 1)
print('檢測到了 %d 個人臉' % len(rects))
for (i, rect) in enumerate(rects):
# 標記人臉中的68個landmark點
landmarks = predictor(gray, rect)
landmarks_lip_tem = []
for n in range(48, 68):
x = landmarks.part(n).x
y = landmarks.part(n).y
landmarks_lip_tem.append((x, y))
# cv.circle(img=img, center=(x, y), radius=3, color=(0, 255, 0), thickness=-1)
landmarks_lip.append(landmarks_lip_tem)
draw_lip(screen, img, landmarks_lip)
# 繪制嘴唇線條
def draw_lip(screen, img, landmarks_lip):
img_makeup = img.copy()
for i in range(np.array(landmarks_lip).shape[0]):
for m in range(2):
for n in range(lip_order_num - 1):
cv.line(img_makeup, landmarks_lip[i][lip_order_dlib[m][n]], landmarks_lip[i][lip_order_dlib[m][n + 1]],
color=(0, 255, 0),
thickness=2, lineType=8)
screen.blit(cvimage_to_pygame(cv.cvtColor(img_makeup, cv.COLOR_BGR2RGB)), (0, 0))
# 獲取攝像頭序號
def search_cap_num():
for i in range(2000):
cap = cv.VideoCapture(i)
cap_opened = cap.isOpened()
if cap_opened == True:
return i
# 獲取攝像頭所能拍攝的像素
# 注意這個地方輸出的shape是高度,寬度,深度,所以要轉化一下
def set_screen_size():
ret, img = cap.read()
return img.shape[1::-1]
# 將opencv圖片轉化為pygame中圖片
def cvimage_to_pygame(image):
"""Convert cvimage into a pygame image"""
return pygame.image.frombuffer(image.tobytes(), image.shape[1::-1], "RGB")
# 處理pygame按鈕界面的點擊事件
def deal_with_event():
global button_makeup_clicked, button_quit_clicked, \
button_circle_quit, button_circle_makeup
for event in pygame.event.get():
if event.type == QUIT:
# 接收到退出事件后退出程式
sys.exit()
if event.type == MOUSEBUTTONDOWN:
pressed_array = pygame.mouse.get_pressed()
if pressed_array[0]:
mouse_pos = pygame.mouse.get_pos()
mouse_x = mouse_pos[0]
mouse_y = mouse_pos[1]
# print(mouse_pos)
# print(button_circle.button_size[0]-button_circle.button_size[2],button_circle.button_size[0]+button_circle.button_size[2],button_circle.button_size[1] - button_circle.button_size[2],button_circle.button_size[1] + button_circle.button_size[2])
if (not button_makeup_clicked) and \
button_circle_makeup.button_size[0] - button_circle_makeup.button_size[2] <= mouse_x <= \
button_circle_makeup.button_size[0] + button_circle_makeup.button_size[2] and \
button_circle_makeup.button_size[1] - button_circle_makeup.button_size[2] <= mouse_y <= \
button_circle_makeup.button_size[1] + button_circle_makeup.button_size[2]:
button_makeup_clicked = True
button_quit_clicked = False
elif (not button_quit_clicked) and \
button_circle_quit.button_size[0] - button_circle_quit.button_size[2] <= mouse_x <= \
button_circle_quit.button_size[0] + button_circle_quit.button_size[2] and \
button_circle_quit.button_size[1] - button_circle_quit.button_size[2] <= mouse_y <= \
button_circle_quit.button_size[1] + button_circle_quit.button_size[2]:
button_quit_clicked = True
button_makeup_clicked = False
cap_num = search_cap_num()
cap = cv.VideoCapture(cap_num)
detector = dlib.get_frontal_face_detector()
predictor = dlib.shape_predictor("shape_predictor_68_face_landmarks.dat")
lip_order_dlib = np.array([[48, 49, 50, 51, 52, 53, 54, 64, 63, 62, 61, 60, 48],
[48, 59, 58, 57, 56, 55, 54, 64, 65, 66, 67, 60, 48]]) - 48
lip_order_num = lip_order_dlib.shape[1]
# 初始化pygame,為使用硬體做準備
pygame.init()
# 創建一個視窗
screen_video = pygame.display.set_mode(set_screen_size(), 0, 32)
screen_width = screen_video.get_size()[0]
screen_height = screen_video.get_size()[1]
white = (255, 255, 255)
button_circle_pos = [(screen_width - screen_width // 6), screen_height - screen_height // 7]
button_circle_raduis = [screen_height // 10]
button_circle_size = tuple(button_circle_pos + button_circle_raduis)
button_makeup_clicked = False
button_quit_clicked = True
# 字體
font = pygame.font.Font("font.ttf", 24)
# 字體
# font = pygame.font.SysFont(None, 24)
button_circle_makeup = SurfaceButton(screen_video, font, white, button_circle_size, "化妝")
button_circle_quit = SurfaceButton(screen_video, font, white, button_circle_size, "退出")
# 設定視窗標題
pygame.display.set_caption("Hello, World!")
img = {}
# 主回圈
while cap.isOpened():
if button_makeup_clicked:
lip_detector(screen_video, img)
button_circle_quit.draw()
pygame.display.flip()
while True:
deal_with_event()
if button_quit_clicked:
break
elif button_quit_clicked:
ret, img = cap.read()
img_rgb = cv.cvtColor(img, cv.COLOR_BGR2RGB)
screen_video.blit(cvimage_to_pygame(img_rgb), (0, 0))
button_circle_makeup.draw()
deal_with_event()
pygame.display.flip()
3.1 代碼邏輯說明
其中MyThread類不需要管,我一開始想嘗試多執行緒,后來覺得沒有必要,
上述代碼主要實作的功能是加入兩個按鈕,一個是化妝,一個是退出,
一開始的時候按鈕是化妝,這個時候界面上顯示的是利用opencv獲取的影像資訊,整個程序不會用到dlib中有關人臉檢測和嘴唇特征點檢測的代碼,
當點擊了化妝之后,按鈕變成了退出,我們獲取此時的一張圖片,這個圖片用來檢測,我們只檢測一次,繪制出圖片上面存在的嘴唇特征,然后就開始等待退出按鈕被點擊,
當退出按鈕點擊之后,按鈕變成化妝,界面上重新顯示的是利用opencv獲取的影像資訊,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/348515.html
標籤:其他
