From df609cce2f1cd0af2d3fbf3c5d6f89019ca66eea Mon Sep 17 00:00:00 2001
From: programmerWsy <113076850+Cunninger@users.noreply.github.com>
Date: Sat, 11 Jan 2025 12:03:54 +0800
Subject: [PATCH 01/23] Initial commit
---
README.md | 2 ++
1 file changed, 2 insertions(+)
create mode 100644 README.md
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..4885b47
--- /dev/null
+++ b/README.md
@@ -0,0 +1,2 @@
+# ocr-based-qwen
+逆向https://chat.qwenlm.ai/ 的OCR
From 62054add3b7c28654b7a3b2e481a622188983d27 Mon Sep 17 00:00:00 2001
From: programmerWsy <113076850+Cunninger@users.noreply.github.com>
Date: Sat, 11 Jan 2025 12:05:59 +0800
Subject: [PATCH 02/23] Create worker.js
---
worker.js | 772 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 772 insertions(+)
create mode 100644 worker.js
diff --git a/worker.js b/worker.js
new file mode 100644
index 0000000..54a7e91
--- /dev/null
+++ b/worker.js
@@ -0,0 +1,772 @@
+addEventListener('fetch', event => {
+ event.respondWith(handleRequest(event.request));
+});
+
+async function handleRequest(request) {
+ const url = new URL(request.url);
+
+ // 处理 CORS 预检请求
+ if (request.method === 'OPTIONS') {
+ return new Response(null, {
+ headers: {
+ 'Access-Control-Allow-Origin': '*',
+ 'Access-Control-Allow-Methods': 'POST, OPTIONS',
+ 'Access-Control-Allow-Headers': 'Content-Type, Authorization',
+ },
+ });
+ }
+
+ // 处理 POST 请求
+ if (request.method === 'POST' && url.pathname === '/recognize') {
+ try {
+ const { token, imageId } = await request.json();
+
+ if (!token || !imageId) {
+ return new Response(JSON.stringify({ error: 'Missing token or imageId' }), {
+ status: 400,
+ headers: { 'Content-Type': 'application/json' },
+ });
+ }
+
+ // 调用 QwenLM API
+ const response = await fetch('https://chat.qwenlm.ai/api/chat/completions', {
+ method: 'POST',
+ headers: {
+ 'accept': '*/*',
+ 'authorization': `Bearer ${token}`,
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify({
+ stream: false,
+ model: 'qwen-vl-max-latest',
+ messages: [
+ {
+ role: 'user',
+ content: [
+ { type: 'text', text: '请严格只返回图片中的内容,不要添加任何解释、描述或多余的文字' },
+ { type: 'image', image: imageId }, // 使用上传后的图片 ID
+ ],
+ },
+ ],
+ session_id: '1',
+ chat_id: '2',
+ id: '3',
+ }),
+ });
+
+ const data = await response.json();
+ return new Response(JSON.stringify(data), {
+ headers: {
+ 'Content-Type': 'application/json',
+ 'Access-Control-Allow-Origin': '*',
+ },
+ });
+ } catch (error) {
+ return new Response(JSON.stringify({ error: 'Internal Server Error' }), {
+ status: 500,
+ headers: { 'Content-Type': 'application/json' },
+ });
+ }
+ }
+
+ // 返回前端界面
+ return new Response(getHTML(), {
+ headers: { 'Content-Type': 'text/html' },
+ });
+}
+
+function getHTML() {
+ return `
+
+
+
+
+
+ 智能图片识别
+
+
+
+
+
+
+
+
智能图片识别
+
+
📸
+
+ 拖拽图片到这里,或点击上传
+ 支持复制粘贴图片
+
+
![]()
+
+
+
+
+
+
+
+
+
![]()
+
+
+
+
+
+ `;
+}
From 6b0d0520fe8b3a6f4c7f1a89ae4169276620d075 Mon Sep 17 00:00:00 2001
From: programmerWsy <113076850+Cunninger@users.noreply.github.com>
Date: Sat, 11 Jan 2025 12:08:38 +0800
Subject: [PATCH 03/23] Update README.md
---
README.md | 63 +++++++++++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 61 insertions(+), 2 deletions(-)
diff --git a/README.md b/README.md
index 4885b47..d5d8a15 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,61 @@
-# ocr-based-qwen
-逆向https://chat.qwenlm.ai/ 的OCR
+# 🖼️ QwenLM OCR 逆向工程项目
+
+本项目是对 [QwenLM](https://chat.qwenlm.ai/) 的 OCR 功能进行逆向工程的实现。通过调用 QwenLM 的 API,你可以从图片中提取文字内容,并且该项目支持一键部署到 **Cloudflare Workers** (CF) 上。
+
+## 🚀 功能特性
+
+- **图片 OCR**:使用 QwenLM 强大的 OCR 功能从图片中提取文字。
+- **拖拽上传**:直接将图片拖拽到页面即可识别。
+- **复制粘贴**:支持从剪贴板直接粘贴图片进行识别。
+- **Token 管理**:支持多 Token 轮询使用,提升稳定性。
+- **历史记录**:保存每次识别的结果和图片,方便查看。
+- **一键复制**:轻松复制识别结果到剪贴板。
+
+## 🛠️ 部署指南
+
+### 1. 部署到 Cloudflare Workers
+
+1. **配置 Cloudflare Workers**:
+ - 登录 [Cloudflare Dashboard](https://dash.cloudflare.com/)。
+ - 创建一个新的 Worker。
+ - 将 `worker.js` 中的代码复制到 Worker 编辑器中。
+
+2. **部署**:
+ - 保存并部署 Worker。
+ - 获取 Worker 的访问地址,即可使用。
+
+
+## 🧩 使用说明
+
+1. **设置 Token**:
+ - 点击右上角的 **⚙️ Token设置** 按钮。
+ - 输入你的 QwenLM API Token(多个 Token 用英文逗号分隔)。
+ - 点击 **保存**。
+
+2. **上传图片**:
+ - 拖拽图片到页面,或点击上传区域选择图片。
+ - 支持直接粘贴图片。
+
+3. **查看结果**:
+ - 识别结果会显示在页面下方。
+ - 点击 **复制结果** 按钮,将识别内容复制到剪贴板。
+
+4. **查看历史记录**:
+ - 点击左侧的 **📋 识别历史** 按钮,查看历史识别记录。
+ - 点击历史记录中的图片,可以查看大图。
+
+
+
+## 📜 许可证
+
+本项目基于 MIT 许可证开源。详情请查看 [LICENSE](LICENSE) 文件。
+
+## 🙏 致谢
+
+- 感谢 [QwenLM](https://chat.qwenlm.ai/) 提供的 OCR 功能。
+- 感谢 Cloudflare 提供的 Workers 服务。
+
+
+---
+
+🌟 如果觉得这个项目对你有帮助,欢迎点个 Star 支持一下!🌟
From 74ed7662fb915da67510359496f07c76264aedfd Mon Sep 17 00:00:00 2001
From: programmerWsy <113076850+Cunninger@users.noreply.github.com>
Date: Sat, 11 Jan 2025 12:11:40 +0800
Subject: [PATCH 04/23] Update README.md
---
README.md | 2 ++
1 file changed, 2 insertions(+)
diff --git a/README.md b/README.md
index d5d8a15..fc68982 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,8 @@
# 🖼️ QwenLM OCR 逆向工程项目
本项目是对 [QwenLM](https://chat.qwenlm.ai/) 的 OCR 功能进行逆向工程的实现。通过调用 QwenLM 的 API,你可以从图片中提取文字内容,并且该项目支持一键部署到 **Cloudflare Workers** (CF) 上。
+## 项目展示
+
## 🚀 功能特性
From 1afe2cbb00151154b5b6f836b6aa55d9d79bd811 Mon Sep 17 00:00:00 2001
From: programmerWsy <113076850+Cunninger@users.noreply.github.com>
Date: Mon, 13 Jan 2025 10:45:05 +0800
Subject: [PATCH 05/23] Update README.md
---
README.md | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)
diff --git a/README.md b/README.md
index fc68982..002e4a9 100644
--- a/README.md
+++ b/README.md
@@ -30,19 +30,22 @@
## 🧩 使用说明
1. **设置 Token**:
+ - 获取token:
+ 
+
- 点击右上角的 **⚙️ Token设置** 按钮。
- 输入你的 QwenLM API Token(多个 Token 用英文逗号分隔)。
- 点击 **保存**。
-2. **上传图片**:
+3. **上传图片**:
- 拖拽图片到页面,或点击上传区域选择图片。
- 支持直接粘贴图片。
-3. **查看结果**:
+4. **查看结果**:
- 识别结果会显示在页面下方。
- 点击 **复制结果** 按钮,将识别内容复制到剪贴板。
-4. **查看历史记录**:
+5. **查看历史记录**:
- 点击左侧的 **📋 识别历史** 按钮,查看历史识别记录。
- 点击历史记录中的图片,可以查看大图。
From f251d38ca0f7eb5e224a05b6480dabbab0b13fde Mon Sep 17 00:00:00 2001
From: programmerWsy <113076850+Cunninger@users.noreply.github.com>
Date: Mon, 13 Jan 2025 10:47:17 +0800
Subject: [PATCH 06/23] Update README.md
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 002e4a9..325b9d2 100644
--- a/README.md
+++ b/README.md
@@ -30,7 +30,7 @@
## 🧩 使用说明
1. **设置 Token**:
- - 获取token:
+ - 前往https://chat.qwenlm.ai/ 获取token:

- 点击右上角的 **⚙️ Token设置** 按钮。
From c6e39048c71e04d4d6dda6a863d27904989c5574 Mon Sep 17 00:00:00 2001
From: programmerWsy <113076850+Cunninger@users.noreply.github.com>
Date: Mon, 13 Jan 2025 16:55:45 +0800
Subject: [PATCH 07/23] Update README.md
---
README.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/README.md b/README.md
index 325b9d2..ed8a47c 100644
--- a/README.md
+++ b/README.md
@@ -59,6 +59,7 @@
- 感谢 [QwenLM](https://chat.qwenlm.ai/) 提供的 OCR 功能。
- 感谢 Cloudflare 提供的 Workers 服务。
+- 感谢 @JIU-W,@lonelykkk的思路支持
---
From 3b1523b6f2f935d44c5357c1a90c5095349905d5 Mon Sep 17 00:00:00 2001
From: JIU-W <142167539+JIU-W@users.noreply.github.com>
Date: Mon, 13 Jan 2025 17:37:13 +0800
Subject: [PATCH 08/23] Update README.md
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index ed8a47c..560f0ff 100644
--- a/README.md
+++ b/README.md
@@ -59,7 +59,7 @@
- 感谢 [QwenLM](https://chat.qwenlm.ai/) 提供的 OCR 功能。
- 感谢 Cloudflare 提供的 Workers 服务。
-- 感谢 @JIU-W,@lonelykkk的思路支持
+- 感谢 @lonelykkk的思路支持
---
From 8e8e09067150168fb07d4425177e3d03e08de48f Mon Sep 17 00:00:00 2001
From: lonelykkk <140876474+lonelykkk@users.noreply.github.com>
Date: Mon, 13 Jan 2025 18:12:34 +0800
Subject: [PATCH 09/23] Update README.md
---
README.md | 1 -
1 file changed, 1 deletion(-)
diff --git a/README.md b/README.md
index 560f0ff..325b9d2 100644
--- a/README.md
+++ b/README.md
@@ -59,7 +59,6 @@
- 感谢 [QwenLM](https://chat.qwenlm.ai/) 提供的 OCR 功能。
- 感谢 Cloudflare 提供的 Workers 服务。
-- 感谢 @lonelykkk的思路支持
---
From 838adbd6e0df40e3e50f516607456bec07e851f9 Mon Sep 17 00:00:00 2001
From: programmerWsy <113076850+Cunninger@users.noreply.github.com>
Date: Mon, 13 Jan 2025 18:45:57 +0800
Subject: [PATCH 10/23] Update README.md
---
README.md | 82 +++++++++++++++++++++++++++++++++++++++++++++++++------
1 file changed, 73 insertions(+), 9 deletions(-)
diff --git a/README.md b/README.md
index 325b9d2..7d7ad5f 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,7 @@
# 🖼️ QwenLM OCR 逆向工程项目
本项目是对 [QwenLM](https://chat.qwenlm.ai/) 的 OCR 功能进行逆向工程的实现。通过调用 QwenLM 的 API,你可以从图片中提取文字内容,并且该项目支持一键部署到 **Cloudflare Workers** (CF) 上。
+
## 项目展示

@@ -12,6 +13,8 @@
- **Token 管理**:支持多 Token 轮询使用,提升稳定性。
- **历史记录**:保存每次识别的结果和图片,方便查看。
- **一键复制**:轻松复制识别结果到剪贴板。
+- **数学公式识别**:特别优化了对数学公式的提取,支持 LaTeX 格式输出。
+- **API 支持**:提供 `curl` 接口调用,支持 base64 和图片 URL 两种方式。
## 🛠️ 部署指南
@@ -26,30 +29,45 @@
- 保存并部署 Worker。
- 获取 Worker 的访问地址,即可使用。
-
## 🧩 使用说明
1. **设置 Token**:
- - 前往https://chat.qwenlm.ai/ 获取token:
- 
-
+ - 前往 [QwenLM](https://chat.qwenlm.ai/) 获取 Token。
- 点击右上角的 **⚙️ Token设置** 按钮。
- 输入你的 QwenLM API Token(多个 Token 用英文逗号分隔)。
- 点击 **保存**。
-3. **上传图片**:
+2. **上传图片**:
- 拖拽图片到页面,或点击上传区域选择图片。
- 支持直接粘贴图片。
-4. **查看结果**:
+3. **查看结果**:
- 识别结果会显示在页面下方。
- 点击 **复制结果** 按钮,将识别内容复制到剪贴板。
-5. **查看历史记录**:
+4. **查看历史记录**:
- 点击左侧的 **📋 识别历史** 按钮,查看历史识别记录。
- 点击历史记录中的图片,可以查看大图。
-
+5. **API 调用**:
+ - **支持 base64**:
+ ```bash
+ curl --location 'https://ocr.doublefenzhuan.me/api/recognize/base64' \
+ --header 'Content-Type: application/json' \
+ --data '{
+ "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjUzZTk0Nzg4LWMwM2QtNDY4Mi05OTNhLWE0ZDNjNGUyZDY0OSIsImV4cCI6MTczOTA3NTE0MX0.FtwG6xDLYd2rngWUhuldg56WXCiLSTL0RI6xJJQ4vHM",
+ "base64Image": "xxx"
+ }'
+ ```
+ - **支持图片 URL**:
+ ```bash
+ curl --location 'https://ocr.doublefenzhuan.me/api/recognize/url' \
+ --header 'Content-Type: application/json' \
+ --data '{
+ "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjUzZTk0Nzg4LWMwM2QtNDY4Mi05OTNhLWE0ZDNjNGUyZDY0OSIsImV4cCI6MTczOTA3NTE0MX0.FtwG6xDLYd2rngWUhuldg56WXCiLSTL0RI6xJJQ4vHM",
+ "imageUrl": "xxxx"
+ }'
+ ```
## 📜 许可证
@@ -60,7 +78,53 @@
- 感谢 [QwenLM](https://chat.qwenlm.ai/) 提供的 OCR 功能。
- 感谢 Cloudflare 提供的 Workers 服务。
-
---
🌟 如果觉得这个项目对你有帮助,欢迎点个 Star 支持一下!🌟
+
+**体验地址**:[智能图片识别 (doublefenzhuan.me)](https://ocr.doublefenzhuan.me/)
+
+**GitHub 仓库**:[Cunninger/ocr-based-qwen](https://github.com/Cunninger/ocr-based-qwen)
+
+---
+
+#### 后续计划
+- 优化数学公式识别精度;
+- 增加更多 API 功能支持;
+- 提升识别速度和稳定性。
+
+快来体验吧!如果有任何问题或建议,欢迎在 GitHub 上提 Issue 或直接联系我!
+
+## 更新
+### 2025/01/13 应佬友需求,优化了对数学公式的识别,效果如下图
+ - 原图:
+
+
+ 识别效果图:
+ 
+
+### 2025/01/13 18点34分 支持`curl`接口调用
+- **支持base64**:
+```
+curl --location 'https://ocr.doublefenzhuan.me/api/recognize/base64' \
+--header 'Content-Type: application/json' \
+--data '{
+ "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjUzZTk0Nzg4LWMwM2QtNDY4Mi05OTNhLWE0ZDNjNGUyZDY0OSIsImV4cCI6MTczOTA3NTE0MX0.FtwG6xDLYd2rngWUhuldg56WXCiLSTL0RI6xJJQ4vHM",
+ "base64Image": "xxx"
+}'
+```
+- 效果图:
+
+
+- **支持图片URL**:
+```bash
+curl --location 'https://ocr.doublefenzhuan.me/api/recognize/url' \
+--header 'Content-Type: application/json' \
+--data '{
+ "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjUzZTk0Nzg4LWMwM2QtNDY4Mi05OTNhLWE0ZDNjNGUyZDY0OSIsImV4cCI6MTczOTA3NTE0MX0.FtwG6xDLYd2rngWUhuldg56WXCiLSTL0RI6xJJQ4vHM",
+
+ "imageUrl": "xxxx"
+}'
+```
+- 效果图:
+
From 1178a56d624027a5724b3216d3b62e790dbd9da7 Mon Sep 17 00:00:00 2001
From: programmerWsy <113076850+Cunninger@users.noreply.github.com>
Date: Mon, 13 Jan 2025 18:46:31 +0800
Subject: [PATCH 11/23] Update worker.js
---
worker.js | 1803 ++++++++++++++++++++++++++++++++---------------------
1 file changed, 1078 insertions(+), 725 deletions(-)
diff --git a/worker.js b/worker.js
index 54a7e91..b2b525c 100644
--- a/worker.js
+++ b/worker.js
@@ -16,757 +16,1110 @@ async function handleRequest(request) {
});
}
- // 处理 POST 请求
- if (request.method === 'POST' && url.pathname === '/recognize') {
- try {
- const { token, imageId } = await request.json();
-
- if (!token || !imageId) {
- return new Response(JSON.stringify({ error: 'Missing token or imageId' }), {
- status: 400,
- headers: { 'Content-Type': 'application/json' },
- });
+ // API路由处理
+ switch (url.pathname) {
+ // 1. 通过图片URL识别
+ case '/api/recognize/url':
+ if (request.method === 'POST') {
+ return handleImageUrlRecognition(request);
}
+ break;
- // 调用 QwenLM API
- const response = await fetch('https://chat.qwenlm.ai/api/chat/completions', {
- method: 'POST',
- headers: {
- 'accept': '*/*',
- 'authorization': `Bearer ${token}`,
- 'Content-Type': 'application/json',
- },
- body: JSON.stringify({
- stream: false,
- model: 'qwen-vl-max-latest',
- messages: [
- {
- role: 'user',
- content: [
- { type: 'text', text: '请严格只返回图片中的内容,不要添加任何解释、描述或多余的文字' },
- { type: 'image', image: imageId }, // 使用上传后的图片 ID
- ],
- },
- ],
- session_id: '1',
- chat_id: '2',
- id: '3',
- }),
- });
+ // 2. 通过Base64识别
+ case '/api/recognize/base64':
+ if (request.method === 'POST') {
+ return handleBase64Recognition(request);
+ }
+ break;
- const data = await response.json();
- return new Response(JSON.stringify(data), {
- headers: {
- 'Content-Type': 'application/json',
- 'Access-Control-Allow-Origin': '*',
- },
+ // 3. 通过图片文件识别 (原有的/recognize端点)
+ case '/recognize':
+ if (request.method === 'POST') {
+ return handleFileRecognition(request);
+ }
+ break;
+
+ // 返回前端界面
+ case '/':
+ return new Response(getHTML(), {
+ headers: { 'Content-Type': 'text/html' },
});
- } catch (error) {
- return new Response(JSON.stringify({ error: 'Internal Server Error' }), {
- status: 500,
+ }
+
+ return new Response('Not Found', { status: 404 });
+}
+
+// 处理图片URL识别
+async function handleImageUrlRecognition(request) {
+ try {
+ const { token, imageUrl } = await request.json();
+
+ if (!token || !imageUrl) {
+ return new Response(JSON.stringify({
+ error: 'Missing token or imageUrl'
+ }), {
+ status: 400,
headers: { 'Content-Type': 'application/json' },
});
}
- }
- // 返回前端界面
- return new Response(getHTML(), {
- headers: { 'Content-Type': 'text/html' },
+ // 下载图片
+ const imageResponse = await fetch(imageUrl);
+ const imageBlob = await imageResponse.blob();
+
+ // 上传到QwenLM
+ const formData = new FormData();
+ formData.append('file', imageBlob);
+
+ const uploadResponse = await fetch('https://chat.qwenlm.ai/api/v1/files/', {
+ method: 'POST',
+ headers: {
+ 'accept': 'application/json',
+ 'authorization': `Bearer ${token}`,
+ },
+ body: formData,
+ });
+
+ const uploadData = await uploadResponse.json();
+ if (!uploadData.id) throw new Error('File upload failed');
+
+ // 调用识别API
+ return await recognizeImage(token, uploadData.id);
+ } catch (error) {
+ return new Response(JSON.stringify({
+ error: error.message || 'Internal Server Error'
+ }), {
+ status: 500,
+ headers: { 'Content-Type': 'application/json' },
+ });
+ }
+}
+
+// 处理Base64识别
+async function handleBase64Recognition(request) {
+ try {
+ const { token, base64Image } = await request.json();
+
+ if (!token || !base64Image) {
+ return new Response(JSON.stringify({
+ error: 'Missing token or base64Image'
+ }), {
+ status: 400,
+ headers: { 'Content-Type': 'application/json' },
+ });
+ }
+
+ // 转换Base64为Blob
+ const imageData = base64Image.startsWith('data:') ?
+ base64Image :
+ 'data:image/png;base64,' + base64Image;
+
+ const response = await fetch(imageData);
+ const blob = await response.blob();
+
+ // 上传到QwenLM
+ const formData = new FormData();
+ formData.append('file', blob);
+
+ const uploadResponse = await fetch('https://chat.qwenlm.ai/api/v1/files/', {
+ method: 'POST',
+ headers: {
+ 'accept': 'application/json',
+ 'authorization': `Bearer ${token}`,
+ },
+ body: formData,
+ });
+
+ const uploadData = await uploadResponse.json();
+ if (!uploadData.id) throw new Error('File upload failed');
+
+ // 调用识别API
+ return await recognizeImage(token, uploadData.id);
+ } catch (error) {
+ return new Response(JSON.stringify({
+ error: error.message || 'Internal Server Error'
+ }), {
+ status: 500,
+ headers: { 'Content-Type': 'application/json' },
+ });
+ }
+}
+
+// 处理文件识别 (原有功能)
+async function handleFileRecognition(request) {
+ try {
+ const { token, imageId } = await request.json();
+
+ if (!token || !imageId) {
+ return new Response(JSON.stringify({
+ error: 'Missing token or imageId'
+ }), {
+ status: 400,
+ headers: { 'Content-Type': 'application/json' },
+ });
+ }
+
+ return await recognizeImage(token, imageId);
+ } catch (error) {
+ return new Response(JSON.stringify({
+ error: error.message || 'Internal Server Error'
+ }), {
+ status: 500,
+ headers: { 'Content-Type': 'application/json' },
+ });
+ }
+}
+
+// 通用的识别函数
+async function recognizeImage(token, imageId) {
+ const response = await fetch('https://chat.qwenlm.ai/api/chat/completions', {
+ method: 'POST',
+ headers: {
+ 'accept': '*/*',
+ 'authorization': `Bearer ${token}`,
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify({
+ stream: false,
+ model: 'qwen-vl-max-latest',
+ messages: [
+ {
+ role: 'user',
+ content: [
+ {
+ type: 'text',
+ text: '请识别图片中的内容。对于数学公式和数学符号,请使用标准的LaTeX格式输出。' +
+ '要求:\n' +
+ '1. 所有数学公式和单个数学符号都要用LaTeX格式\n' +
+ '2. 普通文本保持原样\n' +
+ '3. 对于行内公式使用$单个符号$\n' +
+ '4. 对于独立公式块使用$$公式$$\n' +
+ '5. 严格保持原文的段落格式和换行\n' +
+ '6. 当文本明显换行时,使用\\n进行换行处理'
+ },
+ { type: 'image', image: imageId },
+ ],
+ },
+ ],
+ session_id: '1',
+ chat_id: '2',
+ id: '3',
+ }),
+ });
+
+ const data = await response.json();
+
+ // 处理识别结果
+ let result = data.choices[0]?.message?.content || '识别失败';
+ result = result
+ .replace(/\\(/g, '\\(')
+ .replace(/\\)/g, '\\)')
+ .replace(/\n{3,}/g, '\n\n')
+ .replace(/([^\n])\n([^\n])/g, '$1\n$2')
+ .trim();
+
+ return new Response(JSON.stringify({
+ success: true,
+ result: result
+ }), {
+ headers: {
+ 'Content-Type': 'application/json',
+ 'Access-Control-Allow-Origin': '*',
+ },
});
}
function getHTML() {
- return `
-
-
-
-
-
- 智能图片识别
-
-
-
-
-
-
-
-
智能图片识别
-
-
📸
-
- 拖拽图片到这里,或点击上传
- 支持复制粘贴图片
-
-
![]()
-
-
-
-
-
-
-
-
-
![]()
-
-
- ',
+ '',
+ '',
+
+ '',
+ '',
+ '',
+ '',
+ '',
+
+ '',
+ '
智能图片识别
',
+ '
',
+ '
📸',
+ '
',
+ '拖拽图片到这里,点击上传,或粘贴Base64图片内容
',
+ '支持复制粘贴图片',
+ '
',
+ '
',
+ '
',
+ '
![]()
',
+ '
',
+ '
',
+ '
',
+ '
',
+ '',
+ '
',
+
+ '',
+ '
![]()
',
+ '
',
+
+ '',
+ '',
+ ''
+ ].join('\n');
+
+ return html;
}
From 3da4260d990e2bb932647f33f623348f103746a1 Mon Sep 17 00:00:00 2001
From: programmerWsy <113076850+Cunninger@users.noreply.github.com>
Date: Mon, 13 Jan 2025 18:47:59 +0800
Subject: [PATCH 12/23] Update README.md
---
README.md | 10 ++++++----
1 file changed, 6 insertions(+), 4 deletions(-)
diff --git a/README.md b/README.md
index 7d7ad5f..d496b17 100644
--- a/README.md
+++ b/README.md
@@ -97,11 +97,13 @@
## 更新
### 2025/01/13 应佬友需求,优化了对数学公式的识别,效果如下图
- - 原图:
+- 原图:
-
- 识别效果图:
- 
+
+
+- 识别效果图:
+
+
### 2025/01/13 18点34分 支持`curl`接口调用
- **支持base64**:
From a6a5241c269a70a388dc862ea3c08ee8a5a7a219 Mon Sep 17 00:00:00 2001
From: programmerWsy <113076850+Cunninger@users.noreply.github.com>
Date: Mon, 13 Jan 2025 18:50:30 +0800
Subject: [PATCH 13/23] Update worker.js
---
worker.js | 379 +++++++++++-------------------------------------------
1 file changed, 77 insertions(+), 302 deletions(-)
diff --git a/worker.js b/worker.js
index b2b525c..12269de 100644
--- a/worker.js
+++ b/worker.js
@@ -16,216 +16,86 @@ async function handleRequest(request) {
});
}
- // API路由处理
- switch (url.pathname) {
- // 1. 通过图片URL识别
- case '/api/recognize/url':
- if (request.method === 'POST') {
- return handleImageUrlRecognition(request);
+ // 处理 POST 请求
+ if (request.method === 'POST' && url.pathname === '/recognize') {
+ try {
+ const { token, imageId } = await request.json();
+
+ if (!token || !imageId) {
+ return new Response(JSON.stringify({ error: 'Missing token or imageId' }), {
+ status: 400,
+ headers: { 'Content-Type': 'application/json' },
+ });
}
- break;
- // 2. 通过Base64识别
- case '/api/recognize/base64':
- if (request.method === 'POST') {
- return handleBase64Recognition(request);
- }
- break;
-
- // 3. 通过图片文件识别 (原有的/recognize端点)
- case '/recognize':
- if (request.method === 'POST') {
- return handleFileRecognition(request);
- }
- break;
-
- // 返回前端界面
- case '/':
- return new Response(getHTML(), {
- headers: { 'Content-Type': 'text/html' },
- });
- }
-
- return new Response('Not Found', { status: 404 });
-}
-
-// 处理图片URL识别
-async function handleImageUrlRecognition(request) {
- try {
- const { token, imageUrl } = await request.json();
-
- if (!token || !imageUrl) {
- return new Response(JSON.stringify({
- error: 'Missing token or imageUrl'
- }), {
- status: 400,
- headers: { 'Content-Type': 'application/json' },
- });
- }
-
- // 下载图片
- const imageResponse = await fetch(imageUrl);
- const imageBlob = await imageResponse.blob();
-
- // 上传到QwenLM
- const formData = new FormData();
- formData.append('file', imageBlob);
-
- const uploadResponse = await fetch('https://chat.qwenlm.ai/api/v1/files/', {
- method: 'POST',
- headers: {
- 'accept': 'application/json',
- 'authorization': `Bearer ${token}`,
- },
- body: formData,
- });
-
- const uploadData = await uploadResponse.json();
- if (!uploadData.id) throw new Error('File upload failed');
-
- // 调用识别API
- return await recognizeImage(token, uploadData.id);
- } catch (error) {
- return new Response(JSON.stringify({
- error: error.message || 'Internal Server Error'
- }), {
- status: 500,
- headers: { 'Content-Type': 'application/json' },
- });
- }
-}
-
-// 处理Base64识别
-async function handleBase64Recognition(request) {
- try {
- const { token, base64Image } = await request.json();
-
- if (!token || !base64Image) {
- return new Response(JSON.stringify({
- error: 'Missing token or base64Image'
- }), {
- status: 400,
- headers: { 'Content-Type': 'application/json' },
- });
- }
-
- // 转换Base64为Blob
- const imageData = base64Image.startsWith('data:') ?
- base64Image :
- 'data:image/png;base64,' + base64Image;
-
- const response = await fetch(imageData);
- const blob = await response.blob();
-
- // 上传到QwenLM
- const formData = new FormData();
- formData.append('file', blob);
-
- const uploadResponse = await fetch('https://chat.qwenlm.ai/api/v1/files/', {
- method: 'POST',
- headers: {
- 'accept': 'application/json',
- 'authorization': `Bearer ${token}`,
- },
- body: formData,
- });
-
- const uploadData = await uploadResponse.json();
- if (!uploadData.id) throw new Error('File upload failed');
-
- // 调用识别API
- return await recognizeImage(token, uploadData.id);
- } catch (error) {
- return new Response(JSON.stringify({
- error: error.message || 'Internal Server Error'
- }), {
- status: 500,
- headers: { 'Content-Type': 'application/json' },
- });
- }
-}
-
-// 处理文件识别 (原有功能)
-async function handleFileRecognition(request) {
- try {
- const { token, imageId } = await request.json();
-
- if (!token || !imageId) {
- return new Response(JSON.stringify({
- error: 'Missing token or imageId'
- }), {
- status: 400,
- headers: { 'Content-Type': 'application/json' },
- });
- }
-
- return await recognizeImage(token, imageId);
- } catch (error) {
- return new Response(JSON.stringify({
- error: error.message || 'Internal Server Error'
- }), {
- status: 500,
- headers: { 'Content-Type': 'application/json' },
- });
- }
-}
-
-// 通用的识别函数
-async function recognizeImage(token, imageId) {
- const response = await fetch('https://chat.qwenlm.ai/api/chat/completions', {
- method: 'POST',
- headers: {
- 'accept': '*/*',
- 'authorization': `Bearer ${token}`,
- 'Content-Type': 'application/json',
- },
- body: JSON.stringify({
- stream: false,
- model: 'qwen-vl-max-latest',
- messages: [
- {
- role: 'user',
- content: [
- {
- type: 'text',
- text: '请识别图片中的内容。对于数学公式和数学符号,请使用标准的LaTeX格式输出。' +
- '要求:\n' +
- '1. 所有数学公式和单个数学符号都要用LaTeX格式\n' +
- '2. 普通文本保持原样\n' +
- '3. 对于行内公式使用$单个符号$\n' +
- '4. 对于独立公式块使用$$公式$$\n' +
- '5. 严格保持原文的段落格式和换行\n' +
- '6. 当文本明显换行时,使用\\n进行换行处理'
- },
- { type: 'image', image: imageId },
- ],
+ // 调用 QwenLM API
+ const response = await fetch('https://chat.qwenlm.ai/api/chat/completions', {
+ method: 'POST',
+ headers: {
+ 'accept': '*/*',
+ 'authorization': `Bearer ${token}`,
+ 'Content-Type': 'application/json',
},
- ],
- session_id: '1',
- chat_id: '2',
- id: '3',
- }),
- });
+ body: JSON.stringify({
+ stream: false,
+ model: 'qwen-vl-max-latest',
+ messages: [
+ {
+ role: 'user',
+ content: [
+ {
+ type: 'text',
+ text: '请识别图片中的内容。对于数学公式和数学符号,请使用标准的LaTeX格式输出。' +
+ '要求:\n' +
+ '1. 所有数学公式和单个数学符号都要用LaTeX格式\n' +
+ '2. 普通文本保持原样\n' +
+ '3. 对于行内公式使用$单个符号$\n' +
+ '4. 对于独立公式块使用$$公式$$\n' +
+ '5. 严格保持原文的段落格式和换行\n' +
+ '6. 当文本明显换行时,使用\\n进行换行处理\n' +
+ '请尽可能精确地转换每个数学符号并保持原始排版格式。'
+ },
+ { type: 'image', image: imageId }, // 使用上传后的图片 ID
+ ],
+ },
+ ],
+ session_id: '1',
+ chat_id: '2',
+ id: '3',
+ }),
+ });
- const data = await response.json();
-
- // 处理识别结果
- let result = data.choices[0]?.message?.content || '识别失败';
- result = result
- .replace(/\\(/g, '\\(')
- .replace(/\\)/g, '\\)')
- .replace(/\n{3,}/g, '\n\n')
- .replace(/([^\n])\n([^\n])/g, '$1\n$2')
- .trim();
+ const data = await response.json();
+
+ // 对识别结果进行后处理,确保LaTeX格式正确并保持换行
+ let result = data.choices[0]?.message?.content || '识别失败';
+ result = result
+ // 修复可能的LaTeX格式问题
+ .replace(/\\(/g, '\\(')
+ .replace(/\\)/g, '\\)')
+ // 确保连续的换行符被保留(2个以上的换行符表示段落分隔)
+ .replace(/\n{3,}/g, '\n\n')
+ // 保留单个换行符,不合并
+ .replace(/([^\n])\n([^\n])/g, '$1\n$2')
+ .trim();
- return new Response(JSON.stringify({
- success: true,
- result: result
- }), {
- headers: {
- 'Content-Type': 'application/json',
- 'Access-Control-Allow-Origin': '*',
- },
+ return new Response(JSON.stringify({ ...data, choices: [{ message: { content: result } }] }), {
+ headers: {
+ 'Content-Type': 'application/json',
+ 'Access-Control-Allow-Origin': '*',
+ },
+ });
+ } catch (error) {
+ return new Response(JSON.stringify({ error: 'Internal Server Error' }), {
+ status: 500,
+ headers: { 'Content-Type': 'application/json' },
+ });
+ }
+ }
+
+ // 返回前端界面
+ return new Response(getHTML(), {
+ headers: { 'Content-Type': 'text/html' },
});
}
@@ -686,30 +556,6 @@ function getHTML() {
' .copy-btn.copied {',
' background: #27ae60;',
' }',
-
- ' /* Base64输入相关样式 */',
- ' #base64Input {',
- ' width: 100%;',
- ' height: 100px;',
- ' padding: 10px;',
- ' margin-top: 10px;',
- ' border: 1px solid #dcdde1;',
- ' border-radius: 8px;',
- ' resize: vertical;',
- ' }',
- ' .toggle-btn {',
- ' background: #3498db;',
- ' color: white;',
- ' border: none;',
- ' padding: 8px 15px;',
- ' border-radius: 5px;',
- ' cursor: pointer;',
- ' margin-top: 10px;',
- ' transition: background 0.3s ease;',
- ' }',
- ' .toggle-btn:hover {',
- ' background: #2980b9;',
- ' }',
'',
'',
'',
@@ -732,11 +578,9 @@ function getHTML() {
'',
'
📸',
'
',
- '拖拽图片到这里,点击上传,或粘贴Base64图片内容
',
+ '拖拽图片到这里,或点击上传
',
'支持复制粘贴图片',
'
',
- '
',
- '
',
'
![]()
',
'
',
'',
@@ -976,15 +820,7 @@ function getHTML() {
' });',
' // 点击上传',
- ' uploadArea.addEventListener(\'click\', (e) => {',
- ' // 如果点击的是 base64Input 或 toggleBase64 按钮,不触发文件上传',
- ' if (e.target.id === \'base64Input\' || ',
- ' e.target.id === \'toggleBase64\' || ',
- ' e.target.closest(\'#base64Input\') || ',
- ' e.target.closest(\'#toggleBase64\')) {',
- ' return;',
- ' }',
-
+ ' uploadArea.addEventListener(\'click\', () => {',
' const input = document.createElement(\'input\');',
' input.type = \'file\';',
' input.accept = \'image/*\';',
@@ -1039,7 +875,6 @@ function getHTML() {
' // 复制结果功能',
' copyBtn.addEventListener(\'click\', async () => {',
' const result = resultDiv.textContent;',
- ' toggleBase64.textContent = \'隐藏Base64输入\';',
' try {',
' await navigator.clipboard.writeText(result);',
' copyBtn.textContent = \'已复制\';',
@@ -1056,66 +891,6 @@ function getHTML() {
' document.getElementById("closeSidebar").addEventListener("click", () => {',
' sidebar.classList.remove("open");',
' });',
- ' // Base64 输入相关功能',
- ' const base64Input = document.getElementById(\'base64Input\');',
- ' const toggleBase64 = document.getElementById(\'toggleBase64\');',
-
- ' // 切换 Base64 输入框显示',
- ' toggleBase64.addEventListener(\'click\', (e) => {',
- ' e.stopPropagation(); // 阻止事件冒泡到 uploadArea',
- ' if (base64Input.style.display === \'none\') {',
- ' base64Input.style.display = \'block\';',
- ' toggleBase64.textContent = \'隐藏Base64输入\';',
- ' } else {',
- ' base64Input.style.display = \'none\';',
- ' toggleBase64.textContent = \'切换Base64输入\';',
- ' }',
- ' });',
-
- ' // 为 base64Input 添加阻止事件冒泡',
- ' document.getElementById(\'base64Input\').addEventListener(\'click\', (e) => {',
- ' e.stopPropagation(); // 阻止事件冒泡到 uploadArea',
- ' });',
-
- ' // base64Input 的 input 事件处理也需要阻止冒泡',
- ' base64Input.addEventListener(\'input\', async (e) => {',
- ' e.stopPropagation();',
- ' const base64Content = base64Input.value.trim();',
- ' if (base64Content) {',
- ' try {',
- ' // 尝试转换Base64为Blob',
- ' let imageData;',
- ' if (base64Content.startsWith(\'data:image\')) {',
- ' imageData = base64Content;',
- ' } else {',
- ' imageData = \'data:image/png;base64,\' + base64Content;',
- ' }',
-
- ' // 验证Base64是否为有效图片',
- ' const img = new Image();',
- ' img.src = imageData;',
- ' await new Promise((resolve, reject) => {',
- ' img.onload = resolve;',
- ' img.onerror = reject;',
- ' });',
-
- ' // 转换Base64为Blob',
- ' const response = await fetch(imageData);',
- ' const blob = await response.blob();',
- ' const file = new File([blob], "image.png", { type: "image/png" });',
-
- ' // 显示预览',
- ' previewImage.src = imageData;',
- ' previewImage.style.display = \'block\';',
-
- ' // 处理图片',
- ' processImage(file);',
- ' } catch (error) {',
- ' alert(\'无效的Base64图片内容\');',
- ' console.error(\'Base64处理错误:\', error);',
- ' }',
- ' }',
- ' });',
'',
'',
'