وبلاگ فربد | Farbod Blog

توی این وبلاگ راجع به پروژه هام صحبت می کنم

ساخت برنامه تشخیص چهره هوش مصنوعی با پایتون

  • ۳۸۰

سلام دوستان :)

توی پست امروز قراره که یک برنامه که صورت رو تشخیص میده و میتونه افراد مختلف رو از هم تفکیک کنه بسازیم و قراره که از الگوریتم های هوش مصنوعی و یادگیری ماشین استفاده کنیم!

شاید شما با opencv و... کار های پردازش تصویر انجام داده باشید و اگر اینطور باشه حتما با cascade ها آشنا هستید اما باید بگم که ما قرار نیست از cascade استفاده کنیم! برنامه ای که قراره بسازیم face recognition هست و face detection نیست. یعنی به جای اینکه فقط بفهمه که داخل یک تصویر یک یا چند صورت هست میتونه تشخیص بده که صورت ها مال چه فردی هستن. دقیقا مثل قفل چهره های موبایل که صورت شما رو تشخیص میده ولی اگر یک صورت دیگه جلوش بذارین قفل باز نمیشه.

توی این پست ما در نهایت برنامه ای خواهیم داشت که CLI هست و میشه داخل ترمینال یا cmd قبل از اجرا کردن بهش چند تا ورودی بدیم(مثل اکثر برنامه های حرفه ای CLI) و با استفاده از همون برنامه میتونیم مدلمون رو train کنیم و یا ازش خروجی بگیریم و در نهایت نتیجه رو در قالب یک عکس به کاربر ارائه بدیم. برای train کردن این مدل بر خلاف پست قبلی(ساخت ربات چت با هوش مصنوعی در پایتون) به عکس های زیادی نیاز نداریم. خب دیگه از این بیشتر توضیح نمیدم فعلا بریم سراغ پیش نیاز ها و بعد کار اصلی رو شروع میکنیم.

برای مرتب شدن کار هامون بهتره که یک ساختار منظم داخل یک فولدر مشخص داشته باشیم. از نظر من همچین ساختاری مناسبه:

face_recognizer/
│
├── output/
│
├── training/
│
├── validation/
│
├── detector.py
├── requirements.txt

برای اینکه سریعتر این فولدر ها رو درست کنید من توصیه میکنم که از cmd یا ترمینال استفاده کنید(برای همه کار ها همینطوره) پس برای ویندوز داخل powershell دستور های زیر رو وارد کنید:

PS> mkdir face_recognizer
PS> cd face_recognizer
PS> mkdir output
PS> mkdir training
PS> mkdir validation

برای مک یا لینوکس هم داخل ترمینال این دستور ها رو وارد کنید:

$ mkdir face_recognizer
$ cd face_recognizer
$ mkdir output training validation

خب حالا ساختار پروژمون آماده شد.

مرحله بعدی نصب کتابخونه ها و ابزار های مورد نیاز هست. این پست از اونجایی که از بقیه پست ها نسبتا پیچیده تره برای همین به غیر از چند تا کتابخونه پایتون به چیز های دیگه هم نیاز داریم.

قبل از اینکه کتابخونه ها رو نصب کنید باید این رو بدونید که برای انجام این کار به یک کامپایلر زبان برنامه نویسی C  و ابزار CMake نیاز دارید.

خب اول بریم که اینا رو برای ویندوز نصب کنیم:

خب همونطور که میدونید ما نمیتونیم توی ویندوز پکیج gcc یا CMake رو به صورت مستقل نصب کنیم. واسطه هایی برای این کار وچود دارن مثل MinGW و Cygwin. اینجا قراره که از MinGW استفاده کنیم اما شما میتونید با هر کدوم که راحت تر هستید پیش برید.

برای نصب MinGW روش های مختلفی وجود داره اما روشی که من پیشنهاد میکنم استفاده از پکیج منیجر chocolatey برای ویندوز هست بعد از اینکه این پکیج منیجر رو نصب کردید میریم سراغ نصب کامپایلر. برای انجام این کار وارد پاورشل(یا cmd) بشید و کد های زیر رو وارد کنید:

PS> choco install mingw

