В нашем офисе блокировка экрана - это привычка, которую нужно быстро выработать. Потому что, если вы оставите свой компьютер разблокированным, кто-то повеселится и поменяет ваши обои или псевдоним `sudo` на что-нибудь;)
И однажды я начал думать, а почему бы мне не автоматизировать это? И вот я перехожу к библиотеке Python для распознавания лиц. Его действительно легко настроить и использовать.
Но обо всем по порядку. Нам нужно проверить, можно ли заблокировать экран с Python и как это сделать.
Блокировка экрана
Я использую Linux Mint с окружением рабочего стола Cinnamon. И, к счастью, в случае с Cinnamon довольно легко заблокировать или разблокировать экран с помощью команды заставки.
cinnamon-screensaver-command --activate # to lock the screen cinnamon-screensaver-command --deactivate # to unlock the screen
И запустить команду терминала из python совсем несложно:
from subprocess import call LOCK_ARGS = { True: '--activate', False: '--deactivate', } def lock_screen(lock): call(('cinnamon-screensaver-command', LOCK_ARGS[lock])) lock_screen(True) # will lock the screen lock_screen(False) # will unlock the screen
Настройка face_recognition
Следующий шаг - узнать свое прекрасное лицо. Воспользуемся библиотекой распознавания лиц. В репозитории можно найти много хороших примеров, думаю, тот нам пригодится.
Он использует OpenCV для захвата потока с камеры. Также я решил использовать конституционную нейронную сеть для определения лиц в кадре. Чтобы точность была лучше.
from threading import Timer import cv2 import face_recognition def load_user_encoding(): user_image = face_recognition.load_image_file(os.path.join(BASE_DIR, 'user.jpg')) user_image_face_encoding = face_recognition.face_encodings(user_image, num_jitters=10)[0] return user_image_face_encoding def find_user_in_frame(frame, user_encoding): face_locations = face_recognition.face_locations(frame, model='cnn') face_encodings = face_recognition.face_encodings(frame, face_locations, num_jitters=2) for face_encoding in face_encodings: matches = face_recognition.compare_faces((user_encoding, ), face_encoding, tolerance=0.9) return any(matches) if __name__ == '__main__': user_encoding = load_user_encoding() video_capture = cv2.VideoCapture(0) # get a reference to webcam #0 (the default one) lock_timer = None process_this_frame = True while True: ret, frame = video_capture.read() small_frame = cv2.resize(frame, (0, 0), fx=0.25, fy=0.25) rgb_small_frame = small_frame[:, :, ::-1] if process_this_frame: user_found = find_user_in_frame(rgb_small_frame, user_encoding) if user_found: print('user found') lock_screen(False) if lock_timer is not None: # cancel lock timer if it exists lock_timer.cancel() lock_timer = None else: print('user not found') if lock_timer is None: # start timer if it's not started already lock_timer = Timer(5, lock_screen, (True,)) lock_timer.start() process_this_frame = not process_this_frame
Как видите, я использовал threading.Timer
для блокировки экрана через 5 секунд, если пользователь не найден. Я рекомендую немного подождать, прежде чем заблокировать экран, потому что иногда он не может распознать ваше лицо на некоторых кадрах. Или вы можете просто отвернуться на мгновение.
Оптимизация
С этим решением у него есть неприятная задержка для чтения кадра и плохой кадровый пропуск. Поэтому я решил оптимизировать его и переместить процесс распознавания в отдельный процесс с помощью multiprocessing
Прежде всего, нам нужно переписать нашу функцию для поиска пользователя, чтобы она могла вызываться как Process
с Pipe
вместо return:
def find_user_in_frame(conn, frame, user_encoding): face_locations = face_recognition.face_locations(frame, model='cnn') face_encodings = face_recognition.face_encodings(frame, face_locations, num_jitters=2) found_user = False for face_encoding in face_encodings: matches = face_recognition.compare_faces((user_encoding, ), face_encoding, tolerance=0.9) found_user = any(matches) if found_user: break conn.send(found_user)
И после этого нам нужно вызвать эту функцию, используя multiprocessing.Process
в основном цикле:
if __name__ == '__main__': user_encoding = load_user_encoding() video_capture = cv2.VideoCapture(0) # get a reference to webcam #0 (the default one) lock_timer = None parent_conn, child_conn = Pipe() find_user_process = None while True: ret, frame = video_capture.read() small_frame = cv2.resize(frame, (0, 0), fx=0.25, fy=0.25) rgb_small_frame = small_frame[:, :, ::-1] # if process of finding user is not working - start new one if find_user_process is None: find_user_process = Process(target=find_user_in_frame, args=(child_conn, rgb_small_frame, user_encoding)) find_user_process.start() # if process of finding user is already working - check is it done elif find_user_process is not None and not find_user_process.is_alive(): user_found = parent_conn.recv() find_user_process = None if user_found: print('user found') lock_screen(False) if lock_timer is not None: lock_timer.cancel() lock_timer = None else: print('user not found') if lock_timer is None: lock_timer = Timer(LOCK_TIMEOUT, lock_screen, (True,)) lock_timer.start()
Теперь он работает более плавно и задержка минимальна.
Спасибо за прочтение! Надеюсь, это было интересно и вам понравилось.
Исходный код можно найти на github:
https://github.com/Ignisor/face-screenlock
Игнисор при поддержке Go Wombat Team