Impl OCR. Close #46
This commit is contained in:
parent
45826ec8be
commit
4ef96205e6
@ -56,3 +56,8 @@ typedef struct {
|
|||||||
string mobile;
|
string mobile;
|
||||||
string home;
|
string home;
|
||||||
} UserInfo_t;
|
} UserInfo_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
int32_t status;
|
||||||
|
string result;
|
||||||
|
} OcrResult_t;
|
||||||
|
@ -30,6 +30,7 @@ enum Functions {
|
|||||||
FUNC_GET_CONTACT_INFO = 0x55;
|
FUNC_GET_CONTACT_INFO = 0x55;
|
||||||
FUNC_REVOKE_MSG = 0x56;
|
FUNC_REVOKE_MSG = 0x56;
|
||||||
FUNC_DECRYPT_IMAGE = 0x60;
|
FUNC_DECRYPT_IMAGE = 0x60;
|
||||||
|
FUNC_EXEC_OCR = 0x61;
|
||||||
FUNC_ADD_ROOM_MEMBERS = 0x70;
|
FUNC_ADD_ROOM_MEMBERS = 0x70;
|
||||||
FUNC_DEL_ROOM_MEMBERS = 0x71;
|
FUNC_DEL_ROOM_MEMBERS = 0x71;
|
||||||
FUNC_INV_ROOM_MEMBERS = 0x72;
|
FUNC_INV_ROOM_MEMBERS = 0x72;
|
||||||
@ -73,6 +74,7 @@ message Response
|
|||||||
DbTables tables = 8; // 表列表
|
DbTables tables = 8; // 表列表
|
||||||
DbRows rows = 9; // 行列表
|
DbRows rows = 9; // 行列表
|
||||||
UserInfo ui = 10; // 个人信息
|
UserInfo ui = 10; // 个人信息
|
||||||
|
OcrMsg ocr = 11; // OCR 结果
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -217,3 +219,9 @@ message PatMsg
|
|||||||
string roomid = 1; // 群 id
|
string roomid = 1; // 群 id
|
||||||
string wxid = 2; // wxid
|
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)
|
string DecryptImage(string src, string dir)
|
||||||
{
|
{
|
||||||
|
if (!fs::exists(src)) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
ifstream in(src.c_str(), ios::binary);
|
ifstream in(src.c_str(), ios::binary);
|
||||||
if (!in.is_open()) {
|
if (!in.is_open()) {
|
||||||
LOG_ERROR("Failed to read file {}", src);
|
LOG_ERROR("Failed to read file {}", src);
|
||||||
@ -335,3 +339,72 @@ string GetAudio(uint64_t id, string dir)
|
|||||||
|
|
||||||
return mp3path;
|
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 RefreshPyq(uint64_t id);
|
||||||
int DownloadAttach(uint64_t id, std::string thumb, std::string extra);
|
int DownloadAttach(uint64_t id, std::string thumb, std::string extra);
|
||||||
int RevokeMsg(uint64_t id);
|
int RevokeMsg(uint64_t id);
|
||||||
|
OcrResult_t GetOcrResult(std::string path);
|
||||||
|
@ -37,7 +37,9 @@ WxCalls_t wxCalls = {
|
|||||||
/* call1, call2, call3 */
|
/* call1, call2, call3 */
|
||||||
{0x931730, 0x1D58751, 0x1421940},
|
{0x931730, 0x1D58751, 0x1421940},
|
||||||
/* call1, call2, call3, call4, call5, call6, call7, call8*/
|
/* 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)
|
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;
|
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)
|
bool func_add_room_members(char *roomid, char *wxids, uint8_t *out, size_t *len)
|
||||||
{
|
{
|
||||||
Response rsp = Response_init_default;
|
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);
|
ret = func_decrypt_image(req.msg.dec, out, out_len);
|
||||||
break;
|
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: {
|
case Functions_FUNC_ADD_ROOM_MEMBERS: {
|
||||||
LOG_DEBUG("[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);
|
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;
|
DWORD call8;
|
||||||
} CallInviteCM_t;
|
} CallInviteCM_t;
|
||||||
|
|
||||||
|
typedef struct CallOcr {
|
||||||
|
DWORD call1;
|
||||||
|
DWORD call2;
|
||||||
|
DWORD call3;
|
||||||
|
} CallOcr_t;
|
||||||
|
|
||||||
typedef struct WxCalls {
|
typedef struct WxCalls {
|
||||||
DWORD login; // 登录状态
|
DWORD login; // 登录状态
|
||||||
UserInfoCall_t ui; // 用户信息
|
UserInfoCall_t ui; // 用户信息
|
||||||
@ -166,6 +172,7 @@ typedef struct WxCalls {
|
|||||||
CallRichText_t rt; // 发送消息卡片
|
CallRichText_t rt; // 发送消息卡片
|
||||||
CallPatMsg_t pm; // 发送拍一拍消息
|
CallPatMsg_t pm; // 发送拍一拍消息
|
||||||
CallInviteCM_t irm; // 邀请群成员
|
CallInviteCM_t irm; // 邀请群成员
|
||||||
|
CallOcr_t ocr; // OCR
|
||||||
} WxCalls_t;
|
} WxCalls_t;
|
||||||
|
|
||||||
struct WxString {
|
struct WxString {
|
||||||
|
@ -755,6 +755,40 @@ class Wcf():
|
|||||||
rsp = self._send_request(req)
|
rsp = self._send_request(req)
|
||||||
return rsp.str
|
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:
|
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