Webcam Face Detection Using JavaScript, PHP, and MySQL 2023

In the past, I had published a very popular blog post regarding JavaScript Face Detection and Recognition Libraries. So, in this tutorial, I’ll use a library from that list which is known as “pico.js“.

We’ll use this JavaScript library to detect a human face in webcam feed. An interesting thing is that this library is consists of just around 200 lines of code. Meaning that it is very lightweight and easy to use inside web projects.

Basic functionality of this project

  • Real-time human face detection in Webcam video.
  • When face is detected then get its image from Webcam.
  • Upload the image to server.
  • Save image path in database so that we can display it later.

Create Database and Table

Create a database using phpMyAdmin or below SQL.

CREATE DATABASE face_detection_project;

Create a table to store images path.

CREATE TABLE `detected_faces` (
  `id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT,
  `image` varchar(255) NOT NULL,
  `created_time` datetime NOT NULL DEFAULT current_timestamp(),
  PRIMARY KEY (id)
) ENGINE=InnoDB;

Files and folders used in this tutorial

First of all, create a folder on your local environment (e.g. inside XAMPP “htdocs” folder). Name it anything you like. But, for the sake of this tutorial, I’ll name it as face_detection_project.

Basically, it will be used to hold our project files and folders.

Now, below is a list of files and folders that we will create in this tutorial. It will help you get an overview of the project’s folder structure.

  • images
  • picojs
  • db_connect.php
  • save.php
  • view.php
  • index.php

Install Pico.js Face Detection Library

  • Download pico.js library from GitHub.
  • Extract the .zip file inside your project folder.
  • Now, change the name of the extracted folder to picojs.

Important Note

I’ve explained important and complex code using comments. So, read them in the below code-snippets.


Detect Face in Webcam using JavaScript

Detect Face in Webcam using JavaScript

Here’s the content for index.php.

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<meta http-equiv="X-UA-Compatible" content="chrome=1">
		<title>Webcam Face Detection Using JavaScript, PHP, and MySQL - Edopedia.com</title>
		
		<!-- Add these JavaScript files from pico.js library -->
		<script src="picojs/examples/camvas.js"></script>
		<script src="picojs/pico.js"></script>
		<script src="picojs/lploc.js"></script>
		
		<script>
			var initialized = false;
			function button_callback() {
				/*
					(0) check whether we're already running face detection
				*/
				if(initialized)
					return; // if yes, then do not initialize everything again
				/*
					(1) initialize the pico.js face detector
				*/
				var update_memory = pico.instantiate_detection_memory(1); // we will use the detecions of the last 1 frame
				var facefinder_classify_region = function(r, c, s, pixels, ldim) {return -1.0;};
				var cascadeurl = 'https://raw.githubusercontent.com/nenadmarkus/pico/c2e81f9d23cc11d1a612fd21e4f9de0921a5d0d9/rnt/cascades/facefinder';
				fetch(cascadeurl).then(function(response) {
					response.arrayBuffer().then(function(buffer) {
						var bytes = new Int8Array(buffer);
						facefinder_classify_region = pico.unpack_cascade(bytes);
						console.log('* facefinder loaded');
					})
				})
				/*
					(2) initialize the lploc.js library with a pupil localizer
				*/
				var do_puploc = function(r, c, s, nperturbs, pixels, nrows, ncols, ldim) {return [-1.0, -1.0];};
				//var puplocurl = '../puploc.bin';
				var puplocurl = 'https://f002.backblazeb2.com/file/tehnokv-www/posts/puploc-with-trees/demo/puploc.bin'
				fetch(puplocurl).then(function(response) {
					response.arrayBuffer().then(function(buffer) {
						var bytes = new Int8Array(buffer);
						do_puploc = lploc.unpack_localizer(bytes);
						console.log('* puploc loaded');
					})
				})
				/*
					(3) get the drawing context on the canvas and define a function to transform an RGBA image to grayscale
				*/
				var ctx = document.getElementsByTagName('canvas')[0].getContext('2d');
				function rgba_to_grayscale(rgba, nrows, ncols) {
					var gray = new Uint8Array(nrows*ncols);
					for(var r=0; r<nrows; ++r)
						for(var c=0; c<ncols; ++c)
							// gray = 0.2*red + 0.7*green + 0.1*blue
							gray[r*ncols + c] = (2*rgba[r*4*ncols+4*c+0]+7*rgba[r*4*ncols+4*c+1]+1*rgba[r*4*ncols+4*c+2])/10;
					return gray;
				}
				/*
					(4) this function is called each time a video frame becomes available
				*/
				var processfn = function(video, dt) {
					// render the video frame to the canvas element and extract RGBA pixel data
					ctx.drawImage(video, 0, 0);
					var rgba = ctx.getImageData(0, 0, 640, 480).data;
					// prepare input to `run_cascade`
					image = {
						"pixels": rgba_to_grayscale(rgba, 480, 640),
						"nrows": 480,
						"ncols": 640,
						"ldim": 640
					}
					params = {
						"shiftfactor": 0.1, // move the detection window by 10% of its size
						"minsize": 100,     // minimum size of a face
						"maxsize": 1000,    // maximum size of a face
						"scalefactor": 1.1  // for multiscale processing: resize the detection window by 10% when moving to the higher scale
					}
					// run the cascade over the frame and cluster the obtained detections
					// dets is an array that contains (r, c, s, q) quadruplets
					// (representing row, column, scale and detection score)
					dets = pico.run_cascade(image, facefinder_classify_region, params);
					dets = update_memory(dets);
					dets = pico.cluster_detections(dets, 0.2); // set IoU threshold to 0.2
					// draw detections
					for(i=0; i<dets.length; ++i)
					{
						// check the detection score
						// if it's above the threshold, draw it
						// (the constant 50.0 is empirical: other cascades might require a different one)
						if(dets[i][3]>50.0)
						{
							var r, c, s;
							//
							ctx.beginPath();
							ctx.arc(dets[i][1], dets[i][0], dets[i][2]/2, 0, 2*Math.PI, false);
							ctx.lineWidth = 3;
							ctx.strokeStyle = 'red';
							ctx.stroke();
							//
							// find the eye pupils for each detected face
							// starting regions for localization are initialized based on the face bounding box
							// (parameters are set empirically)
							// first eye
							r = dets[i][0] - 0.075*dets[i][2];
							c = dets[i][1] - 0.175*dets[i][2];
							s = 0.35*dets[i][2];
							[r, c] = do_puploc(r, c, s, 63, image)
							if(r>=0 && c>=0)
							{
								ctx.beginPath();
								ctx.arc(c, r, 1, 0, 2*Math.PI, false);
								ctx.lineWidth = 3;
								ctx.strokeStyle = 'red';
								ctx.stroke();
							}
							// second eye
							r = dets[i][0] - 0.075*dets[i][2];
							c = dets[i][1] + 0.175*dets[i][2];
							s = 0.35*dets[i][2];
							[r, c] = do_puploc(r, c, s, 63, image)
							if(r>=0 && c>=0)
							{
								ctx.beginPath();
								ctx.arc(c, r, 1, 0, 2*Math.PI, false);
								ctx.lineWidth = 3;
								ctx.strokeStyle = 'red';
								ctx.stroke();
							}
							
							// At this point, we already know that the human face is detected in webcam. So, We'll simply create an image from canvas that is displaying the webcam result in real-time.
							var can = document.getElementsByTagName('canvas')[0]
							var img = new Image();
							img.src = can.toDataURL('image/jpeg', 1.0);
							
							// Now, we will send the image to server and process it using PHP. Also, we have to save its path in MySQL database for later use.
							var data = JSON.stringify({ image: img.src });
							fetch("save.php",
							{
								method: "POST",
								body: data
							})
							.then(function(res){ return res.json(); })
							.then(function(data){ return alert( data.message ); })
							
							// This alert statement is a little hack to temporarily stop the execution of script.
							alert('Face found!');
						}
					}
				}
				/*
					(5) instantiate camera handling (see https://github.com/cbrandolino/camvas)
				*/
				var mycamvas = new camvas(ctx, processfn);
				/*
					(6) it seems that everything went well
				*/
				initialized = true;
			}
		</script>
	</head>
	
	<body>
		<div>
			<h3>Webcam Face Detection Using JavaScript, PHP, and MySQL by Edopedia.com</h3>
			<p>Click the "Start Webcam" button below and allow the page to access your webcam.</p>
			
			<p>View Tutorial: <a href="https://www.edopedia.com/blog/webcam-face-detection-javascript-php-mysql/" target="_blank">https://www.edopedia.com/blog/webcam-face-detection-javascript-php-mysql/</a></p>
		</div>
		<hr />
		<p>
			<center>
				<input type="button" value="Start Webcam" onclick="button_callback()">
				&nbsp;
				<a href="view.php" target="_blank">View Saved Images</a>
			</center>
		</p>
		<hr />
		<p>
			<center>
				<canvas width="640" height="480"></canvas>
			</center>
		</p>
	</body>
</html>

Connect With Database

Here’s the content for db_connect.php

<?php
$db_host = "localhost";               // Database Host
$db_user = "root";                    // Database User
$db_pass = "";                        // Database Password
$db_name = "face_detection_project";  // Database Name
$conn = mysqli_connect($db_host, $db_user, $db_pass, $db_name); // Connect to Database
if(!$conn) // Check connection
{
  die("Connection failed: " . mysqli_connect_error()); // Display error if not connected
}
?>

Store Image on the Server and Database

Here’s the content for save.php

<?php

// Database connection file
require_once('db_connect.php');

// Receive uploaded image file
$data = file_get_contents('php://input');

if(isset($data))
{
	// convert json to array
	$data_arr = json_decode($data, true);
	
	// The image we receive will be encoded in base64.
	// So, convert it to actual image file.
	$img = $data_arr['image'];
	$img = str_replace('data:image/jpeg;base64,', '', $img);
	$img = str_replace(' ', '+', $img);
	$file_data = base64_decode($img);
	$file_name = 'images/'.time().'-'.mt_rand(100000,999999).'.jpg';
	file_put_contents($file_name, $file_data);
	
	$created_time = date('Y-m-d H:i:s');
	
	// SQL query to insert image file path in database.
	$query = "INSERT INTO detected_faces (image, created_time) VALUES ('$file_name', '$created_time')";
	
	if(mysqli_query($conn, $query))
    {
		$response = array(
			'status' => true,
			'message' => "Image Saved Successfully."
		);
    }
    else
    {
		$response = array(
			'status' => true,
			'message' => "Unable to save image."
		);
    }
}
else
{
	$response = array(
		'status' => false,
		'message' => "Failed"
	);
}

// return the response
header('Content-type: application/json');
echo json_encode($response);

?>

View Saved Images

view detected face images

Here’s the content for view.php

<?php

// Database connection file
require_once('db_connect.php');

// Select all images from database and display them in HTML <table>.
$query = "SELECT * FROM detected_faces";
$result = mysqli_query($conn, $query);

if(mysqli_num_rows($result) > 0)
{
?>

<style>
table, th, td {
  border: 1px solid black;
}
</style>

<table>
	<tr>
		<th>ID</th>
		<th>Image</th>
		<th>Open Image in New Tab</th>
		<th>Created Time</th>
	</tr>
	<?php
	while($row = $result->fetch_assoc())
	{
	?>
	<tr>
		<td><?=$row['id']?></td>
		<td>
			<img src="<?=$row['image']?>" style="width: 100px;" />
		</td>
		<td>
			<a href="<?=$row['image']?>" target="_blank">Click Here</a>
		</td>
		<td><?=$row['created_time']?></td>
	</tr>
	<?php
	}
	?>
</table>
<?php
}
else
{
  echo "No records found!";
}

?>

Run the Project

  • Create a folder called images inside the project root. Our detected face images will be uploaded here.
  • Open XAMPP or any other PHP local development environment. Now, start Apache and MySQL.
  • Finally, open the project by typing this URL in your web browser’s address bar: localhost/face_detection_project

Download Full Project

Download

6 thoughts on “Webcam Face Detection Using JavaScript, PHP, and MySQL 2023”

  1. thank you for the tutorial but i have a problem in my database because the script is storing more than one image in my database, i would like to know how to change it please

    Reply

Leave a Comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.