QMonitor Admin commited on
Commit
d786fb0
·
1 Parent(s): 086b042

Update LSB OCR service with app.py, requirements.txt, and README.md

Browse files
Files changed (3) hide show
  1. README.md +44 -13
  2. app.py +221 -4
  3. requirements.txt +5 -0
README.md CHANGED
@@ -1,13 +1,44 @@
1
- ---
2
- title: LSB
3
- emoji: 🦀
4
- colorFrom: blue
5
- colorTo: yellow
6
- sdk: gradio
7
- sdk_version: 5.23.0
8
- app_file: app.py
9
- pinned: false
10
- short_description: 'LSB '
11
- ---
12
-
13
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # LSB Digital OCR dengan Hugging Face
2
+
3
+ Aplikasi ini menggunakan OCR (Optical Character Recognition) untuk mengekstrak data dari formulir Laporan Sumber Bahaya (LSB) fisik ke format digital.
4
+
5
+ ## Fitur
6
+
7
+ - Deteksi dan ekstraksi teks dari gambar formulir LSB
8
+ - Pengenalan checkbox dan status (dicentang/tidak dicentang)
9
+ - Pre-processing gambar untuk meningkatkan akurasi OCR
10
+ - API endpoint untuk integrasi dengan aplikasi mobile
11
+
12
+ ## Teknologi yang Digunakan
13
+
14
+ - Pytesseract untuk OCR
15
+ - OpenCV untuk pemrosesan gambar
16
+ - Gradio untuk antarmuka web
17
+ - Flask untuk endpoint API
18
+
19
+ ## Cara Menggunakan
20
+
21
+ 1. Upload gambar formulir LSB melalui interface web
22
+ 2. Klik tombol "Proses OCR"
23
+ 3. Hasil ekstraksi teks akan ditampilkan di sebelah kanan
24
+ 4. Bisa juga diakses via API dari aplikasi Flutter
25
+
26
+ ## Format Output
27
+
28
+ Output dalam format JSON dengan struktur berikut:
29
+
30
+ ```json
31
+ {
32
+ "nama_pelapor": "nama yang terdeteksi",
33
+ "posisi_jabatan": "posisi/jabatan terdeteksi",
34
+ "lokasi_kejadian": "lokasi terdeteksi",
35
+ "tanggal_waktu": "tanggal & waktu terdeteksi",
36
+ "jenis_pengamatan": "Unsafe Condition, Unsafe Action, Intervensi",
37
+ "uraian_pengamatan": "uraian terdeteksi",
38
+ "tindakan_intervensi": "tindakan terdeteksi"
39
+ }
40
+ ```
41
+
42
+ ## Integrasi dengan Aplikasi Mobile
43
+
44
+ Aplikasi ini terintegrasi dengan LSB Digital Mobile yang dikembangkan menggunakan Flutter. Form LSB dapat diisi secara manual atau dengan mengunggah gambar formulir fisik untuk diproses dengan OCR.
app.py CHANGED
@@ -1,7 +1,224 @@
1
  import gradio as gr
 
 
 
 
 
 
 
2
 
3
- def greet(name):
4
- return "Hello " + name + "!!"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5
 
6
- demo = gr.Interface(fn=greet, inputs="text", outputs="text")
7
- demo.launch()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  import gradio as gr
2
+ import pytesseract
3
+ from PIL import Image, ImageEnhance
4
+ import cv2
5
+ import numpy as np
6
+ import tempfile
7
+ import os
8
+ import io
9
 
10
+ # Fungsi untuk meningkatkan kualitas gambar
11
+ def preprocess_image(image):
12
+ # Konversi ke array numpy
13
+ img = np.array(image)
14
+
15
+ # Konversi ke grayscale
16
+ gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
17
+
18
+ # Thresholding adaptif
19
+ thresh = cv2.adaptiveThreshold(gray, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
20
+ cv2.THRESH_BINARY, 11, 2)
21
+
22
+ # Noise removal
23
+ kernel = np.ones((1, 1), np.uint8)
24
+ opening = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel)
25
+
26
+ return opening
27
 
