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

Firefox扩展无法遍历全部图片:getImageData()相关问题求助

问题:Firefox扩展中遍历页面图片时中途停止,移除getImageData相关逻辑则正常运行

问题描述

我正在开发一款Firefox扩展,通过model_startup()函数加载机器学习模型的过程一切正常。但在后续执行blockk()函数时,尝试使用document.getElementsByTagName('img')获取页面所有图片,并借助已加载的模型对图片进行预测。目前遇到的问题是:该函数仅能处理页面开头的若干张图片后就停止;若移除代码中的const rightSizeImageData = inferenceCtx.getImageData(0, 0, 64, 64)行及相关逻辑,则可正常遍历全部图片。我是JavaScript新手,恳请帮忙排查该问题的原因。

相关代码如下:

console.log('STARTING UP')
const MODEL_PATH = 'mdl/model.json'
const IMAGE_SIZE = 64;
let model;
async function model_startup() {
    console.log('Launching TF.js!');
    tf.ENV.set('WEBGL_PACK',false);
    await tf.ready();
    console.log('TensorflowJS backend is: '+tf.getBackend());
    let url = browser.runtime.getURL(MODEL_PATH);
    console.log('Loading model... '+url);
    try {
        model = await tf.loadLayersModel(url);
    } catch(e) {
        console.log('Failed to load model! '+e);
    }
    console.log('Model: ' + model);
    console.log('Warming up...');
    let dummy_data = tf.zeros([1, IMAGE_SIZE, IMAGE_SIZE, 3]);
    // dummy_data.print();
    let warmup_result = model.predict(dummy_data);
    console.log("finding type : $$$$$$$$ " + typeof warmup_result)
    warmup_result.print();
    warmup_result.dispose();
    console.log('Ready to go!');
    // blockk();
};
model_startup().then(blockk);
function blockk() {
    const x = document.getElementsByTagName('img');
    console.log("wn ############################## " + x.length);
    let inferenceCanvas = document.createElement('canvas');
    inferenceCanvas.width = 64;
    inferenceCanvas.height = 64;
    let inferenceCtx = inferenceCanvas.getContext('2d', { alpha: false});
    inferenceCtx.imageSmoothingEnabled = true;
    for(var i = 0; i < x.length; i++) {
        console.log('pixel kmvsl lsvn');
        let img = x[i];
        inferenceCtx.drawImage(img, 0, 0, img.width, img.height, 0, 0, 64, 64);
        const rightSizeImageData = inferenceCtx.getImageData(0, 0, 64, 64);
        //write promise. It may work.
        const rightSizeImageDataTF = tf.browser.fromPixels(rightSizeImageData);
        const floatImg = rightSizeImageDataTF.toFloat();
        console.log('predicting...');
        let scaled = floatImg.div(tf.scalar(255));
        let batched = tf.stack([scaled]);
        let result = model.predict(batched);
        const val = result.dataSync()[0];
        console.log(val);
        if (val > 0.4) {
            console.log("blocking" + i);
            let h = img.height
            let w = img.width
            pth = img.src
            x[i].setAttribute("src", "https://indianonlineseller.com/wp-content/uploads/2017/05/blocked-listing.png");
            // x[i].setAttribute("onclick", ` this.src = '${pth}' `);
            x[i].setAttribute("data-src", "https://indianonlineseller.com/wp-content/uploads/2017/05/blocked-listing.png");
            x[i].height = h;
            x[i].width = w;
            console.log('blocked');
        }
        console.log("co @@@@@@@@@@@@@@@@@@@@@@@@@@@ " + x.length)
    }
};

问题原因分析

我帮你排查了两个核心问题,这应该就是导致遍历中途停止的原因:

  1. 动态HTMLCollection的遍历冲突
    document.getElementsByTagName('img')返回的是动态HTMLCollection,它会实时同步DOM的变化。当你在循环中修改img的src属性时,这个集合的内容和长度可能会发生变化(比如图片重新加载触发DOM状态更新)。而你用var i配合实时变化的x.length作为循环条件,就会导致循环提前终止。

  2. TensorFlow.js张量未释放导致内存泄漏
    预测逻辑中创建的多个张量(rightSizeImageDataTF、floatImg、scaled、batched、result)没有被及时释放,随着循环进行内存占用持续飙升,浏览器为了避免性能崩溃,会强制终止脚本执行——这也是移除预测逻辑后遍历能正常完成的原因。


