File size: 9,560 Bytes
e67e349
f163499
948e6bf
e67e349
 
 
 
f345c9a
 
7314c74
 
 
e67e349
 
5c2662a
 
948e6bf
 
f345c9a
e67e349
 
 
 
948e6bf
f345c9a
33b332e
e67e349
f345c9a
5c2662a
948e6bf
e67e349
 
948e6bf
f345c9a
e67e349
 
f345c9a
5c2662a
e67e349
5c2662a
e67e349
5c2662a
e67e349
 
f345c9a
5c2662a
e67e349
 
 
5c2662a
e67e349
 
 
 
 
 
 
f345c9a
e67e349
 
f345c9a
5c2662a
e67e349
5c2662a
 
 
 
 
 
f345c9a
5c2662a
 
 
 
f345c9a
5c2662a
 
 
 
 
 
f345c9a
5c2662a
e67e349
7954296
8fb051f
 
5c2662a
 
bebc5d4
5c2662a
8fb051f
 
 
 
 
 
 
 
5c2662a
f345c9a
 
5c2662a
 
 
f345c9a
884a0c3
f345c9a
 
884a0c3
 
 
 
 
 
 
 
 
 
f345c9a
d779151
5c2662a
 
f345c9a
5c2662a
 
f345c9a
d779151
884a0c3
8fb051f
884a0c3
 
 
 
8fb051f
f345c9a
 
 
f163499
f345c9a
 
8fb051f
f345c9a
f163499
f345c9a
 
 
 
 
8fb051f
f163499
5c2662a
 
 
8fb051f
f345c9a
5c2662a
f345c9a
5c2662a
8fb051f
f345c9a
 
5c2662a
f345c9a
 
 
8fb051f
f345c9a
 
5c2662a
 
 
8fb051f
5c2662a
8fb051f
7954296
5c2662a
f345c9a
 
5c2662a
f345c9a
 
 
 
 
 
 
8fb051f
f345c9a
8fb051f
 
f345c9a
 
 
 
 
8fb051f
f345c9a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8fb051f
f345c9a
 
 
8fb051f
 
f345c9a
8fb051f
 
5c2662a
f345c9a
5c2662a
 
8fb051f
948e6bf
1a94588
8fb051f
948e6bf
5c2662a
8fb051f
7954296
948e6bf
c1ceff9
8fb051f
 
 
 
948e6bf
c1ceff9
 
f345c9a
8fb051f
884a0c3
8fb051f
7954296
1a94588
948e6bf
1a94588
8fb051f
948e6bf
8fb051f
948e6bf
 
d779151
8fb051f
948e6bf
8fb051f
948e6bf
 
d779151
8fb051f
948e6bf
8fb051f
948e6bf
 
d9c4acd
7314c74
 
f345c9a
7314c74
948e6bf
e67e349
884a0c3
 
 
 
 
8fb051f
 
 
 
 
 
884a0c3
 
 
8fb051f
 
884a0c3
8fb051f
884a0c3
8fb051f
 
 
 
 
 
 
5c2662a
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
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
import os
import gradio as gr
from PIL import Image
import pandas as pd
from ultralytics import YOLO
import easyocr
import numpy as np
from tqdm import tqdm
from datetime import datetime
import zipfile
import tempfile
from pathlib import Path

# Initialize variables
annotations = []
current_index = 0
output_excel = "results.xlsx"

# Directories for saving images
original_dir = "original_images"
yoloed_dir = "yoloed_images"
os.makedirs(original_dir, exist_ok=True)
os.makedirs(yoloed_dir, exist_ok=True)

# Load YOLO model
model_path = "best.pt"
if not os.path.exists(model_path):
    raise FileNotFoundError(f"YOLO model not found at {model_path}")
model = YOLO(model_path)

# Initialize EasyOCR reader
reader = easyocr.Reader(['en'])

# Global counter for cropped images
global_crop_counter = 0

