SohomToom commited on
Commit
7d1906d
·
verified ·
1 Parent(s): 8babd96

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +162 -176
app.py CHANGED
@@ -1,176 +1,162 @@
1
- # app.py
2
- import os
3
- import shutil
4
- import tempfile
5
- import cv2
6
- import numpy as np
7
- import gradio as gr
8
- from paddleocr import PaddleOCR
9
-
10
- from PIL import Image
11
-
12
- def is_valid_image(path):
13
- try:
14
- img = Image.open(path)
15
- img.verify()
16
- return True
17
- except:
18
- return False
19
-
20
-
21
- ocr = PaddleOCR(use_angle_cls=True, lang='en', det_model_dir='models/det', rec_model_dir='models/rec', cls_model_dir='models/cls')
22
-
23
- def classify_background_color(avg_color, white_thresh=230, black_thresh=50, yellow_thresh=100):
24
- r, g, b = avg_color
25
- if r >= white_thresh and g >= white_thresh and b >= white_thresh:
26
- return (255, 255, 255)
27
- if r <= black_thresh and g <= black_thresh and b <= black_thresh:
28
- return (0, 0, 0)
29
- if r >= yellow_thresh and g >= yellow_thresh and b < yellow_thresh:
30
- return (255, 255, 0)
31
- return None
32
-
33
- def sample_border_color(image, box, padding=2):
34
- h, w = image.shape[:2]
35
- x_min, y_min, x_max, y_max = box
36
- x_min = max(0, x_min - padding)
37
- x_max = min(w-1, x_max + padding)
38
- y_min = max(0, y_min - padding)
39
- y_max = min(h-1, y_max + padding)
40
-
41
- top = image[y_min:y_min+padding, x_min:x_max]
42
- bottom = image[y_max-padding:y_max, x_min:x_max]
43
- left = image[y_min:y_max, x_min:x_min+padding]
44
- right = image[y_min:y_max, x_max-padding:x_max]
45
-
46
- border_pixels = np.vstack((top.reshape(-1, 3), bottom.reshape(-1, 3),
47
- left.reshape(-1, 3), right.reshape(-1, 3)))
48
- if border_pixels.size == 0:
49
- return (255, 255, 255)
50
- median_color = np.median(border_pixels, axis=0)
51
- return tuple(map(int, median_color))
52
-
53
- def detect_text_boxes(image, max_dim=1280):
54
- try:
55
- # Check if image is valid
56
- if image is None or not hasattr(image, 'shape'):
57
- print("Invalid image. Skipping...")
58
- return []
59
-
60
- # Resize large images to reduce memory load
61
- height, width = image.shape[:2]
62
- if max(height, width) > max_dim:
63
- scale = max_dim / float(max(height, width))
64
- image = cv2.resize(image, (int(width * scale), int(height * scale)))
65
-
66
- # Ensure image is in RGB
67
- if image.shape[2] == 1:
68
- image = cv2.cvtColor(image, cv2.COLOR_GRAY2RGB)
69
- elif image.shape[2] == 3:
70
- image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
71
-
72
- # Call PaddleOCR correctly
73
- results = ocr.ocr(image, cls=True)
74
-
75
- if results is None or not results[0]:
76
- print("No OCR results found or OCR returned None.")
77
- return []
78
-
79
- boxes = []
80
- for line in results[0]:
81
- box, (text, confidence) = line
82
- if text.strip():
83
- x_min = int(min(pt[0] for pt in box))
84
- x_max = int(max(pt[0] for pt in box))
85
- y_min = int(min(pt[1] for pt in box))
86
- y_max = int(max(pt[1] for pt in box))
87
- boxes.append(((x_min, y_min, x_max, y_max), text, confidence))
88
- return boxes
89
-
90
- except Exception as e:
91
- print(f"OCR failed on image: {e}")
92
- return []
93
-
94
-
95
-
96
- def remove_text_dynamic_fill(img_path, output_path):
97
- image = cv2.imread(img_path)
98
- if image is None:
99
- return
100
-
101
- if len(image.shape) == 2:
102
- image = cv2.cvtColor(image, cv2.COLOR_GRAY2RGB)
103
- elif image.shape[2] == 1:
104
- image = cv2.cvtColor(image, cv2.COLOR_GRAY2RGB)
105
- else:
106
- image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
107
-
108
- boxes = detect_text_boxes(image)
109
-
110
- for (bbox, text, confidence) in boxes:
111
- if confidence < 0.4 or not text.strip():
112
- continue
113
-
114
- x_min, y_min, x_max, y_max = bbox
115
- height = y_max - y_min
116
-
117
- if height <= 30:
118
- padding = 2
119
- elif height <= 60:
120
- padding = 4
121
- else:
122
- padding = 6
123
-
124
- x_min_p = max(0, x_min - padding)
125
- y_min_p = max(0, y_min - padding)
126
- x_max_p = min(image.shape[1]-1, x_max + padding)
127
- y_max_p = min(image.shape[0]-1, y_max + padding)
128
-
129
- sample_crop = image[y_min_p:y_max_p, x_min_p:x_max_p]
130
- avg_color = np.mean(sample_crop.reshape(-1, 3), axis=0)
131
-
132
- fill_color = classify_background_color(avg_color)
133
- if fill_color is None:
134
- fill_color = sample_border_color(image, (x_min, y_min, x_max, y_max))
135
-
136
- cv2.rectangle(image, (x_min_p, y_min_p), (x_max_p, y_max_p), fill_color, -1)
137
-
138
- image = cv2.cvtColor(image, cv2.COLOR_RGB2BGR)
139
- cv2.imwrite(output_path, image)
140
-
141
- def process_folder(input_files):
142
- temp_output = tempfile.mkdtemp()
143
- for file in input_files:
144
- filename = os.path.basename(file.name)
145
- output_path = os.path.join(temp_output, filename)
146
- remove_text_dynamic_fill(file.name, output_path)
147
-
148
- zip_path = "/tmp/cleaned_output.zip"
149
- output_dir = os.path.join(tempfile.gettempdir(), "cleaned_output")
150
- os.makedirs(output_dir, exist_ok=True)
151
- zip_folder(output_dir, zip_path)
152
- return zip_path
153
-
154
- import zipfile
155
-
156
- def zip_folder(folder_path, output_zip):
157
- with zipfile.ZipFile(output_zip, 'w', zipfile.ZIP_DEFLATED) as zipf:
158
- for root, _, files in os.walk(folder_path):
159
- for file in files:
160
- file_path = os.path.join(root, file)
161
- arcname = os.path.relpath(file_path, folder_path)
162
- zipf.write(file_path, arcname)
163
-
164
-
165
-
166
-
167
-
168
- demo = gr.Interface(
169
- fn=process_folder,
170
- inputs=gr.File(file_types=[".jpg", ".jpeg", ".png"], file_count="multiple", label="Upload Comic Images"),
171
- outputs=gr.File(label="Download Cleaned Zip"),
172
- title="Comic Text Cleaner",
173
- description="Upload comic images and get a zip of cleaned versions (text removed). Uses PaddleOCR for detection."
174
- )
175
-
176
- demo.launch()
 
