wx-ocr 0.1.3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- wx_ocr/__init__.py +139 -0
- wx_ocr/cli.py +296 -0
- wx_ocr/default_callback.py +45 -0
- wx_ocr/mmmojo_dll.py +175 -0
- wx_ocr/ocr_manager.py +160 -0
- wx_ocr/ocr_protobuf_pb2.py +39 -0
- wx_ocr/simple_api.py +303 -0
- wx_ocr/utility_protobuf_pb2.py +39 -0
- wx_ocr/utils.py +161 -0
- wx_ocr/wco_data/Model/FPOCRRecog.xnet +0 -0
- wx_ocr/wco_data/Model/OCRDetFP32.xnet.nas +0 -0
- wx_ocr/wco_data/Model/OCRParaDetV1.1.0.26.xnet +0 -0
- wx_ocr/wco_data/Model/OCRRecogFP32V1.1.0.26.xnet +0 -0
- wx_ocr/wco_data/Model/RecDict +12043 -0
- wx_ocr/wco_data/Model/sohu_simp.txt +5031 -0
- wx_ocr/wco_data/WeChatOCR.exe +0 -0
- wx_ocr/wco_data/mmmojo.dll +0 -0
- wx_ocr/wco_data/mmmojo_64.dll +0 -0
- wx_ocr/wco_data/x64.config +1 -0
- wx_ocr/winapi.py +195 -0
- wx_ocr/xplugin_manager.py +194 -0
- wx_ocr-0.1.3.dist-info/METADATA +84 -0
- wx_ocr-0.1.3.dist-info/RECORD +27 -0
- wx_ocr-0.1.3.dist-info/WHEEL +5 -0
- wx_ocr-0.1.3.dist-info/entry_points.txt +2 -0
- wx_ocr-0.1.3.dist-info/licenses/LICENSE +21 -0
- wx_ocr-0.1.3.dist-info/top_level.txt +1 -0
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
x64
|
wx_ocr/winapi.py
ADDED
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
from ctypes import *
|
|
2
|
+
from ctypes.wintypes import *
|
|
3
|
+
|
|
4
|
+
user32 = WinDLL('user32', use_last_error=True)
|
|
5
|
+
kernel32 = WinDLL('kernel32', use_last_error=True)
|
|
6
|
+
|
|
7
|
+
class _SECURITY_ATTRIBUTES(Structure):
|
|
8
|
+
_fields_ = [('nLength', DWORD),
|
|
9
|
+
('lpSecurityDescriptor', LPVOID),
|
|
10
|
+
('bInheritHandle', BOOL),]
|
|
11
|
+
|
|
12
|
+
ULONG_PTR = c_ulong
|
|
13
|
+
INFINITE = 0xFFFFFFFF
|
|
14
|
+
PROCESS_ALL_ACCESS = 0x1FFFFF
|
|
15
|
+
MEM_COMMIT = 0x00001000
|
|
16
|
+
PAGE_EXECUTE_READWRITE = 0x40
|
|
17
|
+
MEM_RELEASE = 0x00008000
|
|
18
|
+
TH32CS_SNAPMODULE = 0x00000008
|
|
19
|
+
TH32CS_SNAPMODULE32 = 0x00000010
|
|
20
|
+
TH32CS_SNAPPROCESS = 0x00000002
|
|
21
|
+
PAGE_EXECUTE_READWRITE = 0x40
|
|
22
|
+
CS_HREDRAW = 2
|
|
23
|
+
CS_VREDRAW = 1
|
|
24
|
+
COLOR_WINDOW = 5
|
|
25
|
+
WS_OVERLAPPEDWINDOW = 13565952
|
|
26
|
+
CW_USEDEFAULT = -2147483648
|
|
27
|
+
SW_HIDE = 0
|
|
28
|
+
WM_COPYDATA = 0x004A
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
void = None
|
|
32
|
+
LPSECURITY_ATTRIBUTES = POINTER(_SECURITY_ATTRIBUTES)
|
|
33
|
+
LPTHREAD_START_ROUTINE = LPVOID
|
|
34
|
+
FARPROC = SIZE_T = c_ulong
|
|
35
|
+
HCURSOR = c_void_p
|
|
36
|
+
LRESULT = c_int64
|
|
37
|
+
WNDPROC = WINFUNCTYPE(LRESULT, HWND, UINT, WPARAM, LPARAM)
|
|
38
|
+
|
|
39
|
+
class COPYDATASTRUCT(Structure):
|
|
40
|
+
_fields_ = [
|
|
41
|
+
('dwData', LPARAM),
|
|
42
|
+
('cbData', DWORD),
|
|
43
|
+
('lpData', c_void_p)
|
|
44
|
+
]
|
|
45
|
+
|
|
46
|
+
class MODULEENTRY32(Structure):
|
|
47
|
+
_fields_ = [
|
|
48
|
+
("dwSize", DWORD), # 结构的大小,以字节为单位,必须先初始化
|
|
49
|
+
("th32ModuleID", DWORD), # 该成员不再使用,并且始终设置为 1
|
|
50
|
+
("th32ProcessID", DWORD), # 进程pid
|
|
51
|
+
("GlblcntUsage", DWORD), # 无意义, 一般等于0xFFFF
|
|
52
|
+
("ProccntUsage", DWORD), # 无意义, 一般等于0xFFFF
|
|
53
|
+
("modBaseAddr", POINTER(BYTE)), # 拥有进程上下文中模块的基地址
|
|
54
|
+
("modBaseSize", DWORD), # 模块的大小,以字节为单位
|
|
55
|
+
("hModule", HMODULE), # 拥有进程上下文中的模块句柄
|
|
56
|
+
("szModule", c_char*256), # 模块名称
|
|
57
|
+
("szExePath", c_char*260), # 模块路径
|
|
58
|
+
]
|
|
59
|
+
|
|
60
|
+
class PROCESSENTRY32(Structure):
|
|
61
|
+
_fields_ = [
|
|
62
|
+
("dwSize", DWORD), # 结构的大小,以字节为单位,必须先初始化
|
|
63
|
+
("cntUsage", DWORD), # 该成员不再使用,并且始终设置为 1
|
|
64
|
+
("th32ProcessID", DWORD), # 进程pid
|
|
65
|
+
("th32DefaultHeapID", ULONG_PTR), # 无意义, 一般等于0xFFFF
|
|
66
|
+
("th32ModuleID", DWORD), # 无意义, 一般等于0xFFFF
|
|
67
|
+
("cntThreads", DWORD), # 拥有进程上下文中模块的基地址
|
|
68
|
+
("th32ParentProcessID", DWORD), # 模块的大小,以字节为单位
|
|
69
|
+
("pcPriClassBase", LONG), # 拥有进程上下文中的模块句柄
|
|
70
|
+
("dwFlags", DWORD), # 模块名称
|
|
71
|
+
("szExeFile", c_char*260), # 模块路径
|
|
72
|
+
]
|
|
73
|
+
|
|
74
|
+
class WNDCLASS(Structure):
|
|
75
|
+
_fields_ = [('style', UINT),
|
|
76
|
+
('lpfnWndProc', WNDPROC),
|
|
77
|
+
('cbClsExtra', c_int),
|
|
78
|
+
('cbWndExtra', c_int),
|
|
79
|
+
('hInstance', HINSTANCE),
|
|
80
|
+
('hIcon', HICON),
|
|
81
|
+
('hCursor', HCURSOR),
|
|
82
|
+
('hbrBackground', HBRUSH),
|
|
83
|
+
('lpszMenuName', LPCWSTR),
|
|
84
|
+
('lpszClassName', LPCWSTR)]
|
|
85
|
+
|
|
86
|
+
def func_def(name, restype, *argtypes, dll=kernel32):
|
|
87
|
+
def errcheck(result, func, args):
|
|
88
|
+
if not result:
|
|
89
|
+
raise WinError(get_last_error())
|
|
90
|
+
return result
|
|
91
|
+
cfunc = getattr(dll, name)
|
|
92
|
+
cfunc.argtypes = argtypes
|
|
93
|
+
cfunc.restype = restype
|
|
94
|
+
#cfunc.errcheck = errcheck
|
|
95
|
+
return cfunc
|
|
96
|
+
|
|
97
|
+
OpenProcess = func_def("OpenProcess", HANDLE, *(DWORD, BOOL, DWORD))
|
|
98
|
+
VirtualAllocEx = func_def("VirtualAllocEx", LPVOID, *(HANDLE, LPVOID, SIZE_T, DWORD, DWORD))
|
|
99
|
+
VirtualFreeEx = func_def("VirtualFreeEx", BOOL, *(HANDLE, LPVOID, SIZE_T, DWORD))
|
|
100
|
+
WriteProcessMemory = func_def("WriteProcessMemory", BOOL, *(HANDLE, LPVOID, LPCVOID, SIZE_T, POINTER(SIZE_T)))
|
|
101
|
+
GetModuleHandleA = func_def("GetModuleHandleA", HMODULE, *(LPCSTR,))
|
|
102
|
+
GetModuleHandleW = func_def("GetModuleHandleW", HMODULE, *(LPCWSTR, ))
|
|
103
|
+
GetProcAddress = func_def("GetProcAddress", c_void_p, *(HMODULE, LPCSTR))
|
|
104
|
+
CreateRemoteThread = func_def("CreateRemoteThread", HANDLE, *(HANDLE, LPSECURITY_ATTRIBUTES, DWORD, LPTHREAD_START_ROUTINE, LPVOID, DWORD, LPDWORD))
|
|
105
|
+
CloseHandle = func_def("CloseHandle", BOOL, *(HANDLE,))
|
|
106
|
+
CreateToolhelp32Snapshot = func_def("CreateToolhelp32Snapshot", HANDLE, *(DWORD, DWORD))
|
|
107
|
+
Module32First = func_def("Module32First", BOOL, *(HANDLE, POINTER(MODULEENTRY32)))
|
|
108
|
+
Module32Next = func_def("Module32Next", BOOL, *(HANDLE, POINTER(MODULEENTRY32)))
|
|
109
|
+
Process32First = func_def("Process32First", BOOL, *(HANDLE, POINTER(PROCESSENTRY32)))
|
|
110
|
+
Process32Next = func_def("Process32Next", BOOL, *(HANDLE, POINTER(PROCESSENTRY32)))
|
|
111
|
+
ReadProcessMemory = func_def("ReadProcessMemory", BOOL, *(HANDLE, LPCVOID, LPVOID, c_size_t, POINTER(c_size_t)))
|
|
112
|
+
FindWindowW = func_def("FindWindowW", HWND, *(LPCWSTR, LPCWSTR), dll=user32)
|
|
113
|
+
GetWindowThreadProcessId = func_def("GetWindowThreadProcessId", DWORD, *(HWND, LPDWORD), dll=user32)
|
|
114
|
+
LoadLibraryW = func_def("LoadLibraryW", HMODULE, *(LPCWSTR,))
|
|
115
|
+
FreeLibrary = func_def("FreeLibrary", BOOL, *(HMODULE, ))
|
|
116
|
+
VirtualProtect = func_def("VirtualProtect", BOOL, *(LPVOID, SIZE_T, DWORD, PDWORD))
|
|
117
|
+
VirtualProtectEx = func_def("VirtualProtectEx", BOOL, *(HANDLE, LPVOID, SIZE_T, DWORD, PDWORD))
|
|
118
|
+
DefWindowProcW = func_def("DefWindowProcW", LRESULT, *(HWND, UINT, WPARAM, LPARAM), dll=user32)
|
|
119
|
+
RegisterClassW = func_def("RegisterClassW", ATOM, *(POINTER(WNDCLASS), ), dll=user32)
|
|
120
|
+
CreateWindowExW = func_def("CreateWindowExW", HWND, *(DWORD, LPCWSTR, LPCWSTR, DWORD, c_int, c_int, c_int, c_int, HWND, HMENU, HINSTANCE, LPVOID), dll=user32)
|
|
121
|
+
ShowWindow = func_def('ShowWindow', BOOL, *(HWND, c_int), dll=user32)
|
|
122
|
+
UpdateWindow = func_def("UpdateWindow", BOOL, *(HWND, ), dll=user32)
|
|
123
|
+
GetMessageW = func_def("GetMessageW", BOOL, *(POINTER(MSG), HWND, UINT, UINT), dll=user32)
|
|
124
|
+
TranslateMessage = func_def('TranslateMessage', BOOL, *(POINTER(MSG), ), dll=user32)
|
|
125
|
+
DispatchMessageW = func_def('DispatchMessageW', LRESULT, *(POINTER(MSG),), dll=user32)
|
|
126
|
+
WaitForSingleObject = func_def('WaitForSingleObject', DWORD, *(HANDLE, DWORD))
|
|
127
|
+
GetExitCodeThread = func_def('GetExitCodeThread', BOOL, *(HANDLE, LPDWORD))
|
|
128
|
+
|
|
129
|
+
def CloseSomeHandle(*args):
|
|
130
|
+
'''关闭多个句柄'''
|
|
131
|
+
for arg in args:
|
|
132
|
+
if arg:
|
|
133
|
+
CloseHandle(arg)
|
|
134
|
+
|
|
135
|
+
def getPidByName(name=None, cls=None):
|
|
136
|
+
'''根据给定的name(进程名)和cls(类名)查找进程,返回进程pid'''
|
|
137
|
+
hwnd = FindWindowW(cls, name)
|
|
138
|
+
cpid = c_ulong()
|
|
139
|
+
if GetWindowThreadProcessId(hwnd, byref(cpid)):
|
|
140
|
+
return cpid.value
|
|
141
|
+
return 0
|
|
142
|
+
|
|
143
|
+
def getModuleInfo(moduleName, pid):
|
|
144
|
+
'''获取模块信息,返回模块信息的字典'''
|
|
145
|
+
hModuleSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE|TH32CS_SNAPMODULE32, pid)
|
|
146
|
+
|
|
147
|
+
me32 = MODULEENTRY32()
|
|
148
|
+
me32.dwSize = sizeof(MODULEENTRY32)
|
|
149
|
+
|
|
150
|
+
bRet = Module32First(hModuleSnap, pointer(me32))
|
|
151
|
+
while bRet:
|
|
152
|
+
szModule = me32.szModule.decode()
|
|
153
|
+
if szModule.upper() == moduleName.upper():
|
|
154
|
+
addr = cast(me32.modBaseAddr, c_void_p).value # hex(addressof(modBaseAddr.contents))
|
|
155
|
+
CloseHandle(hModuleSnap)
|
|
156
|
+
try:
|
|
157
|
+
me32.szExePath.decode("gbk")
|
|
158
|
+
except UnicodeDecodeError:
|
|
159
|
+
print(me32.szExePath)
|
|
160
|
+
module = {
|
|
161
|
+
'modBaseSize': me32.modBaseSize, # 模块字节大小
|
|
162
|
+
'th32ProcessID': me32.th32ProcessID, # 进程pid
|
|
163
|
+
'modBaseAddr': addr, # 模块基址
|
|
164
|
+
"hModule": me32.hModule, # 模块句柄
|
|
165
|
+
'szModule': me32.szModule.decode("ansi"), # 模块名称
|
|
166
|
+
'szExePath': me32.szExePath.decode("ansi") # 模块路径
|
|
167
|
+
}
|
|
168
|
+
return module
|
|
169
|
+
bRet = Module32Next(hModuleSnap, pointer(me32) )
|
|
170
|
+
CloseHandle(hModuleSnap)
|
|
171
|
+
|
|
172
|
+
def getModuleBaseAddress(moduleName, pid):
|
|
173
|
+
'''获取模块基址'''
|
|
174
|
+
module = getModuleInfo(moduleName, pid)
|
|
175
|
+
if module:
|
|
176
|
+
return module["modBaseAddr"]
|
|
177
|
+
|
|
178
|
+
def enumProcess(procName):
|
|
179
|
+
'''枚举进程'''
|
|
180
|
+
hModuleSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0)
|
|
181
|
+
|
|
182
|
+
pe32 = PROCESSENTRY32()
|
|
183
|
+
pe32.dwSize = sizeof(PROCESSENTRY32)
|
|
184
|
+
pids = []
|
|
185
|
+
bRet = Process32First(hModuleSnap, pointer(pe32))
|
|
186
|
+
while bRet:
|
|
187
|
+
szExeFile = pe32.szExeFile.decode()
|
|
188
|
+
if szExeFile.upper() == procName.upper():
|
|
189
|
+
pid = pe32.th32ProcessID # hex(addressof(modBaseAddr.contents))
|
|
190
|
+
pids.append(pid)
|
|
191
|
+
bRet = Process32Next(hModuleSnap, pointer(pe32) )
|
|
192
|
+
CloseHandle(hModuleSnap)
|
|
193
|
+
return pids
|
|
194
|
+
|
|
195
|
+
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import time
|
|
3
|
+
import platform
|
|
4
|
+
from typing import Callable, Dict, List
|
|
5
|
+
from enum import Enum
|
|
6
|
+
from .winapi import *
|
|
7
|
+
from . import default_callback
|
|
8
|
+
from .mmmojo_dll import MmmojoDll, MMMojoEnvironmentCallbackType, MMMojoEnvironmentInitParamType
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class RequestIdUtility(Enum):
|
|
12
|
+
'''
|
|
13
|
+
enum RequestIdUtility
|
|
14
|
+
{
|
|
15
|
+
UtilityHiPush = 10001, //是Utility启动发送的
|
|
16
|
+
UtilityInitPullReq = 10002, //初始化请求
|
|
17
|
+
UtilityInitPullResp = 10003, //回复创建的都是Shared类型的info, 但是调用了SwapMMMojoWriteInfoCallback, 所以回调的还是Pull
|
|
18
|
+
UtilityResampleImagePullReq = 10010,
|
|
19
|
+
UtilityResampleImagePullResp = 10011,
|
|
20
|
+
UtilityDecodeImagePullReq = 10020,
|
|
21
|
+
UtilityDecodeImagePullResp = 10021,
|
|
22
|
+
UtilityPicQRScanPullReq = 10030, //10030是点击OCR时(也是打开图片时)发送的请求, 参数是图片路径
|
|
23
|
+
UtilityQRScanPullReq = 10031, //10031是截图框选时发送的请求, 参数应该是某种编码后的图片数据
|
|
24
|
+
UtilityQRScanPullResp = 10032, //这两种请求的返回ID都是10032
|
|
25
|
+
UtilityTextScanPushResp = 10040 //TextScan具体在扫什么不是很清楚 可能是用来判断图片上是否有文字
|
|
26
|
+
};
|
|
27
|
+
'''
|
|
28
|
+
UtilityHiPush = 10001
|
|
29
|
+
UtilityInitPullReq = 10002
|
|
30
|
+
UtilityInitPullResp = 10003
|
|
31
|
+
UtilityResampleImagePullReq = 10010
|
|
32
|
+
UtilityResampleImagePullResp = 10011
|
|
33
|
+
UtilityDecodeImagePullReq = 10020
|
|
34
|
+
UtilityDecodeImagePullResp = 10021
|
|
35
|
+
UtilityPicQRScanPullReq = 10030
|
|
36
|
+
UtilityQRScanPullReq = 10031
|
|
37
|
+
UtilityQRScanPullResp = 10032
|
|
38
|
+
UtilityTextScanPushResp = 10040
|
|
39
|
+
|
|
40
|
+
class RequestIdPlayer(Enum):
|
|
41
|
+
PlayerHiPush = 10001
|
|
42
|
+
PlayerInitPullReq = 10002
|
|
43
|
+
PlayerInitPullResp = 10003
|
|
44
|
+
PlayerInitPlayerCorePush = 10010
|
|
45
|
+
PlayerCreatePlayerCorePullReq = 10011
|
|
46
|
+
PlayerCreatePlayerCorePullResp = 10012
|
|
47
|
+
PlayerDestroyPlayerCorePush = 10013
|
|
48
|
+
PlayerPrepareAsyncPush = 10014
|
|
49
|
+
PlayerStartPush = 10015
|
|
50
|
+
PlayerStopPush = 10016
|
|
51
|
+
PlayerPausePush = 10017
|
|
52
|
+
PlayerResumePush = 10018
|
|
53
|
+
PlayerSetAudioMutePush = 10019
|
|
54
|
+
PlayerSeekToAsyncPush = 10020
|
|
55
|
+
PlayerGetCurrentPositionMsPullReq = 10021
|
|
56
|
+
PlayerGetCurrentPositionMsPullResp = 10022
|
|
57
|
+
PlayerSetVideoSurfacePush = 10023
|
|
58
|
+
PlayerSetAudioVolumePush = 10024
|
|
59
|
+
PlayerSetDataSourcePush = 10025
|
|
60
|
+
PlayerSetLoaderSourcePush = 10026
|
|
61
|
+
PlayerSetRepeatPush = 10027
|
|
62
|
+
PlayerResizePush = 10028
|
|
63
|
+
PlayerSetPlaySpeedRatio = 10029
|
|
64
|
+
PlayerInfoPush = 10030
|
|
65
|
+
PlayerErrorPlayerPush = 10031
|
|
66
|
+
PlayerVideoSizeChangedPush = 10032
|
|
67
|
+
PlayerUnknown0Push = 10033
|
|
68
|
+
PlayerStatePush = 10034
|
|
69
|
+
PlayerUnknonw1Push = 10035
|
|
70
|
+
PlayerUnknown2Push = 10036
|
|
71
|
+
PlayerStartTaskProxyPush = 10050
|
|
72
|
+
PlayerStartRequestProxyPush = 10051
|
|
73
|
+
PlayerCloseRequestProxyPush = 10052
|
|
74
|
+
PlayerPollingDatProxyPullReq = 10054
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
class XPluginManager(object):
|
|
79
|
+
m_cb_usrdata:py_object
|
|
80
|
+
m_exe_path:c_wchar_p
|
|
81
|
+
m_switch_native: Dict[str, str] = {}
|
|
82
|
+
m_cmdline: List[str] = []
|
|
83
|
+
m_mmmojo_env_ptr: c_void_p = c_void_p(None)
|
|
84
|
+
m_init_mmmojo_env = False
|
|
85
|
+
m_callbacks: Dict[str, Callable] = {}
|
|
86
|
+
|
|
87
|
+
def __init__(self, wechat_path) -> None:
|
|
88
|
+
python_bit = platform.architecture()[0]
|
|
89
|
+
if python_bit == "64bit":
|
|
90
|
+
dll_name = "mmmojo_64.dll"
|
|
91
|
+
else:
|
|
92
|
+
dll_name = "mmmojo.dll"
|
|
93
|
+
mmmojo_dllpath = os.path.join(wechat_path, dll_name)
|
|
94
|
+
if not os.path.exists(mmmojo_dllpath):
|
|
95
|
+
raise Exception("给定的微信路径不存在mmmojo.dll")
|
|
96
|
+
self._dll = MmmojoDll(mmmojo_dllpath)
|
|
97
|
+
self.m_cb_usrdata = self
|
|
98
|
+
# 增加callback的引用计数,防止被垃圾回收机制处理
|
|
99
|
+
self._callbacks_refer = {}
|
|
100
|
+
|
|
101
|
+
def __del__(self):
|
|
102
|
+
if self.m_init_mmmojo_env:
|
|
103
|
+
self.StopMMMojoEnv()
|
|
104
|
+
|
|
105
|
+
def SetExePath(self, exe_path:str) -> None:
|
|
106
|
+
ocr_exe_name = "WeChatOCR.exe"
|
|
107
|
+
if not exe_path.endswith(ocr_exe_name) and os.path.isdir(exe_path):
|
|
108
|
+
exe_path = os.path.join(exe_path, ocr_exe_name)
|
|
109
|
+
if not os.path.exists(exe_path):
|
|
110
|
+
raise Exception(f"指定的{ocr_exe_name}路径不存在!")
|
|
111
|
+
self.m_exe_path = c_wchar_p(exe_path)
|
|
112
|
+
|
|
113
|
+
def AppendSwitchNativeCmdLine(self, arg:str, value:str) -> None:
|
|
114
|
+
self.m_switch_native[arg] = value
|
|
115
|
+
|
|
116
|
+
def SetCommandLine(self, cmdline:List[str]) -> None:
|
|
117
|
+
self.m_cmdline = cmdline
|
|
118
|
+
|
|
119
|
+
def SetOneCallback(self, name:str, func:Callable) -> None:
|
|
120
|
+
self.m_callbacks[name] = func
|
|
121
|
+
|
|
122
|
+
def SetCallbacks(self, callbacks:Dict[str, Callable]) -> None:
|
|
123
|
+
self.m_callbacks.update(callbacks)
|
|
124
|
+
|
|
125
|
+
def SetCallbackUsrData(self, cb_usrdata:py_object):
|
|
126
|
+
self.m_cb_usrdata = cb_usrdata
|
|
127
|
+
|
|
128
|
+
def InitMMMojoEnv(self):
|
|
129
|
+
if not self.m_exe_path or not os.path.exists(self.m_exe_path.value):
|
|
130
|
+
raise Exception(f"给定的WeChatOcr.exe路径错误(m_exe_path): {self.m_exe_path}")
|
|
131
|
+
if self.m_init_mmmojo_env and self.m_mmmojo_env_ptr:
|
|
132
|
+
return
|
|
133
|
+
# 初始化环境
|
|
134
|
+
self._dll.InitializeMMMojo(0, None)
|
|
135
|
+
self.m_mmmojo_env_ptr = c_void_p(self._dll.CreateMMMojoEnvironment())
|
|
136
|
+
if not self.m_mmmojo_env_ptr:
|
|
137
|
+
raise Exception("CreateMMMojoEnvironment失败!")
|
|
138
|
+
# 设置回调函数的最后一个参数user_data的值
|
|
139
|
+
self._dll.SetMMMojoEnvironmentCallbacks(self.m_mmmojo_env_ptr, MMMojoEnvironmentCallbackType.kMMUserData.value, py_object(self.m_cb_usrdata))
|
|
140
|
+
self.SetDefaultCallbaks()
|
|
141
|
+
# 设置启动所需参数
|
|
142
|
+
SetMMMojoEnvironmentInitParams = self._dll.func_def("SetMMMojoEnvironmentInitParams", void, *(c_void_p, c_int, c_int))
|
|
143
|
+
SetMMMojoEnvironmentInitParams(self.m_mmmojo_env_ptr, MMMojoEnvironmentInitParamType.kMMHostProcess.value, 1)
|
|
144
|
+
SetMMMojoEnvironmentInitParams = self._dll.func_def("SetMMMojoEnvironmentInitParams", void, *(c_void_p, c_int, c_wchar_p))
|
|
145
|
+
SetMMMojoEnvironmentInitParams(self.m_mmmojo_env_ptr, MMMojoEnvironmentInitParamType.kMMExePath.value, self.m_exe_path)
|
|
146
|
+
# 设置SwitchNative命令行参数
|
|
147
|
+
for k,v in self.m_switch_native.items():
|
|
148
|
+
ck = c_char_p(k.encode())
|
|
149
|
+
cv = c_wchar_p(v)
|
|
150
|
+
self._dll.AppendMMSubProcessSwitchNative(self.m_mmmojo_env_ptr, ck, cv)
|
|
151
|
+
# 启动
|
|
152
|
+
self._dll.StartMMMojoEnvironment(self.m_mmmojo_env_ptr)
|
|
153
|
+
self.m_init_mmmojo_env = True
|
|
154
|
+
|
|
155
|
+
def SetDefaultCallbaks(self):
|
|
156
|
+
for i in MMMojoEnvironmentCallbackType:
|
|
157
|
+
fname = i.name
|
|
158
|
+
if fname == "kMMUserData":
|
|
159
|
+
continue
|
|
160
|
+
callback = self.m_callbacks.get(fname, None) or getattr(default_callback, f"Default{fname[3:]}")
|
|
161
|
+
callback_def = default_callback.callbacks_def[fname]
|
|
162
|
+
SetMMMojoEnvironmentCallbacks = self._dll.func_def("SetMMMojoEnvironmentCallbacks", void, *(c_void_p, c_int, callback_def))
|
|
163
|
+
self._callbacks_refer[fname] = callback_def(callback)
|
|
164
|
+
SetMMMojoEnvironmentCallbacks(self.m_mmmojo_env_ptr, i.value, self._callbacks_refer[fname])
|
|
165
|
+
|
|
166
|
+
def StopMMMojoEnv(self):
|
|
167
|
+
if self.m_init_mmmojo_env and self.m_mmmojo_env_ptr:
|
|
168
|
+
self._dll.StopMMMojoEnvironment(self.m_mmmojo_env_ptr)
|
|
169
|
+
self._dll.RemoveMMMojoEnvironment(self.m_mmmojo_env_ptr)
|
|
170
|
+
self.m_mmmojo_env_ptr = c_void_p(None)
|
|
171
|
+
self.m_init_mmmojo_env = False
|
|
172
|
+
|
|
173
|
+
def SendPbSerializedData(self, pb_data:bytes, pb_size:int, method:int, sync:int, request_id:int) -> None:
|
|
174
|
+
write_info:c_void_p = self._dll.CreateMMMojoWriteInfo(c_int(method), c_int(sync), c_uint32(request_id))
|
|
175
|
+
request:c_void_p = self._dll.GetMMMojoWriteInfoRequest(write_info, c_uint32(pb_size))
|
|
176
|
+
memmove(request, c_char_p(pb_data), pb_size)
|
|
177
|
+
self._dll.SendMMMojoWriteInfo(self.m_mmmojo_env_ptr, write_info)
|
|
178
|
+
|
|
179
|
+
def GetPbSerializedData(self, request_info:c_void_p, data_size:c_uint32) -> c_void_p:
|
|
180
|
+
pb_data = self._dll.GetMMMojoReadInfoRequest(request_info, byref(data_size))
|
|
181
|
+
return pb_data
|
|
182
|
+
|
|
183
|
+
def GetReadInfoAttachData(self, request_info:c_void_p, data_size:c_uint32) -> c_void_p:
|
|
184
|
+
attach_data = self._dll.GetMMMojoReadInfoAttach(request_info, byref(data_size))
|
|
185
|
+
return attach_data
|
|
186
|
+
|
|
187
|
+
def RemoveReadInfo(self, request_info:c_void_p) -> void:
|
|
188
|
+
return self._dll.RemoveMMMojoReadInfo(request_info)
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: wx-ocr
|
|
3
|
+
Version: 0.1.3
|
|
4
|
+
Summary: 超级简单的微信 OCR - 一行代码识别图片文字 | Super simple WeChat OCR
|
|
5
|
+
Author: wx-ocr contributors
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Original Project, https://github.com/kanadeblisst00/wechat_ocr
|
|
8
|
+
Keywords: wechat,ocr,text-recognition,image-processing,微信,文字识别,wx
|
|
9
|
+
Classifier: Development Status :: 4 - Beta
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
+
Classifier: Operating System :: Microsoft :: Windows
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Topic :: Multimedia :: Graphics :: Capture :: Screen Capture
|
|
21
|
+
Classifier: Topic :: Scientific/Engineering :: Image Recognition
|
|
22
|
+
Requires-Python: >=3.8
|
|
23
|
+
Description-Content-Type: text/markdown
|
|
24
|
+
License-File: LICENSE
|
|
25
|
+
Requires-Dist: protobuf>=3.20.0
|
|
26
|
+
Dynamic: license-file
|
|
27
|
+
|
|
28
|
+
# wx-ocr
|
|
29
|
+
|
|
30
|
+
一行代码识别图片文字,无需安装微信
|
|
31
|
+
|
|
32
|
+
## 安装
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
pip install wx-ocr
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## 使用
|
|
39
|
+
|
|
40
|
+
### Python API
|
|
41
|
+
|
|
42
|
+
```python
|
|
43
|
+
from wx_ocr import ocr
|
|
44
|
+
|
|
45
|
+
# 识别图片
|
|
46
|
+
texts = ocr("image.png", return_text_only=True)
|
|
47
|
+
print(texts) # ['文字1', '文字2', ...]
|
|
48
|
+
|
|
49
|
+
# 获取完整结果(包含位置信息)
|
|
50
|
+
result = ocr("image.png")
|
|
51
|
+
for item in result['ocrResult']:
|
|
52
|
+
print(item['text'], item['location'])
|
|
53
|
+
|
|
54
|
+
# 批量识别
|
|
55
|
+
from wx_ocr import ocr_batch
|
|
56
|
+
results = ocr_batch(["1.png", "2.png", "3.png"], return_text_only=True)
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### 命令行
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
# 识别图片
|
|
63
|
+
wx-ocr image.png
|
|
64
|
+
|
|
65
|
+
# JSON 格式输出
|
|
66
|
+
wx-ocr --format json image.png
|
|
67
|
+
|
|
68
|
+
# 批量处理
|
|
69
|
+
wx-ocr *.png
|
|
70
|
+
|
|
71
|
+
# 保存结果
|
|
72
|
+
wx-ocr --save --output results/ image.png
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## 说明
|
|
76
|
+
|
|
77
|
+
- 仅支持 Windows
|
|
78
|
+
- 需要 Python 3.8+
|
|
79
|
+
- 无需安装微信(项目自带 OCR 文件)
|
|
80
|
+
- 基于 [wechat_ocr](https://github.com/kanadeblisst00/wechat_ocr) 开发
|
|
81
|
+
|
|
82
|
+
## License
|
|
83
|
+
|
|
84
|
+
MIT
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
wx_ocr/__init__.py,sha256=MvD1EOE2ietqkQizcifkHyi0ZE7jTDb7S3VF0ioRLuw,4250
|
|
2
|
+
wx_ocr/cli.py,sha256=9gNR4TYCeEdfQE9oAeGNXtCICZtnSya2P3w7ggwLg_o,9513
|
|
3
|
+
wx_ocr/default_callback.py,sha256=ACiaTs7_TsRgHXLDMGwwSAivNAUdQ4FLEdVBjMOc7Ps,1973
|
|
4
|
+
wx_ocr/mmmojo_dll.py,sha256=9Wnv8-HqNnFMQxSLRWzeLwULiraTgBnLj66VENRTL9k,6655
|
|
5
|
+
wx_ocr/ocr_manager.py,sha256=AzoM7B6wtRMyE9Fiixq-ktOCdCXF_5SaXCTD_biCwCc,5868
|
|
6
|
+
wx_ocr/ocr_protobuf_pb2.py,sha256=S7g4JWkl_TxPjxO506gekneI0w35lK2g5gFBMIAU4fI,3249
|
|
7
|
+
wx_ocr/simple_api.py,sha256=6HA7G_7VvajBvNWk-StlhNBT1ykHEHLWDfkhr81vi3k,9983
|
|
8
|
+
wx_ocr/utility_protobuf_pb2.py,sha256=76Fy777BK_-B5ZXhOwKD_SsR8P1B6lDPysiAv2PtAHg,3064
|
|
9
|
+
wx_ocr/utils.py,sha256=tML-ajGRk-RT72_A2YsYuisd8kwQgcQtDGYvJb0tPRo,4492
|
|
10
|
+
wx_ocr/winapi.py,sha256=yrPqgcrLZSGPkRPpwJUl2_WYiR9aPGZdbtf66ewt_Ec,8559
|
|
11
|
+
wx_ocr/xplugin_manager.py,sha256=5QtTSfN5XUBVrBPg2a-OSK5DZKsL5Sj1xsbcwuYMNNE,8573
|
|
12
|
+
wx_ocr/wco_data/WeChatOCR.exe,sha256=mJ5cLQ5RwbK3XoMTrHtoYv6bZ94lkZVaO-EUnIf_NaE,36592232
|
|
13
|
+
wx_ocr/wco_data/mmmojo.dll,sha256=nxZMcAU1bTG9FKpH8kmo_FXbky31AwrKAn2jy2ftOqg,2077064
|
|
14
|
+
wx_ocr/wco_data/mmmojo_64.dll,sha256=1RM90skodkbNw0Zhy1pcSy5HpyzWC_COQAJTMhqMuJQ,2495368
|
|
15
|
+
wx_ocr/wco_data/x64.config,sha256=Vgn3KEA-GXuyVe9Qxirqux-TsJ97fDeZA0QLZc1DGcs,3
|
|
16
|
+
wx_ocr/wco_data/Model/FPOCRRecog.xnet,sha256=HPs6fXm5naksbYpZr9tvxEF0C8b1oW39xAtoEKTMH08,9578175
|
|
17
|
+
wx_ocr/wco_data/Model/OCRDetFP32.xnet.nas,sha256=qempkMpohyurp6N6m_HK2rucVLhMnLQ84ZY0YZ90Rng,1604697
|
|
18
|
+
wx_ocr/wco_data/Model/OCRParaDetV1.1.0.26.xnet,sha256=rZKTtdZHpXM36uh3ND6t81r4CmmbLaR2di-Bskt0Om4,2834840
|
|
19
|
+
wx_ocr/wco_data/Model/OCRRecogFP32V1.1.0.26.xnet,sha256=BhyDtbP8p9ehBjzTSSQq3ryTvmTNmwe1-BobyxqHV6A,7642697
|
|
20
|
+
wx_ocr/wco_data/Model/RecDict,sha256=1yWlaEHaBi25CIWLvXKZNsg1i5orbNVEQ2OzsIzm2vk,59801
|
|
21
|
+
wx_ocr/wco_data/Model/sohu_simp.txt,sha256=HNB23VmdGxZoVWK8SqC0f448w2tkQOYcEmTo-vv-iMk,24950
|
|
22
|
+
wx_ocr-0.1.3.dist-info/licenses/LICENSE,sha256=BAqHW4gQ1q31CJhJfP7FAwcYaUI1jUPnWvmf52dP_JM,1101
|
|
23
|
+
wx_ocr-0.1.3.dist-info/METADATA,sha256=iEBg4hpxTiE8BE5IFlxQCgBqkJl-3LrkoB3AXSgje20,2180
|
|
24
|
+
wx_ocr-0.1.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
25
|
+
wx_ocr-0.1.3.dist-info/entry_points.txt,sha256=RmsGyMOv-Efc-_kSfHFF9KzQX13c6Gd7APExrKsiJGA,43
|
|
26
|
+
wx_ocr-0.1.3.dist-info/top_level.txt,sha256=mJ57_4n8IkcgYcyOV6Z3ZMacnFhy73XDGRUfo9feH2c,7
|
|
27
|
+
wx_ocr-0.1.3.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 wechat-ocr contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
wx_ocr
|