خب بعد از نصب کامپایلر باید ابزار CMake رو نصب کنیم. برای اینکار وارد صفحه دانلود سایت CMake بشید و دانلود و نصب رو انجام بدید.

حالا بریم سراغ نصب برای لینوکس. برای اینکار شما باید از طریق پکیج منیجر apt یا هر پکیج منیجری که مربوط به توزیع لینوکستون هست اینکار رو انجام بدید. پس وارد ترمینال بشید و دستورات زیر رو وارد کنید:

$ sudo apt-get update
$ sudo apt-get install cmake

خب این برای CMake بود برای نصب gcc و... وارد ترمینال بشید و دستور زیر رو وارد کنید:

$ sudo apt-get install build-essential

خب حالا بریم سراغ نصب روی مک. نصب کردن این ابزار ها روی مک خیلی آسونه و تنها کاری که باید بکنید اینه که اول پکیج منیجر homebrew رو نصب کنید(اگر از قبل نصب نکرده باشید) و بعد دستورات زیر رو وارد کنید:

$ brew update
$ brew install cmake gcc

بعد از اینکه کامپایلر و CMake رو نصب کردیم، از اونجایی که کار اصلیمون قراره با پایتون باشه باید چند تا کتابخونه پایتون هم نصب کنیم اما چون تعدادشون نسبتا زیاده اسم و ورژنشون رو داخل یک فایل txt مینویسیم و بعد از روی اون فایل نصب رو انجام میدیم پس یک فایل txt درست کنید و این ها رو داخلش قرار بدید:

dlib==19.24.0
face-recognition==1.3.0
numpy==1.24.2
Pillow==9.4.0

بعد از اتمام کار برای نصب روی ویندوز دستور زیر رو وارد کنید:

pip install -r requirements.txt

برای نصب توی مک یا لینوکس هم وارد ترمینال بشید و دستور زیر رو وارد کنید:

sudo pip3 install -r requirements.txt

(دقت داشته باشید که برای هر دو مورد آدرس کامل فایل رو قرار بدید)

ورژن های کتابخونه ها قبلا تست شده اند و قطعا با کدی که خواهیم نوشت کار میکنن اما در مورد ورژن پایتون، برنامه روی پایتون 3.9 و 3.10 کار میکنه اما توصیه نمیکنم که برنامه رو روی پایتون 3.11 اجرا کنید چون ممکنه به مشکل بخورید.

خب بلاخره تمام پیش نیاز های پروژه نصب شد و الان میریم سراغ کار اصلی. برای انجام اینکار احتمالا میدونید که به یکسری داده برای train کردن و به یکسری دیگه هم برای امتحان کردن مدل نیاز داریم. استفاده از صورت سلبریتی ها احتمالا گزینه خیلی خوبیه چون توی اینترنت خیلی راحت میشه عکس های صورتشون رو پیدا کرد. دقت کنید که اگر قراره از داده دیگه ای استفاده کنید بهتره که توی داده ای که برای train کردن استفاده می کنید فقط تصویر صورت فردی باشه که قراره مدل برای اون train بشه و نه فرد دیگه ای یعنی داخل عکس نباید بیشتر از یک صورت وجود داشته باشه. چون در غیر این صورت مدل دقیق کار نمیکنه. البته بعد از train کردن و برای امتحان کردن و استفاده کردن از مدل فرقی نمیکنه که فرد دیگه ای هم توی عکس باشه یعنی برنامه میتونه دو نفر رو کنار هم جداگانه تشخیص بده. من چند تا عکس از توی اینترنت پیدا کردم که میتونید از اونا استفاده کنید.

برای تشخیص اشیاء(یا توی برنامه ی ما صورت های متفاوت) به طور کلی از دو الگوریتم معروف استفاده میشه:

  1. HOG(histogram of oriented gradients): این الگوریتم یک تکنیک گسترده برای تشخیص اشیاء هست که با CPU خیلی بهتر کار میکنه.
  2. CNN(convolutional neural network): این الگوریتم هم از هیستوگرام استفاده میکنه اما فرقی که داره اینه که با GPU یا کارت گرافیک بهتر کار میکنه.