1
+ # app.py
2
+ import os
3
+ import shutil
4
+ import tempfile
5
+ import cv2
6
+ import numpy as np
7
+ import gradio as gr
8
+ from paddleocr import PaddleOCR
9
+
10
+ from PIL import Image
11
+
12
+ def is_valid_image(path):
13
+ try:
14
+ img = Image.open(path)
15
+ img.verify()
16
+ return True
17
+ except:
18
+ return False
19
+
20
+
21
+ ocr = PaddleOCR(use_angle_cls=True, lang='en', det_model_dir='models/det', rec_model_dir='models/rec', cls_model_dir='models/cls')
22
+
23
+ def classify_background_color(avg_color, white_thresh=230, black_thresh=50, yellow_thresh=100):
24
+ r, g, b = avg_color
25
+ if r >= white_thresh and g >= white_thresh and b >= white_thresh:
26
+ return (255, 255, 255)
27
+ if r <= black_thresh and g <= black_thresh and b <= black_thresh:
28
+ return (0, 0, 0)
29
+ if r >= yellow_thresh and g >= yellow_thresh and b < yellow_thresh:
30
+ return (255, 255, 0)
31
+ return None
32
+
33
+ def sample_border_color(image, box, padding=2):
34
+ h, w = image.shape[:2]
35
+ x_min, y_min, x_max, y_max = box
36
+ x_min = max(0, x_min - padding)
37
+ x_max = min(w-1, x_max + padding)
38
+ y_min = max(0, y_min - padding)
39
+ y_max = min(h-1, y_max + padding)
40
+
41
+ top = image[y_min:y_min+padding, x_min:x_max]
42
+ bottom = image[y_max-padding:y_max, x_min:x_max]
43
+ left = image[y_min:y_max, x_min:x_min+padding]
44
+ right = image[y_min:y_max, x_max-padding:x_max]
45
+
46
+ border_pixels = np.vstack((top.reshape(-1, 3), bottom.reshape(-1, 3),
47
+ left.reshape(-1, 3), right.reshape(-1, 3)))
48
+ if border_pixels.size == 0:
49
+ return (255, 255, 255)
50
+ median_color = np.median(border_pixels, axis=0)
51
+ return tuple(map(int, median_color))
52
+
53
+ def detect_text_boxes(image, max_dim=1280):
54
+ try:
55
+ # Check if image is valid
56
+ if image is None or not hasattr(image, 'shape'):
57
+ print("Invalid image. Skipping...")
58
+ return []
59
+
60
+ # Resize large images to reduce memory load
61
+ height, width = image.shape[:2]
62
+ if max(height, width) > max_dim:
63
+ scale = max_dim / float(max(height, width))
64
+ image = cv2.resize(image, (int(width * scale), int(height * scale)))
65
+
66
+ # Ensure image is in RGB
67
+ if image.shape[2] == 1:
68
+ image = cv2.cvtColor(image, cv2.COLOR_GRAY2RGB)
69
+ elif image.shape[2] == 3:
70
+ image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
71
+
72
+ # Call PaddleOCR correctly
73
+ results = ocr.ocr(image, cls=True)
74
+
75
+ if results is None or not results[0]:
76
+ print("No OCR results found or OCR returned None.")
77
+ return []
78
+
79
+ boxes = []
80
+ for line in results[0]:
81
+ box, (text, confidence) = line
82
+ if text.strip():
83
+ x_min = int(min(pt[0] for pt in box))
84
+ x_max = int(max(pt[0] for pt in box))
85
+ y_min = int(min(pt[1] for pt in box))
86
+ y_max = int(max(pt[1] for pt in box))
87
+ boxes.append(((x_min, y_min, x_max, y_max), text, confidence))
88
+ return boxes
89
+
90
+ except Exception as e:
91
+ print(f"OCR failed on image: {e}")
92
+ return []
93
+
94
+
95
+
96
+ def remove_text_dynamic_fill(img_path, output_path):
97
+ image = cv2.imread(img_path)
98
+ if image is None:
99
+ return
100
+
101
+ if len(image.shape) == 2:
102
+ image = cv2.cvtColor(image, cv2.COLOR_GRAY2RGB)
103
+ elif image.shape[2] == 1:
104
+ image = cv2.cvtColor(image, cv2.COLOR_GRAY2RGB)
105
+ else:
106
+ image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
107
+
108
+ boxes = detect_text_boxes(image)
109
+
110
+ for (bbox, text, confidence) in boxes:
111
+ if confidence < 0.4 or not text.strip():
112
+ continue
113
+
114
+ x_min, y_min, x_max, y_max = bbox
115
+ height = y_max - y_min
116
+
117
+ if height <= 30:
118
+ padding = 2
119
+ elif height <= 60:
120
+ padding = 4
121
+ else:
122
+ padding = 6
123
+
124
+ x_min_p = max(0, x_min - padding)
125
+ y_min_p = max(0, y_min - padding)
126
+ x_max_p = min(image.shape[1]-1, x_max + padding)
127
+ y_max_p = min(image.shape[0]-1, y_max + padding)
128
+
129
+ sample_crop = image[y_min_p:y_max_p, x_min_p:x_max_p]
130
+ avg_color = np.mean(sample_crop.reshape(-1, 3), axis=0)
131
+
132
+ fill_color = classify_background_color(avg_color)
133
+ if fill_color is None:
134
+ fill_color = sample_border_color(image, (x_min, y_min, x_max, y_max))
135
+
136
+ cv2.rectangle(image, (x_min_p, y_min_p), (x_max_p, y_max_p), fill_color, -1)
137
+
138
+ image = cv2.cvtColor(image, cv2.COLOR_RGB2BGR)
139
+ cv2.imwrite(output_path, image)
140
+
141
+ def process_folder(input_files):
142
+ temp_output = tempfile.mkdtemp()
143
+ for file in input_files:
144
+ filename = os.path.basename(file.name)
145
+ output_path = os.path.join(temp_output, filename)
146
+ remove_text_dynamic_fill(file.name, output_path)
147
+ zip_path = shutil.make_archive(temp_output, 'zip', temp_output)
148
+ return zip_path
149
+
150
+
151
+
152
+
153
+
154
+ demo = gr.Interface(
155
+ fn=process_folder,
156
+ inputs=gr.File(file_types=[".jpg", ".jpeg", ".png"], file_count="multiple", label="Upload Comic Images"),
157
+ outputs=gr.File(label="Download Cleaned Zip"),
158
+ title="Comic Text Cleaner",
159
+ description="Upload comic images and get a zip of cleaned versions (text removed). Uses PaddleOCR for detection."
160
+ )
161
+
162
+ demo.launch()