editctc005 / android.py
Rzx008's picture
Update android.py
cf57dc0 verified
import cv2
import pytesseract
from PIL import Image, ImageDraw, ImageFont
import numpy as np
import argparse
import io
import base64
class AndroidEditor:
def __init__(self, font_path="Roboto-Regular.ttf"):
self.font_path = font_path
@staticmethod
def find_text_position(text_list, target_text, start_idx=0):
"""Mencari posisi teks target dalam list teks"""
for i in range(start_idx, len(text_list)):
if target_text in text_list[i]:
return i
return None
@staticmethod
def get_position_data(extracted_data, idx):
"""Mendapatkan data posisi dari indeks tertentu"""
if idx is None or idx >= len(extracted_data['left']):
return None
return {
"left": extracted_data['left'][idx],
"top": extracted_data['top'][idx],
"width": extracted_data['width'][idx],
"height": extracted_data['height'][idx],
}
@staticmethod
def is_dark_mode(bg_color):
"""Mendeteksi apakah background menggunakan dark mode berdasarkan kecerahan warna"""
r, g, b = float(bg_color[0]), float(bg_color[1]), float(bg_color[2])
brightness = (r * 299 + g * 587 + b * 114) / 1000
return brightness < 128
def process_image(self, image_path, anggota):
image = cv2.imread(image_path)
if image is None:
return
result = self._process_core(image, anggota, show_preview=True)
return result
def process_image_bytes(self, image_bytes, anggota):
image_stream = io.BytesIO(image_bytes)
pil_image = Image.open(image_stream).convert('RGB')
image = cv2.cvtColor(np.array(pil_image), cv2.COLOR_RGB2BGR)
result, theme = self._process_core(image, anggota, show_preview=False, return_theme=True)
if result is not None:
pil_result = Image.fromarray(cv2.cvtColor(result, cv2.COLOR_BGR2RGB))
output_io = io.BytesIO()
pil_result.save(output_io, format='PNG')
img_b64 = base64.b64encode(output_io.getvalue()).decode('utf-8')
return img_b64, theme
return None, None
def _process_core(self, image, anggota, show_preview=False, return_theme=False):
# Ekstrak teks dari gambar
extracted_data = pytesseract.image_to_data(image, output_type=pytesseract.Output.DICT)
text_list = extracted_data['text']
# Inisialisasi variabel posisi
group_position = None
split_position = None
member_position = None
member_count_position = None
second_member_position = None
second_member_count_position = None
lang = ''
group_idx = self.find_text_position(text_list, "Grup")
if group_idx is None:
group_idx = self.find_text_position(text_list, "Group")
if group_idx is not None:
lang = 'id' if "Grup" in text_list[group_idx] else 'en'
group_position = self.get_position_data(extracted_data, group_idx)
split_idx = self.find_text_position(text_list, "路", group_idx)
if split_idx is None:
for i in range(group_idx, min(group_idx + 4, len(text_list))):
if "-" in text_list[i]:
split_idx = i
break
split_position = self.get_position_data(extracted_data, split_idx)
member_idx = self.find_text_position(text_list, "anggota", group_idx)
if member_idx is None:
member_idx = self.find_text_position(text_list, "member", group_idx)
member_position = self.get_position_data(extracted_data, member_idx)
for i in range(group_idx, min(group_idx + 5, len(text_list))):
if text_list[i].isdigit():
member_count_position = self.get_position_data(extracted_data, i)
break
second_member_idx = self.find_text_position(text_list, "Anggota", group_idx + 4)
if second_member_idx is not None:
second_member_position = self.get_position_data(extracted_data, second_member_idx)
for i in range(second_member_idx - 3, second_member_idx):
if i >= 0 and text_list[i].isdigit():
second_member_count_position = self.get_position_data(extracted_data, i)
break
else:
return None
if member_position is None:
return None
x = member_position['left'] + member_position['width'] + 100
y = member_position['top'] + member_position['height'] - 100
bg_color = image[y, x]
rgb = (int(bg_color[0]), int(bg_color[1]), int(bg_color[2]))
is_dark = self.is_dark_mode(bg_color)
theme = 'Dark Mode' if is_dark else 'Light Mode'
text_color = (147,151,154,255) if is_dark else (90, 94, 95, 255)
for position in [group_position, split_position, member_position, member_count_position,
second_member_position, second_member_count_position]:
if position:
margin_horizontal = 10
cv2.rectangle(
image,
(position['left'] - margin_horizontal, position['top']),
(position['left'] + position['width'] + margin_horizontal, position['top'] + position['height']),
rgb,
-1,
)
if member_count_position:
updated_member_count = {
'id': f"Grup 路 {anggota} anggota",
'en': f"Group 路 {anggota} members"
}.get(lang)
original_height = member_count_position['height']
original_width = member_count_position['width']
font_size = int(original_height * 2.0)
font = ImageFont.truetype(self.font_path, font_size)
image_pil = Image.fromarray(cv2.cvtColor(image, cv2.COLOR_BGR2RGB))
draw = ImageDraw.Draw(image_pil)
min_size = int(original_height * 1.5)
max_size = int(original_height * 2.5)
max_iterations = 10
iteration = 0
while min_size <= max_size and iteration < max_iterations:
font = ImageFont.truetype(self.font_path, font_size)
text_bbox = draw.textbbox((0, 0), updated_member_count, font=font)
text_height = text_bbox[3] - text_bbox[1]
text_width = text_bbox[2] - text_bbox[0]
if abs(text_height - original_height) <= 2 and text_width <= original_width * 1.5:
break
if text_height > original_height or text_width > original_width * 1.5:
font_size = int(font_size * 0.95)
else:
font_size = int(font_size * 1.05)
font_size = max(min_size, min(max_size, font_size))
iteration += 1
text_width = text_bbox[2] - text_bbox[0]
image_width = image.shape[1]
text_x = (image_width - text_width) // 2
text_y = member_count_position['top'] - 2
draw.text((text_x, text_y), updated_member_count, font=font, fill=text_color)
image = cv2.cvtColor(np.array(image_pil), cv2.COLOR_RGB2BGR)
if second_member_count_position:
updated_second_member_count = {
'id': f"{anggota} Anggota",
'en': f"{anggota} Members"
}.get(lang)
original_height = second_member_count_position['height']
original_width = second_member_count_position['width']
font_size = int(original_height * 2.0)
font = ImageFont.truetype(self.font_path, font_size)
image_pil = Image.fromarray(cv2.cvtColor(image, cv2.COLOR_BGR2RGB))
draw = ImageDraw.Draw(image_pil)
min_size = int(original_height * 1.5)
max_size = int(original_height * 2.5)
max_iterations = 10
iteration = 0
while min_size <= max_size and iteration < max_iterations:
font = ImageFont.truetype(self.font_path, font_size)
text_bbox = draw.textbbox((0, 0), updated_second_member_count, font=font)
text_height = text_bbox[3] - text_bbox[1]
text_width = text_bbox[2] - text_bbox[0]
if abs(text_height - original_height) <= 2 and text_width <= original_width * 1.5:
break
if text_height > original_height or text_width > original_width * 1.5:
font_size = int(font_size * 0.95)
else:
font_size = int(font_size * 1.05)
font_size = max(min_size, min(max_size, font_size))
iteration += 1
text_width = text_bbox[2] - text_bbox[0]
text_x = second_member_count_position['left']
text_y = second_member_count_position['top'] - 2
draw.text((text_x, text_y), updated_second_member_count, font=font, fill=text_color)
image = cv2.cvtColor(np.array(image_pil), cv2.COLOR_RGB2BGR)
cv2.imwrite('output.png', image)
if show_preview:
cv2.imshow('Preview (Tekan q untuk keluar)', image)
while True:
key = cv2.waitKey(1) & 0xFF
if key == ord('q'):
break
cv2.destroyAllWindows()
if return_theme:
return image, theme
return image
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='Proses gambar grup')
parser.add_argument('image_path', help='Path ke file gambar')
parser.add_argument('anggota', help='Jumlah anggota')
args = parser.parse_args()
editor = AndroidEditor()
editor.process_image(args.image_path, args.anggota)