البته این رو باید بدونید که هیچکدوم از این الگوریتم ها بر پایه deep learning نیستن و شبیه مدل هایی مثل YOLOv3 کار نمیکنن.

 

خب برگردیم به train کردن مدلمون. برای دانلود عکس های مورد نیاز کلیک کنید.

اگر قراره از داده های خودتون استفاده کنید باید به دو دسته تقسیمش کنید. یک دسته عکس برای train کردن که باید عکس های هر فرد رو داخل یک فولدر با نام خود شخص بذارید و دسته دوم باید برای اعتبار سنجی باشه که فقط باید چند تا عکس که میخواید چهره(های) داخلش رو تشخیص بدید باشه و اسم فایل ها هم مهم نیست. ساختار عکس هایی که من آماده کردم هم به همین صورت هستن.

خب بریم سراع کار اصلی. اول از همه قراره داده هامون رو بخونیم و مدلمون رو بر اساس داده هایی که خوندیم train کنیم.

اول از همه یک فایل درست کنید و کد زیر رو داخلش قرار بدید:

import pickle
from pathlib import Path

import face_recognition

DEFAULT_ENCODINGS_PATH = Path("output/encodings.pkl")

Path("training").mkdir(exist_ok=True)
Path("output").mkdir(exist_ok=True)
Path("validation").mkdir(exist_ok=True)


def encode_known_faces(
    model: str = "hog", encodings_location: Path = DEFAULT_ENCODINGS_PATH
) -> None:
    names = []
    encodings = []
    for filepath in Path("training").glob("*/*"):
        name = filepath.parent.name
        image = face_recognition.load_image_file(filepath)

        face_locations = face_recognition.face_locations(image, model=model)
        face_encodings = face_recognition.face_encodings(image, face_locations)

        for encoding in face_encodings:
            names.append(name)
            encodings.append(encoding)

    name_encodings = {"names": names, "encodings": encodings}
    with encodings_location.open(mode="wb") as f:
        pickle.dump(name_encodings, f)


encode_known_faces()

توضیح: خب اول از همه کتابخونه های مورد نظرمون رو import کردیم.کتابخونه pickle برای encode کردن داده های عکس ها و ذخیره اونا هست.کتابخونه pathlib برای کار کردن راحت تر با فایل ها هست و کتابخونه face_recognition هم که کتابخونه اصلیمون برای تشخیص چهره هست. بعد از اون یک مسیر مشخص کردیم که برنامه encoding ها رو داخلش قرار بده بعد هم فولدر هایی که از قبل ساخته بودیم رو گفتیم که ببینه اگر وجود ندارن خودش اونا رو بسازه. توی تابع اصلی برنامه تمام فایل های داخل فولدر training باز میشن و مکان چهره های داخلش تشخیص داده میشن و به عنوان داده training داخل فایل ذخیره میشن و در آخر برنامه هم تابع اصلی اجرا میشه.

حالا برنامه رو اجرا میکنیم و فایل خروجیمون توی فولدر output ذخیره شده.

الان که مدلمون کاملا train شده آمادست و میتونه هر عکسی که بهش بدیم چهره هایی که میشناسه رو داخلش تشخیص بده.

قبل از اجرا کردن حواستون باشه که هر جایی آدرس یک فایل یا فولدر توی برنامه استفاده شده حتما آدرس کامل فایل رو قرار بدید چون ممکنه براتون مشکل پیش بیاد.

برای تست کردن مدل یک عکس پیدا کردم که برای دانلودش میتونید اینجا کلیک کنید. یا اینکه خودتون یک عکس برای تست استفاده کنید.

برای ادامه داخل همون برنامه کد های زیر رو جایگزین کنید:

import pickle
from collections import Counter
from pathlib import Path

import face_recognition

DEFAULT_ENCODINGS_PATH = Path("output/encodings.pkl")

# Create directories if they don't already exist
Path("training").mkdir(exist_ok=True)
Path("output").mkdir(exist_ok=True)
Path("validation").mkdir(exist_ok=True)


