joeVenner / FaceRecognition-GUI-APP

A very Simple GUI APP for Face Detection and Recognition
254 stars 119 forks source link

Problem while training the dataset #6

Open theoptimist76 opened 2 years ago

theoptimist76 commented 2 years ago

I did some changes with the code too, but not the functional part but the training of datasets output the error as:

Exception in Tkinter callback
Traceback (most recent call last):
  File "C:\Users\{name}\AppData\Local\Programs\Python\Python39\lib\tkinter\__init__.py", line 1892, in __call__
    return self.func(*args)
  File "d:\{path}\system.py", line 198, in trainmodel
    train_classifer(self.controller.active_name)
  File "d:\{path}\create_classifier.py", line 11, in train_classifer
    path = os.path.abspath.join(os.getcwd() + "/data/" + name + "/")
AttributeError: 'function' object has no attribute 'join'
phudinhtruongk18 commented 2 years ago

I did some changes with the code too, but not the functional part but the training of datasets output the error as:

Exception in Tkinter callback
Traceback (most recent call last):
  File "C:\Users\{name}\AppData\Local\Programs\Python\Python39\lib\tkinter\__init__.py", line 1892, in __call__
    return self.func(*args)
  File "d:\{path}\system.py", line 198, in trainmodel
    train_classifer(self.controller.active_name)
  File "d:\{path}\create_classifier.py", line 11, in train_classifer
    path = os.path.abspath.join(os.getcwd() + "/data/" + name + "/")
AttributeError: 'function' object has no attribute 'join'

try

os.path.abspath(".").join("/data/" + name + "/")

theoptimist76 commented 2 years ago

Could you address this issue too ?

Exception in Tkinter callback
Traceback (most recent call last):
  File "C:\Users\{name}\AppData\Local\Programs\Python\Python39\lib\tkinter\__init__.py", line 1892, in __call__
    return self.func(*args)
  File "d:\{path}\system.py", line 198, in trainmodel
    train_classifer(self.controller.active_name)
  File "d:\{path}\create_classifier.py", line 37, in train_classifer
    clf.train(faces, ids)
cv2.error: OpenCV(4.5.2) C:\Users\runneradmin\AppData\Local\Temp\pip-req-build-sgoydvi3\opencv_contrib\modules\face\src\lbph_faces.cpp:362: error:
(-210:Unsupported format or combination of formats) Empty training data was given. You'll need more than one sample to learn a model. 
in function 'cv::face::LBPH::train
joeVenner commented 2 years ago

Could you address this issue too ?

Exception in Tkinter callback
Traceback (most recent call last):
  File "C:\Users\{name}\AppData\Local\Programs\Python\Python39\lib\tkinter\__init__.py", line 1892, in __call__
    return self.func(*args)
  File "d:\{path}\system.py", line 198, in trainmodel
    train_classifer(self.controller.active_name)
  File "d:\{path}\create_classifier.py", line 37, in train_classifer
    clf.train(faces, ids)
cv2.error: OpenCV(4.5.2) C:\Users\runneradmin\AppData\Local\Temp\pip-req-build-sgoydvi3\opencv_contrib\modules\face\src\lbph_faces.cpp:362: error:
(-210:Unsupported format or combination of formats) Empty training data was given. You'll need more than one sample to learn a model. 
in function 'cv::face::LBPH::train

Make sure that after you add a user, a repo created in the data folder with the username.

=> the issue here coming from the script can't find any data to train the model, so the app it's not capturing any data

also when you are in the phase of capturing the data, make sure that the app is detecting the face and its counting how many data captured so far.

theoptimist76 commented 2 years ago

I am sure that after I add a user, a repo is created in the data folder with the username. The question is : When I am in the phase of capturing data how can I make sure that the app is detecting the face and its counting how many data are captured so far?

Files:

system.py =>

from Detector import main_app
from create_classifier import train_classifer
from create_dataset import start_capture
import tkinter as tk
from tkinter import  font as tkf
from tkinter import messagebox, PhotoImage

names = set()

 Full UI WorkPage

