MediaWiki:Gadget-LabelScan.js: Unterschied zwischen den Versionen

Keine Bearbeitungszusammenfassung
Keine Bearbeitungszusammenfassung
Zeile 11: Zeile 11:
     maxSide: 1024,
     maxSide: 1024,
     debug: true,
     debug: true,
    // fürs Debug: Modell beim Klick einmal „warmladen“
     forceModelWarmup: true
     forceModelWarmup: true
   };
   };
Zeile 67: Zeile 68:
   }
   }


   // ------------------------- CLIP / Transformers -------------------------
   // ------------------------- CLIP / Transformers (vision-only) -------------------------
   let _clipModulePromise=null;
   let _visionLoadPromise=null;
   async function ensureClipExtractor(){
   async function ensureClipVision(){
     if(_clipModulePromise) return _clipModulePromise;
     if(_visionLoadPromise) return _visionLoadPromise;


     setStatus('Modell laden …'); setProgress(0.08);
     setStatus('Modell laden …'); setProgress(0.08);


     _clipModulePromise = (async()=>{
     _visionLoadPromise = (async()=>{
       try{
       try{
         const mod = await import(/* webpackIgnore: true */ CFG.transformersURL);
         const mod = await import(/* webpackIgnore: true */ CFG.transformersURL);


         // Keine lokalen Pfade setzen; nur Remote-Modelle
         // Runtime-Umgebung
         mod.env.remoteModels = true;
         mod.env.remoteModels = true;
         mod.env.allowRemoteModels = true;
         mod.env.allowRemoteModels = true;
         mod.env.useBrowserCache = true;
         mod.env.useBrowserCache = true;


         // *** WICHTIGER FIX ***
         // WASM-Pfade: nutze das Transformers-CDN (enthält ort-wasm-simd.wasm)
        // Auf Transformers-CDN zeigen – enthält ort-wasm-simd.wasm (non-threaded),
        // das in onnxruntime-web@1.20.0 fehlt.
         mod.env.backends = mod.env.backends || {};
         mod.env.backends = mod.env.backends || {};
         mod.env.backends.onnx = mod.env.backends.onnx || {};
         mod.env.backends.onnx = mod.env.backends.onnx || {};
Zeile 91: Zeile 90:
         mod.env.backends.onnx.wasm.wasmPaths =
         mod.env.backends.onnx.wasm.wasmPaths =
           'https://cdn.jsdelivr.net/npm/@xenova/transformers@2.15.0/dist/';
           'https://cdn.jsdelivr.net/npm/@xenova/transformers@2.15.0/dist/';
        // Threads konservativ
        mod.env.backends.onnx.wasm.numThreads = 1;
        mod.env.backends.onnx.wasm.simd = true;


         // OPTIONAL: Wenn du WebGPU explizit probieren willst (falls verfügbar)
         // (optional) WebGPU probieren
         try {
         try {
           if ('gpu' in navigator) {
           if ('gpu' in navigator) {
Zeile 100: Zeile 102:
         } catch(_) {}
         } catch(_) {}


         // Korrekte Pipeline
         // ⬇️ Statt pipeline: explizit Vision-Modell + Processor laden
         const pipe = await mod.pipeline(
         const processor = await mod.AutoProcessor.from_pretrained(CFG.modelId);
          'feature-extraction',
        // Wichtig: den VISION-Zweig laden, nicht den Text-Zweig
        const model = await mod.CLIPVisionModelWithProjection.from_pretrained(
           CFG.modelId,
           CFG.modelId,
           { quantized:true }
           { quantized: true }
         );
         );


         // Backend-Info (falls verfügbar)
         // kleines Warmup (1x 32x32 „Bild“) – optional
         let backend = 'unknown';
         try{
        try { backend = pipe?.engine?.backend || pipe?.model?.session?.executionProvider || backend; } catch(_){}
          const dummy = new ImageData(32,32);
        log('CLIP ready:', pipe.model?.constructor?.name || 'unknown', '| Backend:', backend);
          const inputs = await processor(dummy, { return_tensors: 'pt' });
          await model.forward({ pixel_values: inputs.pixel_values });
        } catch(_) {}


         return { mod, pipe };
        let backend='unknown';
        try { backend = model?.session?.executionProvider || backend; } catch(_){}
        log('CLIP ready (vision):', model?.constructor?.name || 'unknown', '| Backend:', backend);
 
         return { mod, processor, model };
       } catch(e){
       } catch(e){
         err('CLIP load failed:', e);
         err('CLIP load failed:', e);
Zeile 119: Zeile 128:
     })();
     })();


     return _clipModulePromise;
     return _visionLoadPromise;
   }
   }


Zeile 145: Zeile 154:
     }
     }


     const { pipe } = await ensureClipExtractor();
     const { processor, model } = await ensureClipVision();
     setStatus('Bild vorbereiten …'); setProgress(0.20);
     setStatus('Bild vorbereiten …'); setProgress(0.20);


Zeile 153: Zeile 162:
     setStatus('Bild analysieren …'); setProgress(0.38);
     setStatus('Bild analysieren …'); setProgress(0.38);


     const out = await pipe(canvas);
    // Processor wandelt Canvas → pixel_values Tensor
     const raw = out && out.data;
     const inputs = await processor(canvas, { return_tensors: 'pt' });
 
     const out = await model.forward({ pixel_values: inputs.pixel_values });
     let vec;
     // Embedding: image_embeds (Float32Array auf .data)
    if (raw instanceof Float32Array) vec = raw;
    const vec = out?.image_embeds?.data || out?.image_embeds;
     else if (Array.isArray(raw)) vec = Array.isArray(raw[0]) ? meanPool2D(raw) : new Float32Array(raw);
     if (!(vec instanceof Float32Array)) {
    else throw new Error('Embedding-Format unerwartet');
      throw new Error('Embedding-Format unerwartet (kein Float32Array).');
 
    }
     return normalize(vec);
     return normalize(vec);
   }
   }


  function meanPool2D(arr2d){
    const rows=arr2d.length;
    const dim=rows?arr2d[0].length:0;
    const sum=new Float32Array(dim);
    for(let r=0;r<rows;r++){
      const row=arr2d[r];
      for(let i=0;i<dim;i++) sum[i]+=row[i]||0;
    }
    for(let i=0;i<dim;i++) sum[i]/=(rows||1);
    return sum;
  }
   function normalize(v){
   function normalize(v){
     let n=0; for(let i=0;i<v.length;i++) n+=v[i]*v[i];
     let n=0; for(let i=0;i<v.length;i++) n+=v[i]*v[i];
Zeile 269: Zeile 267:


       await loadIndex({ ui:true });
       await loadIndex({ ui:true });
       if (CFG.forceModelWarmup) await ensureClipExtractor();
 
       if (CFG.forceModelWarmup) {
        await ensureClipVision();
      }


       if(!INDEX_EMB.some(v=>v&&v.length)){
       if(!INDEX_EMB.some(v=>v&&v.length)){