def encode_known_faces(
    model: str = "hog", encodings_location: Path = DEFAULT_ENCODINGS_PATH
) -> None:
    """
    Loads images in the training directory and builds a dictionary of their
    names and encodings.
    """
    names = []
    encodings = []
    for filepath in Path("training").glob("*/*"):
        name = filepath.parent.name
        image = face_recognition.load_image_file(filepath)

        face_locations = face_recognition.face_locations(image, model=model)
        face_encodings = face_recognition.face_encodings(image, face_locations)

        for encoding in face_encodings:
            names.append(name)
            encodings.append(encoding)

    name_encodings = {"names": names, "encodings": encodings}
    with encodings_location.open(mode="wb") as f:
        pickle.dump(name_encodings, f)


def recognize_faces(
    image_location: str,
    model: str = "hog",
    encodings_location: Path = DEFAULT_ENCODINGS_PATH,
) -> None:
    """
    Given an unknown image, get the locations and encodings of any faces and
    compares them against the known encodings to find potential matches.
    """
    with encodings_location.open(mode="rb") as f:
        loaded_encodings = pickle.load(f)

    input_image = face_recognition.load_image_file(image_location)

    input_face_locations = face_recognition.face_locations(
        input_image, model=model
    )
    input_face_encodings = face_recognition.face_encodings(
        input_image, input_face_locations
    )

    for bounding_box, unknown_encoding in zip(
        input_face_locations, input_face_encodings
    ):
        name = _recognize_face(unknown_encoding, loaded_encodings)
        if not name:
            name = "Unknown"
        print(name, bounding_box)


def _recognize_face(unknown_encoding, loaded_encodings):
    """
    Given an unknown encoding and all known encodings, find the known
    encoding with the most matches.
    """
    boolean_matches = face_recognition.compare_faces(
        loaded_encodings["encodings"], unknown_encoding
    )
    votes = Counter(
        name
        for match, name in zip(boolean_matches, loaded_encodings["names"])
        if match
    )
    if votes:
        return votes.most_common(1)[0][0]


recognize_faces("unknown.jpg")

توضیح: تابع encode مثل کد قبلی هست اما دو تابع به برنامه اضافه شدن. تابع recognize_face و تابع recognize_face_. تابع اولی عکسی که میخوایم چهره های داخلش رو تشخیص بدیم باز میکنه و صورت ها رو تشخیص میده(فقط اینکه صورت هستن نه اینکه صورت کی هستن) و اونها رو encode میکنه. تابع دوم هم داده encode شده از تابع اول رو با encoding های اصلی که از داده train داشتیم مقایسه میکنه تا ببینه که کدوم یکی از چهره های داخل عکس با چهره های train شده تطبیق دارن. و در نهایت یک یا چند اسم با یک یا چند مختصات به ما تحویل میده که به ما میگه که هر چهره موجود توی تصویر متعلق به چه فردی هست.

برنامه رو روی این عکس اجرا میکنیم:

خروجی برنامه:

$ python detector.py
jerry_seinfeld (480, 972, 1035, 418)
Unknown (726, 2513, 1281, 1959)

میبینید که فقط یکی از افراد داخل عکس رو تشخیص داد. دلیلش هم اینه که ما فقط یکی از افراد رو برای train کردن مدل استفاده کردیم و مدل ما فرد دیگه رو نمیشناسه.

خب حالا دیدید که برنامه به خوبی چهره ها رو تشخیص میده ولی این خروجی برای ما خیلی مناسب نیست برای بهتر کردنش میتونیم توی خود برنامه به جای یک اسم و مختصات چهره فرد دور چهره افراد تشخیص داده شده یک مربع بکشیم و اسمش رو زیرش بنویسیم.

برای انجام اینکار قراره از کتابخونه Pillow استفاده کنیم، البته انتخاب های دیگه ای هم داریم مثل opencv ولی بهتره برنامه رو خیلی سنگین نکنیم.

برای انجام اینکار توی همون برنامه کد های زیر رو جایگزین کنید:

import pickle
from collections import Counter
from pathlib import Path

import face_recognition
from PIL import Image, ImageDraw

DEFAULT_ENCODINGS_PATH = Path("output/encodings.pkl")
BOUNDING_BOX_COLOR = "blue"
TEXT_COLOR = "white"