class MainUI(tk.Tk):

    def __init__(self, *args, **kwargs):
        tk.Tk.__init__(self, *args, **kwargs)
        global names
        with open("Detected Names/nameslist.txt", "r") as f:
            x = f.read()
            z = x.rstrip().split(" ")
            for i in z:
                names.add(i)
        self.title_font = tkf.Font(family='Helvetica', size=19, weight="bold")
        self.title("Face Recognition by 6th Sem")
        self.resizable(False, False)
        self.geometry("700x360")
        self.protocol("WM_DELETE_WINDOW", self.on_closing)
        self.active_name = None
        container = tk.Frame(self)
        container.grid(sticky="")
        container.grid_rowconfigure(0, weight=1)
        container.grid_columnconfigure(0, weight=1)
        self.frames = {}
        for F in (StartPage, PageOne, PageTwo, PageThree, PageFour):
            page_name = F.__name__
            frame = F(parent=container, controller=self)
            self.frames[page_name] = frame
            frame.grid(row=0, column=0, sticky="nsew")
        self.show_frame("StartPage")

    def show_frame(self, page_name):
        frame = self.frames[page_name]
        frame.tkraise()

    def on_closing(self):

        # if messagebox.askokcancel("Quit", "Are you sure?"):
        #     global names
        #     f = open("nameslist.txt", "a+")
        #     for i in names:
        #         f.write(i + " ")
        self.destroy()

 Making first page UI

class StartPage(tk.Frame):

    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)
        self.controller = controller
        render = PhotoImage(file='files/homepagepic.png')
        img = tk.Label(self, image=render)
        img.image = render
        img.grid(row=1, column=0, rowspan=2, sticky="nsew")
        render1 = PhotoImage(file='files/cosmos.png')
        img1 = tk.Label(self, image=render1)
        img1.image = render1
        img1.grid(row=1, column=1, rowspan=2, sticky="nsew")
        render2 = PhotoImage(file='files/homepagepic.png')
        img2 = tk.Label(self, image=render2)
        img2.image = render2
        img2.grid(row=1, column=2, rowspan=2, sticky="nsew")
        label1 = tk.Label(self, text="Face Recognition System", font=self.controller.title_font, fg="#263942")
        label1.grid(row=0, columnspan=6, sticky="ew")
        # line1= Canvas.create_line(15, 25, 200, 25)
        button1 = tk.Button(self, text="Register a new User", fg="#ffffff", bg="#263942",
                            command=lambda: self.controller.show_frame("PageOne"))
        button2 = tk.Button(self, text="Check existing User", fg="#ffffff", bg="#263942",
                            command=lambda: self.controller.show_frame("PageTwo"))
        button3 = tk.Button(self, text="        Quit       ", fg="#ffffff", bg="red", command=self.on_closing)
        button1.grid(row=5, column=0, ipady=3, ipadx=7)
        button2.grid(row=5, column=1, ipady=3, ipadx=7)
        button3.grid(row=5, column=2, ipady=3, ipadx=32)

    def on_closing(self):
        # if messagebox.askokcancel("Quit", "Are you sure?"):
        #     global names
        #     with open("nameslist.txt", "w") as f:
        #         for i in names:
        #             f.write(i + " ")
        self.controller.destroy()

 Making register page of user

class PageOne(tk.Frame):
    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)
        self.controller = controller
        label1 = tk.Label(self, text="Face Recognition System", font=self.controller.title_font, fg="#263942")
        label1.grid(row=0, columnspan=8,sticky="ew", padx=50, ipadx=100)
        render1 = PhotoImage(file='files/cosmos.png')
        img1 = tk.Label(self, image=render1)
        img1.image = render1
        img1.grid(row=1, column=4, rowspan=2, columnspan=2, sticky="nsew")
        tk.Label(self, text="Enter the name", fg="#263942", font='Helvetica 13 bold').grid(row=5, column=4, pady=10,
                                                                                           padx=5)
        self.user_name = tk.Entry(self, borderwidth=2, bg="lightgrey", font='Helvetica 12')
        self.user_name.grid(row=5, column=5, pady=10, padx=10)
        self.buttoncanc = tk.Button(self, text="    Cancel     ", bg="red", fg="#ffffff",
                                    command=lambda: controller.show_frame("StartPage"))
        self.buttonext = tk.Button(self, text="      Next       ", fg="#ffffff", bg="#263942", command=self.start_training)
        self.buttoncanc.grid(row=6, column=4, pady=10, ipadx=5, ipady=4)
        self.buttonext.grid(row=6, column=5, pady=10, ipadx=5, ipady=4, sticky="e")

    def start_training(self):
        global names
        if self.user_name.get() == "None":
            messagebox.showerror("Error", "Name cannot be 'None'")
            return
        elif self.user_name.get() in names:
            messagebox.showerror("Error", "User already exists!")
            return
        elif len(self.user_name.get()) == 0:
            messagebox.showerror("Error", "Name cannot be empty!")
            return
        name = self.user_name.get()
        names.add(name)
        self.controller.active_name = name
        self.controller.frames["PageTwo"].refresh_names()
        self.controller.show_frame("PageThree")

 Making face detection page

