Antharee commited on
Commit
35e6335
·
verified ·
1 Parent(s): e99c4a3

Upload 7 files

Browse files
.env ADDED
@@ -0,0 +1 @@
 
 
1
+ HUGGINGFACE_TOKEN=your_token_here
README.md CHANGED
@@ -1,12 +0,0 @@
1
- ---
2
- title: Typhoon OCR 7B
3
- emoji: 💻
4
- colorFrom: gray
5
- colorTo: yellow
6
- sdk: gradio
7
- sdk_version: 5.38.0
8
- app_file: app.py
9
- pinned: false
10
- ---
11
-
12
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
app.py ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import torch
3
+ from flask import Flask, render_template, request
4
+ from transformers import AutoProcessor, AutoModelForVision2Seq
5
+ from PIL import Image
6
+ from dotenv import load_dotenv
7
+
8
+ # โหลด token
9
+ load_dotenv()
10
+ token = os.getenv("HUGGINGFACE_TOKEN")
11
+ if not token:
12
+ raise ValueError("HUGGINGFACE_TOKEN is not set or .env file not loaded")
13
+
14
+ # Flask setup
15
+ app = Flask(__name__)
16
+ UPLOAD_FOLDER = "static/uploads"
17
+ app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
18
+ os.makedirs(UPLOAD_FOLDER, exist_ok=True)
19
+
20
+ # โหลดโมเดลและ processor
21
+ processor = AutoProcessor.from_pretrained("scb10x/typhoon-ocr-7b", token=token)
22
+ model = AutoModelForVision2Seq.from_pretrained(
23
+ "scb10x/typhoon-ocr-7b",
24
+ torch_dtype=torch.float16,
25
+ device_map="auto",
26
+ token=token
27
+ )
28
+
29
+ @app.route('/', methods=['GET', 'POST'])
30
+ def index():
31
+ result_text = ""
32
+ image_path = ""
33
+
34
+ if request.method == 'POST':
35
+ file = request.files['image']
36
+ if file:
37
+ filepath = os.path.join(app.config['UPLOAD_FOLDER'], file.filename)
38
+ file.save(filepath)
39
+ image_path = filepath
40
+
41
+ image = Image.open(filepath).convert("RGB")
42
+ pixel_values = processor(images=image, return_tensors="pt").pixel_values.to(model.device)
43
+ generated_ids = model.generate(pixel_values, max_new_tokens=256)
44
+ result_text = processor.batch_decode(generated_ids, skip_special_tokens=True)[0]
45
+
46
+ return render_template("index.html", result=result_text, image_path=image_path)
47
+
48
+ if __name__ == '__main__':
49
+ app.run(debug=True)
example.jpg ADDED
requirements.txt ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ Flask
2
+ transformers>=4.40.0
3
+ torch>=2.0
4
+ Pillow
5
+ python-dotenv
6
+ accelerate
static/uploads/ThaiIDCard_Mr._Sample500x500.jpg ADDED
templates/index.html ADDED
@@ -0,0 +1,411 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="th">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>Typhoon OCR Web - ระบบแปลงภาพเป็นข้อความ</title>
7
+ <link
8
+ href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"
9
+ rel="stylesheet"
10
+ />
11
+ <style>
12
+ * {
13
+ margin: 0;
14
+ padding: 0;
15
+ box-sizing: border-box;
16
+ }
17
+
18
+ body {
19
+ font-family: "Segoe UI", -apple-system, BlinkMacSystemFont, sans-serif;
20
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
21
+ min-height: 100vh;
22
+ display: flex;
23
+ align-items: center;
24
+ justify-content: center;
25
+ padding: 20px;
26
+ line-height: 1.6;
27
+ }
28
+
29
+ .container {
30
+ background: rgba(255, 255, 255, 0.95);
31
+ backdrop-filter: blur(20px);
32
+ border-radius: 20px;
33
+ box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
34
+ padding: 40px;
35
+ max-width: 800px;
36
+ width: 100%;
37
+ border: 1px solid rgba(255, 255, 255, 0.2);
38
+ }
39
+
40
+ .header {
41
+ text-align: center;
42
+ margin-bottom: 40px;
43
+ }
44
+
45
+ .header h1 {
46
+ color: #2c3e50;
47
+ font-size: 2.5rem;
48
+ font-weight: 700;
49
+ margin-bottom: 10px;
50
+ background: linear-gradient(135deg, #667eea, #764ba2);
51
+ -webkit-background-clip: text;
52
+ -webkit-text-fill-color: transparent;
53
+ background-clip: text;
54
+ }
55
+
56
+ .header p {
57
+ color: #7f8c8d;
58
+ font-size: 1.1rem;
59
+ font-weight: 400;
60
+ }
61
+
62
+ .upload-section {
63
+ background: #f8f9fa;
64
+ border-radius: 15px;
65
+ padding: 30px;
66
+ margin-bottom: 30px;
67
+ border: 2px dashed #e9ecef;
68
+ transition: all 0.3s ease;
69
+ text-align: center;
70
+ }
71
+
72
+ .upload-section:hover {
73
+ border-color: #667eea;
74
+ background: #f1f3f9;
75
+ transform: translateY(-2px);
76
+ }
77
+
78
+ .file-input-wrapper {
79
+ position: relative;
80
+ display: inline-block;
81
+ width: 100%;
82
+ }
83
+
84
+ .file-input {
85
+ opacity: 0;
86
+ position: absolute;
87
+ z-index: -1;
88
+ }
89
+
90
+ .file-input-button {
91
+ display: inline-flex;
92
+ align-items: center;
93
+ gap: 12px;
94
+ background: linear-gradient(135deg, #667eea, #764ba2);
95
+ color: white;
96
+ padding: 15px 30px;
97
+ border-radius: 12px;
98
+ border: none;
99
+ font-size: 1.1rem;
100
+ font-weight: 600;
101
+ cursor: pointer;
102
+ transition: all 0.3s ease;
103
+ box-shadow: 0 4px 15px rgba(102, 126, 234, 0.3);
104
+ }
105
+
106
+ .file-input-button:hover {
107
+ transform: translateY(-2px);
108
+ box-shadow: 0 8px 25px rgba(102, 126, 234, 0.4);
109
+ }
110
+
111
+ .upload-info {
112
+ margin-top: 20px;
113
+ color: #6c757d;
114
+ font-size: 0.95rem;
115
+ }
116
+
117
+ .submit-btn {
118
+ background: linear-gradient(135deg, #28a745, #20c997);
119
+ color: white;
120
+ border: none;
121
+ padding: 15px 40px;
122
+ border-radius: 12px;
123
+ font-size: 1.1rem;
124
+ font-weight: 600;
125
+ cursor: pointer;
126
+ transition: all 0.3s ease;
127
+ box-shadow: 0 4px 15px rgba(40, 167, 69, 0.3);
128
+ margin-top: 20px;
129
+ }
130
+
131
+ .submit-btn:hover {
132
+ transform: translateY(-2px);
133
+ box-shadow: 0 8px 25px rgba(40, 167, 69, 0.4);
134
+ }
135
+
136
+ .submit-btn:disabled {
137
+ opacity: 0.6;
138
+ cursor: not-allowed;
139
+ transform: none;
140
+ }
141
+
142
+ .result-section {
143
+ margin-top: 30px;
144
+ }
145
+
146
+ .section-title {
147
+ color: #2c3e50;
148
+ font-size: 1.4rem;
149
+ font-weight: 600;
150
+ margin-bottom: 20px;
151
+ display: flex;
152
+ align-items: center;
153
+ gap: 10px;
154
+ }
155
+
156
+ .uploaded-image {
157
+ background: #f8f9fa;
158
+ border-radius: 15px;
159
+ padding: 20px;
160
+ margin-bottom: 30px;
161
+ text-align: center;
162
+ border: 1px solid #e9ecef;
163
+ }
164
+
165
+ .uploaded-image img {
166
+ max-width: 100%;
167
+ height: auto;
168
+ border-radius: 12px;
169
+ box-shadow: 0 8px 20px rgba(0, 0, 0, 0.1);
170
+ transition: transform 0.3s ease;
171
+ }
172
+
173
+ .uploaded-image img:hover {
174
+ transform: scale(1.02);
175
+ }
176
+
177
+ .text-result {
178
+ background: #f8f9fa;
179
+ border-radius: 15px;
180
+ padding: 25px;
181
+ border: 1px solid #e9ecef;
182
+ }
183
+
184
+ .result-textarea {
185
+ width: 100%;
186
+ min-height: 200px;
187
+ border: 2px solid #e9ecef;
188
+ border-radius: 12px;
189
+ padding: 20px;
190
+ font-family: "Courier New", monospace;
191
+ font-size: 0.95rem;
192
+ line-height: 1.6;
193
+ background: white;
194
+ resize: vertical;
195
+ transition: border-color 0.3s ease;
196
+ }
197
+
198
+ .result-textarea:focus {
199
+ outline: none;
200
+ border-color: #667eea;
201
+ box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
202
+ }
203
+
204
+ .copy-btn {
205
+ background: #6c757d;
206
+ color: white;
207
+ border: none;
208
+ padding: 10px 20px;
209
+ border-radius: 8px;
210
+ font-size: 0.9rem;
211
+ cursor: pointer;
212
+ transition: all 0.3s ease;
213
+ margin-top: 15px;
214
+ display: flex;
215
+ align-items: center;
216
+ gap: 8px;
217
+ }
218
+
219
+ .copy-btn:hover {
220
+ background: #5a6268;
221
+ transform: translateY(-1px);
222
+ }
223
+
224
+ .loading {
225
+ display: none;
226
+ text-align: center;
227
+ color: #667eea;
228
+ font-weight: 500;
229
+ margin: 20px 0;
230
+ }
231
+
232
+ .loading i {
233
+ animation: spin 1s linear infinite;
234
+ margin-right: 10px;
235
+ }
236
+
237
+ @keyframes spin {
238
+ 0% {
239
+ transform: rotate(0deg);
240
+ }
241
+ 100% {
242
+ transform: rotate(360deg);
243
+ }
244
+ }
245
+
246
+ .file-name {
247
+ margin-top: 15px;
248
+ padding: 10px;
249
+ background: rgba(102, 126, 234, 0.1);
250
+ border-radius: 8px;
251
+ color: #667eea;
252
+ font-weight: 500;
253
+ display: none;
254
+ }
255
+
256
+ @media (max-width: 768px) {
257
+ .container {
258
+ padding: 30px 20px;
259
+ }
260
+
261
+ .header h1 {
262
+ font-size: 2rem;
263
+ }
264
+
265
+ .upload-section {
266
+ padding: 20px;
267
+ }
268
+ }
269
+ </style>
270
+ </head>
271
+ <body>
272
+ <div class="container">
273
+ <div class="header">
274
+ <h1><i class="fas fa-eye"></i> Typhoon OCR</h1>
275
+ <p>ระบบแปลงภาพเป็นข้อความด้วยปัญญาประดิษฐ์</p>
276
+ </div>
277
+
278
+ <form method="POST" enctype="multipart/form-data" id="ocrForm">
279
+ <div class="upload-section">
280
+ <div class="file-input-wrapper">
281
+ <input
282
+ type="file"
283
+ name="image"
284
+ accept="image/*"
285
+ required
286
+ class="file-input"
287
+ id="imageInput"
288
+ />
289
+ <label for="imageInput" class="file-input-button">
290
+ <i class="fas fa-cloud-upload-alt"></i>
291
+ เลือกไฟล์ภาพ
292
+ </label>
293
+ </div>
294
+ <div class="file-name" id="fileName"></div>
295
+ <div class="upload-info">
296
+ <i class="fas fa-info-circle"></i>
297
+ รองรับไฟล์: JPG, PNG, GIF, BMP | ขนาดสูงสุด: 10MB
298
+ </div>
299
+ <button type="submit" class="submit-btn" id="submitBtn" disabled>
300
+ <i class="fas fa-magic"></i>
301
+ แปลงภาพเป็นข้อความ
302
+ </button>
303
+ </div>
304
+ </form>
305
+
306
+ <div class="loading" id="loading">
307
+ <i class="fas fa-spinner"></i>
308
+ กำลังประมวลผลภาพ กรุณารอสักครู่...
309
+ </div>
310
+
311
+ {% if image_path %}
312
+ <div class="result-section">
313
+ <h3 class="section-title">
314
+ <i class="fas fa-image"></i>
315
+ ภาพที่อัปโหลด
316
+ </h3>
317
+ <div class="uploaded-image">
318
+ <img src="{{ image_path }}" alt="Uploaded Image" />
319
+ </div>
320
+ </div>
321
+ {% endif %} {% if result %}
322
+ <div class="result-section">
323
+ <h3 class="section-title">
324
+ <i class="fas fa-file-text"></i>
325
+ ข้อความที่ตรวจจับได้
326
+ </h3>
327
+ <div class="text-result">
328
+ <textarea class="result-textarea" readonly id="resultText">
329
+ {{ result }}</textarea
330
+ >
331
+ <button class="copy-btn" onclick="copyToClipboard()">
332
+ <i class="fas fa-copy"></i>
333
+ คัดลอกข้อความ
334
+ </button>
335
+ </div>
336
+ </div>
337
+ {% endif %}
338
+ </div>
339
+
340
+ <script>
341
+ const imageInput = document.getElementById("imageInput");
342
+ const fileName = document.getElementById("fileName");
343
+ const submitBtn = document.getElementById("submitBtn");
344
+ const ocrForm = document.getElementById("ocrForm");
345
+ const loading = document.getElementById("loading");
346
+
347
+ imageInput.addEventListener("change", function (e) {
348
+ if (e.target.files.length > 0) {
349
+ const file = e.target.files[0];
350
+ fileName.textContent = `ไฟล์ที่เลือก: ${file.name}`;
351
+ fileName.style.display = "block";
352
+ submitBtn.disabled = false;
353
+ } else {
354
+ fileName.style.display = "none";
355
+ submitBtn.disabled = true;
356
+ }
357
+ });
358
+
359
+ ocrForm.addEventListener("submit", function () {
360
+ loading.style.display = "block";
361
+ submitBtn.disabled = true;
362
+ submitBtn.innerHTML =
363
+ '<i class="fas fa-spinner fa-spin"></i> กำลังประมวลผล...';
364
+ });
365
+
366
+ function copyToClipboard() {
367
+ const resultText = document.getElementById("resultText");
368
+ resultText.select();
369
+ document.execCommand("copy");
370
+
371
+ const copyBtn = document.querySelector(".copy-btn");
372
+ const originalText = copyBtn.innerHTML;
373
+ copyBtn.innerHTML = '<i class="fas fa-check"></i> คัดลอกแล้ว!';
374
+ copyBtn.style.background = "#28a745";
375
+
376
+ setTimeout(() => {
377
+ copyBtn.innerHTML = originalText;
378
+ copyBtn.style.background = "#6c757d";
379
+ }, 2000);
380
+ }
381
+
382
+ // Drag and drop functionality
383
+ const uploadSection = document.querySelector(".upload-section");
384
+
385
+ uploadSection.addEventListener("dragover", function (e) {
386
+ e.preventDefault();
387
+ uploadSection.style.borderColor = "#667eea";
388
+ uploadSection.style.background = "#f1f3f9";
389
+ });
390
+
391
+ uploadSection.addEventListener("dragleave", function (e) {
392
+ e.preventDefault();
393
+ uploadSection.style.borderColor = "#e9ecef";
394
+ uploadSection.style.background = "#f8f9fa";
395
+ });
396
+
397
+ uploadSection.addEventListener("drop", function (e) {
398
+ e.preventDefault();
399
+ uploadSection.style.borderColor = "#e9ecef";
400
+ uploadSection.style.background = "#f8f9fa";
401
+
402
+ const files = e.dataTransfer.files;
403
+ if (files.length > 0) {
404
+ imageInput.files = files;
405
+ const event = new Event("change", { bubbles: true });
406
+ imageInput.dispatchEvent(event);
407
+ }
408
+ });
409
+ </script>
410
+ </body>
411
+ </html>