Spaces:
Sleeping
Sleeping
| import gradio as gr | |
| import pytesseract | |
| from PIL import Image | |
| import cv2 | |
| import numpy as np | |
| import tempfile | |
| import os | |
| import io | |
| # LSB Digital OCR Service | |
| # Layanan OCR untuk Formulir Laporan Sumber Bahaya | |
| # https://huggingface.co/spaces/Unlimitedlevel19/LSB | |
| # Fungsi untuk meningkatkan kualitas gambar | |
| def preprocess_image(image): | |
| # Konversi ke array numpy | |
| img = np.array(image) | |
| # Konversi ke grayscale | |
| gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) | |
| # Thresholding adaptif | |
| thresh = cv2.adaptiveThreshold(gray, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, | |
| cv2.THRESH_BINARY, 11, 2) | |
| # Noise removal | |
| kernel = np.ones((1, 1), np.uint8) | |
| opening = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel) | |
| return opening | |
| # Fungsi untuk mendeteksi tanda centang pada kotak | |
| def detect_checkboxes(image, orig_image): | |
| # Deteksi kotak-kotak (checkbox) | |
| img = np.array(image) | |
| gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) | |
| # Threshold gambar untuk mendapatkan area checkbox | |
| _, thresh = cv2.threshold(gray, 150, 255, cv2.THRESH_BINARY_INV) | |
| # Deteksi kontur | |
| contours, _ = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) | |
| # Filter kontur yang mungkin merupakan checkbox | |
| checkboxes = [] | |
| for cnt in contours: | |
| x, y, w, h = cv2.boundingRect(cnt) | |
| # Filter berdasarkan ukuran | |
| if 10 < w < 50 and 10 < h < 50: | |
| # Periksa apakah checkbox dicentang | |
| roi = orig_image[y:y+h, x:x+w] | |
| gray_roi = cv2.cvtColor(roi, cv2.COLOR_BGR2GRAY) | |
| _, binary_roi = cv2.threshold(gray_roi, 150, 255, cv2.THRESH_BINARY_INV) | |
| non_zero_pixels = cv2.countNonZero(binary_roi) | |
| ratio = non_zero_pixels / (w * h) | |
| # Jika rasio piksel non-zero cukup tinggi, kemungkinan dicentang | |
| is_checked = ratio > 0.1 | |
| checkboxes.append((x, y, w, h, is_checked)) | |
| # Mendeteksi jenis pengamatan berdasarkan posisi checkbox | |
| jenis_pengamatan = { | |
| 'Unsafe Condition': False, | |
| 'Unsafe Action': False, | |
| 'Intervensi': False | |
| } | |
| # Cari checkbox yang dicentang dan tentukan posisinya untuk jenis pengamatan | |
| for (x, y, w, h, is_checked) in checkboxes: | |
| # Atur kondisi berdasarkan posisi checkbox pada form standard LSB | |
| if is_checked: | |
| if x < img.shape[1] // 3: # Checkbox di posisi pertama | |
| jenis_pengamatan['Unsafe Condition'] = True | |
| elif img.shape[1] // 3 < x < 2 * img.shape[1] // 3: # Checkbox di posisi kedua | |
| jenis_pengamatan['Unsafe Action'] = True | |
| else: # Checkbox di posisi ketiga | |
| jenis_pengamatan['Intervensi'] = True | |
| return jenis_pengamatan | |
| # Fungsi untuk memparse teks dari form LSB | |
| def parse_form_text(text): | |
| lines = text.split('\n') | |
| data = { | |
| 'nama_pelapor': '', | |
| 'posisi_jabatan': '', | |
| 'lokasi_kejadian': '', | |
| 'tanggal_waktu': '', | |
| 'uraian_pengamatan': '', | |
| 'tindakan_intervensi': '', | |
| } | |
| # Cari setiap field dalam teks | |
| current_field = None | |
| for i, line in enumerate(lines): | |
| # Deteksi field berdasarkan kata kunci | |
| if 'NAMA PELAPOR' in line: | |
| current_field = 'nama_pelapor' | |
| if i+1 < len(lines) and lines[i+1].strip(): | |
| data[current_field] = lines[i+1].strip() | |
| elif 'POSISI' in line or 'JABATAN' in line: | |
| current_field = 'posisi_jabatan' | |
| if i+1 < len(lines) and lines[i+1].strip(): | |
| data[current_field] = lines[i+1].strip() | |
| elif 'LOKASI' in line: | |
| current_field = 'lokasi_kejadian' | |
| if i+1 < len(lines) and lines[i+1].strip(): | |
| data[current_field] = lines[i+1].strip() | |
| elif 'TANGGAL' in line or 'WAKTU' in line: | |
| current_field = 'tanggal_waktu' | |
| if i+1 < len(lines) and lines[i+1].strip(): | |
| data[current_field] = lines[i+1].strip() | |
| elif 'URAIAN' in line and 'PENGAMATAN' in line: | |
| current_field = 'uraian_pengamatan' | |
| # Ambil beberapa baris untuk uraian | |
| for j in range(i+1, min(i+4, len(lines))): | |
| if lines[j].strip() and not any(keyword in lines[j] for keyword in ['INTERVENSI', 'TINDAKAN', 'SARAN', 'PELAPOR']): | |
| data[current_field] += ' ' + lines[j].strip() | |
| elif ('TINDAKAN' in line and 'INTERVENSI' in line) or 'PERBAIKAN' in line: | |
| current_field = 'tindakan_intervensi' | |
| # Ambil beberapa baris untuk tindakan | |
| for j in range(i+1, min(i+4, len(lines))): | |
| if lines[j].strip() and not any(keyword in lines[j] for keyword in ['PELAPOR', 'HSE', 'PENERIMA']): | |
| data[current_field] += ' ' + lines[j].strip() | |
| # Bersihkan teks | |
| for key in data: | |
| data[key] = data[key].strip() | |
| return data | |
| # Fungsi utama untuk OCR | |
| def perform_ocr(image): | |
| try: | |
| # Jika image adalah PIL.Image, konversi ke numpy array | |
| if not isinstance(image, np.ndarray): | |
| image = np.array(image) | |
| # Simpan gambar ke file temporari | |
| with tempfile.NamedTemporaryFile(delete=False, suffix='.jpg') as temp: | |
| image_path = temp.name | |
| img_pil = Image.fromarray(image) | |
| img_pil.save(image_path) | |
| # Preprocess gambar untuk OCR | |
| preprocessed = preprocess_image(img_pil) | |
| cv2.imwrite(image_path + '_processed.jpg', preprocessed) | |
| # Lakukan OCR pada gambar yang telah diproses | |
| text = pytesseract.image_to_string(Image.open(image_path + '_processed.jpg'), lang='ind') | |
| # Hapus file temporari | |
| os.unlink(image_path) | |
| os.unlink(image_path + '_processed.jpg') | |
| # Lakukan juga deteksi checkbox | |
| jenis_pengamatan = detect_checkboxes(img_pil, image) | |
| # Parse teks hasil OCR menjadi data terstruktur | |
| data = parse_form_text(text) | |
| # Tambahkan hasil deteksi checkbox ke data | |
| data['jenis_pengamatan'] = [] | |
| for jenis, checked in jenis_pengamatan.items(): | |
| if checked: | |
| data['jenis_pengamatan'].append(jenis) | |
| # Gabungkan menjadi string | |
| if data['jenis_pengamatan']: | |
| data['jenis_pengamatan'] = ', '.join(data['jenis_pengamatan']) | |
| else: | |
| data['jenis_pengamatan'] = '' | |
| return data | |
| except Exception as e: | |
| print(f"Error in OCR: {e}") | |
| return { | |
| 'error': str(e), | |
| 'nama_pelapor': '', | |
| 'posisi_jabatan': '', | |
| 'lokasi_kejadian': '', | |
| 'tanggal_waktu': '', | |
| 'uraian_pengamatan': '', | |
| 'tindakan_intervensi': '', | |
| 'jenis_pengamatan': '' | |
| } | |
| # Buat interface Gradio sederhana | |
| def process_image(img): | |
| if img is None: | |
| return "No image uploaded" | |
| # Proses OCR | |
| result = perform_ocr(img) | |
| # Return hasil sebagai string untuk ditampilkan | |
| return str(result) | |
| # Buat interface yang sangat sederhana untuk API | |
| demo = gr.Interface( | |
| fn=process_image, | |
| inputs=gr.Image(type="pil"), | |
| outputs="text", | |
| title="LSB Form OCR API", | |
| description="Upload gambar formulir LSB untuk ekstraksi data otomatis", | |
| examples=[], | |
| cache_examples=False, | |
| ) | |
| # Launch dengan API enabled | |
| demo.launch(share=True) |