Impl OCR. Close #46
This commit is contained in:
parent
45826ec8be
commit
4ef96205e6
@ -56,3 +56,8 @@ typedef struct {
|
||||
string mobile;
|
||||
string home;
|
||||
} UserInfo_t;
|
||||
|
||||
typedef struct {
|
||||
int32_t status;
|
||||
string result;
|
||||
} OcrResult_t;
|
||||
|
@ -30,6 +30,7 @@ enum Functions {
|
||||
FUNC_GET_CONTACT_INFO = 0x55;
|
||||
FUNC_REVOKE_MSG = 0x56;
|
||||
FUNC_DECRYPT_IMAGE = 0x60;
|
||||
FUNC_EXEC_OCR = 0x61;
|
||||
FUNC_ADD_ROOM_MEMBERS = 0x70;
|
||||
FUNC_DEL_ROOM_MEMBERS = 0x71;
|
||||
FUNC_INV_ROOM_MEMBERS = 0x72;
|
||||
@ -73,6 +74,7 @@ message Response
|
||||
DbTables tables = 8; // 表列表
|
||||
DbRows rows = 9; // 行列表
|
||||
UserInfo ui = 10; // 个人信息
|
||||
OcrMsg ocr = 11; // OCR 结果
|
||||
};
|
||||
}
|
||||
|
||||
@ -217,3 +219,9 @@ message PatMsg
|
||||
string roomid = 1; // 群 id
|
||||
string wxid = 2; // wxid
|
||||
}
|
||||
|
||||
message OcrMsg
|
||||
{
|
||||
int32 status = 1; // 状态
|
||||
string result = 2; // 结果
|
||||
}
|
||||
|
@ -50,6 +50,10 @@ static string get_key(uint8_t header1, uint8_t header2, uint8_t *key)
|
||||
|
||||
string DecryptImage(string src, string dir)
|
||||
{
|
||||
if (!fs::exists(src)) {
|
||||
return "";
|
||||
}
|
||||
|
||||
ifstream in(src.c_str(), ios::binary);
|
||||
if (!in.is_open()) {
|
||||
LOG_ERROR("Failed to read file {}", src);
|
||||
@ -335,3 +339,72 @@ string GetAudio(uint64_t id, string dir)
|
||||
|
||||
return mp3path;
|
||||
}
|
||||
|
||||
OcrResult_t GetOcrResult(string path)
|
||||
{
|
||||
OcrResult_t ret = { -1, "" };
|
||||
|
||||
if (!fs::exists(path)) {
|
||||
LOG_ERROR("Can not find: {}", path);
|
||||
return ret;
|
||||
}
|
||||
|
||||
// 路径分隔符有要求,必须为 `\`
|
||||
wstring wsPath = String2Wstring(fs::path(path).make_preferred().string());
|
||||
|
||||
WxString wxPath(wsPath);
|
||||
WxString nullObj;
|
||||
WxString ocrBuffer;
|
||||
|
||||
DWORD ocrCall1 = g_WeChatWinDllAddr + g_WxCalls.ocr.call1;
|
||||
DWORD ocrCall2 = g_WeChatWinDllAddr + g_WxCalls.ocr.call2;
|
||||
DWORD ocrCall3 = g_WeChatWinDllAddr + g_WxCalls.ocr.call3;
|
||||
|
||||
DWORD tmp = 0;
|
||||
int status = -1;
|
||||
__asm {
|
||||
pushad;
|
||||
pushfd;
|
||||
lea ecx, ocrBuffer;
|
||||
call ocrCall1;
|
||||
call ocrCall2;
|
||||
lea ecx, nullObj;
|
||||
push ecx;
|
||||
lea ecx, tmp;
|
||||
push ecx;
|
||||
lea ecx, ocrBuffer;
|
||||
push ecx;
|
||||
push 0x0;
|
||||
lea ecx, wxPath;
|
||||
push ecx;
|
||||
mov ecx, eax;
|
||||
call ocrCall3;
|
||||
mov status, eax;
|
||||
popfd;
|
||||
popad;
|
||||
}
|
||||
|
||||
if (status != 0)
|
||||
{
|
||||
LOG_ERROR("OCR status: {}", to_string(status));
|
||||
return ret; // 识别出错
|
||||
}
|
||||
|
||||
ret.status = status;
|
||||
|
||||
DWORD addr = (DWORD)&ocrBuffer;
|
||||
DWORD header = GET_DWORD(addr);
|
||||
DWORD num = GET_DWORD(addr + 0x4);
|
||||
if (num <= 0) {
|
||||
return ret; // 识别内容为空
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < num; i++) {
|
||||
DWORD content = GET_DWORD(header);
|
||||
ret.result += Wstring2String(GET_WSTRING(content + 0x14));
|
||||
ret.result += "\n";
|
||||
header = content;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -8,3 +8,4 @@ std::string DecryptImage(std::string src, std::string dst);
|
||||
int RefreshPyq(uint64_t id);
|
||||
int DownloadAttach(uint64_t id, std::string thumb, std::string extra);
|
||||
int RevokeMsg(uint64_t id);
|
||||
OcrResult_t GetOcrResult(std::string path);
|
||||
|
@ -37,7 +37,9 @@ WxCalls_t wxCalls = {
|
||||
/* call1, call2, call3 */
|
||||
{0x931730, 0x1D58751, 0x1421940},
|
||||
/* call1, call2, call3, call4, call5, call6, call7, call8*/
|
||||
{0x78CB40, 0x7F99D0, 0x78CF20, 0x78CEF0, 0xF59E40, 0xBD1A00, 0x7FA980, 0x755060}
|
||||
{0x78CB40, 0x7F99D0, 0x78CF20, 0x78CEF0, 0xF59E40, 0xBD1A00, 0x7FA980, 0x755060},
|
||||
/* call1, call2, call3 */
|
||||
{0x80A800, 0x80F270, 0x13DA3E0}
|
||||
};
|
||||
|
||||
int LoadCalls(const wchar_t *version, WxCalls_t *calls)
|
||||
|
@ -688,6 +688,31 @@ bool func_decrypt_image(DecPath dec, uint8_t *out, size_t *len)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool func_exec_ocr(char *path, uint8_t *out, size_t *len)
|
||||
{
|
||||
Response rsp = Response_init_default;
|
||||
rsp.func = Functions_FUNC_EXEC_OCR;
|
||||
rsp.which_msg = Response_ocr_tag;
|
||||
OcrResult_t ret = { -1, "" };
|
||||
|
||||
if (path == NULL) {
|
||||
LOG_ERROR("Empty path.");
|
||||
} else {
|
||||
ret = GetOcrResult(path);
|
||||
}
|
||||
|
||||
rsp.msg.ocr.status = ret.status;
|
||||
rsp.msg.ocr.result = (char *)ret.result.c_str();
|
||||
|
||||
pb_ostream_t stream = pb_ostream_from_buffer(out, *len);
|
||||
if (!pb_encode(&stream, Response_fields, &rsp)) {
|
||||
LOG_ERROR("Encoding failed: {}", PB_GET_ERROR(&stream));
|
||||
return false;
|
||||
}
|
||||
*len = stream.bytes_written;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool func_add_room_members(char *roomid, char *wxids, uint8_t *out, size_t *len)
|
||||
{
|
||||
Response rsp = Response_init_default;
|
||||
@ -901,6 +926,11 @@ static bool dispatcher(uint8_t *in, size_t in_len, uint8_t *out, size_t *out_len
|
||||
ret = func_decrypt_image(req.msg.dec, out, out_len);
|
||||
break;
|
||||
}
|
||||
case Functions_FUNC_EXEC_OCR: {
|
||||
LOG_DEBUG("[Functions_FUNC_EXEC_OCR]");
|
||||
ret = func_exec_ocr(req.msg.str, out, out_len);
|
||||
break;
|
||||
}
|
||||
case Functions_FUNC_ADD_ROOM_MEMBERS: {
|
||||
LOG_DEBUG("[Functions_FUNC_ADD_ROOM_MEMBERS]");
|
||||
ret = func_add_room_members(req.msg.m.roomid, req.msg.m.wxids, out, out_len);
|
||||
|
@ -145,6 +145,12 @@ typedef struct CallInviteCM {
|
||||
DWORD call8;
|
||||
} CallInviteCM_t;
|
||||
|
||||
typedef struct CallOcr {
|
||||
DWORD call1;
|
||||
DWORD call2;
|
||||
DWORD call3;
|
||||
} CallOcr_t;
|
||||
|
||||
typedef struct WxCalls {
|
||||
DWORD login; // 登录状态
|
||||
UserInfoCall_t ui; // 用户信息
|
||||
@ -166,6 +172,7 @@ typedef struct WxCalls {
|
||||
CallRichText_t rt; // 发送消息卡片
|
||||
CallPatMsg_t pm; // 发送拍一拍消息
|
||||
CallInviteCM_t irm; // 邀请群成员
|
||||
CallOcr_t ocr; // OCR
|
||||
} WxCalls_t;
|
||||
|
||||
struct WxString {
|
||||
|
@ -755,6 +755,40 @@ class Wcf():
|
||||
rsp = self._send_request(req)
|
||||
return rsp.str
|
||||
|
||||
def get_ocr_result(self, extra: str, timeout: int = 2) -> str:
|
||||
"""获取 OCR 结果。鸡肋,需要图片能自动下载;通过下载接口下载的图片无法识别。
|
||||
|
||||
Args:
|
||||
extra (str): 待识别的图片路径,消息里的 extra
|
||||
|
||||
Returns:
|
||||
str: OCR 结果
|
||||
"""
|
||||
def _inner(extra):
|
||||
req = wcf_pb2.Request()
|
||||
req.func = wcf_pb2.FUNC_EXEC_OCR # FUNC_EXEC_OCR
|
||||
req.str = extra
|
||||
rsp = self._send_request(req)
|
||||
ocr = json_format.MessageToDict(rsp.ocr)
|
||||
return ocr.get("status", 0), ocr.get("result", "")
|
||||
|
||||
cnt = 0
|
||||
while True:
|
||||
status, result = _inner(extra)
|
||||
if status == 0:
|
||||
break
|
||||
|
||||
cnt += 1
|
||||
if cnt > timeout:
|
||||
break
|
||||
|
||||
sleep(1)
|
||||
|
||||
if status != 0:
|
||||
self.LOG.error(f"OCR failed, status: {status}")
|
||||
|
||||
return result
|
||||
|
||||
def download_image(self, id: int, extra: str, dir: str, timeout: int = 30) -> str:
|
||||
"""下载图片
|
||||
|
||||
|
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue
Block a user