# Image validation
def is_valid_image(image_path):
    try:
        Image.open(image_path)
        return True
    except IOError:
        return False

# YOLO detection
def detect_objects(image_path):
    if not is_valid_image(image_path):
        return []
    try:
        results = model.predict(source=image_path, save=False, conf=0.25)
        detections = []
        for result in results:
            for box in result.boxes.xyxy.cpu().numpy():
                x1, y1, x2, y2 = map(int, box)
                detections.append((x1, y1, x2, y2))
        return detections
    except Exception as e:
        print(f"Error: {e}")
        return []

# Image cropping
def crop_image(image_path, detections):
    global global_crop_counter
    image = Image.open(image_path)
    cropped_images = []
    for bbox in detections:
        x1, y1, x2, y2 = bbox
        cropped_image = image.crop((x1, y1, x2, y2))
        global_crop_counter += 1
        cropped_path = os.path.join(yoloed_dir, f"crop_{global_crop_counter}.jpg")
        cropped_image.save(cropped_path)
        cropped_images.append(cropped_image)
    return cropped_images

# OCR processing
def perform_ocr(image):
    image_np = np.array(image)
    result = reader.readtext(image_np, detail=0)
    numbers = ''.join(filter(str.isdigit, ''.join(result)))
    return numbers.strip()

# GUI update
def update_gui():
    global current_index
    if not annotations:
        return "No images processed.", "", "", "0/0"

    annotation = annotations[current_index]
    original_image = annotation["original_image"]
    thumbnail_size = (600, 600)
    original_thumbnail = original_image.copy().resize(thumbnail_size)

    progress_text = f"Image {current_index + 1}/{len(annotations)}"
    return (
        original_thumbnail,
        annotation.get("room_number", ""),
        annotation.get("meter_value", ""),
        progress_text
    )

# Navigation functions
def save_current_annotation(room_number, meter_value):
    if 0 <= current_index < len(annotations):
        annotations[current_index]["room_number"] = room_number
        annotations[current_index]["meter_value"] = meter_value

def key_handler(key, room_number, meter_value):
    global current_index
    save_current_annotation(room_number, meter_value)

    if key == "-" or key == "[":
        if current_index > 0:
            current_index -= 1
    elif key == "+" or key == "=" or key == "]":
        if current_index < len(annotations) - 1:
            current_index += 1
    else:
        return [gr.update()] * 4  # No change if unsupported key pressed

    return update_gui()

def prev_image(room_number, meter_value):
    global current_index
    save_current_annotation(room_number, meter_value)
    if current_index > 0:
        current_index -= 1
    return update_gui()

def next_image(room_number, meter_value):
    global current_index
    save_current_annotation(room_number, meter_value)
    if current_index < len(annotations) - 1:
        current_index += 1
    return update_gui()

# Export functionality
def export_to_excel(room_number, meter_value):
    global current_index, annotations
    try:
        # Save current edits
        save_current_annotation(room_number, meter_value)

        # Prepare data for Excel
        data = []
        for annotation in annotations:
            rn = annotation.get("room_number", "").strip()
            mv = annotation.get("meter_value", "").strip()
            if rn or mv:
                data.append({"Room Number": rn, "Meter Value": mv})

        df = pd.DataFrame(data)
        temp_dir = tempfile.mkdtemp()
        output_excel = Path(temp_dir) / "results.xlsx"
        df.to_excel(output_excel, index=False)

        # Prepare images for ZIP
        today_date = datetime.now().strftime("%Y-%m-%d")
        images_dir = Path(temp_dir) / f"electricity_meter_images_{today_date}"
        images_dir.mkdir(exist_ok=True)

        for annotation in annotations:
            rn = annotation.get("room_number", "").strip()
            original_image = annotation.get("original_image")
            if rn and original_image:
                new_path = images_dir / f"{rn}.jpg"
                original_image.save(new_path)

        # Create ZIP file
        zip_path = Path(temp_dir) / f"meter_images_{today_date}.zip"
        with zipfile.ZipFile(zip_path, 'w') as zipf:
            for img_file in images_dir.glob("*.jpg"):
                zipf.write(img_file, arcname=img_file.name)

        return str(output_excel), str(zip_path)

    except Exception as e:
        error_dir = tempfile.mkdtemp()
        error_file = Path(error_dir) / "error.txt"
        with open(error_file, 'w') as f:
            f.write(f"Export failed: {str(e)}")
        return str(error_file), str(error_file)