28
+ # Fungsi untuk mendeteksi tanda centang pada kotak
29
+ def detect_checkboxes(image, orig_image):
30
+ # Deteksi kotak-kotak (checkbox)
31
+ img = np.array(image)
32
+ gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
33
+
34
+ # Threshold gambar untuk mendapatkan area checkbox
35
+ _, thresh = cv2.threshold(gray, 150, 255, cv2.THRESH_BINARY_INV)
36
+
37
+ # Deteksi kontur
38
+ contours, _ = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
39
+
40
+ # Filter kontur yang mungkin merupakan checkbox
41
+ checkboxes = []
42
+ for cnt in contours:
43
+ x, y, w, h = cv2.boundingRect(cnt)
44
+ # Filter berdasarkan ukuran
45
+ if 10 < w < 50 and 10 < h < 50:
46
+ # Periksa apakah checkbox dicentang
47
+ roi = orig_image[y:y+h, x:x+w]
48
+ gray_roi = cv2.cvtColor(roi, cv2.COLOR_BGR2GRAY)
49
+ _, binary_roi = cv2.threshold(gray_roi, 150, 255, cv2.THRESH_BINARY_INV)
50
+ non_zero_pixels = cv2.countNonZero(binary_roi)
51
+ ratio = non_zero_pixels / (w * h)
52
+
53
+ # Jika rasio piksel non-zero cukup tinggi, kemungkinan dicentang
54
+ is_checked = ratio > 0.1
55
+ checkboxes.append((x, y, w, h, is_checked))
56
+
57
+ # Mendeteksi jenis pengamatan berdasarkan posisi checkbox
58
+ jenis_pengamatan = {
59
+ 'Unsafe Condition': False,
60
+ 'Unsafe Action': False,
61
+ 'Intervensi': False
62
+ }
63
+
64
+ # Cari checkbox yang dicentang dan tentukan posisinya untuk jenis pengamatan
65
+ for (x, y, w, h, is_checked) in checkboxes:
66
+ # Atur kondisi berdasarkan posisi checkbox pada form standard LSB
67
+ if is_checked:
68
+ if x < img.shape[1] // 3: # Checkbox di posisi pertama
69
+ jenis_pengamatan['Unsafe Condition'] = True
70
+ elif img.shape[1] // 3 < x < 2 * img.shape[1] // 3: # Checkbox di posisi kedua
71
+ jenis_pengamatan['Unsafe Action'] = True
72
+ else: # Checkbox di posisi ketiga
73
+ jenis_pengamatan['Intervensi'] = True
74
+
75
+ return jenis_pengamatan
76
+
77
+ # Fungsi utama untuk OCR
78
+ def perform_ocr(image):
79
+ # Simpan gambar ke file temporari
80
+ with tempfile.NamedTemporaryFile(delete=False, suffix='.jpg') as temp:
81
+ image_path = temp.name
82
+ img_pil = Image.fromarray(image)
83
+ img_pil.save(image_path)
84
+
85
+ # Preprocess gambar untuk OCR
86
+ preprocessed = preprocess_image(img_pil)
87
+ cv2.imwrite(image_path + '_processed.jpg', preprocessed)
88
+
89
+ # Lakukan OCR pada gambar yang telah diproses
90
+ text = pytesseract.image_to_string(Image.open(image_path + '_processed.jpg'), lang='ind')
91
+
92
+ # Hapus file temporari
93
+ os.unlink(image_path)
94
+ os.unlink(image_path + '_processed.jpg')
95
+
96
+ # Lakukan juga deteksi checkbox
97
+ jenis_pengamatan = detect_checkboxes(img_pil, image)
98
+
99
+ # Parse teks hasil OCR menjadi data terstruktur
100
+ data = parse_form_text(text)
101
+
102
+ # Tambahkan hasil deteksi checkbox ke data
103
+ data['jenis_pengamatan'] = []
104
+ for jenis, checked in jenis_pengamatan.items():
105
+ if checked:
106
+ data['jenis_pengamatan'].append(jenis)
107
+
108
+ # Gabungkan menjadi string
109
+ if data['jenis_pengamatan']:
110
+ data['jenis_pengamatan'] = ', '.join(data['jenis_pengamatan'])
111
+ else:
112
+ data['jenis_pengamatan'] = ''
113
+
114
+ return data
115
+
116
+ # Fungsi untuk memparse teks dari form LSB
117
+ def parse_form_text(text):
118
+ lines = text.split('\n')
119
+ data = {
120
+ 'nama_pelapor': '',
121
+ 'posisi_jabatan': '',
122
+ 'lokasi_kejadian': '',
123
+ 'tanggal_waktu': '',
124
+ 'uraian_pengamatan': '',
125
+ 'tindakan_intervensi': '',
126
+ }
127
+
128
+ # Cari setiap field dalam teks
129
+ current_field = None
130
+ for i, line in enumerate(lines):
131
+ # Deteksi field berdasarkan kata kunci
132
+ if 'NAMA PELAPOR' in line:
133
+ current_field = 'nama_pelapor'
134
+ if i+1 < len(lines) and lines[i+1].strip():
135
+ data[current_field] = lines[i+1].strip()
136
+ elif 'POSISI' in line or 'JABATAN' in line:
137
+ current_field = 'posisi_jabatan'
138
+ if i+1 < len(lines) and lines[i+1].strip():
139
+ data[current_field] = lines[i+1].strip()
140
+ elif 'LOKASI' in line:
141
+ current_field = 'lokasi_kejadian'
142
+ if i+1 < len(lines) and lines[i+1].strip():
143
+ data[current_field] = lines[i+1].strip()
144
+ elif 'TANGGAL' in line or 'WAKTU' in line:
145
+ current_field = 'tanggal_waktu'
146
+ if i+1 < len(lines) and lines[i+1].strip():
147
+ data[current_field] = lines[i+1].strip()
148
+ elif 'URAIAN' in line and 'PENGAMATAN' in line:
149
+ current_field = 'uraian_pengamatan'
150
+ # Ambil beberapa baris untuk uraian
151
+ for j in range(i+1, min(i+4, len(lines))):
152
+ if lines[j].strip() and not any(keyword in lines[j] for keyword in ['INTERVENSI', 'TINDAKAN', 'SARAN', 'PELAPOR']):
153
+ data[current_field] += ' ' + lines[j].strip()
154
+ elif ('TINDAKAN' in line and 'INTERVENSI' in line) or 'PERBAIKAN' in line:
155
+ current_field = 'tindakan_intervensi'
156
+ # Ambil beberapa baris untuk tindakan
157
+ for j in range(i+1, min(i+4, len(lines))):
158
+ if lines[j].strip() and not any(keyword in lines[j] for keyword in ['PELAPOR', 'HSE', 'PENERIMA']):
159
+ data[current_field] += ' ' + lines[j].strip()
160
+
161
+ # Bersihkan teks
162
+ for key in data:
163
+ data[key] = data[key].strip()
164
+
165
+ return data
166
+
167
+ # API endpoint untuk prediksi
168
+ def predict_api(image):
169
+ if image is None:
170
+ return {"error": "No image provided"}
171
+
172
+ # Konversi file gambar ke numpy array
173
+ img = np.array(Image.open(io.BytesIO(image.read())))
174
+
175
+ # Lakukan OCR
176
+ result = perform_ocr(img)
177
+
178
+ return {"output": result}
179
+
180
+ # Interface web dengan Gradio
181
+ with gr.Blocks() as demo:
182
+ gr.Markdown("# LSB Form OCR")
183
+ gr.Markdown("Upload gambar formulir LSB untuk ekstraksi data otomatis")
184
+
185
+ with gr.Row():
186
+ with gr.Column():
187
+ input_image = gr.Image(type="pil", label="Upload Gambar Formulir LSB")
188
+ submit_btn = gr.Button("Proses OCR")
189
+
190
+ with gr.Column():
191
+ nama_output = gr.Textbox(label="Nama Pelapor")
192
+ posisi_output = gr.Textbox(label="Posisi/Jabatan")
193
+ lokasi_output = gr.Textbox(label="Lokasi Kejadian")
194
+ tanggal_output = gr.Textbox(label="Tanggal/Waktu")
195
+ jenis_output = gr.Textbox(label="Jenis Pengamatan")
196
+ uraian_output = gr.Textbox(label="Uraian Pengamatan")
197
+ tindakan_output = gr.Textbox(label="Tindakan Intervensi")
198
+
199
+ def process_image(img):
200
+ if img is None:
201
+ return ["No image uploaded"] * 7
202
+
203
+ result = perform_ocr(np.array(img))
204
+
205
+ return [
206
+ result['nama_pelapor'],
207
+ result['posisi_jabatan'],
208
+ result['lokasi_kejadian'],
209
+ result['tanggal_waktu'],
210
+ result['jenis_pengamatan'],
211
+ result['uraian_pengamatan'],
212
+ result['tindakan_intervensi']
213
+ ]
214
+
215
+ submit_btn.click(
216
+ process_image,
217
+ inputs=[input_image],
218
+ outputs=[nama_output, posisi_output, lokasi_output, tanggal_output,
219
+ jenis_output, uraian_output, tindakan_output]
220
+ )
221
+
222
+ # Konfigurasi API
223
+ demo.queue()
224
+ demo.launch()
requirements.txt ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ gradio==4.19.2
2
+ pytesseract==0.3.10
3
+ opencv-python==4.8.1.78
4
+ numpy==1.24.3
5
+ Pillow==10.1.0