I wrote some code that uses face-api.js to detect and differentiate between male and female faces and then blur out female bodies, meant to be an experimental extension I’m working on that’ll eventually be used for censoring potentially unwanted media.
The code seems to be fine and loads well onto chrome, only repeatedly runs into errors and I don’t know why. I’ve made a contentScript.js file, a manifest.json file, a background.js file, popup.html, popup.css and popup.js file, some icons and a root directory, and gone through all of them repeatedly looking for what is wrong but can’t seem to find a solution. Any ideas?
contentScript.js:
// Load the face detection and gender classification models
Promise.all([
faceapi.nets.ssdMobilenetv1.loadFromUri('https://cdn.jsdelivr.net/npm/@vladmandic/face-api@latest/weights'),
faceapi.nets.genderRecognitionNet.loadFromUri('https://cdn.jsdelivr.net/npm/@vladmandic/face-api@latest/weights')
]).then(startVideo)
// Define the canvas element for drawing the boxes
const canvas = document.createElement('canvas')
canvas.width = window.innerWidth
canvas.height = window.innerHeight
canvas.style.position = 'fixed'
canvas.style.top = '0'
canvas.style.left = '0'
canvas.style.pointerEvents = 'none'
document.body.appendChild(canvas)
const ctx = canvas.getContext('2d')
// Get the video element
const video = document.querySelector('video')
// Start the video playback and detection loop
async function startVideo() {
// Play the video
await video.play()
// Define the face detection and gender classification options
const options = new faceapi.SsdMobilenetv1Options({ minConfidence: 0.5 })
// Define the loop function
async function detectFacesAndGender() {
// Detect all faces in the video frame and classify their gender
const detections = await faceapi.detectAllFaces(video, options).withFaceLandmarks().withGender()
// Loop through each face and determine its gender
detections.forEach(async detection => {
const gender = detection.gender // "male", "female", or "genderless"
// Determine whether to draw a box around the face or blur the body
if (gender === "female") {
// Place a red box around the face
const box = detection.detection.box
ctx.beginPath()
ctx.lineWidth = '6'
ctx.strokeStyle = 'red'
ctx.rect(box.x, box.y, box.width, box.height)
ctx.stroke()
// Blur female bodies
const image = await faceapi.bufferToImage(detection.detection._imageData)
const canvas2 = faceapi.createCanvasFromMedia(image)
const displaySize = { width: image.width, height: image.height }
faceapi.matchDimensions(canvas2, displaySize)
const resizedDetection = faceapi.resizeResults(detection, displaySize)
const bodyPart = resizedDetection.detection.imageDims !== displaySize ? faceapi.CANVAS : undefined
const blurAmount = 10
faceapi.drawDetection(canvas2, resizedDetection, { color: 'rgba(255, 0, 0, 1)' })
await faceapi.blurFaces(canvas2, [resizedDetection], blurAmount, bodyPart)
const blurredImage = new Image()
blurredImage.src = canvas2.toDataURL()
const parentElement = detection.forSize(canvas.width, canvas.height).parentElement
let femaleBody = parentElement.querySelector('.female-body')
if (!femaleBody) {
const img = document.createElement('img')
img.classList.add('female-body')
img.style.position = 'absolute'
img.style.top = '0'
img.style.left = '0'
img.style.width = '100%'
img.style.height = '100%'
img.style.objectFit = 'contain'
parentElement.appendChild(img)
femaleBody = img
}
femaleBody.src = blurredImage.src
} else if (gender === "male" || gender === "genderless") {
// Unblur male and genderless bodies
const parentElement = detection.forSize(canvas.width, canvas.height).parentElement
const maleOrGenderlessBody
} else if (gender === "male" || gender === "genderless") {
// Unblur male and genderless bodies
const parentElement = detection.forSize(canvas.width, canvas.height).parentElement
const maleOrGenderlessBody = parentElement.querySelector(".body-male, .body-genderless")
if (maleOrGenderlessBody) {
maleOrGenderlessBody.style.filter = "none"
}
}
// Add the modified canvas back to the DOM
parentElement.appendChild(canvas)
}
}
// Load the models and start the detection process
Promise.all([
faceapi.nets.tinyFaceDetector.loadFromUri(modelURI),
faceapi.nets.faceLandmark68Net.loadFromUri(modelURI),
faceapi.nets.faceRecognitionNet.loadFromUri(modelURI),
faceapi.nets.faceExpressionNet.loadFromUri(modelURI),
faceapi.nets.ageGenderNet.loadFromUri(modelURI),
faceapi.nets.ssdMobilenetv1.loadFromUri(modelURI)
]).then(startDetection)
// Display a message if an error occurs during detection
const errorElement = document.getElementById("error-message")
const displayError = (errorMessage) => {
errorElement.innerHTML = errorMessage
errorElement.style.display = "block"
}
window.onerror = (message, source, lineno, colno, error) => {
displayError(`An error occurred: ${message}`)
return true
}
Promise.all([
faceapi.nets.tinyFaceDetector.loadFromUri(modelURI),
faceapi.nets.faceLandmark68Net.loadFromUri(modelURI),
faceapi.nets.faceRecognitionNet.loadFromUri(modelURI),
faceapi.nets.faceExpressionNet.loadFromUri(modelURI),
faceapi.nets.ageGenderNet.loadFromUri(modelURI),
faceapi.nets.ssdMobilenetv1.loadFromUri(modelURI)
]).catch((error) => displayError(`There was an error loading the models: ${error}`))
}
manifest.json:
{
"manifest_version": 3,
"name": "Image/Video Blur",
"version": "1.0",
"description": "An extension to blur images and videos",
"icons": {
"16": "icon16.png",
"32": "icon32.png",
"48": "icon48.png",
"128": "icon128.png"
},
"permissions": ["activeTab", "scripting", "tabs"],
"host_permissions": ["*://*/*"],
"action": {
"default_popup": "popup.html",
"default_icon": {
"16": "icon16.png",
"32": "icon32.png",
"48": "icon48.png",
"128": "icon128.png"
}
},
"background": {
"service_worker": "background.js"
},
"content_scripts": [
{
"matches": ["*://*/*"],
"js": ["contentScript.js"]
}
]
}
background.js:
chrome.action.onClicked.addListener((tab) => {
chrome.scripting.executeScript({
target: {tabId: tab.id},
files: ['contentScript.js']
});
});
popup.html:
<!DOCTYPE html>
<html>
<head>
<title>My Extension</title>
<link rel="stylesheet" href="popup.css">
</head>
<body>
<h1>Welcome to my extension!</h1>
<p>Click the button below to blur the current page.</p>
<button id="blur-button">Blur Page</button>
<script src="popup.js"></script>
</body>
</html>
popup.css:
body {
font-family: Arial, sans-serif;
font-size: 16px;
margin: 0;
padding: 0;
}
h1 {
font-size: 24px;
margin-bottom: 10px;
}
p {
margin-top: 0;
margin-bottom: 20px;
}
button {
padding: 10px 20px;
background-color: #4CAF50;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
button:hover {
background-color: #3e8e41;
}
popup.js:
document.addEventListener('DOMContentLoaded', function() {
var blurButton = document.getElementById('blur-button');
blurButton.addEventListener('click', function() {
chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
chrome.runtime.sendMessage({action: 'blur'});
});
});
});
I keep getting errors on Chrome like:
“Uncaught (in promise) Error: Could not establish connection. Receiving end does not exist.
Context
popup.html
Stack Trace
popup.html:0 (anonymous function)”
“Uncaught SyntaxError: Unexpected end of input
Context
example.org
Stack Trace
contentScript.js:87 (anonymous function)”
Errors like this repeatedly even after I make amends. Something is wrong I’m not able to tell and my little friend can’t tell either. Let me know if you’ve noticed what’s wrong.