# Image processing
def process_images(uploaded_files):
    global annotations, current_index
    annotations.clear()
    current_index = 0

    if not uploaded_files:
        return None, "", "", "0/0"

    try:
        for temp_file in tqdm(uploaded_files, desc="Processing"):
            image_path = temp_file.name
            original = Image.open(image_path)
            detections = detect_objects(image_path)

            if detections:
                cropped_images = crop_image(image_path, detections)
                for cropped in cropped_images:
                    annotations.append({
                        "image_path": image_path,
                        "original_image": original.copy(),
                        "cropped_image": cropped,
                        "meter_value": perform_ocr(cropped),
                        "room_number": ""
                    })
            else:
                annotations.append({
                    "image_path": image_path,
                    "original_image": original.copy(),
                    "cropped_image": None,
                    "meter_value": "",
                    "room_number": ""
                })

        if annotations:
            current_index = 0
            return update_gui()
        return None, "", "", "0/0"

    except Exception as e:
        return str(e), "", "", "0/0"


# Gradio Interface
with gr.Blocks() as demo:
    gr.Markdown("## Electricity Meter Reader")

    with gr.Row():
        image_input = gr.File(label="Upload Images", file_types=["image"], file_count="multiple")

    with gr.Row():
        original_image_output = gr.Image(label="Original Image")

    with gr.Row():
        room_number_output = gr.Textbox(label="Room Number", interactive=True)
        meter_value_output = gr.Textbox(label="Meter Value", interactive=True)

    with gr.Row():
        progress_label = gr.Textbox(label="Progress", value="0/0", interactive=False)

    with gr.Row():
        prev_button = gr.Button("Previous")
        next_button = gr.Button("Next")
        export_button = gr.Button("Export to Excel & ZIP")

    key_input = gr.Textbox(visible=False, label="Key Handler")

    # Event handlers
    image_input.change(
        fn=process_images,
        inputs=[image_input],
        outputs=[original_image_output, room_number_output, meter_value_output, progress_label]
    )

    prev_button.click(
        fn=prev_image,
        inputs=[room_number_output, meter_value_output],
        outputs=[original_image_output, room_number_output, meter_value_output, progress_label]
    )

    next_button.click(
        fn=next_image,
        inputs=[room_number_output, meter_value_output],
        outputs=[original_image_output, room_number_output, meter_value_output, progress_label]
    )

    export_button.click(
        fn=export_to_excel,
        inputs=[room_number_output, meter_value_output],
        outputs=[
            gr.File(label="Download Excel File"),
            gr.File(label="Download Images ZIP")
        ]
    )

    key_input.submit(
        fn=key_handler,
        inputs=[key_input, room_number_output, meter_value_output],
        outputs=[original_image_output, room_number_output, meter_value_output, progress_label]
    )

    # JavaScript to capture keyboard events
    demo.load(
        js="""
        () => {
            document.addEventListener('keydown', function(e) {
                const allowedKeys = ['-', '=', '[', ']'];
                if (allowedKeys.includes(e.key)) {
                    e.preventDefault();
                    const hiddenInput = document.querySelector('#key_input input');
                    if (hiddenInput) {
                        hiddenInput.value = e.key;
                        hiddenInput.dispatchEvent(new Event('input'));
                        hiddenInput.dispatchEvent(new Event('change'));
                    }
                }
            });
        }
        """
    )

demo.launch()