# Create directories if they don't already exist
Path("training").mkdir(exist_ok=True)
Path("output").mkdir(exist_ok=True)
Path("validation").mkdir(exist_ok=True)


def encode_known_faces(
    model: str = "hog", encodings_location: Path = DEFAULT_ENCODINGS_PATH
) -> None:
    """
    Loads images in the training directory and builds a dictionary of their
    names and encodings.
    """
    names = []
    encodings = []
    for filepath in Path("training").glob("*/*"):
        name = filepath.parent.name
        image = face_recognition.load_image_file(filepath)

        face_locations = face_recognition.face_locations(image, model=model)
        face_encodings = face_recognition.face_encodings(image, face_locations)

        for encoding in face_encodings:
            names.append(name)
            encodings.append(encoding)

    name_encodings = {"names": names, "encodings": encodings}
    with encodings_location.open(mode="wb") as f:
        pickle.dump(name_encodings, f)


def recognize_faces(
    image_location: str,
    model: str = "hog",
    encodings_location: Path = DEFAULT_ENCODINGS_PATH,
) -> None:
    """
    Given an unknown image, get the locations and encodings of any faces and
    compares them against the known encodings to find potential matches.
    """
    with encodings_location.open(mode="rb") as f:
        loaded_encodings = pickle.load(f)

    input_image = face_recognition.load_image_file(image_location)

    input_face_locations = face_recognition.face_locations(
        input_image, model=model
    )
    input_face_encodings = face_recognition.face_encodings(
        input_image, input_face_locations
    )

    pillow_image = Image.fromarray(input_image)
    draw = ImageDraw.Draw(pillow_image)

    for bounding_box, unknown_encoding in zip(
        input_face_locations, input_face_encodings
    ):
        name = _recognize_face(unknown_encoding, loaded_encodings)
        if not name:
            name = "Unknown"
        _display_face(draw, bounding_box, name)

    del draw
    pillow_image.show()


def _recognize_face(unknown_encoding, loaded_encodings):
    """
    Given an unknown encoding and all known encodings, find the known
    encoding with the most matches.
    """
    boolean_matches = face_recognition.compare_faces(
        loaded_encodings["encodings"], unknown_encoding
    )
    votes = Counter(
        name
        for match, name in zip(boolean_matches, loaded_encodings["names"])
        if match
    )
    if votes:
        return votes.most_common(1)[0][0]


def _display_face(draw, bounding_box, name):
    """
    Draws bounding boxes around faces, a caption area, and text captions.
    """
    top, right, bottom, left = bounding_box
    draw.rectangle(((left, top), (right, bottom)), outline=BOUNDING_BOX_COLOR)
    text_left, text_top, text_right, text_bottom = draw.textbbox(
        (left, bottom), name
    )
    draw.rectangle(
        ((text_left, text_top), (text_right, text_bottom)),
        fill=BOUNDING_BOX_COLOR,
        outline=BOUNDING_BOX_COLOR,
    )
    draw.text(
        (text_left, text_top),
        name,
        fill=TEXT_COLOR,
    )


recognize_faces("unknown.jpg")

توضیح: توی این برنامه همه چیز مثل قبلشه فقط با استفاده از pillow مختصات چهره ها رو از خروجی میگیریم و دورش یک مربع با رنگ دلخواه میکشیم و اسم فرد رو زیرش مینویسیم. شما میتونید رنگ، اندازه فونت و... رو به دلخواه خودتون تغییر بدید.

خروجی:

همینطور که میبینید چهره هایی که تشخیص داده رو دورش مربع کشیده و اسمشون رو زیرش نوشته. البته یکم فونتش کوچیکه اما شما خودتون میتونید تغیرش بدید.

 

الان یک برنامه داریم که کاملا کار میکنه و چهره های داخل عکس ورودی رو تشخیص میده. اما یک مرحله مهم رو جا انداختیم! اون هم اعتبار سنجی مدل هست. ما توی تحلیل داده و هوش مصنوعی بعد از تموم کردن ساخت یک مدل و train کردن اون باید مدل رو اعتبار سنجی کنیم و ببینیم که مدلمون چند درصد دقت داره یعنی هرچهره ای که بهش ندادیم رو تشخیص نده یا برعکسش فقط محدود به داده train شده نباشه. برای این داخل فایل validation که قبلا دانلود کردید تعداد نسبتا زیادی از عکس های مختلف وجود داره که برنامه ما قراره تشخیص بده و با استفاده از اون عکس های label نخورده قراره برناممون رو امتحان کنیم.

