From bbcabd8145306ea84e1489202c88315981de1ed1 Mon Sep 17 00:00:00 2001
From: changy1105 <18392600593@163.com>
Date: Mon, 20 Dec 2021 22:54:41 +0800
Subject: [PATCH] [TIPC] Add js infer test
---
.gitignore | 2 +
test_tipc/docs/test_inference_js.md | 50 +++++++++++
test_tipc/prepare_js.sh | 92 ++++++++++++++++++++
test_tipc/test_inference_js.sh | 8 ++
test_tipc/web/expect.json | 20 +++++
test_tipc/web/index.html | 13 +++
test_tipc/web/index.test.js | 82 ++++++++++++++++++
test_tipc/web/jest-puppeteer.config.js | 14 ++++
test_tipc/web/jest.config.js | 111 +++++++++++++++++++++++++
test_tipc/web/test.jpg | Bin 0 -> 287898 bytes
10 files changed, 392 insertions(+)
create mode 100644 test_tipc/docs/test_inference_js.md
create mode 100644 test_tipc/prepare_js.sh
create mode 100644 test_tipc/test_inference_js.sh
create mode 100644 test_tipc/web/expect.json
create mode 100644 test_tipc/web/index.html
create mode 100644 test_tipc/web/index.test.js
create mode 100644 test_tipc/web/jest-puppeteer.config.js
create mode 100644 test_tipc/web/jest.config.js
create mode 100644 test_tipc/web/test.jpg
diff --git a/.gitignore b/.gitignore
index 9d85e7a8..caf886a2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -29,3 +29,5 @@ paddleocr.egg-info/
/deploy/android_demo/app/PaddleLite/
/deploy/android_demo/app/.cxx/
/deploy/android_demo/app/cache/
+test_tipc/web/models/
+test_tipc/web/node_modules/
diff --git a/test_tipc/docs/test_inference_js.md b/test_tipc/docs/test_inference_js.md
new file mode 100644
index 00000000..c0b7d653
--- /dev/null
+++ b/test_tipc/docs/test_inference_js.md
@@ -0,0 +1,50 @@
+# Web 端基础预测功能测试
+
+Web 端主要基于 Jest-Puppeteer 完成 e2e 测试,其中 Puppeteer 操作 Chrome 完成推理流程,Jest 完成测试流程。
+>Puppeteer 是一个 Node 库,它提供了一个高级 API 来通过 DevTools 协议控制 Chromium 或 Chrome
+>Jest 是一个 JavaScript 测试框架,旨在确保任何 JavaScript 代码的正确性。
+#### 环境准备
+
+* 安装 Node(包含 npm ) (https://nodejs.org/zh-cn/download/)
+* 确认是否安装成功,在命令行执行
+```sh
+# 显示所安 node 版本号,即表示成功安装
+node -v
+```
+* 确认 npm 是否安装成成
+```sh
+# npm 随着 node 一起安装,一般无需额外安装
+# 显示所安 npm 版本号,即表示成功安装
+npm -v
+```
+
+#### 使用
+```sh
+# web 测试环境准备
+bash test_tipc/prepare_js.sh 'js_infer'
+# web 推理测试
+bash test_tipc/test_inference_js.sh
+```
+
+#### 流程设计
+
+###### paddlejs prepare
+ 1. 判断 node, npm 是否安装
+ 2. 下载测试模型,当前检测模型是 ch_PP-OCRv2_det_infer ,识别模型是 ch_PP-OCRv2_rec_infer[1, 3, 32, 320]。如果需要替换模型,可直接将模型文件放在test_tipc/web/models/目录下。
+ - 文本检测模型:https://paddleocr.bj.bcebos.com/PP-OCRv2/chinese/ch_PP-OCRv2_det_infer.tar
+ - 文本识别模型:https://paddleocr.bj.bcebos.com/PP-OCRv2/chinese/ch_PP-OCRv2_rec_infer.tar
+ - 文本识别模型[1, 3, 32, 320]:https://paddlejs.bj.bcebos.com/models/ch_PP-OCRv2_rec_infer.tar
+ - 保证较为准确的识别效果,需要将文本识别模型导出为输入shape是[1, 3, 32, 320]的静态模型
+ 3. 转换模型, model.pdmodel model.pdiparams 转换为 model.json chunk.dat(检测模型保存地址:test_tipc/web/models/ch_PP-OCRv2/det,识别模型保存地址:test_tipc/web/models/ch_PP-OCRv2/rec)
+ 4. 安装最新版本 ocr sdk @paddlejs-models/ocr@latest
+ 5. 安装测试环境依赖 puppeteer、jest、jest-puppeteer,如果检查到已经安装,则不会进行二次安装
+
+ ###### paddlejs infer test
+ 1. Jest 执行 server command:`python3 -m http.server 9811` 开启本地服务
+ 2. 启动 Jest 测试服务,通过 jest-puppeteer 插件完成 chrome 操作,加载 @paddlejs-models/ocr 脚本完成推理流程
+ 3. 测试用例为原图识别后的文本结果与预期文本结果(expect.json)进行对比,测试通过有两个标准:
+ * 原图识别结果逐字符与预期结果对比,误差不超过 **10个字符**;
+ * 原图识别结果每个文本框字符内容与预期结果进行相似度对比,相似度不小于 0.9(全部一致则相似度为1)。
+
+ 只有满足上述两个标准,视为测试通过。通过为如下显示:
+
diff --git a/test_tipc/prepare_js.sh b/test_tipc/prepare_js.sh
new file mode 100644
index 00000000..7bcdeb35
--- /dev/null
+++ b/test_tipc/prepare_js.sh
@@ -0,0 +1,92 @@
+#!/bin/bash
+
+set -o errexit
+set -o nounset
+shopt -s extglob
+
+# paddlejs prepare 主要流程
+# 1. 判断 node, npm 是否安装
+# 2. 下载测试模型,当前检测模型是 ch_PP-OCRv2_det_infer ,识别模型是 ch_PP-OCRv2_rec_infer [1, 3, 32, 320]。如果需要替换模型,可直接将模型文件放在test_tipc/web/models/目录下。
+# - 文本检测模型:https://paddleocr.bj.bcebos.com/PP-OCRv2/chinese/ch_PP-OCRv2_det_infer.tar
+# - 文本识别模型:https://paddleocr.bj.bcebos.com/PP-OCRv2/chinese/ch_PP-OCRv2_rec_infer.tar
+# - 文本识别模型[1, 3, 32, 320]:https://paddlejs.bj.bcebos.com/models/ch_PP-OCRv2_rec_infer.tar
+# - 保证较为准确的识别效果,需要将文本识别模型导出为输入shape[1, 3, 32, 320]的静态模型
+# 3. 转换模型, model.pdmodel model.pdiparams 转换为 model.json chunk.dat(检测模型保存地址:test_tipc/web/models/ch_PP-OCRv2/det,识别模型保存地址:test_tipc/web/models/ch_PP-OCRv2/rec)
+# 4. 安装最新版本 ocr sdk @paddlejs-models/ocr@latest
+# 5. 安装测试环境依赖 puppeteer、jest、jest-puppeteer,如果检查到已经安装,则不会进行二次安装
+
+# 判断是否安装了node
+if ! type node >/dev/null 2>&1; then
+ echo -e "\033[31m node 未安装 \033[0m"
+ exit
+fi
+
+# 判断是否安装了npm
+if ! type npm >/dev/null 2>&1; then
+ echo -e "\033[31m npm 未安装 \033[0m"
+ exit
+fi
+
+# MODE be 'js_infer'
+MODE=$1
+# js_infer MODE , load model file and convert model to js_infer
+if [ ${MODE} != "js_infer" ];then
+ echo "Please change mode to 'js_infer'"
+ exit
+fi
+
+
+# saved_model_name
+det_saved_model_name=ch_PP-OCRv2_det_infer
+rec_saved_model_name=ch_PP-OCRv2_rec_infer
+
+# model_path
+model_path=test_tipc/web/models/
+
+rm -rf $model_path
+
+echo ${model_path}${det_saved_model_name}
+echo ${model_path}${rec_saved_model_name}
+
+# download ocr_det inference model
+wget -nc -P $model_path https://paddleocr.bj.bcebos.com/PP-OCRv2/chinese/ch_PP-OCRv2_det_infer.tar
+cd $model_path && tar xf ch_PP-OCRv2_det_infer.tar && cd ../../../
+
+# download ocr_rec inference model
+wget -nc -P $model_path https://paddlejs.bj.bcebos.com/models/ch_PP-OCRv2_rec_infer.tar
+cd $model_path && tar xf ch_PP-OCRv2_rec_infer.tar && cd ../../../
+
+MYDIR=`pwd`
+echo $MYDIR
+
+pip3 install paddlejsconverter
+
+# convert inference model to web model: model.json、chunk.dat
+paddlejsconverter \
+ --modelPath=$model_path$det_saved_model_name/inference.pdmodel \
+ --paramPath=$model_path$det_saved_model_name/inference.pdiparams \
+ --outputDir=$model_path$det_saved_model_name/ \
+
+paddlejsconverter \
+ --modelPath=$model_path$rec_saved_model_name/inference.pdmodel \
+ --paramPath=$model_path$rec_saved_model_name/inference.pdiparams \
+ --outputDir=$model_path$rec_saved_model_name/ \
+
+# always install latest ocr sdk
+cd test_tipc/web
+echo -e "\033[33m Installing the latest ocr sdk... \033[0m"
+npm install @paddlejs-models/ocr@latest
+npm info @paddlejs-models/ocr
+echo -e "\033[32m The latest ocr sdk installed completely.!~ \033[0m"
+
+# install dependencies
+if [ `npm list --dept 0 | grep puppeteer | wc -l` -ne 0 ] && [ `npm list --dept 0 | grep jest | wc -l` -ne 0 ];then
+ echo -e "\033[32m Dependencies have installed \033[0m"
+else
+ echo -e "\033[33m Installing dependencies ... \033[0m"
+ npm install jest jest-puppeteer puppeteer
+ echo -e "\033[32m Dependencies installed completely.!~ \033[0m"
+fi
+
+# del package-lock.json
+rm package-lock.json
diff --git a/test_tipc/test_inference_js.sh b/test_tipc/test_inference_js.sh
new file mode 100644
index 00000000..e96b1875
--- /dev/null
+++ b/test_tipc/test_inference_js.sh
@@ -0,0 +1,8 @@
+#!/bin/bash
+
+set -o errexit
+set -o nounset
+
+cd test_tipc/web
+# run ocr test in chrome
+./node_modules/.bin/jest --config ./jest.config.js
diff --git a/test_tipc/web/expect.json b/test_tipc/web/expect.json
new file mode 100644
index 00000000..a60c80a7
--- /dev/null
+++ b/test_tipc/web/expect.json
@@ -0,0 +1,20 @@
+{
+ "text": [
+ "纯臻营养护发素",
+ "产品信息/参数",
+ "(45元/每公斤,100公斤起订)",
+ "每瓶22元,1000瓶起订)",
+ "【品牌】:代加工方式/OEMODM",
+ "【品名】:纯臻营养护发素",
+ "【产品编号】:YM-X-3011",
+ "ODMOEM",
+ "【净含量】:220ml",
+ "【适用人群】:适合所有肤质",
+ "【主要成分】:鲸蜡硬脂醇、燕麦β-葡聚",
+ "糖、椰油酰胺丙基甜菜碱、泛醌",
+ "(成品包材)",
+ "【主要功能】:可紧致头发磷层,从而达到",
+ "即时持久改善头发光泽的效果,给干燥的头",
+ "发足够的滋养"
+ ]
+}
diff --git a/test_tipc/web/index.html b/test_tipc/web/index.html
new file mode 100644
index 00000000..39921fbf
--- /dev/null
+++ b/test_tipc/web/index.html
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+ ocr test
+
+
+
+
+
+
\ No newline at end of file
diff --git a/test_tipc/web/index.test.js b/test_tipc/web/index.test.js
new file mode 100644
index 00000000..e07aed82
--- /dev/null
+++ b/test_tipc/web/index.test.js
@@ -0,0 +1,82 @@
+const expectData = require('./expect.json');
+
+describe('e2e test ocr model', () => {
+
+ beforeAll(async () => {
+ await page.goto(PATH);
+ });
+
+ it('ocr infer and diff test', async () => {
+ page.on('console', msg => console.log('PAGE LOG:', msg.text()));
+
+ const text = await page.evaluate(async () => {
+ const $ocr = document.querySelector('#ocr');
+ const ocr = paddlejs['ocr'];
+ await ocr.init('./models/ch_PP-OCRv2_det_infer', './models/ch_PP-OCRv2_rec_infer');
+ const res = await ocr.recognize($ocr);
+ return res.text;
+ });
+ // 模型文字识别结果与预期结果diff的字符数
+ let diffNum = 0;
+ // 文本框字符串相似度
+ let similarity = 0;
+ // 预期字符diff数
+ const expectedDiffNum = 10;
+ // 预期文本框字符串相似度
+ const expecteSimilarity = 0.9;
+ // 预期文本内容
+ const expectResult = expectData.text;
+
+ expectResult && expectResult.forEach((item, index) => {
+ const word = text[index];
+ // 逐字符对比
+ for(let i = 0; i < item.length; i++) {
+ if (item[i] !== word[i]) {
+ console.log('expect: ', item[i], ' word: ', word[i]);
+ diffNum++;
+ }
+ }
+ // 文本框字符串相似度对比
+ const s = similar(item, word);
+ similarity += s;
+ });
+
+ similarity = similarity / expectResult.length;
+
+ expect(diffNum).toBeLessThanOrEqual(expectedDiffNum);
+
+ expect(similarity).toBeGreaterThanOrEqual(expecteSimilarity);
+
+ function similar(string, expect) {
+ if (!string || !expect) {
+ return 0;
+ }
+ const length = string.length > expect.length ? string.length : expect.length;
+ const n = string.length;
+ const m = expect.length;
+ let data = [];
+ const min = (a, b, c) => {
+ return a < b ? (a < c ? a : c) : (b < c ? b : c);
+ };
+ let i, j, si, ej, cost;
+ if (n === 0) return m;
+ if (m === 0) return n;
+ for (i = 0; i <= n; i++) {
+ data[i] = [];
+ [i][0] = i
+ }
+ for (j = 0; j <= m; j++) {
+ data[0][j] = j;
+ }
+ for (i = 1; i <= n; i++) {
+ si = string.charAt(i - 1);
+ for (j = 1; j <= m; j++) {
+ ej = expect.charAt(j - 1);
+ cost = si === ej ? 0 : 1;
+ data[i][j] = min(data[i - 1][j] + 1, data[i][j - 1] + 1, data[i - 1][j - 1] + cost);
+ }
+ }
+ return (1 - data[n][m] / length);
+ }
+ });
+});
diff --git a/test_tipc/web/jest-puppeteer.config.js b/test_tipc/web/jest-puppeteer.config.js
new file mode 100644
index 00000000..ac60eea6
--- /dev/null
+++ b/test_tipc/web/jest-puppeteer.config.js
@@ -0,0 +1,14 @@
+// jest-puppeteer.config.js
+module.exports = {
+ launch: {
+ headless: false,
+ product: 'chrome'
+ },
+ browserContext: 'default',
+ server: {
+ command: 'python3 -m http.server 9811',
+ port: 9811,
+ launchTimeout: 10000,
+ debug: true
+ }
+};
diff --git a/test_tipc/web/jest.config.js b/test_tipc/web/jest.config.js
new file mode 100644
index 00000000..aed1573e
--- /dev/null
+++ b/test_tipc/web/jest.config.js
@@ -0,0 +1,111 @@
+// For a detailed explanation regarding each configuration property and type check, visit:
+// https://jestjs.io/docs/en/configuration.html
+
+module.exports = {
+ preset: 'jest-puppeteer',
+ // All imported modules in your tests should be mocked automatically
+ // automock: false,
+
+ // Automatically clear mock calls and instances between every test
+ clearMocks: true,
+
+ // An object that configures minimum threshold enforcement for coverage results
+ // coverageThreshold: undefined,
+
+ // A set of global variables that need to be available in all test environments
+ globals: {
+ PATH: 'http://localhost:9811'
+ },
+
+ // The maximum amount of workers used to run your tests. Can be specified as % or a number. E.g. maxWorkers: 10% will use 10% of your CPU amount + 1 as the maximum worker number. maxWorkers: 2 will use a maximum of 2 workers.
+ // maxWorkers: "50%",
+
+ // An array of directory names to be searched recursively up from the requiring module's location
+ // moduleDirectories: [
+ // "node_modules"
+ // ],
+
+ // An array of file extensions your modules use
+ moduleFileExtensions: [
+ 'js',
+ 'json',
+ 'jsx',
+ 'ts',
+ 'tsx',
+ 'node'
+ ],
+
+
+ // The root directory that Jest should scan for tests and modules within
+ // rootDir: undefined,
+
+ // A list of paths to directories that Jest should use to search for files in
+ roots: [
+ ''
+ ],
+
+ // Allows you to use a custom runner instead of Jest's default test runner
+ // runner: "jest-runner",
+
+ // The paths to modules that run some code to configure or set up the testing environment before each test
+ // setupFiles: [],
+
+ // A list of paths to modules that run some code to configure or set up the testing framework before each test
+ // setupFilesAfterEnv: [],
+
+ // The number of seconds after which a test is considered as slow and reported as such in the results.
+ // slowTestThreshold: 5,
+
+ // A list of paths to snapshot serializer modules Jest should use for snapshot testing
+ // snapshotSerializers: [],
+
+ // The test environment that will be used for testing
+ // testEnvironment: 'jsdom',
+
+ // Options that will be passed to the testEnvironment
+ // testEnvironmentOptions: {},
+
+ // An array of regexp pattern strings that are matched against all test paths, matched tests are skipped
+ testPathIgnorePatterns: [
+ '/node_modules/'
+ ],
+
+ // The regexp pattern or array of patterns that Jest uses to detect test files
+ testRegex: '.(.+)\\.test\\.(js|ts)$',
+
+ // This option allows the use of a custom results processor
+ // testResultsProcessor: undefined,
+
+ // This option allows use of a custom test runner
+ // testRunner: "jest-circus/runner",
+
+ // This option sets the URL for the jsdom environment. It is reflected in properties such as location.href
+ testURL: 'http://localhost:9898/',
+
+ // Setting this value to "fake" allows the use of fake timers for functions such as "setTimeout"
+ // timers: "real",
+
+ // A map from regular expressions to paths to transformers
+ transform: {
+ '^.+\\.js$': 'babel-jest'
+ },
+
+ // An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation
+ transformIgnorePatterns: [
+ '/node_modules/',
+ '\\.pnp\\.[^\\/]+$'
+ ],
+
+ // An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them
+ // unmockedModulePathPatterns: undefined,
+
+ // Indicates whether each individual test should be reported during the run
+ verbose: true,
+
+ // An array of regexp patterns that are matched against all source file paths before re-running tests in watch mode
+ // watchPathIgnorePatterns: [],
+
+ // Whether to use watchman for file crawling
+ // watchman: true,
+ testTimeout: 50000
+};
diff --git a/test_tipc/web/test.jpg b/test_tipc/web/test.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..60682be64f34058a3c388b146873d972fa15dd8a
GIT binary patch
literal 287898
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