|
@@ -2,39 +2,97 @@
|
|
|
<el-row :gutter="20">
|
|
|
<el-col :span="12" class="place">
|
|
|
<el-row class="small-title">
|
|
|
- <h2>版面分析(暂未实现)</h2>
|
|
|
+ <h2>版面分析</h2>
|
|
|
</el-row>
|
|
|
+ <el-row style="color: gray; font-size: small;">
|
|
|
+ <p>目前支持检测图片与表格</p>
|
|
|
+ </el-row>
|
|
|
+ <el-row style="color: gray; font-size: small;">
|
|
|
+ <h4>支持jpg, png, bmp, pdf格式</h4>
|
|
|
+ </el-row>
|
|
|
+ <div class="common-layout">
|
|
|
+ <el-container>
|
|
|
+ <el-input type="file" v-model="fileName" @change="uploadImg"></el-input>
|
|
|
+ <el-button type="primary" @click="predict">Predict</el-button>
|
|
|
+ </el-container>
|
|
|
+ </div>
|
|
|
+ <div v-show="is_pdf">
|
|
|
+ <el-row>
|
|
|
+ <el-button-group>
|
|
|
+ <el-button type="primary" :icon="ArrowLeft" @click="prePage">上一页</el-button>
|
|
|
+ <el-button type="primary" @click="nextPage">
|
|
|
+ 下一页<el-icon class="el-icon--right">
|
|
|
+ <ArrowRight />
|
|
|
+ </el-icon>
|
|
|
+ </el-button>
|
|
|
+ </el-button-group>
|
|
|
+ <!-- <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>
|
|
|
+ <el-container>
|
|
|
+ <el-input type="number" v-model="pdf_page"></el-input>
|
|
|
+ <el-button type="primary" @click="SetPdfPage" plain>页面跳转</el-button>
|
|
|
+ </el-container>
|
|
|
+ </el-row>
|
|
|
+ </div>
|
|
|
+ <el-row>
|
|
|
+ <!-- 用于展示图片 -->
|
|
|
+ <img id="show-img" class="show-area" />
|
|
|
+ </el-row>
|
|
|
+ <div class="pdf-preview" v-show="false">
|
|
|
+ <div class="pdf-wrap">
|
|
|
+ <vue-pdf-embed :source="state.source" :scale="3.0" class="vue-pdf-embed" :page="state.pageNum" />
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <canvas id="canvas"></canvas>
|
|
|
</el-col>
|
|
|
<el-col :span="12" class="place">
|
|
|
+ <el-row class="small-title">
|
|
|
+ <h2 style="margin-right: 0;">识别结果展示</h2>
|
|
|
+ <el-button type="danger" @click="submitBug" disabled>提交bug</el-button>
|
|
|
+ </el-row>
|
|
|
+ <div>
|
|
|
+ <el-scrollbar height="600px">
|
|
|
+ <ul>
|
|
|
+ <li v-for="(item, index) in recArr" :key="index" @click="handleClick(index, item)"
|
|
|
+ :style="index == selectedItem ? 'color: blue' : ''">
|
|
|
+ {{ item }}
|
|
|
+ </li>
|
|
|
+ </ul>
|
|
|
+ </el-scrollbar>
|
|
|
+ </div>
|
|
|
+ <el-row>
|
|
|
+ <section> 耗时:{{ predictTime }} ms.</section>
|
|
|
+ </el-row>
|
|
|
</el-col>
|
|
|
</el-row>
|
|
|
</template>
|
|
|
|
|
|
<script lang="ts" setup>
|
|
|
-import { IMMagicColor, SubmitBug } from '../../../../api/api'
|
|
|
-import { useServerIpStore } from '../../../../store/ServerIp';
|
|
|
-import { storeToRefs } from 'pinia'
|
|
|
-
|
|
|
+import { Detection, SubmitBug } from '../../../../api/api'
|
|
|
+import { fabric } from 'fabric';
|
|
|
import { reactive, ref, toRefs, computed } from 'vue'
|
|
|
import { onMounted } from "vue";
|
|
|
import VuePdfEmbed from "vue-pdf-embed";
|
|
|
import { createLoadingTask } from "vue3-pdfjs/esm"; // 获得总页数
|
|
|
+import {
|
|
|
+ ArrowLeft,
|
|
|
+ ArrowRight,
|
|
|
+} from '@element-plus/icons-vue'
|
|
|
|
|
|
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, // 缩放比例
|
|
|
+ // scale: 4, // 缩放比例
|
|
|
numPages: 0, // 总页数
|
|
|
});
|
|
|
|
|
|
-const scale = computed(() => `transform:scale(${state.scale})`)
|
|
|
-
|
|
|
function dataURLtoBlob(dataURL: any) {
|
|
|
var arr = dataURL.split(','),
|
|
|
mime = arr[0].match(/:(.*?);/)[1],
|
|
@@ -52,7 +110,7 @@ function getPdfImage(index: number) {
|
|
|
loadingTask.promise.then((pdf: any) => {
|
|
|
state.numPages = pdf.numPages;
|
|
|
pdf.getPage(index).then((page: any) => {
|
|
|
- const viewport = page.getViewport({ scale: 10.0 })
|
|
|
+ const viewport = page.getViewport({ scale: 4.0 })
|
|
|
canvas.value.height = viewport.height;
|
|
|
canvas.value.width = viewport.width;
|
|
|
// 画布的dom大小, 设置移动端,宽度设置铺满整个屏幕
|
|
@@ -68,8 +126,10 @@ function getPdfImage(index: number) {
|
|
|
});
|
|
|
canvas.value.toBlob(function (blob) {
|
|
|
pdf_img.value = dataURLtoBlob(canvas.value.toDataURL('images/png', 1.0))
|
|
|
- // console.log(pdf_img.value);
|
|
|
- // console.log(canvas.value.toDataURL(), state.pageNum)
|
|
|
+ const showImg = document.getElementById("show-img") as HTMLImageElement;
|
|
|
+ showImg.src = canvas.value.toDataURL('images/png', 1.0);
|
|
|
+ fa_canvas = new fabric.Canvas(canvas.value);
|
|
|
+ rects.splice(0)
|
|
|
});
|
|
|
})
|
|
|
});
|
|
@@ -99,16 +159,6 @@ function nextPage() {
|
|
|
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) {
|
|
@@ -134,29 +184,43 @@ async function convertCanvasToFile(canvas: HTMLCanvasElement, fileName: any) {
|
|
|
return file;
|
|
|
}
|
|
|
|
|
|
-const si = useServerIpStore()
|
|
|
-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);
|
|
|
+
|
|
|
+let selectedItem = ref(-1)
|
|
|
|
|
|
+let recArr: any = ref([])
|
|
|
+
|
|
|
+let fa_canvas: any = null;
|
|
|
+const rec_result = ref("");
|
|
|
let bugId = ref("");
|
|
|
|
|
|
-let predictTime = ref(0)
|
|
|
+const activeName = ref('2')
|
|
|
+const json_result = ref("");
|
|
|
|
|
|
-const is_pdf = ref(false);
|
|
|
+interface iRect {
|
|
|
+ left: number,
|
|
|
+ top: number,
|
|
|
+ width: number,
|
|
|
+ height: number
|
|
|
+}
|
|
|
|
|
|
-const canvas = ref(null as unknown as HTMLCanvasElement);
|
|
|
+let rects: any = []
|
|
|
|
|
|
onMounted(async () => {
|
|
|
canvas.value = document.getElementById("canvas") as HTMLCanvasElement;
|
|
|
});
|
|
|
|
|
|
const uploadImg = () => {
|
|
|
+ /**
|
|
|
+ * 这里由于操作是绑定在 el-input 上;因此需要在内部重新获取 input 再拿到 file
|
|
|
+ */
|
|
|
const reader = new FileReader();
|
|
|
+ // 用于展示
|
|
|
const showImg = document.getElementById("show-img") as HTMLImageElement;
|
|
|
- const rawImg = document.getElementById("raw-img") as HTMLImageElement;
|
|
|
- const predictImg = document.getElementById("predict-img") as HTMLImageElement;
|
|
|
const inputElement = document
|
|
|
.getElementsByClassName("el-input")[0]
|
|
|
.getElementsByTagName("input")[0];
|
|
@@ -165,81 +229,157 @@ const uploadImg = () => {
|
|
|
const file = inputElement.files![0];
|
|
|
reader.onload = () => {
|
|
|
const post_ = file.name.substring(file.name.lastIndexOf("."), file.name.length).toLowerCase();
|
|
|
+ fa_canvas ? fa_canvas.clear() : 0;
|
|
|
if (post_ == ".pdf") {
|
|
|
state.pageNum = 1;
|
|
|
- state.scale = 1;
|
|
|
+ // state.scale = 4;
|
|
|
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);
|
|
|
- rawImg.src = URL.createObjectURL(file);
|
|
|
+ showImg.onload = () => {
|
|
|
+ canvas.value.width = showImg.width;
|
|
|
+ canvas.value.height = showImg.height;
|
|
|
+ fa_canvas = new fabric.Canvas(canvas.value);
|
|
|
+ rects.splice(0)
|
|
|
+ selectedItem.value = -1;
|
|
|
+ }
|
|
|
is_pdf.value = false
|
|
|
} else {
|
|
|
alert('不支持的文件格式!')
|
|
|
- fileName.value = null
|
|
|
+ // fileName.value = null
|
|
|
}
|
|
|
};
|
|
|
reader.readAsDataURL(file);
|
|
|
} catch (err) {
|
|
|
console.error(err);
|
|
|
}
|
|
|
+ if (show_predict.value === true)
|
|
|
+ show_predict.value = !show_predict.value;
|
|
|
};
|
|
|
|
|
|
-const predict = () => {
|
|
|
+type RectWithId = fabric.Rect & { id: number };
|
|
|
+
|
|
|
+function CreateRect(canvas: any, i: number, left: number, top: number, width: number, height: number, highlight: boolean) {
|
|
|
+ var rect = new fabric.Rect({
|
|
|
+ left: left,
|
|
|
+ top: top,
|
|
|
+ width: width,
|
|
|
+ height: height,
|
|
|
+ fill: 'transparent',
|
|
|
+ stroke: highlight ? 'blue' : 'green',
|
|
|
+ strokeWidth: 2,
|
|
|
+ opacity: 1.0,
|
|
|
+ selectable: false,
|
|
|
+ name: highlight ? 'high' : 'normal',
|
|
|
+ }) as RectWithId;
|
|
|
+ rect.set('id', i);
|
|
|
+ canvas.add(rect);
|
|
|
+ // canvas.renderAll();
|
|
|
+}
|
|
|
+
|
|
|
+const predict = async () => {
|
|
|
if (fileName.value == undefined) {
|
|
|
alert('请上传图片!')
|
|
|
return;
|
|
|
}
|
|
|
- const showImg = document.getElementById("show-img") as HTMLImageElement;
|
|
|
- const predictImg = document.getElementById("predict-img") as HTMLImageElement;
|
|
|
+
|
|
|
+ fa_canvas.clear()
|
|
|
+ rects.splice(0)
|
|
|
+
|
|
|
+ const img = document.getElementById("show-img") as HTMLImageElement;
|
|
|
const inputElement = document
|
|
|
.getElementsByClassName("el-input")[0]
|
|
|
.getElementsByTagName("input")[0];
|
|
|
const file = inputElement.files![0];
|
|
|
- // console.log(file)
|
|
|
|
|
|
loading.value = !loading.value
|
|
|
var data = new FormData();
|
|
|
|
|
|
if (is_pdf.value) {
|
|
|
const file: any = convertCanvasToFile(canvas.value, "pdf.png").then(result => {
|
|
|
- console.log(result)
|
|
|
data.append('images', result);
|
|
|
- IMMagicColor(data).then(res => {
|
|
|
+ Detection(data).then(res => {
|
|
|
console.log(res.code)
|
|
|
- // bug_id.value = res.response_id;
|
|
|
- predictImg.src = server_ip.value + res.data.out_image
|
|
|
- predictTime.value = res.data.cost
|
|
|
+ json_result.value = JSON.stringify(res.data, null, 4);
|
|
|
+ console.log(res.data);
|
|
|
+ recArr.value.splice(0);
|
|
|
+ let tmp_text = res.data.labels;
|
|
|
+ if (tmp_text.length == 0) {
|
|
|
+ recArr.value.push("no result!");
|
|
|
+ } else {
|
|
|
+ for (let i = 0; i < tmp_text.length; i++) {
|
|
|
+ recArr.value.push(tmp_text[i]);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ let tmp_boxes = res.data.boxes;
|
|
|
+ // fa_canvas = new fabric.Canvas(canvas.value);
|
|
|
+ const h_radio = img.height / img.naturalHeight;
|
|
|
+ const w_radio = img.width / img.naturalWidth;
|
|
|
+ for (let i = 0; i < tmp_boxes.length; i++) {
|
|
|
+ let left = w_radio * tmp_boxes[i][0];
|
|
|
+ let top = h_radio * tmp_boxes[i][1];
|
|
|
+ let width = w_radio * (tmp_boxes[i][2] - tmp_boxes[i][0]);
|
|
|
+ let height = h_radio * (tmp_boxes[i][3] - tmp_boxes[i][1]);
|
|
|
+ const rect: iRect = { left, top, width, height };
|
|
|
+ rects.push(rect);
|
|
|
+ CreateRect(fa_canvas, i, left, top, width, height, false);
|
|
|
+ }
|
|
|
+ predictTime.value = res.data.cost;
|
|
|
+ loading.value = !loading.value;
|
|
|
bugId.value = res.response_id;
|
|
|
-
|
|
|
-
|
|
|
- loading.value = false
|
|
|
+ if (show_predict.value === false)
|
|
|
+ show_predict.value = !show_predict.value;
|
|
|
}).catch(function (err) {
|
|
|
- loading.value = false
|
|
|
- // bug_id.value = ""
|
|
|
+ loading.value = !loading.value;
|
|
|
+ bugId.value = ""
|
|
|
predictTime.value = 0
|
|
|
});
|
|
|
})
|
|
|
- // console.log(file)
|
|
|
- // data.append('images', file.File);
|
|
|
} else {
|
|
|
data.append('images', file);
|
|
|
|
|
|
- IMMagicColor(data).then(res => {
|
|
|
- predictImg.src = server_ip.value + res.data.out_image
|
|
|
- predictTime.value = res.data.cost
|
|
|
+ Detection(data).then(res => {
|
|
|
+ console.log(res.code)
|
|
|
+ json_result.value = JSON.stringify(res.data, null, 4);
|
|
|
+ console.log(res.data);
|
|
|
+ // rec_result.value = res.data.text.reduce((total: any, cur: any) => total + `<p>${cur}</p>`);
|
|
|
+ recArr.value.splice(0);
|
|
|
+ let tmp_text = res.data.labels;
|
|
|
+ if (tmp_text.length == 0) {
|
|
|
+ alert('未检测到结果!');
|
|
|
+ } else {
|
|
|
+ for (let i = 0; i < tmp_text.length; i++) {
|
|
|
+ recArr.value.push(tmp_text[i]);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ let tmp_boxes = res.data.boxes;
|
|
|
+ // fa_canvas = new fabric.Canvas(canvas.value);
|
|
|
+ const h_radio = img.height / img.naturalHeight;
|
|
|
+ const w_radio = img.width / img.naturalWidth;
|
|
|
+ for (let i = 0; i < tmp_boxes.length; i++) {
|
|
|
+ let left = w_radio * tmp_boxes[i][0];
|
|
|
+ let top = h_radio * tmp_boxes[i][1];
|
|
|
+ let width = w_radio * (tmp_boxes[i][2] - tmp_boxes[i][0]);
|
|
|
+ let height = h_radio * (tmp_boxes[i][3] - tmp_boxes[i][1]);
|
|
|
+ const rect: iRect = { left, top, width, height };
|
|
|
+ rects.push(rect);
|
|
|
+ CreateRect(fa_canvas, i, left, top, width, height, false);
|
|
|
+ }
|
|
|
+ predictTime.value = res.data.cost;
|
|
|
+ loading.value = !loading.value;
|
|
|
bugId.value = res.response_id;
|
|
|
-
|
|
|
- loading.value = !loading.value
|
|
|
+ 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 () => {
|
|
|
if (bugId.value == undefined || bugId.value == "") {
|
|
@@ -247,6 +387,7 @@ const submitBug = async () => {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
+
|
|
|
SubmitBug(bugId.value).then(res => {
|
|
|
console.log(res.code, res.data)
|
|
|
}).catch(function (err) {
|
|
@@ -255,10 +396,37 @@ const submitBug = async () => {
|
|
|
});
|
|
|
};
|
|
|
|
|
|
-let src = ''
|
|
|
+async function handleClick(index: any, item: any) {
|
|
|
+ selectedItem.value = index;
|
|
|
+ let objects = fa_canvas.getObjects();
|
|
|
+ for (let i = 0; i < objects.length; i++) {
|
|
|
+ if (objects[i].name === 'high') {
|
|
|
+ // 找到名为 rectName 的矩形元素,执行删除操作等
|
|
|
+ fa_canvas.remove(objects[i]);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ CreateRect(fa_canvas, index, rects[index].left, rects[index].top, rects[index].width, rects[index].height, true);
|
|
|
+}
|
|
|
</script>
|
|
|
|
|
|
<style scoped>
|
|
|
+#canvas {
|
|
|
+ position: absolute;
|
|
|
+ top: 0;
|
|
|
+ left: 0;
|
|
|
+ z-index: 0;
|
|
|
+ /* 设置Canvas的层级,使其显示在图像之上 */
|
|
|
+ background-color: transparent;
|
|
|
+}
|
|
|
+
|
|
|
+#show-img {
|
|
|
+ position: absolute;
|
|
|
+ top: 0;
|
|
|
+ left: 0;
|
|
|
+ z-index: 0;
|
|
|
+}
|
|
|
+
|
|
|
.demo-image__placeholder .block {
|
|
|
padding: 30px 0;
|
|
|
text-align: center;
|
|
@@ -362,6 +530,7 @@ let src = ''
|
|
|
z-index: 100;
|
|
|
cursor: pointer;
|
|
|
margin-left: 50%;
|
|
|
+ top: 100%;
|
|
|
transform: translateX(-50%);
|
|
|
}
|
|
|
|