برای اینکار یک فایل جدید درست کنید و کد های زیر رو داخلش قرار بدید:

import pickle
from collections import Counter
from pathlib import Path

import face_recognition
from PIL import Image, ImageDraw

DEFAULT_ENCODINGS_PATH = Path("output/encodings.pkl")
BOUNDING_BOX_COLOR = "blue"
TEXT_COLOR = "white"

# Create directories if they don't already exist
Path("training").mkdir(exist_ok=True)
Path("output").mkdir(exist_ok=True)
Path("validation").mkdir(exist_ok=True)


def encode_known_faces(
    model: str = "hog", encodings_location: Path = DEFAULT_ENCODINGS_PATH
) -> None:
    """
    Loads images in the training directory and builds a dictionary of their
    names and encodings.
    """
    names = []
    encodings = []
    for filepath in Path("training").glob("*/*"):
        name = filepath.parent.name
        image = face_recognition.load_image_file(filepath)

        face_locations = face_recognition.face_locations(image, model=model)
        face_encodings = face_recognition.face_encodings(image, face_locations)

        for encoding in face_encodings:
            names.append(name)
            encodings.append(encoding)

    name_encodings = {"names": names, "encodings": encodings}
    with encodings_location.open(mode="wb") as f:
        pickle.dump(name_encodings, f)


def recognize_faces(
    image_location: str,
    model: str = "hog",
    encodings_location: Path = DEFAULT_ENCODINGS_PATH,
) -> None:
    """
    Given an unknown image, get the locations and encodings of any faces and
    compares them against the known encodings to find potential matches.
    """
    with encodings_location.open(mode="rb") as f:
        loaded_encodings = pickle.load(f)

    input_image = face_recognition.load_image_file(image_location)

    input_face_locations = face_recognition.face_locations(
        input_image, model=model
    )
    input_face_encodings = face_recognition.face_encodings(
        input_image, input_face_locations
    )

    pillow_image = Image.fromarray(input_image)
    draw = ImageDraw.Draw(pillow_image)

    for bounding_box, unknown_encoding in zip(
        input_face_locations, input_face_encodings
    ):
        name = _recognize_face(unknown_encoding, loaded_encodings)
        if not name:
            name = "Unknown"
        _display_face(draw, bounding_box, name)

    del draw
    pillow_image.show()


def _recognize_face(unknown_encoding, loaded_encodings):
    """
    Given an unknown encoding and all known encodings, find the known
    encoding with the most matches.
    """
    boolean_matches = face_recognition.compare_faces(
        loaded_encodings["encodings"], unknown_encoding
    )
    votes = Counter(
        name
        for match, name in zip(boolean_matches, loaded_encodings["names"])
        if match
    )
    if votes:
        return votes.most_common(1)[0][0]


def _display_face(draw, bounding_box, name):
    """
    Draws bounding boxes around faces, a caption area, and text captions.
    """
    top, right, bottom, left = bounding_box
    draw.rectangle(((left, top), (right, bottom)), outline=BOUNDING_BOX_COLOR)
    text_left, text_top, text_right, text_bottom = draw.textbbox(
        (left, bottom), name
    )
    draw.rectangle(
        ((text_left, text_top), (text_right, text_bottom)),
        fill=BOUNDING_BOX_COLOR,
        outline=BOUNDING_BOX_COLOR,
    )
    draw.text(
        (text_left, text_top),
        name,
        fill=TEXT_COLOR,
    )


def validate(model: str = "hog"):
    """
    Runs recognize_faces on a set of images with known faces to validate
    known encodings.
    """
    for filepath in Path("validation").rglob("*"):
        if filepath.is_file():
            recognize_faces(
                image_location=str(filepath.absolute()), model=model
            )


