Skip to content
Snippets Groups Projects
Commit 42d4c05b authored by l27-wong's avatar l27-wong
Browse files

Added register feature

parent 55ece8c1
Branches
No related tags found
No related merge requests found
...@@ -24,6 +24,10 @@ ...@@ -24,6 +24,10 @@
A0951310262F8C670066554E /* Session.swift in Sources */ = {isa = PBXBuildFile; fileRef = A095130F262F8C670066554E /* Session.swift */; }; A0951310262F8C670066554E /* Session.swift in Sources */ = {isa = PBXBuildFile; fileRef = A095130F262F8C670066554E /* Session.swift */; };
A0951313262F8C850066554E /* Attendance.swift in Sources */ = {isa = PBXBuildFile; fileRef = A0951312262F8C850066554E /* Attendance.swift */; }; A0951313262F8C850066554E /* Attendance.swift in Sources */ = {isa = PBXBuildFile; fileRef = A0951312262F8C850066554E /* Attendance.swift */; };
A0951318262F8C900066554E /* Module.swift in Sources */ = {isa = PBXBuildFile; fileRef = A0951317262F8C900066554E /* Module.swift */; }; A0951318262F8C900066554E /* Module.swift in Sources */ = {isa = PBXBuildFile; fileRef = A0951317262F8C900066554E /* Module.swift */; };
A0B8A3562630BCC000068B14 /* RegisterViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A0B8A3552630BCC000068B14 /* RegisterViewController.swift */; };
A0B8A3592630BCD900068B14 /* WelcomeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A0B8A3582630BCD900068B14 /* WelcomeViewController.swift */; };
A0B8A35C2630C6D100068B14 /* FaceTrackerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A0B8A35B2630C6D100068B14 /* FaceTrackerViewController.swift */; };
A0B8A35F2630C70A00068B14 /* SuccessViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A0B8A35E2630C70A00068B14 /* SuccessViewController.swift */; };
/* End PBXBuildFile section */ /* End PBXBuildFile section */
/* Begin PBXFileReference section */ /* Begin PBXFileReference section */
...@@ -45,6 +49,10 @@ ...@@ -45,6 +49,10 @@
A095130F262F8C670066554E /* Session.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Session.swift; sourceTree = "<group>"; }; A095130F262F8C670066554E /* Session.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Session.swift; sourceTree = "<group>"; };
A0951312262F8C850066554E /* Attendance.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Attendance.swift; sourceTree = "<group>"; }; A0951312262F8C850066554E /* Attendance.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Attendance.swift; sourceTree = "<group>"; };
A0951317262F8C900066554E /* Module.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Module.swift; sourceTree = "<group>"; }; A0951317262F8C900066554E /* Module.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Module.swift; sourceTree = "<group>"; };
A0B8A3552630BCC000068B14 /* RegisterViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RegisterViewController.swift; sourceTree = "<group>"; };
A0B8A3582630BCD900068B14 /* WelcomeViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WelcomeViewController.swift; sourceTree = "<group>"; };
A0B8A35B2630C6D100068B14 /* FaceTrackerViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FaceTrackerViewController.swift; sourceTree = "<group>"; };
A0B8A35E2630C70A00068B14 /* SuccessViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SuccessViewController.swift; sourceTree = "<group>"; };
AA9F1E4B6F3F1A4222A527CC /* Pods-FaceRecogAttendance.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-FaceRecogAttendance.debug.xcconfig"; path = "Target Support Files/Pods-FaceRecogAttendance/Pods-FaceRecogAttendance.debug.xcconfig"; sourceTree = "<group>"; }; AA9F1E4B6F3F1A4222A527CC /* Pods-FaceRecogAttendance.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-FaceRecogAttendance.debug.xcconfig"; path = "Target Support Files/Pods-FaceRecogAttendance/Pods-FaceRecogAttendance.debug.xcconfig"; sourceTree = "<group>"; };
/* End PBXFileReference section */ /* End PBXFileReference section */
...@@ -130,6 +138,10 @@ ...@@ -130,6 +138,10 @@
A09512EA262F89340066554E /* SelectModuleViewController.swift */, A09512EA262F89340066554E /* SelectModuleViewController.swift */,
A09512ED262F89460066554E /* SelectSessionViewController.swift */, A09512ED262F89460066554E /* SelectSessionViewController.swift */,
A09512F0262F89550066554E /* FaceClassificationViewController.swift */, A09512F0262F89550066554E /* FaceClassificationViewController.swift */,
A0B8A3552630BCC000068B14 /* RegisterViewController.swift */,
A0B8A3582630BCD900068B14 /* WelcomeViewController.swift */,
A0B8A35B2630C6D100068B14 /* FaceTrackerViewController.swift */,
A0B8A35E2630C70A00068B14 /* SuccessViewController.swift */,
); );
path = Controller; path = Controller;
sourceTree = "<group>"; sourceTree = "<group>";
...@@ -267,13 +279,17 @@ ...@@ -267,13 +279,17 @@
A09512F4262F8A4D0066554E /* FaceClassifier.mlmodel in Sources */, A09512F4262F8A4D0066554E /* FaceClassifier.mlmodel in Sources */,
A0940D18262E7E6800AD51BA /* AppDelegate.swift in Sources */, A0940D18262E7E6800AD51BA /* AppDelegate.swift in Sources */,
A09512FA262F8C0E0066554E /* CameraManager.swift in Sources */, A09512FA262F8C0E0066554E /* CameraManager.swift in Sources */,
A0B8A3562630BCC000068B14 /* RegisterViewController.swift in Sources */,
A09512F1262F89550066554E /* FaceClassificationViewController.swift in Sources */, A09512F1262F89550066554E /* FaceClassificationViewController.swift in Sources */,
A0951313262F8C850066554E /* Attendance.swift in Sources */, A0951313262F8C850066554E /* Attendance.swift in Sources */,
A0B8A35C2630C6D100068B14 /* FaceTrackerViewController.swift in Sources */,
A0B8A3592630BCD900068B14 /* WelcomeViewController.swift in Sources */,
A09512EE262F89460066554E /* SelectSessionViewController.swift in Sources */, A09512EE262F89460066554E /* SelectSessionViewController.swift in Sources */,
A09512EB262F89340066554E /* SelectModuleViewController.swift in Sources */, A09512EB262F89340066554E /* SelectModuleViewController.swift in Sources */,
A09512F7262F8B170066554E /* Student.swift in Sources */, A09512F7262F8B170066554E /* Student.swift in Sources */,
A0940D1A262E7E6800AD51BA /* SceneDelegate.swift in Sources */, A0940D1A262E7E6800AD51BA /* SceneDelegate.swift in Sources */,
A0951318262F8C900066554E /* Module.swift in Sources */, A0951318262F8C900066554E /* Module.swift in Sources */,
A0B8A35F2630C70A00068B14 /* SuccessViewController.swift in Sources */,
A0951310262F8C670066554E /* Session.swift in Sources */, A0951310262F8C670066554E /* Session.swift in Sources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
......
//
// FaceTrackerViewController.swift
// FaceRecogAttendance
//
// Created by Lucas on 21/04/2021.
//
import Foundation
import UIKit
import Vision
import AVFoundation
import FirebaseStorage
import FirebaseFirestore
class FaceTrackerViewController: UIViewController, AVCaptureVideoDataOutputSampleBufferDelegate{
let faceDetection = VNDetectFaceRectanglesRequest()
let faceDetectionRequest = VNSequenceRequestHandler()
var faceClassificationRequest: VNCoreMLRequest!
var lastObservation : VNFaceObservation?
private var sampleCounter = 0
private let requiredSamples = 5
private var faceImages = [UIImage]()
private var isIdentifiyingPeople = false
private var isCapturing: Bool = false
var studentID : String?
var studentName: String?
override func viewDidLoad() {
super.viewDidLoad()
promptCommand()
setupCamera()
}
func setupCamera() {
let captureSession = AVCaptureSession()
captureSession.sessionPreset = .high
guard let captureDevice = AVCaptureDevice.default(AVCaptureDevice.DeviceType.builtInWideAngleCamera, for: AVMediaType.video, position: .front) else { preconditionFailure("A Camera is needed to start the AV session") }
//throw error if no camera is found.
guard let input = try? AVCaptureDeviceInput(device: captureDevice) else { return }
captureSession.addInput(input)
captureSession.startRunning()
let previewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
view.layer.addSublayer(previewLayer)
previewLayer.frame = view.frame
let dataOutput = AVCaptureVideoDataOutput()
dataOutput.setSampleBufferDelegate(self, queue: DispatchQueue(label: "videoQueue"))
captureSession.addOutput(dataOutput)
}
func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) {
guard let pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer),
let attachments = CMCopyDictionaryOfAttachments(allocator: kCFAllocatorDefault, target: sampleBuffer, attachmentMode: kCMAttachmentMode_ShouldPropagate)
as? [CIImageOption: Any]
else { return }
let ciImage = CIImage(cvImageBuffer: pixelBuffer, options: attachments)
let ciImageWithOrientation = ciImage.oriented(forExifOrientation: Int32(UIImage.Orientation.leftMirrored.rawValue))
detectFace(on: ciImageWithOrientation)
}
// face detector
func detectFace(on image: CIImage) {
// try to detect face
try? faceDetectionRequest.perform([faceDetection], on: image)
guard let faceObservation = (faceDetection.results as? [VNFaceObservation])?.first else {
print("no faces")
return
}
if isIdentifiyingPeople {
let handler = VNImageRequestHandler(ciImage: image, orientation: .up, options: [:])
self.lastObservation = faceObservation
try? handler.perform([self.faceClassificationRequest])
} else {
let faceImage: UIImage = convert(cmage: image)
sampleCounter += 1
if faceImages.count <= requiredSamples {
if sampleCounter % 20 == 0 {
print(faceImages.count)
faceImages.append(faceImage)
uploadImages(image: faceImage) { (url) in
guard url != nil else { return }
}
print("+1")
}
} else {
print("enough data")
DispatchQueue.main.async {
// go back to previous view controller after getting enough data
self.navigationController?.popViewController(animated: true)
}
return
}
}
}
// upload image to firebase storage
fileprivate func uploadImages(image: UIImage, completion: @escaping (_ url: String?) -> Void) {
// convert UIImage to jpg format
guard let data = image.jpegData(compressionQuality: 1.0) else {
// present error alert in device
let alert = UIAlertController.init(title: "info", message: "something went wrong", preferredStyle: .alert)
alert.addAction(UIAlertAction.init(title: "Ok", style: .default, handler: nil))
self.present(alert, animated: true, completion: nil)
return
}
// create unique id for image
let imageName = UUID().uuidString
// create unique reference for image
let imageReference = Storage.storage().reference().child("\(studentID!)_\(studentName!)").child("\(imageName).png")
// upload image to firebase
DispatchQueue.main.async {
imageReference.putData(data, metadata: nil) {(metadata, error) in
if error != nil {
print("error")
completion(nil)
} else {
imageReference.downloadURL(completion: { (url, error) in
print(url?.absoluteString as Any)
completion(url?.absoluteString)
})
}
}
}
}
// function to convert image to UIImage format
private func convert(cmage:CIImage) -> UIImage {
let context:CIContext = CIContext.init(options: nil)
let cgImage:CGImage = context.createCGImage(cmage, from: cmage.extent)!
let image:UIImage = UIImage.init(cgImage: cgImage)
return image
}
private func promptCommand() {
let alert = UIAlertController.init(title: "Info", message: "The system needs to capture your face images for training purposes. Please align your face at the centre of the screen and look at the camera.", preferredStyle: .alert)
alert.addAction(UIAlertAction.init(title: "Ok", style: .default, handler: nil))
self.present(alert, animated: true, completion: nil)
}
}
//
// RegisterViewController.swift
// FaceRecogAttendance
//
// Created by Lucas on 21/04/2021.
//
import UIKit
import RealmSwift
class RegisterViewController: UIViewController {
// let realm = try! Realm()
var notificationToken: NotificationToken?
var realm : Realm?
@IBOutlet weak var emailTextField: UITextField!
@IBOutlet weak var nameTextField: UITextField!
@IBOutlet weak var studentIDTextField: UITextField!
var studentID = ""
var studentName = ""
override func viewDidLoad() {
super.viewDidLoad()
self.hideKeyboardWhenTappedAround()
signUp()
}
@IBAction func recordButtonPressed(_ sender: UIButton) {
studentID = studentIDTextField.text!
studentName = nameTextField.text!
self.performSegue(withIdentifier: "GoToRecord", sender: self)
}
@IBAction func registerButtonPressed(_ sender: UIButton) {
if emailTextField.text == "" || nameTextField.text == "" || studentIDTextField.text == "" {
let alert = UIAlertController.init(title: "Error", message: "Please fill in all the required fields.", preferredStyle: .alert)
alert.addAction(UIAlertAction.init(title: "Ok", style: .cancel, handler: nil))
self.present(alert, animated: true, completion: nil)
}
// let newStudent = Student()
// newStudent.email = emailTextField.text!
// newStudent.password = passwordTextField.text!
// newStudent.studentName = nameTextField.text!
// newStudent.studentID = studentIDTextField.text!
// newStudent.isImageUpload = true
let newStudent = Student(studentName: nameTextField.text!, studentID: studentIDTextField.text!, email: emailTextField.text!, password: "", isImageUpload: true, isImageTrained: false, partition: "user=\(app.currentUser!.id)")
try! self.realm?.write {
self.realm?.add(newStudent)
}
// self.saveStudent(student: newStudent)
print("successfully registered student")
self.performSegue(withIdentifier: "goToRegistered", sender: self)
}
// func saveStudent(student: Student) {
// do {
// try self.realm.write {
// self.realm.add(student)
// }
// } catch {
// print("Error saving category \(error)")
// }
// }
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "GoToRecord" {
let destinationVC = segue.destination as! FaceTrackerViewController
destinationVC.studentID = studentID
destinationVC.studentName = studentName
}
}
deinit {
notificationToken?.invalidate()
}
@objc func signUp() {
app.login(credentials: Credentials.anonymous) { (result) in
// Remember to dispatch back to the main thread in completion handlers
// if you want to do anything on the UI.
DispatchQueue.main.async {
switch result {
case .failure(let error):
print("Login failed: \(error)")
case .success(let user):
print("Login as \(user) succeeded!")
// Continue below
self.signIn()
}
}
}
}
@objc func signIn() {
let user = app.currentUser!
let partitionValue = "user=\(user.id)"
var configuration = user.configuration(partitionValue: partitionValue)
configuration.objectTypes = [Student.self]
Realm.asyncOpen(configuration: configuration) { (result) in
switch result {
case .failure(let error):
print("failed to open realm: \(error)")
case .success(let realm):
self.onRealmOpened(realm)
self.realm = realm
}
}
}
func onRealmOpened(_ realm: Realm) {
let students = realm.objects(Student.self)
// Retain notificationToken as long as you want to observe
notificationToken = students.observe { (changes) in
switch changes {
case .initial: break
// Results are now populated and can be accessed without blocking the UI
case .update(_, let deletions, let insertions, let modifications):
// Query results have changed.
print("Deleted indices: ", deletions)
print("Inserted indices: ", insertions)
print("Modified modifications: ", modifications)
case .error(let error):
// An error occurred while opening the Realm file on the background worker thread
fatalError("\(error)")
}
}
}
}
// CHANGE THIS TO DELEGATE FUNCTION!!!
extension UIViewController {
func hideKeyboardWhenTappedAround() {
let tap = UITapGestureRecognizer(target: self, action: #selector(UIViewController.dismissKeyboard))
tap.cancelsTouchesInView = false
view.addGestureRecognizer(tap)
}
@objc func dismissKeyboard() {
view.endEditing(true)
}
}
//
// SuccessViewController.swift
// FaceRecogAttendance
//
// Created by Lucas on 21/04/2021.
//
import Foundation
import UIKit
class SuccessViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
@IBAction func doneButtonPressed(_ sender: UIButton) {
self.navigationController?.popToRootViewController(animated: true)
}
}
//
// WelcomeViewController.swift
// FaceRecogAttendance
//
// Created by Lucas on 21/04/2021.
//
import UIKit
import RealmSwift
class WelcomeViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
}
...@@ -263,7 +263,7 @@ ...@@ -263,7 +263,7 @@
<!--Success View Controller--> <!--Success View Controller-->
<scene sceneID="owC-2b-Ai4"> <scene sceneID="owC-2b-Ai4">
<objects> <objects>
<viewController id="rXJ-vt-WP0" customClass="SuccessViewController" customModule="FaceRecogAttendance" customModuleProvider="target" sceneMemberID="viewController"> <viewController id="rXJ-vt-WP0" customClass="SuccessViewController" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="u2s-s4-Y7b"> <view key="view" contentMode="scaleToFill" id="u2s-s4-Y7b">
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/> <rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment