Rzx008 commited on
Commit
8e5b8a6
verified
1 Parent(s): ce5f5e3

Create android.py

Browse files
Files changed (1) hide show
  1. android.py +237 -0
android.py ADDED
@@ -0,0 +1,237 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import cv2
2
+ import pytesseract
3
+ from PIL import Image, ImageDraw, ImageFont
4
+ import numpy as np
5
+ import argparse
6
+ import io
7
+ import base64
8
+
9
+ class AndroidEditor:
10
+ def __init__(self, font_path="Roboto-Regular.ttf"):
11
+ self.font_path = font_path
12
+
13
+ @staticmethod
14
+ def find_text_position(text_list, target_text, start_idx=0):
15
+ """Mencari posisi teks target dalam list teks"""
16
+ for i in range(start_idx, len(text_list)):
17
+ if target_text in text_list[i]:
18
+ return i
19
+ return None
20
+
21
+ @staticmethod
22
+ def get_position_data(extracted_data, idx):
23
+ """Mendapatkan data posisi dari indeks tertentu"""
24
+ if idx is None or idx >= len(extracted_data['left']):
25
+ return None
26
+ return {
27
+ "left": extracted_data['left'][idx],
28
+ "top": extracted_data['top'][idx],
29
+ "width": extracted_data['width'][idx],
30
+ "height": extracted_data['height'][idx],
31
+ }
32
+
33
+ @staticmethod
34
+ def is_dark_mode(bg_color):
35
+ """Mendeteksi apakah background menggunakan dark mode berdasarkan kecerahan warna"""
36
+ brightness = (bg_color[0] * 299 + bg_color[1] * 587 + bg_color[2] * 114) / 1000
37
+ return brightness < 128
38
+
39
+ def process_image(self, image_path, anggota):
40
+ image = cv2.imread(image_path)
41
+ if image is None:
42
+ return
43
+ result = self._process_core(image, anggota, show_preview=True)
44
+ return result
45
+
46
+ def process_image_bytes(self, image_bytes, anggota):
47
+ image_stream = io.BytesIO(image_bytes)
48
+ pil_image = Image.open(image_stream).convert('RGB')
49
+ image = cv2.cvtColor(np.array(pil_image), cv2.COLOR_RGB2BGR)
50
+ result, theme = self._process_core(image, anggota, show_preview=False, return_theme=True)
51
+ if result is not None:
52
+ pil_result = Image.fromarray(cv2.cvtColor(result, cv2.COLOR_BGR2RGB))
53
+ output_io = io.BytesIO()
54
+ pil_result.save(output_io, format='PNG')
55
+ img_b64 = base64.b64encode(output_io.getvalue()).decode('utf-8')
56
+ return img_b64, theme
57
+ return None, None
58
+
59
+ def _process_core(self, image, anggota, show_preview=False, return_theme=False):
60
+ # Ekstrak teks dari gambar
61
+ extracted_data = pytesseract.image_to_data(image, output_type=pytesseract.Output.DICT)
62
+ text_list = extracted_data['text']
63
+
64
+ # Inisialisasi variabel posisi
65
+ group_position = None
66
+ split_position = None
67
+ member_position = None
68
+ member_count_position = None
69
+ second_member_position = None
70
+ second_member_count_position = None
71
+ lang = ''
72
+
73
+ group_idx = self.find_text_position(text_list, "Grup")
74
+ if group_idx is None:
75
+ group_idx = self.find_text_position(text_list, "Group")
76
+
77
+ if group_idx is not None:
78
+ lang = 'id' if "Grup" in text_list[group_idx] else 'en'
79
+ group_position = self.get_position_data(extracted_data, group_idx)
80
+
81
+ split_idx = self.find_text_position(text_list, "路", group_idx)
82
+ if split_idx is None:
83
+ split_idx = self.find_text_position(text_list, "-", group_idx)
84
+ split_position = self.get_position_data(extracted_data, split_idx)
85
+
86
+ member_idx = self.find_text_position(text_list, "anggota", group_idx)
87
+ if member_idx is None:
88
+ member_idx = self.find_text_position(text_list, "member", group_idx)
89
+ member_position = self.get_position_data(extracted_data, member_idx)
90
+
91
+ for i in range(group_idx, min(group_idx + 5, len(text_list))):
92
+ if text_list[i].isdigit():
93
+ member_count_position = self.get_position_data(extracted_data, i)
94
+ break
95
+
96
+ second_member_idx = self.find_text_position(text_list, "Anggota", group_idx + 4)
97
+ if second_member_idx is not None:
98
+ second_member_position = self.get_position_data(extracted_data, second_member_idx)
99
+ for i in range(second_member_idx - 3, second_member_idx):
100
+ if i >= 0 and text_list[i].isdigit():
101
+ second_member_count_position = self.get_position_data(extracted_data, i)
102
+ break
103
+ else:
104
+ return None
105
+
106
+ if member_position is None:
107
+ return None
108
+
109
+ x = member_position['left'] + member_position['width'] + 100
110
+ y = member_position['top'] + member_position['height'] - 100
111
+ bg_color = image[y, x]
112
+ rgb = (int(bg_color[0]), int(bg_color[1]), int(bg_color[2]))
113
+
114
+ is_dark = self.is_dark_mode(bg_color)
115
+ theme = 'Dark Mode' if is_dark else 'Light Mode'
116
+ text_color = (147,151,154,255) if is_dark else (90, 94, 95, 255)
117
+
118
+ for position in [group_position, split_position, member_position, member_count_position,
119
+ second_member_position, second_member_count_position]:
120
+ if position:
121
+ margin_horizontal = 100
122
+ cv2.rectangle(
123
+ image,
124
+ (position['left'] - margin_horizontal, position['top']),
125
+ (position['left'] + position['width'] + margin_horizontal, position['top'] + position['height']),
126
+ rgb,
127
+ -1,
128
+ )
129
+
130
+ if member_count_position:
131
+ updated_member_count = {
132
+ 'id': f"Grup 路 {anggota} anggota",
133
+ 'en': f"Group 路 {anggota} members"
134
+ }.get(lang)
135
+
136
+ original_height = member_count_position['height']
137
+ original_width = member_count_position['width']
138
+
139
+ font_size = int(original_height * 2.0)
140
+ font = ImageFont.truetype(self.font_path, font_size)
141
+
142
+ image_pil = Image.fromarray(cv2.cvtColor(image, cv2.COLOR_BGR2RGB))
143
+ draw = ImageDraw.Draw(image_pil)
144
+
145
+ min_size = int(original_height * 1.5)
146
+ max_size = int(original_height * 2.5)
147
+ max_iterations = 10
148
+ iteration = 0
149
+
150
+ while min_size <= max_size and iteration < max_iterations:
151
+ font = ImageFont.truetype(self.font_path, font_size)
152
+ text_bbox = draw.textbbox((0, 0), updated_member_count, font=font)
153
+ text_height = text_bbox[3] - text_bbox[1]
154
+ text_width = text_bbox[2] - text_bbox[0]
155
+
156
+ if abs(text_height - original_height) <= 2 and text_width <= original_width * 1.5:
157
+ break
158
+
159
+ if text_height > original_height or text_width > original_width * 1.5:
160
+ font_size = int(font_size * 0.95)
161
+ else:
162
+ font_size = int(font_size * 1.05)
163
+
164
+ font_size = max(min_size, min(max_size, font_size))
165
+ iteration += 1
166
+
167
+ text_width = text_bbox[2] - text_bbox[0]
168
+ image_width = image.shape[1]
169
+ text_x = (image_width - text_width) // 2
170
+ text_y = member_count_position['top'] - 2
171
+
172
+ draw.text((text_x, text_y), updated_member_count, font=font, fill=text_color)
173
+ image = cv2.cvtColor(np.array(image_pil), cv2.COLOR_RGB2BGR)
174
+
175
+ if second_member_count_position:
176
+ updated_second_member_count = {
177
+ 'id': f"{anggota} Anggota",
178
+ 'en': f"{anggota} Members"
179
+ }.get(lang)
180
+
181
+ original_height = second_member_count_position['height']
182
+ original_width = second_member_count_position['width']
183
+
184
+ font_size = int(original_height * 2.0)
185
+ font = ImageFont.truetype(self.font_path, font_size)
186
+
187
+ image_pil = Image.fromarray(cv2.cvtColor(image, cv2.COLOR_BGR2RGB))
188
+ draw = ImageDraw.Draw(image_pil)
189
+
190
+ min_size = int(original_height * 1.5)
191
+ max_size = int(original_height * 2.5)
192
+ max_iterations = 10
193
+ iteration = 0
194
+
195
+ while min_size <= max_size and iteration < max_iterations:
196
+ font = ImageFont.truetype(self.font_path, font_size)
197
+ text_bbox = draw.textbbox((0, 0), updated_second_member_count, font=font)
198
+ text_height = text_bbox[3] - text_bbox[1]
199
+ text_width = text_bbox[2] - text_bbox[0]
200
+
201
+ if abs(text_height - original_height) <= 2 and text_width <= original_width * 1.5:
202
+ break
203
+
204
+ if text_height > original_height or text_width > original_width * 1.5:
205
+ font_size = int(font_size * 0.95)
206
+ else:
207
+ font_size = int(font_size * 1.05)
208
+
209
+ font_size = max(min_size, min(max_size, font_size))
210
+ iteration += 1
211
+
212
+ text_width = text_bbox[2] - text_bbox[0]
213
+ text_x = second_member_count_position['left']
214
+ text_y = second_member_count_position['top'] - 2
215
+
216
+ draw.text((text_x, text_y), updated_second_member_count, font=font, fill=text_color)
217
+ image = cv2.cvtColor(np.array(image_pil), cv2.COLOR_RGB2BGR)
218
+
219
+ if show_preview:
220
+ cv2.imshow('Preview (Tekan q untuk keluar)', image)
221
+ while True:
222
+ key = cv2.waitKey(1) & 0xFF
223
+ if key == ord('q'):
224
+ break
225
+ cv2.destroyAllWindows()
226
+ if return_theme:
227
+ return image, theme
228
+ return image
229
+
230
+ if __name__ == '__main__':
231
+ parser = argparse.ArgumentParser(description='Proses gambar grup')
232
+ parser.add_argument('image_path', help='Path ke file gambar')
233
+ parser.add_argument('anggota', help='Jumlah anggota')
234
+
235
+ args = parser.parse_args()
236
+ editor = AndroidEditor()
237
+ editor.process_image(args.image_path, args.anggota)