Compare commits
12 Commits
3f5727e1b0
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| d1c5760329 | |||
| 95b658527a | |||
| 64347726f0 | |||
| b96edf83a5 | |||
| 97b4c38863 | |||
| 7fca8d8df6 | |||
| 79e1da12e8 | |||
| 721bd2bb5c | |||
| fffa977d18 | |||
| eaa8ae41d5 | |||
| f0eda5ed50 | |||
| 34cf6e9cbf |
@@ -34,4 +34,4 @@ jobs:
|
|||||||
context: .
|
context: .
|
||||||
file: Dockerfile
|
file: Dockerfile
|
||||||
push: true
|
push: true
|
||||||
tags: ${{ env.DOCKER_REGISTRY }}/${{ env.IMAGE_NAME }}:latest-${{ env.ARCH }}
|
tags: ${{ env.DOCKER_REGISTRY }}/${{ env.IMAGE_NAME }}:latest
|
||||||
|
|||||||
28
Dockerfile
28
Dockerfile
@@ -1,18 +1,20 @@
|
|||||||
# FROM greyltc/archlinux-aur:yay AS lib
|
FROM archlinux:base-devel AS lib
|
||||||
# WORKDIR /src
|
WORKDIR /src
|
||||||
# RUN yay -Syu --noconfirm cmake python nodejs && \
|
RUN pacman -Syu --noconfirm git cmake python nodejs && \
|
||||||
# git clone https://github.com/swigger/wechat-ocr.git wcocr && \
|
git clone https://github.com/swigger/wechat-ocr.git wcocr && \
|
||||||
# sed -i 's/v3.21.0/v3.21.2/g' wcocr/CMakeLists.txt && \
|
mkdir -p wcocr/build && cd wcocr/build && cmake .. && make -j$(nproc)
|
||||||
# mkdir -p wcocr/build && cd wcocr/build && cmake .. && make -j$(nproc)
|
|
||||||
|
|
||||||
FROM greyltc/archlinux-aur:yay
|
FROM greyltc/archlinux-aur:yay AS runner
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
COPY . .
|
||||||
|
COPY --from=lib /src/wcocr/build/libwcocr.so .
|
||||||
|
# update mirrorlist
|
||||||
|
ADD https://raw.githubusercontent.com/greyltc/docker-archlinux/master/get-new-mirrors.sh /usr/bin/get-new-mirrors
|
||||||
|
RUN chmod +x /usr/bin/get-new-mirrors && \
|
||||||
|
get-new-mirrors
|
||||||
RUN aur-install bun-bin wechat-bin && \
|
RUN aur-install bun-bin wechat-bin && \
|
||||||
echo "Hello World"
|
bun install --frozen-lockfile
|
||||||
# COPY --from=lib /src/wcocr/build/libwcocr.so .
|
|
||||||
# COPY src/index.ts .
|
|
||||||
# RUN bun install hono
|
|
||||||
|
|
||||||
# EXPOSE 3000
|
EXPOSE 3000
|
||||||
|
|
||||||
# CMD ["bun", "src/index.ts"]
|
CMD ["bun", "src/index.ts"]
|
||||||
|
|||||||
6
bun.lock
6
bun.lock
@@ -4,7 +4,9 @@
|
|||||||
"": {
|
"": {
|
||||||
"name": "wxocr",
|
"name": "wxocr",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@hono/zod-validator": "^0.7.1",
|
||||||
"hono": "^4.8.4",
|
"hono": "^4.8.4",
|
||||||
|
"zod": "^4.0.5",
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@antfu/eslint-config": "^4.16.2",
|
"@antfu/eslint-config": "^4.16.2",
|
||||||
@@ -67,6 +69,8 @@
|
|||||||
|
|
||||||
"@eslint/plugin-kit": ["@eslint/plugin-kit@0.3.3", "", { "dependencies": { "@eslint/core": "^0.15.1", "levn": "^0.4.1" } }, "sha512-1+WqvgNMhmlAambTvT3KPtCl/Ibr68VldY2XY40SL1CE0ZXiakFR/cbTspaF5HsnpDMvcYYoJHfl4980NBjGag=="],
|
"@eslint/plugin-kit": ["@eslint/plugin-kit@0.3.3", "", { "dependencies": { "@eslint/core": "^0.15.1", "levn": "^0.4.1" } }, "sha512-1+WqvgNMhmlAambTvT3KPtCl/Ibr68VldY2XY40SL1CE0ZXiakFR/cbTspaF5HsnpDMvcYYoJHfl4980NBjGag=="],
|
||||||
|
|
||||||
|
"@hono/zod-validator": ["@hono/zod-validator@0.7.1", "", { "peerDependencies": { "hono": ">=3.9.0", "zod": "^3.25.0 || ^4.0.0" } }, "sha512-8+vJT1RvezAx5sN7hiZ5Mis0RMuFL77nBEcqQQgT9ufoLkxr+7ll461WlBJQcGoQSY6EGMClVae19v3s/7bbgQ=="],
|
||||||
|
|
||||||
"@humanfs/core": ["@humanfs/core@0.19.1", "", {}, "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA=="],
|
"@humanfs/core": ["@humanfs/core@0.19.1", "", {}, "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA=="],
|
||||||
|
|
||||||
"@humanfs/node": ["@humanfs/node@0.16.6", "", { "dependencies": { "@humanfs/core": "^0.19.1", "@humanwhocodes/retry": "^0.3.0" } }, "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw=="],
|
"@humanfs/node": ["@humanfs/node@0.16.6", "", { "dependencies": { "@humanfs/core": "^0.19.1", "@humanwhocodes/retry": "^0.3.0" } }, "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw=="],
|
||||||
@@ -633,6 +637,8 @@
|
|||||||
|
|
||||||
"yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="],
|
"yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="],
|
||||||
|
|
||||||
|
"zod": ["zod@4.0.5", "", {}, "sha512-/5UuuRPStvHXu7RS+gmvRf4NXrNxpSllGwDnCBcJZtQsKrviYXm54yDGV2KYNLT5kq0lHGcl7lqWJLgSaG+tgA=="],
|
||||||
|
|
||||||
"zwitch": ["zwitch@2.0.4", "", {}, "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A=="],
|
"zwitch": ["zwitch@2.0.4", "", {}, "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A=="],
|
||||||
|
|
||||||
"@eslint-community/eslint-utils/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="],
|
"@eslint-community/eslint-utils/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="],
|
||||||
|
|||||||
@@ -11,7 +11,9 @@
|
|||||||
"typescript": "^5.8.3"
|
"typescript": "^5.8.3"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"hono": "^4.8.4"
|
"@hono/zod-validator": "^0.7.1",
|
||||||
|
"hono": "^4.8.4",
|
||||||
|
"zod": "^4.0.5"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@antfu/eslint-config": "^4.16.2",
|
"@antfu/eslint-config": "^4.16.2",
|
||||||
|
|||||||
26
src/index.ts
26
src/index.ts
@@ -1,7 +1,31 @@
|
|||||||
|
import { zValidator } from '@hono/zod-validator'
|
||||||
import { Hono } from 'hono'
|
import { Hono } from 'hono'
|
||||||
|
import { Buffer } from 'node:buffer'
|
||||||
|
import { z } from 'zod'
|
||||||
|
|
||||||
|
import { WechatOCRClient } from './wcocr'
|
||||||
|
|
||||||
const app = new Hono()
|
const app = new Hono()
|
||||||
|
const client = new WechatOCRClient()
|
||||||
|
|
||||||
app.get('/', c => c.text('Hello World'))
|
app.post(
|
||||||
|
'/ocr',
|
||||||
|
zValidator('form', z.object({
|
||||||
|
image: z.instanceof(File, { message: 'Image file is required' }),
|
||||||
|
})),
|
||||||
|
async (c) => {
|
||||||
|
const { image } = c.req.valid('form')
|
||||||
|
|
||||||
|
const imageBuffer = Buffer.from(await image.arrayBuffer())
|
||||||
|
|
||||||
|
try {
|
||||||
|
const result = await client.recognize(imageBuffer)
|
||||||
|
return c.json(JSON.parse(result))
|
||||||
|
} catch (error) {
|
||||||
|
console.error('OCR processing error:', error)
|
||||||
|
return c.json({ error: 'Failed to process image' }, 500)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
export default app
|
export default app
|
||||||
|
|||||||
55
src/wcocr.ts
55
src/wcocr.ts
@@ -2,10 +2,14 @@ import type { Pointer } from 'bun:ffi'
|
|||||||
|
|
||||||
import { CString, dlopen, FFIType, JSCallback, suffix } from 'bun:ffi'
|
import { CString, dlopen, FFIType, JSCallback, suffix } from 'bun:ffi'
|
||||||
import { Buffer } from 'node:buffer'
|
import { Buffer } from 'node:buffer'
|
||||||
|
import { randomBytes } from 'node:crypto'
|
||||||
|
import { rm, writeFile } from 'node:fs/promises'
|
||||||
|
import { tmpdir } from 'node:os'
|
||||||
|
import { join } from 'node:path'
|
||||||
|
|
||||||
const libPath = `libwcocr.${suffix}`
|
const libPath = `libwcocr.${suffix}`
|
||||||
|
|
||||||
const WechatOCR = dlopen(libPath, {
|
const WechatOCRFFI = dlopen(libPath, {
|
||||||
wechat_ocr: {
|
wechat_ocr: {
|
||||||
args: [
|
args: [
|
||||||
FFIType.cstring, // ocr_exe路径
|
FFIType.cstring, // ocr_exe路径
|
||||||
@@ -21,12 +25,16 @@ const WechatOCR = dlopen(libPath, {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
// 主函数
|
export class WechatOCRClient {
|
||||||
async function callWechatOcr(
|
private ocrExe: string
|
||||||
ocrExe: string,
|
private wechatDir: string
|
||||||
wechatDir: string,
|
|
||||||
imgfn: string,
|
constructor(options?: { ocrExe?: string, wechatDir?: string }) {
|
||||||
): Promise<string> {
|
this.ocrExe = options?.ocrExe ?? '/opt/wechat/wxocr'
|
||||||
|
this.wechatDir = options?.wechatDir ?? '/opt/wechat'
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _callWechatOcr(imgfn: string): Promise<string> {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
// 创建回调函数
|
// 创建回调函数
|
||||||
const callback = new JSCallback(
|
const callback = new JSCallback(
|
||||||
@@ -42,38 +50,33 @@ async function callWechatOcr(
|
|||||||
)
|
)
|
||||||
|
|
||||||
// 调用wechat_ocr函数
|
// 调用wechat_ocr函数
|
||||||
const result = WechatOCR.symbols.wechat_ocr(
|
const result = WechatOCRFFI.symbols.wechat_ocr(
|
||||||
Buffer.from(`${ocrExe}\0`), // 转换为C字符串
|
Buffer.from(`${this.ocrExe}\0`), // 转换为C字符串
|
||||||
Buffer.from(`${wechatDir}\0`),
|
Buffer.from(`${this.wechatDir}\0`),
|
||||||
Buffer.from(`${imgfn}\0`),
|
Buffer.from(`${imgfn}\0`),
|
||||||
callback, // 直接传递JSCallback实例
|
callback, // 直接传递JSCallback实例
|
||||||
)
|
)
|
||||||
|
|
||||||
if (!result) {
|
if (!result) {
|
||||||
callback.close()
|
callback.close()
|
||||||
reject(new Error('OCR调用失败'))
|
reject(new Error('OCR call failed'))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async function main() {
|
public async recognize(image: Buffer): Promise<string> {
|
||||||
// 请在这里替换为你的实际路径
|
const tempPath = join(tmpdir(), `wechat-ocr-${randomBytes(16).toString('hex')}.tmp`)
|
||||||
const ocrExe = '/opt/wechat/wxocr'
|
await writeFile(tempPath, image)
|
||||||
const wechatDir = '/opt/wechat'
|
|
||||||
const imgfn = '/home/imbytecat/Pictures/Screenshots/Screenshot_07-Jul_10-00-20_9907.png'
|
|
||||||
|
|
||||||
console.log('OCR开始...')
|
|
||||||
try {
|
try {
|
||||||
const result = await callWechatOcr(ocrExe, wechatDir, imgfn)
|
const result = await this._callWechatOcr(tempPath)
|
||||||
console.log('OCR结果:', result)
|
return result
|
||||||
} catch (error) {
|
|
||||||
console.error('OCR错误:', error)
|
|
||||||
} finally {
|
} finally {
|
||||||
// 清理资源
|
await rm(tempPath, { force: true })
|
||||||
WechatOCR.symbols.stop_ocr()
|
|
||||||
console.log('OCR结束...')
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 运行主函数
|
public close() {
|
||||||
main()
|
WechatOCRFFI.symbols.stop_ocr()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user