akhaliq HF Staff commited on
Commit
43ac787
·
verified ·
1 Parent(s): 70f90b2

Upload index.js with huggingface_hub

Browse files
Files changed (1) hide show
  1. index.js +173 -67
index.js CHANGED
@@ -1,76 +1,182 @@
1
- import { pipeline } from 'https://cdn.jsdelivr.net/npm/@huggingface/transformers@3.4.1';
2
-
3
- // Reference the elements that we will need
4
- const status = document.getElementById('status');
5
- const fileUpload = document.getElementById('upload');
6
- const imageContainer = document.getElementById('container');
7
- const example = document.getElementById('example');
8
-
9
- const EXAMPLE_URL = 'https://huggingface.co/datasets/Xenova/transformers.js-docs/resolve/main/city-streets.jpg';
10
-
11
- // Create a new object detection pipeline
12
- status.textContent = 'Loading model...';
13
- const detector = await pipeline('object-detection', 'Xenova/detr-resnet-50');
14
- status.textContent = 'Ready';
15
-
16
- example.addEventListener('click', (e) => {
17
- e.preventDefault();
18
- detect(EXAMPLE_URL);
19
- });
20
-
21
- fileUpload.addEventListener('change', function (e) {
22
- const file = e.target.files[0];
23
- if (!file) {
24
- return;
25
  }
26
 
27
- const reader = new FileReader();
 
 
 
 
 
 
 
 
 
 
 
 
28
 
29
- // Set up a callback when the file is loaded
30
- reader.onload = e2 => detect(e2.target.result);
 
 
 
 
 
 
 
 
 
 
 
 
31
 
32
- reader.readAsDataURL(file);
33
- });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
34
 
 
 
 
 
35
 