validate()

توضیح: کد هیچ فرقی نکرده و فقط در آخر برنامه یک تابع با نام validate وجود داره که به جای یک عکس تکی تمام عکس های داخل فولدر validation رو باز میکنه.

 

اگر برنامه رو اجرا کنید میبینید که تمام عکس های داخل فولدر validation باز میشه و تشخیص داده میشه حتی چهره هایی که سیاه و سفید هستن یا جلوشون مانعی مثل عینک وجود داره. من حتما توصیه میکنم که این مرحله رو انجام بدید که اگر مدل ایرادی داشت توی بخش train کردن تجدید نظر کنید. 

الان تقریبا کار تموم شده اما اول کار گفتم که قراره برناممون در محیط CLI کار کنه. بنابراین میخوایم با استفاده از کتابخونه argparse قابلیت گرفتن ورودی در محیط CLI رو به برناممون اضافه کنیم.

برای اینکار داخل برنامه اصلی کد های زیر رو جایگزین کنید:

import argparse
import pickle
from collections import Counter
from pathlib import Path

import face_recognition
from PIL import Image, ImageDraw

DEFAULT_ENCODINGS_PATH = Path("output/encodings.pkl")
BOUNDING_BOX_COLOR = "blue"
TEXT_COLOR = "white"

# Create directories if they don't already exist
Path("training").mkdir(exist_ok=True)
Path("output").mkdir(exist_ok=True)
Path("validation").mkdir(exist_ok=True)

parser = argparse.ArgumentParser(description="Recognize faces in an image")
parser.add_argument("--train", action="store_true", help="Train on input data")
parser.add_argument(
    "--validate", action="store_true", help="Validate trained model"
)
parser.add_argument(
    "--test", action="store_true", help="Test the model with an unknown image"
)
parser.add_argument(
    "-m",
    action="store",
    default="hog",
    choices=["hog", "cnn"],
    help="Which model to use for training: hog (CPU), cnn (GPU)",
)
parser.add_argument(
    "-f", action="store", help="Path to an image with an unknown face"
)
args = parser.parse_args()


def encode_known_faces(
    model: str = "hog", encodings_location: Path = DEFAULT_ENCODINGS_PATH
) -> None:
    """
    Loads images in the training directory and builds a dictionary of their
    names and encodings.
    """
    names = []
    encodings = []

    for filepath in Path("training").glob("*/*"):
        name = filepath.parent.name
        image = face_recognition.load_image_file(filepath)

        face_locations = face_recognition.face_locations(image, model=model)
        face_encodings = face_recognition.face_encodings(image, face_locations)

        for encoding in face_encodings:
            names.append(name)
            encodings.append(encoding)

    name_encodings = {"names": names, "encodings": encodings}
    with encodings_location.open(mode="wb") as f:
        pickle.dump(name_encodings, f)


def recognize_faces(
    image_location: str,
    model: str = "hog",
    encodings_location: Path = DEFAULT_ENCODINGS_PATH,
) -> None:
    """
    Given an unknown image, get the locations and encodings of any faces and
    compares them against the known encodings to find potential matches.
    """
    with encodings_location.open(mode="rb") as f:
        loaded_encodings = pickle.load(f)

    input_image = face_recognition.load_image_file(image_location)

    input_face_locations = face_recognition.face_locations(
        input_image, model=model
    )
    input_face_encodings = face_recognition.face_encodings(
        input_image, input_face_locations
    )

    pillow_image = Image.fromarray(input_image)
    draw = ImageDraw.Draw(pillow_image)

    for bounding_box, unknown_encoding in zip(
        input_face_locations, input_face_encodings
    ):
        name = _recognize_face(unknown_encoding, loaded_encodings)
        if not name:
            name = "Unknown"
        _display_face(draw, bounding_box, name)

    del draw
    pillow_image.show()


def _recognize_face(unknown_encoding, loaded_encodings):
    """
    Given an unknown encoding and all known encodings, find the known
    encoding with the most matches.
    """
    boolean_matches = face_recognition.compare_faces(
        loaded_encodings["encodings"], unknown_encoding
    )
    votes = Counter(
        name
        for match, name in zip(boolean_matches, loaded_encodings["names"])
        if match
    )
    if votes:
        return votes.most_common(1)[0][0]


