- 变更基础镜像阶段为运行阶段,同时添加微信二进制包,复制项目文件并安装依赖。 - 添加处理OCR请求的端点以识别上传图像中的文本。 - 将OCR功能封装到WechatOCRClient类中并添加临时文件处理逻辑。
This commit is contained in:
15
Dockerfile
15
Dockerfile
@@ -5,14 +5,13 @@ RUN pacman -Syu --noconfirm cmake python nodejs && \
|
|||||||
sed -i 's/v3.21.0/v3.21.2/g' wcocr/CMakeLists.txt && \
|
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 AS builder
|
FROM greyltc/archlinux-aur:yay AS runner
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
RUN aur-install bun-bin
|
RUN aur-install bun-bin wechat-bin
|
||||||
|
COPY . .
|
||||||
COPY --from=lib /src/wcocr/build/libwcocr.so .
|
COPY --from=lib /src/wcocr/build/libwcocr.so .
|
||||||
# COPY src/index.ts .
|
RUN bun install --frozen-lockfile
|
||||||
|
|
||||||
# FROM greyltc/archlinux-aur:yay AS runner
|
EXPOSE 3000
|
||||||
# WORKDIR /app
|
|
||||||
# RUN aur-install wechat-bin
|
CMD ["bun", "src/index.ts"]
|
||||||
# COPY --from=builder /app/bun-bin .
|
|
||||||
# COPY --from=lib /src/wcocr/build/libwcocr.so .
|
|
||||||
|
|||||||
23
src/index.ts
23
src/index.ts
@@ -1,7 +1,28 @@
|
|||||||
import { Hono } from 'hono'
|
import { Hono } from 'hono'
|
||||||
|
import { Buffer } from 'node:buffer'
|
||||||
|
|
||||||
|
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', async (c) => {
|
||||||
|
const formData = await c.req.formData()
|
||||||
|
const imageFile = formData.get('image')
|
||||||
|
|
||||||
|
if (!imageFile || typeof imageFile === 'string') {
|
||||||
|
return c.json({ error: 'Image file is required' }, 400)
|
||||||
|
}
|
||||||
|
|
||||||
|
const imageBuffer = Buffer.from(await imageFile.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
|
||||||
|
|||||||
105
src/wcocr.ts
105
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,58 +25,59 @@ const WechatOCR = dlopen(libPath, {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
// 主函数
|
export class WechatOCRClient {
|
||||||
async function callWechatOcr(
|
private ocrExe: string
|
||||||
ocrExe: string,
|
private wechatDir: string
|
||||||
wechatDir: string,
|
|
||||||
imgfn: string,
|
|
||||||
): Promise<string> {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
// 创建回调函数
|
|
||||||
const callback = new JSCallback(
|
|
||||||
(argPtr: Pointer) => {
|
|
||||||
const arg = new CString(argPtr).toString()
|
|
||||||
resolve(arg)
|
|
||||||
callback.close() // 释放回调
|
|
||||||
},
|
|
||||||
{
|
|
||||||
args: [FFIType.ptr],
|
|
||||||
returns: FFIType.void,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
// 调用wechat_ocr函数
|
constructor(options?: { ocrExe?: string, wechatDir?: string }) {
|
||||||
const result = WechatOCR.symbols.wechat_ocr(
|
this.ocrExe = options?.ocrExe ?? '/opt/wechat/wxocr'
|
||||||
Buffer.from(`${ocrExe}\0`), // 转换为C字符串
|
this.wechatDir = options?.wechatDir ?? '/opt/wechat'
|
||||||
Buffer.from(`${wechatDir}\0`),
|
}
|
||||||
Buffer.from(`${imgfn}\0`),
|
|
||||||
callback, // 直接传递JSCallback实例
|
|
||||||
)
|
|
||||||
|
|
||||||
if (!result) {
|
private async _callWechatOcr(imgfn: string): Promise<string> {
|
||||||
callback.close()
|
return new Promise((resolve, reject) => {
|
||||||
reject(new Error('OCR调用失败'))
|
// 创建回调函数
|
||||||
|
const callback = new JSCallback(
|
||||||
|
(argPtr: Pointer) => {
|
||||||
|
const arg = new CString(argPtr).toString()
|
||||||
|
resolve(arg)
|
||||||
|
callback.close() // 释放回调
|
||||||
|
},
|
||||||
|
{
|
||||||
|
args: [FFIType.ptr],
|
||||||
|
returns: FFIType.void,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
// 调用wechat_ocr函数
|
||||||
|
const result = WechatOCRFFI.symbols.wechat_ocr(
|
||||||
|
Buffer.from(`${this.ocrExe}\0`), // 转换为C字符串
|
||||||
|
Buffer.from(`${this.wechatDir}\0`),
|
||||||
|
Buffer.from(`${imgfn}\0`),
|
||||||
|
callback, // 直接传递JSCallback实例
|
||||||
|
)
|
||||||
|
|
||||||
|
if (!result) {
|
||||||
|
callback.close()
|
||||||
|
reject(new Error('OCR call failed'))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
public async recognize(image: Buffer): Promise<string> {
|
||||||
|
const tempPath = join(tmpdir(), `wechat-ocr-temp-${randomBytes(16).toString('hex')}.png`)
|
||||||
|
await writeFile(tempPath, image)
|
||||||
|
|
||||||
|
try {
|
||||||
|
const result = await this._callWechatOcr(tempPath)
|
||||||
|
return result
|
||||||
|
} finally {
|
||||||
|
await rm(tempPath, { force: true })
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
}
|
|
||||||
|
|
||||||
async function main() {
|
public close() {
|
||||||
const ocrExe = '/opt/wechat/wxocr'
|
WechatOCRFFI.symbols.stop_ocr()
|
||||||
const wechatDir = '/opt/wechat'
|
console.log('OCR resources cleaned up.')
|
||||||
const imgfn = '/home/imbytecat/Pictures/Screenshots/Screenshot_07-Jul_10-00-20_9907.png'
|
|
||||||
|
|
||||||
console.log('OCR开始...')
|
|
||||||
try {
|
|
||||||
const result = await callWechatOcr(ocrExe, wechatDir, imgfn)
|
|
||||||
console.log('OCR结果:', result)
|
|
||||||
} catch (error) {
|
|
||||||
console.error('OCR错误:', error)
|
|
||||||
} finally {
|
|
||||||
// 清理资源
|
|
||||||
WechatOCR.symbols.stop_ocr()
|
|
||||||
console.log('OCR结束...')
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 运行主函数
|
|
||||||
main()
|
|
||||||
|
|||||||
Reference in New Issue
Block a user