|
@@ -9,7 +9,7 @@
|
|
|
<p>支持识别图片/通过领先的深度学习技术,对各种表格,图片,文档、证件、面单等多种通用场景进行快速、精准的检测和识别,支持简体中文/繁体中文/英文/西欧主流语言等多种种语言,同时支持印刷体、手写体、倾斜、折叠、旋转等。</p>
|
|
|
</el-row>
|
|
|
<el-row style="color: gray; font-size: small;">
|
|
|
- <h4>上传文本图片</h4>
|
|
|
+ <h4>支持jpg, png, bmp, pdf格式</h4>
|
|
|
</el-row>
|
|
|
<el-row>
|
|
|
<div class="common-layout">
|
|
@@ -20,6 +20,28 @@
|
|
|
</el-container>
|
|
|
</div>
|
|
|
</el-row>
|
|
|
+ <div class="pdf-preview" v-show="is_pdf">
|
|
|
+ <div class="pdf-wrap">
|
|
|
+ <vue-pdf-embed :source="state.source" :style="scale" class="vue-pdf-embed" :page="state.pageNum" />
|
|
|
+ </div>
|
|
|
+ <div class="page-tool">
|
|
|
+ <div class="page-tool-item" @click="prePage">上一页</div>
|
|
|
+ <div class="page-tool-item" @click="nextPage">下一页</div>
|
|
|
+ <div class="page-tool-item">{{ state.pageNum }}/{{ state.numPages }}</div>
|
|
|
+ <div class="page-tool-item" @click="pageZoomOut">放大</div>
|
|
|
+ <div class="page-tool-item" @click="pageZoomIn">缩小</div>
|
|
|
+ </div>
|
|
|
+ <div style="margin-top: 50px;">
|
|
|
+ <el-row class="page-tool">
|
|
|
+ <div class="common-layout">
|
|
|
+ <el-container>
|
|
|
+ <el-input type="number" v-model="pdf_page"></el-input>
|
|
|
+ <el-button type="primary" @click="SetPdfPage" plain>页面跳转</el-button>
|
|
|
+ </el-container>
|
|
|
+ </div>
|
|
|
+ </el-row>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
</el-col>
|
|
|
<el-col :span="8" class="place">
|
|
|
<el-row class="small-title">
|
|
@@ -29,10 +51,10 @@
|
|
|
<!-- <p v-if="loading">处理中</p> -->
|
|
|
</el-row>
|
|
|
<el-row>
|
|
|
- <!-- <canvas id="canvas" class="show-area"></canvas> -->
|
|
|
- <!-- <img id="predict-img" class="show-area" v-show="show_predict" /> -->
|
|
|
+ <!-- 用于展示图片 -->
|
|
|
<img id="show-img" class="show-area" />
|
|
|
</el-row>
|
|
|
+ <canvas id="canvas" style="display: none;"></canvas>
|
|
|
<el-row>
|
|
|
<section> 耗时:{{ predictTime }} ms.</section>
|
|
|
</el-row>
|
|
@@ -51,7 +73,7 @@
|
|
|
</el-collapse-item>
|
|
|
<el-collapse-item title="识别结果" name="2">
|
|
|
<el-scrollbar height="600px">
|
|
|
- <span v-html="result"></span>
|
|
|
+ <span v-html="rec_result"></span>
|
|
|
</el-scrollbar>
|
|
|
</el-collapse-item>
|
|
|
</el-collapse>
|
|
@@ -61,26 +83,144 @@
|
|
|
</template>
|
|
|
|
|
|
<script lang='ts' setup>
|
|
|
-import { reactive, ref, toRefs } from 'vue'
|
|
|
-import { onMounted } from "vue";
|
|
|
import { OcrRec, SubmitBug } from '../../../../api/api'
|
|
|
import { useServerIpStore } from '../../../../store/ServerIp';
|
|
|
import { storeToRefs } from 'pinia'
|
|
|
import OcrLangList from '../../../../components/OcrLangList.vue'
|
|
|
import { useOcrLangStore } from '../../../../store/OcrLang';
|
|
|
|
|
|
+import { reactive, ref, toRefs, computed } from 'vue'
|
|
|
+import { onMounted } from "vue";
|
|
|
+import VuePdfEmbed from "vue-pdf-embed";
|
|
|
+import { createLoadingTask } from "vue3-pdfjs/esm"; // 获得总页数
|
|
|
+
|
|
|
+let loadingTask: any;
|
|
|
+
|
|
|
+const is_pdf = ref(false);
|
|
|
+const pdf_page = ref(1);
|
|
|
+const pdf_img: any = ref("")
|
|
|
+
|
|
|
+const state = reactive({
|
|
|
+ source: "", //预览pdf文件地址
|
|
|
+ pageNum: 1, //当前页面
|
|
|
+ scale: 1, // 缩放比例
|
|
|
+ numPages: 0, // 总页数
|
|
|
+});
|
|
|
+
|
|
|
+const scale = computed(() => `transform:scale(${state.scale})`)
|
|
|
+
|
|
|
+function dataURLtoBlob(dataURL: any) {
|
|
|
+ var arr = dataURL.split(','),
|
|
|
+ mime = arr[0].match(/:(.*?);/)[1],
|
|
|
+ bstr = atob(arr[1]),
|
|
|
+ n = bstr.length,
|
|
|
+ u8arr = new Uint8Array(n);
|
|
|
+ while (n--) {
|
|
|
+ u8arr[n] = bstr.charCodeAt(n);
|
|
|
+ }
|
|
|
+ return new Blob([u8arr], { type: mime });
|
|
|
+}
|
|
|
+
|
|
|
+function getPdfImage(index: number) {
|
|
|
+ // console.log(index, state.pageNum)
|
|
|
+ loadingTask.promise.then((pdf: any) => {
|
|
|
+ state.numPages = pdf.numPages;
|
|
|
+ pdf.getPage(index).then((page: any) => {
|
|
|
+ const viewport = page.getViewport({ scale: 1 })
|
|
|
+ canvas.value.height = viewport.height;
|
|
|
+ canvas.value.width = viewport.width;
|
|
|
+ // 画布的dom大小, 设置移动端,宽度设置铺满整个屏幕
|
|
|
+ // const clientWidth = document.body.clientWidth;
|
|
|
+ const destWidth = 398;
|
|
|
+ canvas.value.style.width = destWidth + 'px';
|
|
|
+ // 根据pdf每页的宽高比例设置canvas的高度
|
|
|
+ canvas.value.style.height = destWidth * (viewport.height / viewport.width) + 'px';
|
|
|
+ const ctx = canvas.value.getContext('2d');
|
|
|
+ page.render({
|
|
|
+ canvasContext: ctx,
|
|
|
+ viewport,
|
|
|
+ });
|
|
|
+ canvas.value.toBlob(function (blob) {
|
|
|
+ pdf_img.value = dataURLtoBlob(canvas.value.toDataURL())
|
|
|
+ const showImg = document.getElementById("show-img") as HTMLImageElement;
|
|
|
+ showImg.src = canvas.value.toDataURL();
|
|
|
+ // console.log(canvas.value.toDataURL(), state.pageNum)
|
|
|
+ });
|
|
|
+ })
|
|
|
+ });
|
|
|
+}
|
|
|
+
|
|
|
+function SetPdfPage() {
|
|
|
+ if (pdf_page.value >= 1 && pdf_page.value <= state.numPages) {
|
|
|
+ console.log(pdf_page.value)
|
|
|
+ state.pageNum = Number(pdf_page.value);
|
|
|
+ getPdfImage(state.pageNum);
|
|
|
+ } else {
|
|
|
+ console.log(pdf_page.value)
|
|
|
+ alert('输入的pdf页面无效!')
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+function prePage() {
|
|
|
+ if (state.pageNum > 1) {
|
|
|
+ state.pageNum -= 1;
|
|
|
+ getPdfImage(state.pageNum);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+function nextPage() {
|
|
|
+ if (state.pageNum < state.numPages) {
|
|
|
+ state.pageNum += 1;
|
|
|
+ getPdfImage(state.pageNum);
|
|
|
+ }
|
|
|
+}
|
|
|
+function pageZoomOut() {
|
|
|
+ if (state.scale < 2) {
|
|
|
+ state.scale += 0.1;
|
|
|
+ }
|
|
|
+}
|
|
|
+function pageZoomIn() {
|
|
|
+ if (state.scale > 1) {
|
|
|
+ state.scale -= 0.1;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 构建 File 对象
|
|
|
+function blobToFile(blob: any, fileName: any) {
|
|
|
+ blob.lastModifiedDate = new Date();
|
|
|
+ blob.name = fileName;
|
|
|
+ return blob;
|
|
|
+}
|
|
|
+
|
|
|
+async function convertCanvasToFile(canvas: HTMLCanvasElement, fileName: any) {
|
|
|
+ // 将 Canvas 转为 Blob 对象
|
|
|
+ const blob = await new Promise(resolve => canvas.toBlob(blob => {
|
|
|
+ resolve(blob);
|
|
|
+ }, pdf_img.value.type, 1.0));
|
|
|
+ // 手动构造 File 对象
|
|
|
+ let file = null;
|
|
|
+ try {
|
|
|
+ file = new File([pdf_img.value], fileName, { type: pdf_img.value.type });
|
|
|
+ } catch (e) {
|
|
|
+ // Safari 浏览器不支持直接通过 new File() 创建文件对象,需要手动构造
|
|
|
+ const rawFile = blobToFile(blob, fileName);
|
|
|
+ file = Object.assign(rawFile, { lastModifiedDate: new Date(), name: fileName });
|
|
|
+ }
|
|
|
+ return file;
|
|
|
+}
|
|
|
+
|
|
|
const ol = useOcrLangStore();
|
|
|
const { ocr_lang } = storeToRefs(ol);
|
|
|
|
|
|
const si = useServerIpStore()
|
|
|
-const {server_ip} = storeToRefs(si);
|
|
|
+const { server_ip } = storeToRefs(si);
|
|
|
|
|
|
let loading = ref(false)
|
|
|
let show_predict = ref(false)
|
|
|
let predictTime = ref(0)
|
|
|
const fileName = ref(null);
|
|
|
const canvas = ref(null as unknown as HTMLCanvasElement);
|
|
|
-const result = ref("");
|
|
|
+const rec_result = ref("");
|
|
|
let bugId = ref("");
|
|
|
|
|
|
const activeName = ref('2')
|
|
@@ -97,8 +237,6 @@ const uploadImg = () => {
|
|
|
const reader = new FileReader();
|
|
|
// 用于展示
|
|
|
const showImg = document.getElementById("show-img") as HTMLImageElement;
|
|
|
- // 用于识别
|
|
|
- // const rawImg = document.getElementById("raw-img") as HTMLImageElement;
|
|
|
const inputElement = document
|
|
|
.getElementsByClassName("el-input")[0]
|
|
|
.getElementsByTagName("input")[0];
|
|
@@ -106,8 +244,21 @@ const uploadImg = () => {
|
|
|
try {
|
|
|
const file = inputElement.files![0];
|
|
|
reader.onload = () => {
|
|
|
- showImg.src = URL.createObjectURL(file);
|
|
|
- // rawImg.src = URL.createObjectURL(file);
|
|
|
+ const post_ = file.name.substring(file.name.lastIndexOf("."), file.name.length).toLowerCase();
|
|
|
+ if (post_ == ".pdf") {
|
|
|
+ state.pageNum = 1;
|
|
|
+ state.scale = 1;
|
|
|
+ state.source = URL.createObjectURL(file);
|
|
|
+ is_pdf.value = true
|
|
|
+ loadingTask = createLoadingTask(state.source);
|
|
|
+ getPdfImage(state.pageNum);
|
|
|
+ } else if (post_ == ".jpg" || post_ == ".png" || post_ == ".bmp") {
|
|
|
+ showImg.src = URL.createObjectURL(file);
|
|
|
+ is_pdf.value = false
|
|
|
+ } else {
|
|
|
+ alert('不支持的文件格式!')
|
|
|
+ fileName.value = null
|
|
|
+ }
|
|
|
};
|
|
|
reader.readAsDataURL(file);
|
|
|
} catch (err) {
|
|
@@ -137,24 +288,48 @@ const predict = async () => {
|
|
|
loading.value = !loading.value
|
|
|
var data = new FormData();
|
|
|
data.append('lang', ocr_lang.value);
|
|
|
- data.append('images', file);
|
|
|
-
|
|
|
- OcrRec(data).then(res => {
|
|
|
- console.log("res code: " + res.code)
|
|
|
- json_result.value = JSON.stringify(res.data, null, 4);
|
|
|
- // let json_obj = JSON.parse(json_result.value)
|
|
|
- result.value = res.data.text.reduce((total: any, cur: any) => total + `<p>${cur}</p>`);
|
|
|
- img.src = server_ip.value + res.data.visualize_img;
|
|
|
- predictTime.value = res.data.cost;
|
|
|
- loading.value = !loading.value;
|
|
|
- bugId.value = res.response_id;
|
|
|
- if (show_predict.value === false)
|
|
|
- show_predict.value = !show_predict.value;
|
|
|
- }).catch(function (err) {
|
|
|
- loading.value = !loading.value;
|
|
|
- bugId.value = ""
|
|
|
- predictTime.value = 0
|
|
|
- });
|
|
|
+
|
|
|
+ if (is_pdf.value) {
|
|
|
+ const file: any = convertCanvasToFile(canvas.value, "pdf.png").then(result => {
|
|
|
+ console.log(result)
|
|
|
+ data.append('images', result);
|
|
|
+ OcrRec(data).then(res => {
|
|
|
+ console.log("res code: " + res.code)
|
|
|
+ json_result.value = JSON.stringify(res.data, null, 4);
|
|
|
+ // let json_obj = JSON.parse(json_result.value)
|
|
|
+ rec_result.value = res.data.text.reduce((total: any, cur: any) => total + `<p>${cur}</p>`);
|
|
|
+ img.src = server_ip.value + res.data.visualize_img;
|
|
|
+ predictTime.value = res.data.cost;
|
|
|
+ loading.value = !loading.value;
|
|
|
+ bugId.value = res.response_id;
|
|
|
+ if (show_predict.value === false)
|
|
|
+ show_predict.value = !show_predict.value;
|
|
|
+ }).catch(function (err) {
|
|
|
+ loading.value = !loading.value;
|
|
|
+ bugId.value = ""
|
|
|
+ predictTime.value = 0
|
|
|
+ });
|
|
|
+ })
|
|
|
+ } else {
|
|
|
+ data.append('images', file);
|
|
|
+
|
|
|
+ OcrRec(data).then(res => {
|
|
|
+ console.log("res code: " + res.code)
|
|
|
+ json_result.value = JSON.stringify(res.data, null, 4);
|
|
|
+ // let json_obj = JSON.parse(json_result.value)
|
|
|
+ rec_result.value = res.data.text.reduce((total: any, cur: any) => total + `<p>${cur}</p>`);
|
|
|
+ img.src = server_ip.value + res.data.visualize_img;
|
|
|
+ predictTime.value = res.data.cost;
|
|
|
+ loading.value = !loading.value;
|
|
|
+ bugId.value = res.response_id;
|
|
|
+ if (show_predict.value === false)
|
|
|
+ show_predict.value = !show_predict.value;
|
|
|
+ }).catch(function (err) {
|
|
|
+ loading.value = !loading.value;
|
|
|
+ bugId.value = ""
|
|
|
+ predictTime.value = 0
|
|
|
+ });
|
|
|
+ }
|
|
|
};
|
|
|
|
|
|
const submitBug = async () => {
|
|
@@ -162,7 +337,7 @@ const submitBug = async () => {
|
|
|
alert('请先预测结果!')
|
|
|
return;
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
|
|
|
SubmitBug(bugId.value).then(res => {
|
|
|
console.log(res.code, res.data)
|
|
@@ -189,4 +364,62 @@ const submitBug = async () => {
|
|
|
margin-left: auto;
|
|
|
border-right: solid 1px #ccc;
|
|
|
}
|
|
|
+
|
|
|
+.pdf-preview {
|
|
|
+ position: relative;
|
|
|
+ height: 100vh;
|
|
|
+ padding: 20px 0;
|
|
|
+ box-sizing: border-box;
|
|
|
+ background: rgb(66, 66, 66);
|
|
|
+}
|
|
|
+
|
|
|
+.vue-pdf-embed {
|
|
|
+ text-align: center;
|
|
|
+ width: 515px;
|
|
|
+ border: 1px solid #e5e5e5;
|
|
|
+ margin: 0 auto;
|
|
|
+ box-sizing: border-box;
|
|
|
+}
|
|
|
+
|
|
|
+.pdf-preview {
|
|
|
+ position: relative;
|
|
|
+ height: 100vh;
|
|
|
+ padding: 20px 0;
|
|
|
+ box-sizing: border-box;
|
|
|
+ background-color: e9e9e9;
|
|
|
+}
|
|
|
+
|
|
|
+.pdf-wrap {
|
|
|
+ overflow-y: auto;
|
|
|
+}
|
|
|
+
|
|
|
+.vue-pdf-embed {
|
|
|
+ text-align: center;
|
|
|
+ width: 515px;
|
|
|
+ border: 1px solid #e5e5e5;
|
|
|
+ margin: 0 auto;
|
|
|
+ box-sizing: border-box;
|
|
|
+}
|
|
|
+
|
|
|
+.page-tool {
|
|
|
+ position: absolute;
|
|
|
+ /* bottom: 35px; */
|
|
|
+ /* padding-left: 15px; */
|
|
|
+ /* padding-right: 15px; */
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ background: rgb(66, 66, 66);
|
|
|
+ color: white;
|
|
|
+ border-radius: 19px;
|
|
|
+ z-index: 100;
|
|
|
+ cursor: pointer;
|
|
|
+ margin-left: 50%;
|
|
|
+ transform: translateX(-50%);
|
|
|
+}
|
|
|
+
|
|
|
+.page-tool-item {
|
|
|
+ padding: 8px 15px;
|
|
|
+ padding-left: 10px;
|
|
|
+ cursor: pointer;
|
|
|
+}
|
|
|
</style>
|