def _display_face(draw, bounding_box, name):
    """
    Draws bounding boxes around faces, a caption area, and text captions.
    """
    top, right, bottom, left = bounding_box
    draw.rectangle(((left, top), (right, bottom)), outline=BOUNDING_BOX_COLOR)
    text_left, text_top, text_right, text_bottom = draw.textbbox(
        (left, bottom), name
    )
    draw.rectangle(
        ((text_left, text_top), (text_right, text_bottom)),
        fill=BOUNDING_BOX_COLOR,
        outline=BOUNDING_BOX_COLOR,
    )
    draw.text(
        (text_left, text_top),
        name,
        fill=TEXT_COLOR,
    )


def validate(model: str = "hog"):
    """
    Runs recognize_faces on a set of images with known faces to validate
    known encodings.
    """
    for filepath in Path("validation").rglob("*"):
        if filepath.is_file():
            recognize_faces(
                image_location=str(filepath.absolute()), model=model
            )


if __name__ == "__main__":
    if args.train:
        encode_known_faces(model=args.m)
    if args.validate:
        validate(model=args.m)
    if args.test:
        recognize_faces(image_location=args.f, model=args.m)

توضیح: توی بخش تشخیص چهره هیچ تغییری ایجاد نشده و تنها تغییر اینه که ما با استفاده از کتابخونه argparse قابلیت گرفتن ورودی از CLI رو به برناممون اضافه کردیم و کاملا برنامه از طریق CLI کنترل میشه و ما برای هیچ کاری نیازی به تغییر کد نداریم.

آرگومان هایی که میتونیم توی برنامه استفاده کنیم اینا هستن:

  • help--: این ورودی برای نمایش دادن راهنمای برنامه هست و اگر این ورودی رو به برنامه بدین برنامه به شما نحوه استفاده رو نشون میده.
  • train--: این دستور برنامه رو train میکنه و شما میتونید انتخاب کنید که مدل بر اساس CPU کار کنه یا GPU.
  • validate--: این دستور فرآیند اعتبار سنجی رو شروع میکنه.
  • test--: این دستور اصلی برنامست. شما باید این آرگومان رو در کنار f- و ورودی فایل استفاده کنید تا از برنامه خروجی بگیرید

برای امتحان برنامه روی یک عکسی که میخواید صورت های داخلش رو تشخیص بدید باید دستور زیر رو اجرا کنید:

python3 detector.py --test -f unknown.jpg

البته این یک مثال کوچیک بود و بهتون توصیه میکنم دستور help رو اجرا کنید تا با تمام دستورات برنامه آشنا بشید.

امیدوارم این پست براتون مفید بوده باشه D:

 


  • ممنون استاد
    قدرت جادوییتون در انتقال مطالب قابل ستایشه
    پاسخ:
    سلام دوست عزیز.
    خیلی ممنونم از پیام پر مهرتون.
    موفق باشید.
  • سلام،وقت بخیر
    با تشکر از آموزش های عالیتون می خواستم بدونم چطور میشه دردوره های آموزشیتون ثبت نام کرد؟
    پاسخ:
    سلام دوست عزیز.
    در حال حاضر آموزش ها بصورت پست در وبلاگ هست و دوره آموزش دیگه ای برگزار نمیشه اما اگر سوالی داشتید میتونید از طریق ایمیل یا در بخش نظرات با من در ارتباط باشید.
    موفق باشید.
  • بخدا بهترینی بهترین
    پاسخ:
    سلام دوست عزیز.
    ممنونم از محبت و نظر لطفتون.
    موفق باشید.
ارسال نظر آزاد است، اما اگر قبلا در بیان ثبت نام کرده اید می توانید ابتدا وارد شوید.
شما میتوانید از این تگهای html استفاده کنید:
<b> یا <strong>، <em> یا <i>، <u>، <strike> یا <s>، <sup>، <sub>، <blockquote>، <code>، <pre>، <hr>، <br>، <p>، <a href="" title="">، <span style="">، <div align="">
تجدید کد امنیتی