解决方案

针对这两个问题,我们可以做以下修改:

1. 将动态集合转为静态数组

把document.getElementsByTagName('img')的结果转为静态数组,这样遍历过程不会受DOM变化影响:

const x = Array.from(document.getElementsByTagName('img'));
// 或者用展开运算符:const x = [...document.getElementsByTagName('img')];

2. 用tf.tidy()自动清理张量

TensorFlow.js提供了tf.tidy()函数,能自动清理函数内部创建的所有临时张量,只保留你返回的结果,完美解决内存泄漏问题。

3. 改用let声明循环变量(推荐)

把循环中的var i改成let i,避免var变量提升带来的潜在作用域问题。


修改后的完整代码

console.log('STARTING UP')
const MODEL_PATH = 'mdl/model.json'
const IMAGE_SIZE = 64;
let model;
async function model_startup() {
    console.log('Launching TF.js!');
    tf.ENV.set('WEBGL_PACK',false);
    await tf.ready();
    console.log('TensorflowJS backend is: '+tf.getBackend());
    let url = browser.runtime.getURL(MODEL_PATH);
    console.log('Loading model... '+url);
    try {
        model = await tf.loadLayersModel(url);
    } catch(e) {
        console.log('Failed to load model! '+e);
    }
    console.log('Model: ' + model);
    console.log('Warming up...');
    let dummy_data = tf.zeros([1, IMAGE_SIZE, IMAGE_SIZE, 3]);
    let warmup_result = model.predict(dummy_data);
    console.log("finding type : $$$$$$$$ " + typeof warmup_result)
    warmup_result.print();
    // 释放预热用的张量
    warmup_result.dispose();
    dummy_data.dispose();
    console.log('Ready to go!');
};
model_startup().then(blockk);
function blockk() {
    // 转为静态数组,避免动态集合的遍历问题
    const x = Array.from(document.getElementsByTagName('img'));
    console.log("Total images to process: " + x.length);
    let inferenceCanvas = document.createElement('canvas');
    inferenceCanvas.width = 64;
    inferenceCanvas.height = 64;
    let inferenceCtx = inferenceCanvas.getContext('2d', { alpha: false});
    inferenceCtx.imageSmoothingEnabled = true;

    // 用let声明循环变量,避免作用域问题
    for(let i = 0; i < x.length; i++) {
        console.log('Processing image index: ', i);
        let img = x[i];
        
        // 先检查图片是否加载完成,避免drawImage失败
        if (!img.complete || img.naturalWidth === 0) {
            console.log('Image not loaded yet, skipping: ', img.src);
            continue;
        }

        inferenceCtx.drawImage(img, 0, 0, img.width, img.height, 0, 0, 64, 64);
        const rightSizeImageData = inferenceCtx.getImageData(0, 0, 64, 64);

        // 使用tf.tidy自动清理所有临时张量
        const val = tf.tidy(() => {
            const rightSizeImageDataTF = tf.browser.fromPixels(rightSizeImageData);
            const floatImg = rightSizeImageDataTF.toFloat();
            const scaled = floatImg.div(tf.scalar(255));
            const batched = tf.stack([scaled]);
            const result = model.predict(batched);
            return result.dataSync()[0];
        });

        console.log('Prediction value: ', val);
        if (val > 0.4) {
            console.log("Blocking image index: ", i);
            const h = img.height;
            const w = img.width;
            const pth = img.src;
            img.setAttribute("src", "https://indianonlineseller.com/wp-content/uploads/2017/05/blocked-listing.png");
            img.setAttribute("data-src", pth); // 保存原地址,方便后续恢复
            img.height = h;
            img.width = w;
            console.log('Blocked image: ', pth);
        }
        console.log("Remaining images: ", x.length - i - 1);
    }
};

额外提示

  • 我添加了图片加载状态检查,避免处理未完全加载的图片导致drawImage报错;
  • 把原图片地址存在data-src而不是覆盖,后续如果需要恢复图片会更方便。

内容的提问来源于stack exchange,提问作者Pankaj Katkar

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.06 20:57:46