36
- // Detect objects in the image
37
- async function detect(img) {
38
- imageContainer.innerHTML = '';
39
- imageContainer.style.backgroundImage = `url(${img})`;
 
 
 
 
40
 
41
- status.textContent = 'Analysing...';
42
- const output = await detector(img, {
43
- threshold: 0.5,
44
- percentage: true,
45
- });
46
- status.textContent = '';
47
- output.forEach(renderBox);
48
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
49
 
50
- // Render a bounding box and label on the image
51
- function renderBox({ box, label }) {
52
- const { xmax, xmin, ymax, ymin } = box;
53
-
54
- // Generate a random color for the box
55
- const color = '#' + Math.floor(Math.random() * 0xFFFFFF).toString(16).padStart(6, 0);
56
-
57
- // Draw the box
58
- const boxElement = document.createElement('div');
59
- boxElement.className = 'bounding-box';
60
- Object.assign(boxElement.style, {
61
- borderColor: color,
62
- left: 100 * xmin + '%',
63
- top: 100 * ymin + '%',
64
- width: 100 * (xmax - xmin) + '%',
65
- height: 100 * (ymax - ymin) + '%',
66
- })
67
-
68
- // Draw label
69
- const labelElement = document.createElement('span');
70
- labelElement.textContent = label;
71
- labelElement.className = 'bounding-box-label';
72
- labelElement.style.backgroundColor = color;
73
-
74
- boxElement.appendChild(labelElement);
75
- imageContainer.appendChild(boxElement);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
76
  }
 
 
 
 
 
 
1
+ import { AutoProcessor, AutoTokenizer, Moondream1ForConditionalGeneration, RawImage } from 'https://cdn.jsdelivr.net/npm/@huggingface/transformers@2.17.2';
2
+
3
+ class MoondreamAssistant {
4
+ constructor() {
5
+ this.model = null;
6
+ this.processor = null;
7
+ this.tokenizer = null;
8
+ this.currentImage = null;
9
+ this.isModelLoaded = false;
10
+
11
+ this.initializeElements();
12
+ this.bindEvents();
13
+ this.loadModel();
 
 
 
 
 
 
 
 
 
 
 
14
  }
15
 
16
+ initializeElements() {
17
+ this.uploadArea = document.getElementById('uploadArea');
18
+ this.fileInput = document.getElementById('fileInput');
19
+ this.imagePreview = document.getElementById('imagePreview');
20
+ this.previewImage = document.getElementById('previewImage');
21
+ this.removeBtn = document.getElementById('removeBtn');
22
+ this.querySection = document.getElementById('querySection');
23
+ this.questionInput = document.getElementById('questionInput');
24
+ this.askBtn = document.getElementById('askBtn');
25
+ this.responseSection = document.getElementById('responseSection');
26
+ this.loading = document.getElementById('loading');
27
+ this.responseText = document.getElementById('responseText');
28
+ }
29
 
30
+ bindEvents() {
31
+ this.uploadArea.addEventListener('click', () => this.fileInput.click());
32
+ this.uploadArea.addEventListener('dragover', this.handleDragOver.bind(this));
33
+ this.uploadArea.addEventListener('drop', this.handleDrop.bind(this));
34
+ this.fileInput.addEventListener('change', this.handleFileSelect.bind(this));
35
+ this.removeBtn.addEventListener('click', this.removeImage.bind(this));
36
+ this.questionInput.addEventListener('input', this.handleQuestionInput.bind(this));
37
+ this.askBtn.addEventListener('click', this.askQuestion.bind(this));
38
+ this.questionInput.addEventListener('keypress', (e) => {
39
+ if (e.key === 'Enter' && !this.askBtn.disabled) {
40
+ this.askQuestion();
41
+ }
42
+ });
43
+ }
44
 
45
+ async loadModel() {
46
+ try {
47
+ const model_id = 'Xenova/moondream2';
48
+
49
+ this.updateUploadArea('Loading AI model...', true);
50
+
51
+ [this.processor, this.tokenizer, this.model] = await Promise.all([
52
+ AutoProcessor.from_pretrained(model_id),
53
+ AutoTokenizer.from_pretrained(model_id),
54
+ Moondream1ForConditionalGeneration.from_pretrained(model_id, {
55
+ dtype: {
56
+ embed_tokens: 'fp16',
57
+ vision_encoder: 'fp16',
58
+ decoder_model_merged: 'q4',
59
+ },
60
+ device: 'webgpu',
61
+ })
62
+ ]);
63
+
64
+ this.isModelLoaded = true;
65
+ this.updateUploadArea('Drop an image here or browse');
66
+ console.log('Model loaded successfully');
67
+ } catch (error) {
68
+ console.error('Error loading model:', error);
69
+ this.updateUploadArea('Error loading AI model. Please refresh.', true);
70
+ }
71
+ }
72
 
73
+ handleDragOver(e) {
74
+ e.preventDefault();
75
+ this.uploadArea.classList.add('dragover');
76
+ }
77
 
78
+ handleDrop(e) {
79
+ e.preventDefault();
80
+ this.uploadArea.classList.remove('dragover');
81
+ const files = e.dataTransfer.files;
82
+ if (files.length > 0) {
83
+ this.handleImageFile(files[0]);
84
+ }
85
+ }
86
 
87
+ handleFileSelect(e) {
88
+ const file = e.target.files[0];
89
+ if (file) {
90
+ this.handleImageFile(file);
91
+ }
92
+ }
93
+
94
+ async handleImageFile(file) {
95
+ if (!file.type.startsWith('image/')) {
96
+ alert('Please select a valid image file');
97
+ return;
98
+ }
99
+
100
+ try {
101
+ this.currentImage = await RawImage.fromBlob(file);
102
+ const url = URL.createObjectURL(file);
103
+
104
+ this.previewImage.src = url;
105
+ this.imagePreview.style.display = 'block';
106
+ this.uploadArea.style.display = 'none';
107
+ this.querySection.style.display = 'block';
108
+ this.questionInput.focus();
109
+ } catch (error) {
110
+ console.error('Error loading image:', error);
111
+ alert('Error loading image. Please try again.');
112
+ }
113
+ }
114
 
115
+ removeImage() {
116
+ this.currentImage = null;
117
+ this.imagePreview.style.display = 'none';
118
+ this.uploadArea.style.display = 'flex';
119
+ this.querySection.style.display = 'none';
120
+ this.responseSection.style.display = 'none';
121
+ this.questionInput.value = '';
122
+ this.askBtn.disabled = true;
123
+
124
+ if (this.previewImage.src) {
125
+ URL.revokeObjectURL(this.previewImage.src);
126
+ }
127
+ }
128
+
129
+ handleQuestionInput() {
130
+ this.askBtn.disabled = this.questionInput.value.trim().length === 0;
131
+ }
132
+
133
+ async askQuestion() {
134
+ if (!this.isModelLoaded || !this.currentImage) return;
135
+
136
+ const question = this.questionInput.value.trim();
137
+ if (!question) return;
138
+
139
+ this.askBtn.disabled = true;
140
+ this.loading.style.display = 'flex';
141
+ this.responseSection.style.display = 'block';
142
+ this.responseText.textContent = '';
143
+
144
+ try {
145
+ const text = `<image>\n\nQuestion: ${question}\n\nAnswer:`;
146
+ const text_inputs = this.tokenizer(text);
147
+ const vision_inputs = await this.processor(this.currentImage);
148
+
149
+ const output = await this.model.generate({
150
+ ...text_inputs,
151
+ ...vision_inputs,
152
+ do_sample: false,
153
+ max_new_tokens: 128,
154
+ });
155
+
156
+ const decoded = this.tokenizer.batch_decode(output, { skip_special_tokens: true });
157
+ const response = decoded[0]
158
+ .split('Answer:')[1]
159
+ .trim()
160
+ .replace(/<\|.*?\|>/g, '');
161
+
162
+ this.responseText.textContent = response;
163
+ } catch (error) {
164
+ console.error('Error generating response:', error);
165
+ this.responseText.textContent = 'Sorry, there was an error processing your question. Please try again.';
166
+ } finally {
167
+ this.loading.style.display = 'none';
168
+ this.askBtn.disabled = false;
169
+ }
170
+ }
171
+
172
+ updateUploadArea(text, isError = false) {
173
+ const p = this.uploadArea.querySelector('p');
174
+ p.textContent = text;
175
+ this.uploadArea.style.pointerEvents = isError ? 'none' : 'auto';
176
+ }
177
  }
178
+
179
+ // Initialize the application
180
+ document.addEventListener('DOMContentLoaded', () => {
181
+ new MoondreamAssistant();
182
+ });