LSB / app.py
QMonitor Admin
Simplify Gradio interface for better API compatibility
962157e
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)