@srsergio/taptapp-ar 1.1.4 → 1.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -23,6 +23,7 @@
23
23
  - [Raw Controller](#3-raw-controller-advanced--custom-engines)
24
24
  - [Vanilla JS (SimpleAR)](#4-vanilla-js-no-framework-)
25
25
  - [🏗️ Protocol V7](#️-protocol-v7-moonshot-packed-format)
26
+ - [🔍 Visual Search & Embeddings](#-visual-search--embeddings-new)
26
27
  - [📄 License & Credits](#-license--credits)
27
28
 
28
29
  ---
@@ -269,7 +270,53 @@ TapTapp AR uses a proprietary **Nanite-style Vision Codec** that is significantl
269
270
 
270
271
  ---
271
272
 
272
- ## 📄 License & Credits
273
+ ## Visual Search & Embeddings (NEW!) 🚀
274
+
275
+ TapTapp AR now includes a state-of-the-art **Image Embedding** system based on Hyperdimensional Computing (HDC). This allows you to convert any image into a tiny mathematical fingerprint (vector) for ultra-fast visual search, deduplication, and clustering.
276
+
277
+ ### 🍱 Embedding Modes
278
+ | Mode | Size | Speed | Recommendation |
279
+ | :--- | :--- | :--- | :--- |
280
+ | `micro` | 4 Bytes | 10M+ ops/s | Extreme IoT |
281
+ | **`compact`** | **16 Bytes** | **44M+ ops/s** | 🏆 **Best for Mega-Databases** |
282
+ | `standard`| 32 Bytes | 18M+ ops/s | Balanced AR |
283
+ | `full` | 128 Bytes| 7M+ ops/s | Maximum Precision |
284
+
285
+ ### 🚀 Usage Example (RAG Standard 🧠)
286
+ Create visual embeddings and compare them just like you do with Text LLMs:
287
+
288
+ ```javascript
289
+ import { visualSearch } from '@srsergio/taptapp-ar';
290
+
291
+ // 1. Get a Dense Vector (Array of numbers) - LLM Standard
292
+ const embedding = await visualSearch.compute('product.jpg');
293
+ const vector = embedding.toFloatArray(); // [1.0, 0.0, 1.0, ...]
294
+
295
+ // 2. Similarity Search (Self-contained)
296
+ const score = await visualSearch.compare('item1.jpg', 'item2.jpg');
297
+ console.log(`Visual Match: ${score * 100}%`);
298
+ ```
299
+
300
+ ### 🗄️ Vector Database Integration
301
+ Use your favorite vector database (Pinecone, Milvus, Weaviate, or `pgvector`) with the standard array format:
302
+
303
+ ```javascript
304
+ // Example: Storing in a standard Vector DB
305
+ await vectorDB.insert({
306
+ id: 'image_42',
307
+ vector: Array.from(embedding.toFloatArray()),
308
+ metadata: { name: 'Vintage Camera', category: 'Electronics' }
309
+ });
310
+
311
+ // Example: Searching in PostgreSQL (pgvector)
312
+ // SELECT * FROM items ORDER BY embedding <=> '[1,0,1,1...]' LIMIT 5;
313
+ ```
314
+
315
+ ---
316
+
317
+ ---
318
+
319
+ ## �📄 License & Credits
273
320
 
274
321
  This project is licensed under the **Fair Source License v0.9**.
275
322
 
@@ -0,0 +1,25 @@
1
+ /**
2
+ * ImageSearchBridge - A high-level helper to make image embeddings
3
+ * extremely easy to use for developers.
4
+ */
5
+ export class ImageSearchBridge {
6
+ constructor(mode?: string);
7
+ embedder: ImageEmbedder;
8
+ /**
9
+ * Compute an embedding from almost any source
10
+ * @param {string|HTMLImageElement|HTMLCanvasElement|Uint8Array|Buffer} source
11
+ * @returns {Promise<ImageEmbedding>}
12
+ */
13
+ compute(source: string | HTMLImageElement | HTMLCanvasElement | Uint8Array | Buffer): Promise<ImageEmbedding>;
14
+ /**
15
+ * Compare two image sources directly
16
+ */
17
+ compare(sourceA: any, sourceB: any): Promise<number>;
18
+ _rgbaToGrayscale(rgba: any, w: any, h: any): Float32Array<ArrayBuffer>;
19
+ _loadImageBrowser(url: any): Promise<any>;
20
+ }
21
+ /**
22
+ * Easy-to-use factory for quick tasks
23
+ */
24
+ export const visualSearch: ImageSearchBridge;
25
+ import { ImageEmbedder } from './image-embedding.js';
@@ -0,0 +1 @@
1
+ import{ImageEmbedder as e}from"./image-embedding.js";export class ImageSearchBridge{constructor(t="compact"){this.embedder=new e(t)}async compute(e){let t=null,a=0,r=0;if("string"==typeof e){if("undefined"==typeof document)throw new Error("URL loading in Node requires a fetch/loading utility. Please provide a Buffer or Uint8Array.");e=await this._loadImageBrowser(e)}if("undefined"!=typeof document&&(e instanceof HTMLImageElement||e instanceof HTMLCanvasElement||e instanceof ImageBitmap)){const o=document.createElement("canvas");a=e.width||e.videoWidth,r=e.height||e.videoHeight,o.width=a,o.height=r;const n=o.getContext("2d");n.drawImage(e,0,0);const i=n.getImageData(0,0,a,r).data;t=this._rgbaToGrayscale(i,a,r)}if(!t&&(e instanceof Uint8Array||"undefined"!=typeof Buffer&&Buffer.isBuffer(e))){if(!(e.data&&e.width&&e.height))throw new Error("Raw data source must be an object { data, width, height }");t=e.data,a=e.width,r=e.height}if(!t)throw new Error("Unsupported image source type");return this.embedder.embed(t,a,r)}async compare(e,t){const a=await this.compute(e),r=await this.compute(t);return this.embedder.compare(a,r)}_rgbaToGrayscale(e,t,a){const r=new Float32Array(t*a);for(let o=0;o<t*a;o++){const t=4*o;r[o]=(.299*e[t]+.587*e[t+1]+.114*e[t+2])/255}return r}_loadImageBrowser(e){return new Promise((t,a)=>{const r=new Image;r.crossOrigin="anonymous",r.onload=()=>t(r),r.onerror=a,r.src=e})}}export const visualSearch=new ImageSearchBridge("compact");
@@ -0,0 +1,185 @@
1
+ /**
2
+ * Genera embedding de una imagen en un solo paso
3
+ *
4
+ * @param {Float32Array|Uint8Array} imageData
5
+ * @param {number} width
6
+ * @param {number} height
7
+ * @param {string} mode - 'micro' | 'compact' | 'standard' | 'full'
8
+ * @returns {ImageEmbedding}
9
+ */
10
+ export function embedImage(imageData: Float32Array | Uint8Array, width: number, height: number, mode?: string): ImageEmbedding;
11
+ /**
12
+ * Compara dos imágenes directamente
13
+ *
14
+ * @param {Float32Array} img1Data
15
+ * @param {number} img1Width
16
+ * @param {number} img1Height
17
+ * @param {Float32Array} img2Data
18
+ * @param {number} img2Width
19
+ * @param {number} img2Height
20
+ * @param {string} mode
21
+ * @returns {number} Similitud 0-1
22
+ */
23
+ export function compareImages(img1Data: Float32Array, img1Width: number, img1Height: number, img2Data: Float32Array, img2Width: number, img2Height: number, mode?: string): number;
24
+ /**
25
+ * Detecta si dos imágenes son duplicados (o casi-duplicados)
26
+ *
27
+ * @param {ImageEmbedding} emb1
28
+ * @param {ImageEmbedding} emb2
29
+ * @param {number} threshold - Umbral de similitud (default: 0.85)
30
+ * @returns {boolean}
31
+ */
32
+ export function isDuplicate(emb1: ImageEmbedding, emb2: ImageEmbedding, threshold?: number): boolean;
33
+ /**
34
+ * Generador de Image Embeddings
35
+ *
36
+ * @example
37
+ * const embedder = new ImageEmbedder('standard');
38
+ *
39
+ * // Generar embedding de una imagen
40
+ * const embedding = embedder.embed(imageData, width, height);
41
+ *
42
+ * // Comparar dos imágenes
43
+ * const similarity = embedder.compare(embedding1, embedding2);
44
+ *
45
+ * // Buscar en base de datos
46
+ * const results = embedder.search(queryEmbedding, database, topK=10);
47
+ */
48
+ export class ImageEmbedder {
49
+ constructor(mode?: string);
50
+ config: any;
51
+ basis: any;
52
+ mode: string;
53
+ /**
54
+ * Genera un embedding de una imagen en escala de grises
55
+ *
56
+ * @param {Float32Array|Uint8Array} imageData - Datos de imagen (grayscale)
57
+ * @param {number} width - Ancho de la imagen
58
+ * @param {number} height - Alto de la imagen
59
+ * @returns {ImageEmbedding} Objeto embedding
60
+ */
61
+ embed(imageData: Float32Array | Uint8Array, width: number, height: number): ImageEmbedding;
62
+ /**
63
+ * Compara dos embeddings y retorna similitud [0, 1]
64
+ *
65
+ * Usa una métrica ajustada donde:
66
+ * - Idénticos = 1.0
67
+ * - Aleatorios (50% hamming) = 0.0
68
+ * - Completamente opuestos = -1.0 (pero clampeamos a 0)
69
+ *
70
+ * @param {ImageEmbedding} emb1
71
+ * @param {ImageEmbedding} emb2
72
+ * @returns {number} Similitud entre 0 (diferente/random) y 1 (idéntico)
73
+ */
74
+ compare(emb1: ImageEmbedding, emb2: ImageEmbedding): number;
75
+ /**
76
+ * Busca los embeddings más similares en una base de datos
77
+ *
78
+ * @param {ImageEmbedding} query - Embedding a buscar
79
+ * @param {ImageEmbedding[]} database - Array de embeddings
80
+ * @param {number} topK - Número de resultados a retornar
81
+ * @returns {Array<{index: number, similarity: number}>} Resultados ordenados
82
+ */
83
+ search(query: ImageEmbedding, database: ImageEmbedding[], topK?: number): Array<{
84
+ index: number;
85
+ similarity: number;
86
+ }>;
87
+ /**
88
+ * Agrupa embeddings por similitud (clustering)
89
+ *
90
+ * @param {ImageEmbedding[]} embeddings
91
+ * @param {number} threshold - Umbral de similitud para agrupar (0-1)
92
+ * @returns {number[]} Array de cluster IDs para cada embedding
93
+ */
94
+ cluster(embeddings: ImageEmbedding[], threshold?: number): number[];
95
+ /**
96
+ * Comprime el hypervector HDC al tamaño de salida deseado
97
+ */
98
+ _compressToSize(hv: any, targetBits: any): Uint32Array<any>;
99
+ /**
100
+ * Weighted Bundle: Combina hypervectors con pesos
101
+ * Features con mayor score contribuyen más al resultado final
102
+ */
103
+ _weightedBundle(hvs: any, weights: any): Uint32Array<ArrayBuffer>;
104
+ }
105
+ /**
106
+ * Representa un embedding de imagen
107
+ */
108
+ export class ImageEmbedding {
109
+ /**
110
+ * Crea un embedding desde bytes
111
+ * @param {Uint8Array} bytes
112
+ * @returns {ImageEmbedding}
113
+ */
114
+ static fromBytes(bytes: Uint8Array): ImageEmbedding;
115
+ /**
116
+ * Crea un embedding desde Base64
117
+ * @param {string} base64
118
+ * @returns {ImageEmbedding}
119
+ */
120
+ static fromBase64(base64: string): ImageEmbedding;
121
+ constructor(vector: any, metadata?: {});
122
+ vector: any;
123
+ metadata: {};
124
+ /**
125
+ * Serializa el embedding a bytes para storage
126
+ * @returns {Uint8Array}
127
+ */
128
+ toBytes(): Uint8Array;
129
+ /**
130
+ * Serializa a Base64 para transmisión
131
+ * @returns {string}
132
+ */
133
+ toBase64(): string;
134
+ /**
135
+ * Serializa a hex string
136
+ * @returns {string}
137
+ */
138
+ toHex(): string;
139
+ /**
140
+ * Convierte el vector binario a un array de floats (0.0 o 1.0)
141
+ * Útil para compatibilidad con bases de datos vectoriales que esperan arrays de números (como LLMs)
142
+ *
143
+ * @returns {Float32Array}
144
+ */
145
+ toFloatArray(): Float32Array;
146
+ /**
147
+ * Número de bits del embedding
148
+ */
149
+ get bits(): number;
150
+ /**
151
+ * Número de bytes del embedding
152
+ */
153
+ get bytes(): number;
154
+ }
155
+ export namespace EMBEDDING_CONFIGS {
156
+ namespace micro {
157
+ let outputBits: number;
158
+ let maxFeatures: number;
159
+ let pyramidOctaves: number;
160
+ }
161
+ namespace compact {
162
+ let outputBits_1: number;
163
+ export { outputBits_1 as outputBits };
164
+ let maxFeatures_1: number;
165
+ export { maxFeatures_1 as maxFeatures };
166
+ let pyramidOctaves_1: number;
167
+ export { pyramidOctaves_1 as pyramidOctaves };
168
+ }
169
+ namespace standard {
170
+ let outputBits_2: number;
171
+ export { outputBits_2 as outputBits };
172
+ let maxFeatures_2: number;
173
+ export { maxFeatures_2 as maxFeatures };
174
+ let pyramidOctaves_2: number;
175
+ export { pyramidOctaves_2 as pyramidOctaves };
176
+ }
177
+ namespace full {
178
+ let outputBits_3: number;
179
+ export { outputBits_3 as outputBits };
180
+ let maxFeatures_3: number;
181
+ export { maxFeatures_3 as maxFeatures };
182
+ let pyramidOctaves_3: number;
183
+ export { pyramidOctaves_3 as pyramidOctaves };
184
+ }
185
+ }
@@ -0,0 +1 @@
1
+ import{DetectorLite as t}from"../detector/detector-lite.js";import{generateBasis as e,projectDescriptor as r,bundle as o,compressToSignature as s,HDC_DIMENSION as n,HDC_WORDS as i}from"../matching/hdc.js";import{HDC_SEED as a}from"../protocol.js";const c={micro:{outputBits:32,maxFeatures:100,pyramidOctaves:3},compact:{outputBits:128,maxFeatures:200,pyramidOctaves:4},standard:{outputBits:256,maxFeatures:300,pyramidOctaves:5},full:{outputBits:1024,maxFeatures:500,pyramidOctaves:6}};let m=null;export class ImageEmbedder{constructor(t="standard"){this.config=c[t]||c.standard,this.basis=(m||(m=e(a,n)),m),this.mode=t}embed(e,s,n){const a=performance.now(),c=new t(s,n,{useGPU:!0,useLSH:!0,maxFeaturesPerBucket:Math.ceil(this.config.maxFeatures/100),maxOctaves:this.config.pyramidOctaves}),{featurePoints:m}=c.detect(e),d=m.sort((t,e)=>(e.score||0)-(t.score||0)).slice(0,this.config.maxFeatures);if(0===d.length)return new ImageEmbedding(new Uint32Array(this.config.outputBits/32),{featureCount:0,mode:this.mode,timeMs:performance.now()-a});const u=[];for(const t of d){if(!t.descriptors||t.descriptors.length<2)continue;const e=t.descriptors instanceof Uint32Array?t.descriptors:new Uint32Array([t.descriptors[0]|t.descriptors[1]<<8|t.descriptors[2]<<16|t.descriptors[3]<<24,t.descriptors[4]|t.descriptors[5]<<8|t.descriptors[6]<<16|t.descriptors[7]<<24]),o=r(e,this.basis),i=Math.floor(t.x/s*3),a=Math.floor(t.y/n*3),c=2654435769*(Math.min(8,Math.max(0,3*a+i))+1);for(let t=0;t<o.length;t++){let e=c+2246822507*t|0;e=Math.imul(e^e>>>16,36094177),e=Math.imul(e^e>>>13,3266489909),e=(e^e>>>16)>>>0,o[t]^=e}u.push(o)}const l=u.length>0?o(u):new Uint32Array(i),h=this._compressToSize(l,this.config.outputBits),f=performance.now()-a;return new ImageEmbedding(h,{featureCount:d.length,mode:this.mode,timeMs:f,width:s,height:n})}compare(t,e){const r=t.vector||t,o=e.vector||e;if(r.length!==o.length)throw new Error("Embeddings must have same dimension");let s=0;const n=32*r.length;for(let t=0;t<r.length;t++)s+=d(r[t]^o[t]);const i=s/(n/2);return Math.max(0,1-i)}search(t,e,r=10){const o=[];for(let r=0;r<e.length;r++){const s=this.compare(t,e[r]);o.push({index:r,similarity:s})}return o.sort((t,e)=>e.similarity-t.similarity),o.slice(0,r)}cluster(t,e=.7){const r=t.length,o=new Array(r).fill(-1);let s=0;for(let n=0;n<r;n++)if(-1===o[n]){o[n]=s;for(let i=n+1;i<r;i++)-1===o[i]&&this.compare(t[n],t[i])>=e&&(o[i]=s);s++}return o}_compressToSize(t,e){const r=e/32;if(r>=i)return new Uint32Array(t.buffer,0,r);const o=new Uint32Array(r);if(32===e)o[0]=s(t);else{const e=Math.ceil(i/r);for(let s=0;s<r;s++){let r=0;for(let o=0;o<e;o++){const n=s*e+o;n<i&&(r^=t[n])}o[s]=(r<<s%32|r>>>32-s%32)>>>0}}return o}_weightedBundle(t,e){const r=new Uint32Array(i),o=e.reduce((t,e)=>t+e,0),s=e.map(t=>t/o),a=new Float32Array(n);for(let e=0;e<t.length;e++){const r=t[e],o=s[e];for(let t=0;t<n;t++)r[t>>>5]&1<<(31&t)&&(a[t]+=o)}for(let t=0;t<n;t++)a[t]>.5&&(r[t>>>5]|=1<<(31&t));return r}}export class ImageEmbedding{constructor(t,e={}){this.vector=t,this.metadata=e}toBytes(){return new Uint8Array(this.vector.buffer)}static fromBytes(t){const e=new Uint32Array(t.buffer);return new ImageEmbedding(e)}toBase64(){const t=this.toBytes();let e="";for(let r=0;r<t.length;r++)e+=String.fromCharCode(t[r]);return btoa(e)}static fromBase64(t){const e=atob(t),r=new Uint8Array(e.length);for(let t=0;t<e.length;t++)r[t]=e.charCodeAt(t);return ImageEmbedding.fromBytes(r)}toHex(){return Array.from(this.vector).map(t=>t.toString(16).padStart(8,"0")).join("")}toFloatArray(){const t=this.bits,e=new Float32Array(t);for(let r=0;r<t;r++){const t=r>>>5,o=31&r;e[r]=this.vector[t]&1<<o?1:0}return e}get bits(){return 32*this.vector.length}get bytes(){return 4*this.vector.length}}export function embedImage(t,e,r,o="standard"){return new ImageEmbedder(o).embed(t,e,r)}export function compareImages(t,e,r,o,s,n,i="standard"){const a=new ImageEmbedder(i),c=a.embed(t,e,r),m=a.embed(o,s,n);return a.compare(c,m)}export function isDuplicate(t,e,r=.85){return(new ImageEmbedder).compare(t,e)>=r}function d(t){return 16843009*((t=(858993459&(t-=t>>1&1431655765))+(t>>2&858993459))+(t>>4)&252645135)>>24}export{c as EMBEDDING_CONFIGS};
@@ -0,0 +1,2 @@
1
+ export { ImageEmbedder, ImageEmbedding, embedImage, compareImages, isDuplicate, EMBEDDING_CONFIGS } from "./image-embedding.js";
2
+ export { ImageSearchBridge, visualSearch } from "./bridge.js";
@@ -0,0 +1 @@
1
+ export{ImageEmbedder,ImageEmbedding,embedImage,compareImages,isDuplicate,EMBEDDING_CONFIGS}from"./image-embedding.js";export{ImageSearchBridge,visualSearch}from"./bridge.js";
package/dist/index.d.ts CHANGED
@@ -4,4 +4,5 @@ export * from "./react/use-ar.js";
4
4
  export * from "./compiler/offline-compiler.js";
5
5
  export { Controller } from "./runtime/controller.js";
6
6
  export { createTracker, startTracking } from "./runtime/track.js";
7
+ export { ImageEmbedder, ImageSearchBridge, visualSearch } from "./core/embeddings/index.js";
7
8
  export * as protocol from "./core/protocol.js";
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- export*from"./react/types.js";export*from"./react/TaptappAR.js";export*from"./react/use-ar.js";export*from"./compiler/offline-compiler.js";export{Controller}from"./runtime/controller.js";export{createTracker,startTracking}from"./runtime/track.js";export*as protocol from"./core/protocol.js";
1
+ export*from"./react/types.js";export*from"./react/TaptappAR.js";export*from"./react/use-ar.js";export*from"./compiler/offline-compiler.js";export{Controller}from"./runtime/controller.js";export{createTracker,startTracking}from"./runtime/track.js";export{ImageEmbedder,ImageSearchBridge,visualSearch}from"./core/embeddings/index.js";export*as protocol from"./core/protocol.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@srsergio/taptapp-ar",
3
- "version": "1.1.4",
3
+ "version": "1.1.6",
4
4
  "description": "Ultra-fast Augmented Reality (AR) SDK for Node.js and Browser. Image tracking with 100% pure JavaScript, zero-dependencies, and high-performance compilation.",
5
5
  "keywords": [
6
6
  "augmented reality",