class PageTwo(tk.Frame):

    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)
        global names
        self.controller = controller
        tk.Label(self, text="Select user", fg="#263942", font='Helvetica 12 bold').grid(row=0, column=0, padx=10,
                                                                                        pady=10)
        self.buttoncanc = tk.Button(self, text="Cancel", command=lambda: controller.show_frame("StartPage"),
                                    bg="#ffffff", fg="#263942")
        self.menuvar = tk.StringVar(self)
        self.dropdown = tk.OptionMenu(self, self.menuvar, *names)
        self.dropdown.config(bg="lightgrey")
        self.dropdown["menu"].config(bg="lightgrey")
        self.buttonext = tk.Button(self, text="Next", command=self.nextfoo, fg="#ffffff", bg="#263942")
        self.dropdown.grid(row=0, column=1, ipadx=8, padx=10, pady=10)
        self.buttoncanc.grid(row=1, ipadx=5, ipady=4, column=0, pady=10)
        self.buttonext.grid(row=1, ipadx=5, ipady=4, column=1, pady=10)

    def nextfoo(self):
        if self.menuvar.get() == "None":
            messagebox.showerror("ERROR", "Name cannot be 'None'")
            return
        self.controller.active_name = self.menuvar.get()
        self.controller.show_frame("PageFour")

    def refresh_names(self):
        global names
        self.menuvar.set('')
        self.dropdown['menu'].delete(0, 'end')
        for name in names:
            self.dropdown['menu'].add_command(label=name, command=tk._setit(self.menuvar, name))

 Image capturing and testing

class PageThree(tk.Frame):

    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)
        self.controller = controller
        self.numimglabel = tk.Label(self, text="Number of images captured = 0", font='Helvetica 12 bold', fg="#263942")
        self.numimglabel.grid(row=0, column=0, columnspan=2, sticky="ew", pady=10)
        self.capturebutton = tk.Button(self, text="Capture Data Set", fg="#ffffff", bg="#263942", command=self.capimg)
        self.trainbutton = tk.Button(self, text="Train The Model", fg="#ffffff", bg="#263942", command=self.trainmodel)
        self.buttoncanc = tk.Button(self, text="     Back     ", bg="red", fg="#ffffff",
                                    command=lambda: controller.show_frame("StartPage"))
        self.capturebutton.grid(row=1, column=0, ipadx=5, ipady=4, padx=10, pady=20)
        self.trainbutton.grid(row=1, column=1, ipadx=5, ipady=4, padx=10, pady=20)
        self.buttoncanc.grid(row=2, ipadx=5, ipady=4, column=0, pady=10)

    def capimg(self):
        self.numimglabel.config(text=str("Captured Images = 0 "))
        messagebox.showinfo("INSTRUCTIONS", "We will Capture 100 pic of your Face.")
        x = start_capture(self.controller.active_name)
        self.controller.num_of_images = x
        self.numimglabel.config(text=str("Number of images captured = " + str(x)))

    def trainmodel(self):
        if self.controller.num_of_images < 100:
            messagebox.showerror("ERROR", "No enough Data, Capture at least 100 images!")
            return
        train_classifer(self.controller.active_name)
        messagebox.showinfo("SUCCESS", "The model has been successfully trained!")
        self.controller.show_frame("PageFour")

 Initializing webcam 

class PageFour(tk.Frame):

    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)
        self.controller = controller
        label = tk.Label(self, text="Face Recognition", font='Helvetica 16 bold')
        label.grid(row=0, column=0, sticky="ew")
        button1 = tk.Button(self, text="Recognize Face", command=self.openwebcam, fg="#ffffff", bg="#263942")
        button4 = tk.Button(self, text="Go to Home Page", command=lambda: self.controller.show_frame("StartPage"),
                            bg="#ffffff", fg="#263942")
        buttoncanc = tk.Button(self, text="Back", bg="red", fg="#ffffff",
                               command=lambda: controller.show_frame("PageTwo"))
        button1.grid(row=1, column=0, sticky="ew", ipadx=5, ipady=4, padx=10, pady=10)
        button4.grid(row=1, column=1, sticky="ew", ipadx=5, ipady=4, padx=10, pady=10)
        buttoncanc.grid(row=1, column=2, sticky="ew", ipadx=5, ipady=4, padx=10, pady=10)

    def openwebcam(self):
        main_app(self.controller.active_name)

app = MainUI()
app.iconphoto(True, tk.PhotoImage(file='files/icon.ico'))
app.mainloop()

create_classifier.py

import cv2
import os
import numpy as np
from PIL import Image

 Method to train custom classifier to recognize face

