Chrome人脸检测JavaScript扩展报错排查求助
Chrome扩展人脸检测性别模糊功能报错排查求助
我开发了一款基于face-api.js的Chrome实验性扩展,功能是检测人脸并区分性别,对女性身体进行模糊处理,用于审查潜在不良媒体。扩展能正常加载到Chrome,但持续报错,反复排查contentScript.js、manifest.json等所有文件后仍未找到解决办法。
当前报错信息
Uncaught (in promise) Error: Could not establish connection. Receiving end does not exist.(上下文:popup.html)Uncaught SyntaxError: Unexpected end of input(上下文:example.org,堆栈指向contentScript.js第87行)
我已修改popup.js代码,但报错仍反复出现,相关代码文件如下:
相关代码文件
contentScript.js
// 加载人脸检测和性别分类模型 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) // 定义用于绘制框的canvas元素 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') // 获取视频元素 const video = document.querySelector('video') // 启动视频播放和检测循环 async function startVideo() { // 播放视频 await video.play() // 定义人脸检测和性别分类选项 const options = new faceapi.SsdMobilenetv1Options({ minConfidence: 0.5 }) // 定义循环函数 async function detectFacesAndGender() { // 检测视频帧中的所有人脸并分类性别 const detections = await faceapi.detectAllFaces(video, options).withFaceLandmarks().withGender() // 遍历每个人脸并判断性别 detections.forEach(async detection => { const gender = detection.gender // "male", "female", or "genderless" // 判断是在人脸周围绘制框还是模糊身体 if (gender === "female") { // 在人脸周围绘制红色框 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() // 模糊女性身体 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") { // 取消男性和无性别者身体的模糊 const parentElement = detection.forSize(canvas.width, canvas.height).parentElement const maleOrGenderlessBody } else if (gender === "male" || gender === "genderless") { // 取消男性和无性别者身体的模糊 const parentElement = detection.forSize(canvas.width, canvas.height).parentElement const maleOrGenderlessBody = parentElement.querySelector(".body-male, .body-genderless") if (maleOrGenderlessBody) { maleOrGenderlessBody.style.filter = "none" } } // 将修改后的canvas添加回DOM parentElement.appendChild(canvas) } } // 加载模型并启动检测流程 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) // 检测过程中出错时显示消息 const errorElement = document.getElementById("error-message") const displayError = (errorMessage) => { errorElement.innerHTML = errorMessage errorElement.style.display = "block" } window.onerror = (message, source, lineno, colno, error) => { displayError(`发生错误: ${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(`加载模型时出错: ${error}`)) }
manifest.json
{ "manifest_version": 3, "name": "图片/视频模糊处理", "version": "1.0", "description": "用于模糊图片和视频的扩展", "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>我的扩展</title> <link rel="stylesheet" href="popup.css"> </head> <body> <h1>欢迎使用我的扩展!</h1> <p>点击下方按钮模糊当前页面。</p> <button id="blur-button">模糊页面</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'}); }); }); });
修改后的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.scripting.executeScript({ target: {tabId: tabs[0].id}, function: () => { // 模糊当前标签页 document.body.style.filter = 'blur(5px)'; } }); }); }); });
报错原因及修复方案
1. Uncaught SyntaxError: Unexpected end of input(contentScript.js第87行)
这个错误是代码语法不完整导致的:
detections.forEach(async detection => {})循环未闭合,缺少})- 存在无效代码段:
const maleOrGenderlessBody未赋值且无闭合语句,还重复了else if分支
修复后的核心代码段:
// 遍历每个人脸并判断性别 detections.forEach(async detection => { const gender = detection.gender // "male", "female", or "genderless" // 判断是在人脸周围绘制框还是模糊身体 if (gender === "female") { // ... 原有女性处理代码 ... } else if (gender === "male" || gender === "genderless") { // 取消男性和无性别者身体的模糊 const parentElement = detection.forSize(canvas.width, canvas.height).parentElement const maleOrGenderlessBody = parentElement.querySelector(".body-male, .body-genderless") if (maleOrGenderlessBody) { maleOrGenderlessBody.style.filter = "none" } } // 将修改后的canvas添加回DOM parentElement.appendChild(canvas) }) // 补充闭合forEach循环 } // 补充闭合detectFacesAndGender函数
2. Uncaught (in promise) Error: Could not establish connection. Receiving end does not exist.(popup.html)
原始popup.js使用chrome.runtime.sendMessage发送消息,但background.js和contentScript中都没有对应的chrome.runtime.onMessage监听,导致连接失败。修改后的popup.js改用chrome.scripting.executeScript直接注入代码,该报错会消失,但需注意:
- manifest.json中已配置content_scripts自动注入contentScript.js,同时background.js点击图标也会再次注入,会导致代码重复执行。建议移除content_scripts配置,仅通过按钮或图标触发注入,避免冲突。
其他潜在问题修复
startVideo函数中调用了未定义的startDetection函数,需补充定义或直接调用detectFacesAndGender并启动循环modelURI变量未定义,需替换为模型的实际加载地址(可复用顶部的CDN地址)- 获取
video元素时需添加判断,避免页面无视频元素时报错:const video = document.querySelector('video'); if (!video) return; - 若
faceapi.blurFaces方法不存在,可替换为手动模糊逻辑:使用canvas的filter属性实现模糊
内容的提问来源于stack exchange,提问作者kuytrf6yh i876tfvb
相关产品推荐
相关产品推荐

