Integração Python com OCR do WeChat para Reconhecimento de Texto e Coordenadas

Este artigo explora como utilizar as capacidades de Reconhecimento Óptico de Caracteres (OCR) nativas do WeChat em conjunto com Python. A abordagem se baseia em engenharia reversa de um arquivo DLL (mmmojo.dll) que gerencia a comunicação com o executável do OCR do WeChat (WeChatOCR.exe). Ao carregar esta DLL, é possível iniciar e interagir com o serviço de OCR sem a necessidade de ter o WeChat aberto.

Detalhes de Implementação e Dicas

Gerenciamento de Callbacks com Ponteiros de Contexto

O sistema de OCR utiliza funções de callback para notificar o aplicativo chamador sobre o status e os resultados do reconhecimento. Uma função de callback notável é OCRRemoteOnConnect, cujo protótipo inclui um parâmetro void* user_data. Este parâmetro permite passar um ponteiro de contexto, que pode ser configurado através da função exportada SetMMMojoEnvironmentCallbacks.

Em implementações C++, este ponteiro user_data é frequentemente usado para passar o ponteiro this de uma instância de classe. Isso permite que o código de callback modifique o estado interno do objeto, como uma variável de conexão (m_connect_con_var), garantindo que operações subsequentes, como o início de uma tarefa de OCR (DoOCRTask), só ocorram após o estabelecimento bem-sucedido da conexão.

Em Python, essa técnica pode ser replicada utilizando o módulo ctypes. O objeto Python pode ser encapsulado em um ctypes.py_object para ser passado como um ponteiro para a camada C. Dentro da função de callback em Python, este ponteiro pode ser convertido de volta para o objeto Python original usando ctypes.cast e acessando o atributo .value.


# Converte a instância 'self' para um ponteiro C e o define como user_data
SetMMMojoEnvironmentCallbacks(self.m_mmmojo_env_ptr, 0, py_object(self))

# Exemplo de uso do user_data no callback
def OCRRemoteOnConnect(is_connected: c_bool, user_data: py_object):
   print(f"Callback OCRRemoteOnConnect chamado. Conectado: {is_connected}")
   if user_data:
       # Recupera o objeto Python do ponteiro de contexto
       manager_obj = cast(user_data, py_object).value
       manager_obj.SetConnectState(True)
 

Armadilhas Comuns (Pitfalls)

Ao interagir com DLLs via ctypes, é crucial gerenciar corretamente o ciclo de vida dos objetos passados como argumentos. Por exemplo, ao passar um ponteiro de caractere largo (c_wchar_p) para funções da DLL, ele deve ser atribuído a uma variável intermediária. Se passado diretamente, o coletor de lixo do Python pode desalocar a memória antes que a DLL a utilize, levando a falhas silenciosas e terminações abruptas do programa.


# Abordagem incorreta (pode levar à coleta de lixo prematura)
SetMMMojoEnvironmentInitParams(self.m_mmmojo_env_ptr, 2, c_wchar_p(self.m_exe_path))

# Abordagem correta (a variável intermediária garante a persistência)
c_exe_path = c_wchar_p(self.m_exe_path)
SetMMMojoEnvironmentInitParams(self.m_mmmojo_env_ptr, 2, c_exe_path)
 

Para garantir que os objetos persistam durante toda a interação com a DLL, considere declará-los como variáveis globais ou atribuí-los a atributos da instância da classe (self).

Instruções de Uso

Instalação

O pacote está disponível no PyPI. Instale-o usando pip:

pip install wechat-ocr

Se estiver usando um mirror de pacotes que pode não ter a versão mais recente, tente especificar o índice oficial:

pip install wechat-ocr -i https://pypi.org/simple

Exemplo de Código

O exemplo a seguir demonstra como inicializar o serviço de OCR, definir um callback para receber os resultados e processar imagens.


import os
import json
import time
from wechat_ocr.ocr_manager import OcrManager, OCR_MAX_TASK_ID

# Caminho para o executável do OCR do WeChat
WECHAT_OCR_EXECUTABLE = r"C:\Users\Administrator\AppData\Roaming\Tencent\WeChat\XPlugin\Plugins\WeChatOCR\7057\extracted\WeChatOCR.exe"
# Caminho para a instalação do WeChat
WECHAT_INSTALL_DIR = r"D:\GreenSoftware\WeChat\3.9.6.32"

def ocr_completion_handler(image_path: str, recognition_results: dict):
   """Callback para processar os resultados do OCR."""
   output_filename = os.path.basename(image_path) + ".json"
   print(f"Reconhecimento concluído para: {image_path}. Salvando em: {output_filename}")
   with open(output_filename, 'w', encoding='utf-8') as f:
       json.dump(recognition_results, f, ensure_ascii=False, indent=2)

def main_ocr_process():
   """Função principal para orquestrar o processo de OCR."""
   ocr_service = OcrManager(WECHAT_INSTALL_DIR)

   # Configura o caminho do executável do OCR
   ocr_service.SetExePath(WECHAT_OCR_EXECUTABLE)
   # Configura o diretório da instalação do WeChat
   ocr_service.SetUsrLibDir(WECHAT_INSTALL_DIR)
   # Define a função de callback para resultados
   ocr_service.SetOcrResultCallback(ocr_completion_handler)

   # Inicia o serviço de OCR
   ocr_service.StartWeChatOCR()

   # Submete tarefas de OCR para diferentes imagens
   ocr_service.DoOCRTask(r"T:\Code\WeChat\OCR\Python\img\image1.png")
   ocr_service.DoOCRTask(r"T:\Code\WeChat\OCR\Python\img\image2.png")
   ocr_service.DoOCRTask(r"T:\Code\WeChat\OCR\Python\img\image3.png")

   # Aguarda a conclusão das tarefas
   # Nota: Um loop de espera pode ser mais robusto dependendo do cenário
   time.sleep(1)
   while ocr_service.m_task_id.qsize() != OCR_MAX_TASK_ID:
       time.sleep(0.1) # Pequena pausa para evitar uso excessivo de CPU

   # Finaliza o serviço de OCR
   ocr_service.KillWeChatOCR()

if __name__ == "__main__":
   main_ocr_process()
 

Repositório de Código Fonte

O código-fonte completo está disponível no GitHub:

https://github.com/kanadeblisst00/wechat_ocr

Versão Aardio

Uma implementação semelhante também foi desenovlvida em Aardio:

https://mp.weixin.qq.com/s/kYGGyjKW-GJxlqGkmQfuBg

Tags: Python ctypes WeChat OCR DLL engenharia reversa

Publicado em 7-19 07:07