[Python] Why is the translated image showing weird characters?

Discussão em 'Python' iniciado por Stack, Julho 7, 2025.

  1. Stack

    Stack Membro Participativo

    Alibi is a full-stack offline web app that helps immigrants translate legal/medical documents and ask questions in their native language. It runs fully locally — no cloud APIs, no user accounts, no internet connection required.

    Image Translator: Uses pytesseract to extract English text from images, translates it using libretranslate, then rebuilds the image with translated text. Tech stack: Frontend: HTML/CSS/JS or React Backend: Flask OCR: pytesseract Translation: libretranslate (self-hosted, using docker volume with downloaded language packs/models);

    This is the image_utils.py methods:

    from PIL import Image, ImageDraw, ImageFont
    import pytesseract
    import os
    from document_translator import translator
    from collections import defaultdict

    # Set Tesseract path (adjust if needed)
    pytesseract.pytesseract.tesseract_cmd = r"C:\Program Files\Tesseract-OCR\tesseract.exe"
    def image_to_string(image_path):
    text = pytesseract.image_to_string(Image.open(image_path))
    print("[OCR OUTPUT]", text)
    return text

    # Extracts readable English text from an image
    def font_algorithm(lang_code: str, font_base_dir="C:/Fonts/Noto", default_font="arial.ttf") -> str:
    """
    Returns the best available font path for a given language code using internal font map.
    Falls back to a default system font if not found.
    """
    LANGUAGE_FONT_MAP = {
    'en': 'NotoSans-Regular.ttf',
    'aa': 'NotoSans-Regular.ttf',
    'ab': 'NotoSans-Regular.ttf',
    'ae': 'NotoSans-Regular.ttf',
    'af': 'NotoSans-Regular.ttf',
    'ak': 'NotoSans-Regular.ttf',
    'am': 'NotoSansEthiopic-Regular.ttf',
    'an': 'NotoSans-Regular.ttf',
    'ar': 'NotoNaskhArabic-Regular.ttf',
    'as': 'NotoSansBengali-Regular.ttf',
    'av': 'NotoSans-Regular.ttf',
    'ay': 'NotoSans-Regular.ttf',
    'az': 'NotoSans-Regular.ttf',
    'ba': 'NotoSans-Regular.ttf',
    'be': 'NotoSans-Regular.ttf',
    'bg': 'NotoSans-Regular.ttf',
    'bh': 'NotoSansDevanagari-Regular.ttf',
    'bi': 'NotoSans-Regular.ttf',
    'bm': 'NotoSans-Regular.ttf',
    'bn': 'NotoSansBengali-Regular.ttf',
    'bo': 'NotoSansTibetan-Regular.ttf',
    'br': 'NotoSans-Regular.ttf',
    'bs': 'NotoSans-Regular.ttf',
    'ca': 'NotoSans-Regular.ttf',
    'ce': 'NotoSans-Regular.ttf',
    'ch': 'NotoSans-Regular.ttf',
    'co': 'NotoSans-Regular.ttf',
    'cr': 'NotoSansCanadianAboriginal-Regular.ttf',
    'cs': 'NotoSans-Regular.ttf',
    'cu': 'NotoSans-Regular.ttf',
    'cv': 'NotoSans-Regular.ttf',
    'cy': 'NotoSans-Regular.ttf',
    'da': 'NotoSans-Regular.ttf',
    'de': 'NotoSans-Regular.ttf',
    'dv': 'NotoSansThaana-Regular.ttf',
    'dz': 'NotoSansTibetan-Regular.ttf',
    'ee': 'NotoSans-Regular.ttf',
    'el': 'NotoSansGreek-Regular.ttf',
    'eo': 'NotoSans-Regular.ttf',
    'es': 'NotoSans-Regular.ttf',
    'et': 'NotoSans-Regular.ttf',
    'eu': 'NotoSans-Regular.ttf',
    'fa': 'NotoNaskhArabic-Regular.ttf',
    'ff': 'NotoSans-Regular.ttf',
    'fi': 'NotoSans-Regular.ttf',
    'fj': 'NotoSans-Regular.ttf',
    'fo': 'NotoSans-Regular.ttf',
    'fr': 'NotoSans-Regular.ttf',
    'fy': 'NotoSans-Regular.ttf',
    'ga': 'NotoSans-Regular.ttf',
    'gd': 'NotoSans-Regular.ttf',
    'gl': 'NotoSans-Regular.ttf',
    'gn': 'NotoSans-Regular.ttf',
    'gu': 'NotoSansGujarati-Regular.ttf',
    'gv': 'NotoSans-Regular.ttf',
    'ha': 'NotoSans-Regular.ttf',
    'he': 'NotoSansHebrew-Regular.ttf',
    'hi': 'NotoSansDevanagari-Regular.ttf',
    'ho': 'NotoSans-Regular.ttf',
    'hr': 'NotoSans-Regular.ttf',
    'ht': 'NotoSans-Regular.ttf',
    'hu': 'NotoSans-Regular.ttf',
    'hy': 'NotoSansArmenian-Regular.ttf',
    'hz': 'NotoSans-Regular.ttf',
    'ia': 'NotoSans-Regular.ttf',
    'id': 'NotoSans-Regular.ttf',
    'ie': 'NotoSans-Regular.ttf',
    'ig': 'NotoSans-Regular.ttf',
    'ii': 'NotoSansYi-Regular.ttf',
    'ik': 'NotoSans-Regular.ttf',
    'io': 'NotoSans-Regular.ttf',
    'is': 'NotoSans-Regular.ttf',
    'it': 'NotoSans-Regular.ttf',
    'iu': 'NotoSansCanadianAboriginal-Regular.ttf',
    'ja': 'NotoSansJP-Regular.otf',
    'jv': 'NotoSans-Regular.ttf',
    'ka': 'NotoSansGeorgian-Regular.ttf',
    'kg': 'NotoSans-Regular.ttf',
    'ki': 'NotoSans-Regular.ttf',
    'kj': 'NotoSans-Regular.ttf',
    'kk': 'NotoSans-Regular.ttf',
    'kl': 'NotoSans-Regular.ttf',
    'km': 'NotoSansKhmer-Regular.ttf',
    'kn': 'NotoSansKannada-Regular.ttf',
    'ko': 'NotoSansKR-Regular.otf',
    'kr': 'NotoSans-Regular.ttf',
    'ks': 'NotoSans-Regular.ttf',
    'ku': 'NotoSans-Regular.ttf',
    'kv': 'NotoSans-Regular.ttf',
    'kw': 'NotoSans-Regular.ttf',
    'ky': 'NotoSans-Regular.ttf',
    'la': 'NotoSans-Regular.ttf',
    'lb': 'NotoSans-Regular.ttf',
    'lg': 'NotoSans-Regular.ttf',
    'li': 'NotoSans-Regular.ttf',
    'ln': 'NotoSans-Regular.ttf',
    'lo': 'NotoSansLao-Regular.ttf',
    'lt': 'NotoSans-Regular.ttf',
    'lu': 'NotoSans-Regular.ttf',
    'lv': 'NotoSans-Regular.ttf',
    'mg': 'NotoSans-Regular.ttf',
    'mh': 'NotoSans-Regular.ttf',
    'mi': 'NotoSans-Regular.ttf',
    'mk': 'NotoSans-Regular.ttf',
    'ml': 'NotoSansMalayalam-Regular.ttf',
    'mn': 'NotoSansMongolian-Regular.ttf',
    'mr': 'NotoSansDevanagari-Regular.ttf',
    'ms': 'NotoSans-Regular.ttf',
    'mt': 'NotoSans-Regular.ttf',
    'my': 'NotoSansMyanmar-Regular.ttf',
    'na': 'NotoSans-Regular.ttf',
    'nb': 'NotoSans-Regular.ttf',
    'nd': 'NotoSans-Regular.ttf',
    'ne': 'NotoSansDevanagari-Regular.ttf',
    'ng': 'NotoSans-Regular.ttf',
    'nl': 'NotoSans-Regular.ttf',
    'nn': 'NotoSans-Regular.ttf',
    'no': 'NotoSans-Regular.ttf',
    'nr': 'NotoSans-Regular.ttf',
    'nv': 'NotoSans-Regular.ttf',
    'ny': 'NotoSans-Regular.ttf',
    'oc': 'NotoSans-Regular.ttf',
    'oj': 'NotoSansCanadianAboriginal-Regular.ttf',
    'om': 'NotoSans-Regular.ttf',
    'or': 'NotoSansOriya-Regular.ttf',
    'os': 'NotoSans-Regular.ttf',
    'pa': 'NotoSansGurmukhi-Regular.ttf',
    'pi': 'NotoSansDevanagari-Regular.ttf',
    'pl': 'NotoSans-Regular.ttf',
    'ps': 'NotoNaskhArabic-Regular.ttf',
    'pt': 'NotoSans-Regular.ttf',
    'qu': 'NotoSans-Regular.ttf',
    'rm': 'NotoSans-Regular.ttf',
    'rn': 'NotoSans-Regular.ttf',
    'ro': 'NotoSans-Regular.ttf',
    'ru': 'NotoSans-Regular.ttf',
    'rw': 'NotoSans-Regular.ttf',
    'sa': 'NotoSansDevanagari-Regular.ttf',
    'sc': 'NotoSans-Regular.ttf',
    'sd': 'NotoSansDevanagari-Regular.ttf',
    'se': 'NotoSans-Regular.ttf',
    'sg': 'NotoSans-Regular.ttf',
    'si': 'NotoSansSinhala-Regular.ttf',
    'sk': 'NotoSans-Regular.ttf',
    'sl': 'NotoSans-Regular.ttf',
    'sm': 'NotoSans-Regular.ttf',
    'sn': 'NotoSans-Regular.ttf',
    'so': 'NotoSans-Regular.ttf',
    'sq': 'NotoSans-Regular.ttf',
    'sr': 'NotoSans-Regular.ttf',
    'ss': 'NotoSans-Regular.ttf',
    'st': 'NotoSans-Regular.ttf',
    'su': 'NotoSans-Regular.ttf',
    'sv': 'NotoSans-Regular.ttf',
    'sw': 'NotoSans-Regular.ttf',
    'ta': 'NotoSansTamil-Regular.ttf',
    'te': 'NotoSansTelugu-Regular.ttf',
    'tg': 'NotoSans-Regular.ttf',
    'th': 'NotoSansThai-Regular.ttf',
    'ti': 'NotoSansEthiopic-Regular.ttf',
    'tk': 'NotoSans-Regular.ttf',
    'tl': 'NotoSans-Regular.ttf',
    'tn': 'NotoSans-Regular.ttf',
    'to': 'NotoSans-Regular.ttf',
    'tr': 'NotoSans-Regular.ttf',
    'ts': 'NotoSans-Regular.ttf',
    'tt': 'NotoSans-Regular.ttf',
    'tw': 'NotoSans-Regular.ttf',
    'ty': 'NotoSans-Regular.ttf',
    'ug': 'NotoSansArabic-Regular.ttf',
    'uk': 'NotoSans-Regular.ttf',
    'ur': 'NotoNastaliqUrdu-Regular.ttf',
    'uz': 'NotoSans-Regular.ttf',
    've': 'NotoSans-Regular.ttf',
    'vi': 'NotoSans-Regular.ttf',
    'vo': 'NotoSans-Regular.ttf',
    'wa': 'NotoSans-Regular.ttf',
    'wo': 'NotoSans-Regular.ttf',
    'xh': 'NotoSans-Regular.ttf',
    'yi': 'NotoSansHebrew-Regular.ttf',
    'yo': 'NotoSans-Regular.ttf',
    'za': 'NotoSans-Regular.ttf',
    'zh': 'NotoSansSC-Regular.otf',
    'zu': 'NotoSans-Regular.ttf'
    }

    font_file = LANGUAGE_FONT_MAP.get(lang_code, LANGUAGE_FONT_MAP['en']) # fallback to English
    font_path = os.path.join(font_base_dir, font_file)

    if os.path.isfile(font_path):
    return font_path

    print(f"[font_algorithm] Font not found on disk for language '{lang_code}': {font_file}. Using fallback: {default_font}")
    return default_font

    # Overlays translated text on image and saves it
    # Overlays translated text on image and saves it
    def overlay_translated_text_on_image(original_image_path: str, lang: str, output_path: str):
    image = Image.open(original_image_path)
    draw = ImageDraw.Draw(image)

    # Dynamically get best font path using the algorithm
    font_path = font_algorithm(lang) # uses the lang argument
    try:
    font = ImageFont.truetype(font_path, size=24)
    except OSError:
    print(f"[WARNING] Font file not found or invalid: {font_path}. Falling back to Arial.")
    font = ImageFont.truetype("arial.ttf", size=24)

    data = pytesseract.image_to_data(image, output_type=pytesseract.Output.DICT)

    lines = defaultdict(list)
    for i in range(len(data["text"])):
    word = data["text"]
    conf = int(data["conf"])

    if word.strip() == "" or conf < 0:
    continue

    key = (data["block_num"], data["line_num"])
    word_info = {
    "text": word,
    "left": data["left"],
    "top": data["top"],
    "width": data["width"],
    "height": data["height"]
    }
    lines[key].append(word_info)

    # Translate and draw each line
    for line_words in lines.values():
    english_line = " ".join([w["text"] for w in line_words])
    if not english_line.strip():
    continue

    print("[TRANSLATE INPUT]", english_line)
    translated_line = translator.translate(english_line, lang)
    print("[TRANSLATE OUTPUT]", translated_line)

    # Position to draw: left/top of first word
    x = line_words[0]["left"]
    y = line_words[0]["top"]

    # Erase original text area with white box
    text_width = sum(w["width"] for w in line_words)
    text_height = max(w["height"] for w in line_words)
    draw.rectangle([x, y, x + text_width, y + text_height], fill="white")

    # Draw translated text
    draw.text((x, y), translated_line, fill="black", font=font)

    image.save(output_path)


    This is the translator.py:

    import requests

    def translate(text: str, target_lang: str) -> str:
    print("[TRANSLATE INPUT]", text)

    response = requests.post(
    "http://localhost:5000/translate",
    data={
    "q": text,
    "source": "en",
    "target": target_lang,
    "format": "text"
    }
    )

    if response.status_code == 200:
    translated = response.json().get("translatedText", "")
    print("[TRANSLATE OUTPUT]", translated)
    return translated
    else:
    print("[TRANSLATE ERROR]", response.status_code, response.text)
    raise Exception(f"Translation failed: {response.status_code} {response.text}")


    Image to be translated to Bengali

    Image after running through translation algorithm

    Continue reading...

Compartilhe esta Página