multimodalart HF Staff commited on
Commit
aa6b5d3
·
verified ·
1 Parent(s): 05e8ea7

Upload folder using huggingface_hub

Browse files
Files changed (3) hide show
  1. .gitattributes +1 -0
  2. app.py +59 -61
  3. ex_nutrition.jpg +0 -0
.gitattributes CHANGED
@@ -34,3 +34,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
  sample_wood.png filter=lfs diff=lfs merge=lfs -text
 
 
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
  sample_wood.png filter=lfs diff=lfs merge=lfs -text
37
+ ex_nutrition.jpg filter=lfs diff=lfs merge=lfs -text
app.py CHANGED
@@ -46,41 +46,41 @@ for key, mid in MODEL_IDS.items():
46
  # --------------------------------------------------------------------------- #
47
  # Schema presets — fill the visual field builder with a single click. #
48
  # --------------------------------------------------------------------------- #
 
49
  PRESETS = {
50
  "wood": {
51
  "label": "🪵 Wood surface",
52
  "fields": [
53
  {"name": "wood_color", "description": "The overall coloration of the wood surface"},
54
  {"name": "wood_texture", "description": "The tactile quality of the wood surface"},
55
- {"name": "wood_pattern", "description": "The pattern types visible on the wood surface"},
56
  ],
57
  },
58
  "receipt": {
59
  "label": "🧾 Receipt",
60
  "fields": [
61
- {"name": "merchant", "description": "Name of the store or business"},
62
- {"name": "date", "description": "Date of the transaction"},
63
- {"name": "total", "description": "Final total amount paid"},
64
- {"name": "currency", "description": "Currency symbol or code"},
65
- {"name": "payment_method", "description": "How the bill was paid"},
66
  ],
67
  },
68
  "nutrition": {
69
  "label": "🥫 Nutrition label",
70
  "fields": [
71
- {"name": "serving_size", "description": "The stated serving size"},
72
- {"name": "calories", "description": "Calories per serving"},
73
- {"name": "protein_g", "description": "Grams of protein per serving"},
74
- {"name": "fat_g", "description": "Grams of total fat per serving"},
75
- {"name": "carbs_g", "description": "Grams of total carbohydrate per serving"},
76
  ],
77
  },
78
  "card": {
79
  "label": "💼 Business card",
80
  "fields": [
81
- {"name": "name", "description": "Full name of the person"},
82
- {"name": "title", "description": "Job title or role"},
83
- {"name": "company", "description": "Company or organization name"},
84
  {"name": "email", "description": "Email address"},
85
  {"name": "phone", "description": "Phone number"},
86
  ],
@@ -88,11 +88,11 @@ PRESETS = {
88
  "product": {
89
  "label": "🛍️ Product photo",
90
  "fields": [
91
- {"name": "product_name", "description": "What the product is"},
92
- {"name": "brand", "description": "Brand or maker, if visible"},
93
- {"name": "primary_color", "description": "Dominant color of the product"},
94
- {"name": "category", "description": "Product category"},
95
- {"name": "condition", "description": "Apparent condition: new, used, worn"},
96
  ],
97
  },
98
  }
@@ -285,32 +285,38 @@ const LQ_PRESETS = __PRESETS__;
285
  setter.call(store, key);
286
  store.dispatchEvent(new Event('input', { bubbles: true }));
287
  }
288
- function loadPreset(mount, key, swapImage) {
289
  const p = LQ_PRESETS[key];
290
- if (!p) return;
291
  mount.querySelector('#lq-rows').innerHTML = '';
292
  p.fields.forEach(f => makeRow(mount, f.name, f.description));
293
- mount.querySelectorAll('.lq-chip').forEach(c =>
294
- c.classList.toggle('active', c.dataset.preset === key));
295
  syncStore(mount);
296
- if (swapImage) setPreset(key);
297
  }
298
- function init() {
299
  const mount = document.getElementById('lq-schema-builder');
300
  if (!mount || mount.dataset.ready) return;
301
  mount.dataset.ready = '1';
302
- let chips = '<div class="lq-presets">';
303
- Object.keys(LQ_PRESETS).forEach(k => {
304
- chips += '<button class="lq-chip" data-preset="' + k + '">' + LQ_PRESETS[k].label + '</button>';
305
- });
306
- chips += '</div>';
307
- mount.innerHTML = chips + '<div id="lq-rows"></div>' +
308
  '<button class="lq-add" id="lq-add">+ add field</button>';
309
- mount.querySelectorAll('.lq-chip').forEach(c =>
310
- c.addEventListener('click', () => loadPreset(mount, c.dataset.preset, true)));
311
  mount.querySelector('#lq-add').addEventListener('click', () => { makeRow(mount, '', ''); });
312
- loadPreset(mount, Object.keys(LQ_PRESETS)[0], false);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
313
  }
 
314
  setInterval(init, 250);
315
 
316
  document.addEventListener('click', function (e) {
@@ -344,27 +350,13 @@ CSS = """
344
  .gradio-container { max-width: 1280px !important; margin: 0 auto !important; }
345
  #lq-schema-store, #lq-preset-store { display: none !important; }
346
 
347
- #lq-hero {
348
- border-radius: 22px; padding: 26px 30px; margin-bottom: 6px;
349
- background: linear-gradient(115deg,#fff4e0 0%,#ffe9b8 45%,#f2ffce 100%);
350
- border: 1px solid rgba(255,150,0,.25);
351
- box-shadow: 0 10px 30px -16px rgba(255,140,0,.55);
352
- }
353
- #lq-hero h1 { margin:0; font-size: 2.05rem; font-weight: 800; letter-spacing:-.02em;
354
- color:#e0590a; -webkit-text-fill-color:#e0590a; text-shadow:0 1px 0 rgba(255,255,255,.4); }
355
- #lq-hero p { margin:.4rem 0 0; color:#7a5a16; font-size:.98rem; max-width: 70ch; }
356
- #lq-hero .lq-badges { margin-top:.7rem; display:flex; gap:.5rem; flex-wrap:wrap; }
357
- #lq-hero .lq-badge { font-size:.74rem; font-weight:700; padding:.28rem .62rem; border-radius:999px;
358
- background:rgba(255,255,255,.7); color:#a85f00; border:1px solid rgba(255,150,0,.3); }
359
 
360
  /* schema builder */
361
  #lq-schema-builder { display:block; }
362
- .lq-presets { display:flex; flex-wrap:wrap; gap:.4rem; margin-bottom:.7rem; }
363
- .lq-chip { border:1px solid rgba(255,150,0,.35); background:#fff; color:#a85f00;
364
- border-radius:999px; padding:.34rem .7rem; font-size:.82rem; font-weight:600; cursor:pointer;
365
- transition:all .12s ease; }
366
- .lq-chip:hover { transform:translateY(-1px); box-shadow:0 4px 12px -6px rgba(255,140,0,.6); }
367
- .lq-chip.active { background:linear-gradient(90deg,var(--lq-orange),var(--lq-amber)); color:#fff; border-color:transparent; }
368
  .lq-row { display:flex; gap:.5rem; margin-bottom:.45rem; align-items:center; }
369
  .lq-row input { border:1px solid rgba(180,160,120,.4); border-radius:11px; padding:.55rem .7rem;
370
  font-size:.9rem; background:#fffdf8; transition:border .12s,box-shadow .12s; }
@@ -380,6 +372,17 @@ CSS = """
380
  cursor:pointer; width:100%; transition:all .12s; }
381
  .lq-add:hover { background:#fff6ea; border-color:var(--lq-orange); }
382
 
 
 
 
 
 
 
 
 
 
 
 
383
  /* result viewer */
384
  .lq-result-shell { border-radius:18px; min-height:360px; padding:16px;
385
  background:linear-gradient(160deg,#fffaf0,#fff7e6); border:1px solid rgba(255,160,0,.22); }
@@ -430,14 +433,8 @@ with gr.Blocks(title="Liquid Image → JSON") as demo:
430
  '<div id="lq-hero">'
431
  "<h1>💧 Liquid Image → JSON</h1>"
432
  "<p>Define the fields you want, drop an image, and watch "
433
- "<b>LFM2.5-VL Extract</b> turn pixels into clean structured JSON — live. "
434
- "Build any schema visually; no prompt engineering required.</p>"
435
- '<div class="lq-badges">'
436
- '<span class="lq-badge">⚡ ZeroGPU</span>'
437
- '<span class="lq-badge">450M &amp; 1.6B</span>'
438
- '<span class="lq-badge">streaming extraction</span>'
439
- '<span class="lq-badge">gr.Citrus</span>'
440
- "</div></div>"
441
  )
442
 
443
  with gr.Row(equal_height=False):
@@ -448,11 +445,12 @@ with gr.Blocks(title="Liquid Image → JSON") as demo:
448
  value="1.6B",
449
  label="Model",
450
  )
451
- gr.Markdown("**Extraction schema** — pick a preset or craft your own fields")
452
  gr.HTML('<div id="lq-schema-builder"></div>')
453
  schema_store = gr.Textbox(elem_id="lq-schema-store", value="[]")
454
  preset_store = gr.Textbox(elem_id="lq-preset-store", value="")
455
  go = gr.Button("💧 Extract JSON", variant="primary", elem_id="lq-go")
 
456
 
457
  with gr.Column(scale=5):
458
  result = gr.HTML(placeholder_html())
 
46
  # --------------------------------------------------------------------------- #
47
  # Schema presets — fill the visual field builder with a single click. #
48
  # --------------------------------------------------------------------------- #
49
+ # Each example's fields are tailored to what is actually visible in its image.
50
  PRESETS = {
51
  "wood": {
52
  "label": "🪵 Wood surface",
53
  "fields": [
54
  {"name": "wood_color", "description": "The overall coloration of the wood surface"},
55
  {"name": "wood_texture", "description": "The tactile quality of the wood surface"},
56
+ {"name": "grain_pattern", "description": "The pattern of the wood grain"},
57
  ],
58
  },
59
  "receipt": {
60
  "label": "🧾 Receipt",
61
  "fields": [
62
+ {"name": "total_amount", "description": "The total amount printed on the receipt"},
63
+ {"name": "cash_paid", "description": "The amount of cash tendered"},
64
+ {"name": "change_due", "description": "The change given back"},
65
+ {"name": "gst_rate", "description": "The GST / tax percentage shown"},
 
66
  ],
67
  },
68
  "nutrition": {
69
  "label": "🥫 Nutrition label",
70
  "fields": [
71
+ {"name": "product_name", "description": "The name of the product on the label"},
72
+ {"name": "brand", "description": "The brand shown on the label"},
73
+ {"name": "net_weight", "description": "The net or drained weight"},
74
+ {"name": "servings_per_container", "description": "Number of servings per container"},
75
+ {"name": "best_before_date", "description": "The best-before or expiry date"},
76
  ],
77
  },
78
  "card": {
79
  "label": "💼 Business card",
80
  "fields": [
81
+ {"name": "full_name", "description": "The person's full name"},
82
+ {"name": "job_title", "description": "Their job title or role"},
83
+ {"name": "company", "description": "Company name or website"},
84
  {"name": "email", "description": "Email address"},
85
  {"name": "phone", "description": "Phone number"},
86
  ],
 
88
  "product": {
89
  "label": "🛍️ Product photo",
90
  "fields": [
91
+ {"name": "product_type", "description": "What kind of product this is"},
92
+ {"name": "brand", "description": "The brand, if a logo is visible"},
93
+ {"name": "primary_color", "description": "The dominant color of the product"},
94
+ {"name": "accent_colors", "description": "Secondary or accent colors"},
95
+ {"name": "closure_type", "description": "How the item fastens or closes"},
96
  ],
97
  },
98
  }
 
285
  setter.call(store, key);
286
  store.dispatchEvent(new Event('input', { bubbles: true }));
287
  }
288
+ function loadPreset(mount, key) {
289
  const p = LQ_PRESETS[key];
290
+ if (!p || !mount) return;
291
  mount.querySelector('#lq-rows').innerHTML = '';
292
  p.fields.forEach(f => makeRow(mount, f.name, f.description));
 
 
293
  syncStore(mount);
294
+ setPreset(key);
295
  }
296
+ function initBuilder() {
297
  const mount = document.getElementById('lq-schema-builder');
298
  if (!mount || mount.dataset.ready) return;
299
  mount.dataset.ready = '1';
300
+ mount.innerHTML = '<div id="lq-rows"></div>' +
 
 
 
 
 
301
  '<button class="lq-add" id="lq-add">+ add field</button>';
 
 
302
  mount.querySelector('#lq-add').addEventListener('click', () => { makeRow(mount, '', ''); });
303
+ makeRow(mount, '', ''); makeRow(mount, '', ''); makeRow(mount, '', '');
304
+ syncStore(mount);
305
+ }
306
+ function initExamples() {
307
+ const ex = document.getElementById('lq-examples');
308
+ if (!ex || ex.dataset.ready) return;
309
+ ex.dataset.ready = '1';
310
+ let html = '<div class="lq-ex-label">Examples — click to try one</div><div class="lq-ex-grid">';
311
+ Object.keys(LQ_PRESETS).forEach(k => {
312
+ html += '<button class="lq-ex" data-preset="' + k + '">' + LQ_PRESETS[k].label + '</button>';
313
+ });
314
+ html += '</div>';
315
+ ex.innerHTML = html;
316
+ ex.querySelectorAll('.lq-ex').forEach(c => c.addEventListener('click', () =>
317
+ loadPreset(document.getElementById('lq-schema-builder'), c.dataset.preset)));
318
  }
319
+ function init() { initBuilder(); initExamples(); }
320
  setInterval(init, 250);
321
 
322
  document.addEventListener('click', function (e) {
 
350
  .gradio-container { max-width: 1280px !important; margin: 0 auto !important; }
351
  #lq-schema-store, #lq-preset-store { display: none !important; }
352
 
353
+ #lq-hero { padding: 6px 2px 2px; margin-bottom: 2px; }
354
+ #lq-hero h1 { margin:0; font-size: 1.75rem; font-weight: 800; letter-spacing:-.02em;
355
+ color:#e0590a; -webkit-text-fill-color:#e0590a; }
356
+ #lq-hero p { margin:.3rem 0 0; color:#8a6a2a; font-size:.95rem; max-width: 72ch; }
 
 
 
 
 
 
 
 
357
 
358
  /* schema builder */
359
  #lq-schema-builder { display:block; }
 
 
 
 
 
 
360
  .lq-row { display:flex; gap:.5rem; margin-bottom:.45rem; align-items:center; }
361
  .lq-row input { border:1px solid rgba(180,160,120,.4); border-radius:11px; padding:.55rem .7rem;
362
  font-size:.9rem; background:#fffdf8; transition:border .12s,box-shadow .12s; }
 
372
  cursor:pointer; width:100%; transition:all .12s; }
373
  .lq-add:hover { background:#fff6ea; border-color:var(--lq-orange); }
374
 
375
+ /* examples */
376
+ #lq-examples { margin-top:.4rem; }
377
+ .lq-ex-label { font-size:.78rem; font-weight:700; text-transform:uppercase; letter-spacing:.05em;
378
+ color:#b88a3a; margin-bottom:.5rem; }
379
+ .lq-ex-grid { display:flex; flex-wrap:wrap; gap:.45rem; }
380
+ .lq-ex { border:1px solid rgba(180,160,120,.35); background:#fffdf8; color:#7a5a20;
381
+ border-radius:11px; padding:.45rem .7rem; font-size:.85rem; font-weight:600; cursor:pointer;
382
+ transition:all .12s ease; }
383
+ .lq-ex:hover { transform:translateY(-1px); border-color:var(--lq-orange); color:#b35900;
384
+ box-shadow:0 5px 14px -8px rgba(255,140,0,.6); background:#fff; }
385
+
386
  /* result viewer */
387
  .lq-result-shell { border-radius:18px; min-height:360px; padding:16px;
388
  background:linear-gradient(160deg,#fffaf0,#fff7e6); border:1px solid rgba(255,160,0,.22); }
 
433
  '<div id="lq-hero">'
434
  "<h1>💧 Liquid Image → JSON</h1>"
435
  "<p>Define the fields you want, drop an image, and watch "
436
+ "<b>LFM2.5-VL Extract</b> turn pixels into clean structured JSON.</p>"
437
+ "</div>"
 
 
 
 
 
 
438
  )
439
 
440
  with gr.Row(equal_height=False):
 
445
  value="1.6B",
446
  label="Model",
447
  )
448
+ gr.Markdown("**Extraction schema** — name a field and describe what to pull out")
449
  gr.HTML('<div id="lq-schema-builder"></div>')
450
  schema_store = gr.Textbox(elem_id="lq-schema-store", value="[]")
451
  preset_store = gr.Textbox(elem_id="lq-preset-store", value="")
452
  go = gr.Button("💧 Extract JSON", variant="primary", elem_id="lq-go")
453
+ gr.HTML('<div id="lq-examples"></div>')
454
 
455
  with gr.Column(scale=5):
456
  result = gr.HTML(placeholder_html())
ex_nutrition.jpg CHANGED

Git LFS Details

  • SHA256: 74acc670867108b46cac7b79f9994bdd3dd3c40f8f69c8a35da857fae38ea57c
  • Pointer size: 131 Bytes
  • Size of remote file: 172 kB