def train_classifer(name):
    # Read all the images in custom data-set
    path = os.path.abspath(".").join("/data/" + name + "/")

    faces = []
    ids = []
    pictures = {}

  Store images in a numpy format and ids of the user on the same index in imageNp and id lists

    for files in os.walk(path):
        pictures = files

    for pic in pictures:
        imgpath = path + pic
        img = Image.open(imgpath).convert('L')
        imageNp = np.array(img, 'uint8')
        id = int(pic.split(name)[0])
        # names[name].append(id)
        faces.append(imageNp)
        ids.append(id)

    ids = np.array(ids)

    Train and save classifier

    clf = cv2.face.LBPHFaceRecognizer_create()
    clf.train(faces, ids)
    clf.write("./data/classifiers/" + name + "_classifier.xml")

create_dataset.py

import cv2
import os

def start_capture(name):
    path = "./data/" + name
    num_of_images = 0
    detector = cv2.CascadeClassifier(
        "./data/haarcascade_frontalface_default.xml")
    try:
        os.makedirs(path)
    except:
        print('Directory Already Created')

    # vid = cv2.VideoCapture(0)
    vid = cv2.VideoCapture(0, cv2.CAP_DSHOW)
    while True:

        ret, img = vid.read()
        new_img = None
        grayimg = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
        face = detector.detectMultiScale(
            image=grayimg, scaleFactor=1.1, minNeighbors=5)
        for x, y, w, h in face:
            cv2.rectangle(img, (x, y), (x + w, y + h), (0, 0, 0), 2)
            cv2.putText(img, "Face Detected", (x, y - 5),
                        cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 0, 255))
            cv2.putText(img, str(str(num_of_images) + " images captured"), (x, y + h + 20), cv2.FONT_HERSHEY_SIMPLEX,
                        0.8, (0, 0, 255))
            new_img = img[y:y + h, x:x + w]
        cv2.imshow("FaceDetection", img)
        key = cv2.waitKey(1) & 0xFF

        try:
            cv2.imwrite(str(path + "/" + name + '_' +
                        str(num_of_images) + ".jpg"), new_img)
            num_of_images += 1
        except:
            pass
        if key == ord("q") or key == 27 or num_of_images == 100:
            break
    cv2.destroyAllWindows()
    return num_of_images

detector.py

import cv2
from time import sleep
from PIL import Image

def main_app(name):
    face_cascade = cv2.CascadeClassifier('./data/haarcascade_frontalface_default.xml')
    recognizer = cv2.face.LBPHFaceRecognizer_create()
    recognizer.read(f"./data/classifiers/{name}_classifier.xml")
    cap = cv2.VideoCapture(0)
    pred = 0
    while True:
        ret, frame = cap.read()
        # default_img = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
        gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
        faces = face_cascade.detectMultiScale(gray, 1.3, 5)

        for (x, y, w, h) in faces:

            roi_gray = gray[y:y + h, x:x + w]

            id, confidence = recognizer.predict(roi_gray)
            confidence = 100 - int(confidence)
            pred = 0
            if confidence > 50:
                # if u want to print confidence level
                confidence = 100 - int(confidence)
                pred += +1
                text = name.upper()
                font = cv2.FONT_HERSHEY_PLAIN
                frame = cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 255, 0), 2)
                frame = cv2.putText(frame, text, (x, y - 4), font, 1, (0, 255, 0), 1, cv2.LINE_AA)

            else:
                pred += -1
                text = "UnknownFace"
                font = cv2.FONT_HERSHEY_PLAIN
                frame = cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 0, 255), 2)
                frame = cv2.putText(frame, text, (x, y - 4), font, 1, (0, 0, 255), 1, cv2.LINE_AA)

        cv2.imshow("image", frame)

        if cv2.waitKey(20) & 0xFF == ord('q'):
            print(pred)
            if pred > 0:
                dim = (124, 124)
                img = cv2.imread(f".\\data\\{name}\\{pred}{name}.jpg", cv2.IMREAD_UNCHANGED)
                resized = cv2.resize(img, dim, interpolation=cv2.INTER_AREA)
                cv2.imwrite(f".\\data\\{name}\\50{name}.jpg", resized)
                Image1 = Image.open(f".\\files\\2.png")

                # make a copy the image so that the
                # original image does not get affected
                Image1copy = Image1.copy()
                Image2 = Image.open(f".\\data\\{name}\\50{name}.jpg")
                Image2copy = Image2.copy()

                # paste image giving dimensions
                Image1copy.paste(Image2copy, (195, 114))

                # save the image
                Image1copy.save("end.png")
                frame = cv2.imread("end.png", 1)

                cv2.imshow("Result", frame)
                cv2.waitKey(5000)
            break

    cap.release()
    cv2.destroyAllWindows()
joeVenner commented 2 years ago

easy thing, only by looking if there is any blue square around the face as you will find below, and as soon as the app detects the face it will add a counter below the square to count how many captured data.

image