You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Chrome人脸检测JavaScript扩展报错排查求助

Chrome扩展人脸检测性别模糊功能报错排查求助

我开发了一款基于face-api.js的Chrome实验性扩展,功能是检测人脸并区分性别,对女性身体进行模糊处理,用于审查潜在不良媒体。扩展能正常加载到Chrome,但持续报错,反复排查contentScript.js、manifest.json等所有文件后仍未找到解决办法。

当前报错信息

  1. Uncaught (in promise) Error: Could not establish connection. Receiving end does not exist.(上下文:popup.html)
  2. 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']
  });
});
<!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>
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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 20:29:53