File size: 7,548 Bytes
086b042
d786fb0
962157e
d786fb0
 
 
 
 
086b042
d11fb0e
 
 
 
d786fb0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
086b042
d786fb0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
962157e
 
 
 
 
 
 
 
 
 
 
 
d786fb0
962157e
 
 
 
 
 
d786fb0
962157e
 
 
d786fb0
962157e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d786fb0
962157e
 
723c64a
962157e
 
 
 
 
 
 
 
 
 
 
 
 
d786fb0
962157e
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
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)