|
|
<template>
|
|
|
<div class="app-container">
|
|
|
<div class="text-centent">
|
|
|
<div class="left">
|
|
|
|
|
|
<el-scrollbar height="650px" style="height:96%">
|
|
|
<el-row>
|
|
|
<el-col :span="24">
|
|
|
<div class="title-back">
|
|
|
<span>任务背景</span>
|
|
|
</div>
|
|
|
</el-col>
|
|
|
</el-row>
|
|
|
<p style="color:#FFFFFF">
|
|
|
小张在写期末课程小论文的时候,上网查资料发现一篇文章特别有参考价值,但是这篇文章无法复制,小张要怎么做才能获得这篇文章的电子版呢?
|
|
|
</p>
|
|
|
<div class="Step" v-for="item in Step1" :key="item">
|
|
|
<div class="Ellipse">
|
|
|
<div class="Step-item">步骤一</div>
|
|
|
<span >{{ item.stemContent }}</span>
|
|
|
</div>
|
|
|
<!-- <span v-if="item.studentAnswer!==''"><span v-if="!item.correctAnswer.includes(item.studentAnswer)">正确答案:{{ item.correctAnswer }}</span></span> -->
|
|
|
<p><el-radio label="A" size="large" v-model="item.studentAnswer" >A. {{ item.optionA }}</el-radio></p>
|
|
|
<p><el-radio label="B" size="large" v-model="item.studentAnswer" >B. {{ item.optionB }}</el-radio></p>
|
|
|
<div class="footer">
|
|
|
<el-button class="--primary" type="primary" round plain size="large" @click="stepOneanswer" >提交任务</el-button>
|
|
|
<el-button class="--primary " type="primary" round plain size="large" @click="downloadArticle" >点击下载文章图片</el-button>
|
|
|
</div>
|
|
|
</div>
|
|
|
<el-divider />
|
|
|
<div class="Step2 Step" v-for="item1 in Step2" :key="item">
|
|
|
<div class="Ellipse">
|
|
|
<div class="Step-item">步骤二</div>
|
|
|
<span>{{ item1.stemContent }}</span>
|
|
|
</div>
|
|
|
<p><el-checkbox label="A" size="large" v-model="item1.studentAnswer" >A. {{ item1.optionA }}</el-checkbox></p>
|
|
|
<p><el-checkbox label="B" size="large" v-model="item1.studentAnswer" >B. {{ item1.optionB }}</el-checkbox></p>
|
|
|
<p><el-checkbox label="C" size="large" v-model="item1.studentAnswer" >C. {{ item1.optionC }}</el-checkbox></p>
|
|
|
<p><el-checkbox label="D" size="large" v-model="item1.studentAnswer" >D. {{ item1.optionD }}</el-checkbox></p>
|
|
|
<p><el-checkbox label="E" size="large" v-model="item1.studentAnswer" >E. {{ item1.optionF }}</el-checkbox></p>
|
|
|
<div class="footer">
|
|
|
<el-button class="--primary" type="primary" round plain size="large" @click="stepTwoanswer">提交任务</el-button>
|
|
|
</div>
|
|
|
</div>
|
|
|
<el-divider />
|
|
|
<div class="Step3 Step">
|
|
|
<div class="Ellipse">
|
|
|
<div class="Step-item">步骤三</div>
|
|
|
<span>识别字符</span>
|
|
|
</div>
|
|
|
<div class="Identify">
|
|
|
<div class="title"><span>请用手机上的文字识别工具对步骤一下载的图片进行文字识别,识别的文字内容请复制在下方文本框中。</span></div>
|
|
|
<el-input v-model="textarea" :rows="2" type="textarea" />
|
|
|
<div class="footer">
|
|
|
<el-button class="--primary" type="primary" round plain size="large" @click="identifyText" >提交任务</el-button>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
</el-scrollbar>
|
|
|
<div class="dialog-footer">
|
|
|
<el-button type="primary" round plain size="large" @click="submit">提交任务</el-button>
|
|
|
<el-button type="primary" round plain size="large" @click='reStart'>重新开始</el-button>
|
|
|
</div>
|
|
|
</div>
|
|
|
<div class="right">
|
|
|
<span @click="textDialogVisible=true"></span>
|
|
|
</div>
|
|
|
<el-dialog
|
|
|
custom-class="gdialog"
|
|
|
v-model="textDialogVisible"
|
|
|
title="文字识别OCR"
|
|
|
width="70%">
|
|
|
<el-scrollbar height="666px">
|
|
|
<p> OCR是一个简称,全称叫Optical CharacterRecognition,中文是:光学字符识别。</p>
|
|
|
<p>它的本质是:把图像形状转变为文本字符。</p>
|
|
|
<p>下面说一下,我在教育行业是如何应用OCR的</p>
|
|
|
<p>信息化教学越来越普及,很多教学素材都要搬到信息化平台,比如试卷试题。那么,纸质的试卷要电子化,就会用到OCR识别技术</p>
|
|
|
<p><img src="@/assets/images/szjj/u852.png" alt=""></p>
|
|
|
<p>这么一张图,需要识别成结构化(图片、文字、公式、表格可独立提取出来)的数据,识别结果如下所示:</p>
|
|
|
<p><img src="@/assets/images/szjj/u853.png" alt=""></p>
|
|
|
<p>而且,识别结果还可以下载成word文档,便于老师校对并进行二次编辑.</p>
|
|
|
<p>除此之外,我们常见的各种证件识别、名片识别、车牌识别等,也涉及OCR技术。</p>
|
|
|
<p>OCR技术的实现,总体上可以分为五步:预处理图片、切割字符、识别字符、恢复版面、后处理文字。</p>
|
|
|
<p><img src="@/assets/images/szjj/u856.png" alt=""></p>
|
|
|
<span class="headline">1.预处理图片</span>
|
|
|
<p>我们买回来水果,需要洗一下再吃。如果运气不好的话,还需要挖掉虫眼和糙皮才能吃。我们把吃水果前的这些步骤,叫做CSG(吃水果)的预处理。</p>
|
|
|
<p>在进行OCR之前,也需要对图片进行预处理。因为,一般待识别的图片千奇百怪,来源复杂:有拍照、有扫描、有截图。</p>
|
|
|
<p>拿拍照来说,有夏至那天中午头儿,在阳光直射下拍的;也有人在傍晚,拿着大顶转着圈儿拍的。如果不进行预处理,OCR会很为难,因此需要进行灰度化,二值化,噪声去除,倾斜矫正等预处理过程。 </p>
|
|
|
<p><img src="@/assets/images/szjj/u869.png" alt=""></p>
|
|
|
<span class="headline">2.切割字符</span>
|
|
|
<p>假设,通过了预处理,我们的图片都变得很规范,于是,我们需要切割字符,把每一个字都给他挖出来。</p>
|
|
|
<p><img src="@/assets/images/szjj/u859.png" alt=""></p>
|
|
|
<p>并且,还需要对每个字符做好标记,因为识别完了,还得还原回去。识别完了,结果是一堆单蹦的“1”、“2”、“3”、“+”、“-”字符。我们需要根据它们的相对<br/>位置,还原成“8-7=1”。所以,我们就知道了,哪个题目做对了,做错了,从而给出批改结果。</p>
|
|
|
<span class="headline">3.识别字符</span>
|
|
|
<p>图片究竟是怎么变为字符的?它还能自己学习。计算机通过学习一些样本之后,遇到一些从未遇到过的同类样本,也能正确地识别出结果,原理如下:<br/>
|
|
|
我带着孩子去公园,公园门口有一对大狮子。孩子指着狮子说,狗!我说,哦,那不是狗,那是狮子,跟狗有点像是不是。又走了一段路,公园里又出现一个麒麟的雕塑。孩子指着它说,狮子!我说,那不是狮子。<br/>
|
|
|
孩子说,是狗。我说也不是狗,它叫:麒麟。我感觉到,孩子的大脑在反向矫正信息,这就是监督学习。<br/>
|
|
|
当我给他看狗的图片时,我告诉她这是狗。<br/>
|
|
|
她根据自己的认知,找了几个特征,构建了一个模型:长嘴+尖牙=狗。<br/>
|
|
|
虽然只是看过图片,但是出门遇到真狗,她根据这个模型也认识对了。<br/>
|
|
|
后来,她遇到了狮子,她修改了模型:长嘴+尖牙+鬃毛≠狗=狮子。<br/>
|
|
|
后来,又遇到了麒麟,这个公式变得越来越复杂……决策项越来越多。<br/>
|
|
|
人工智能,就是模拟的人类的神经元,构建神经网络来尝试寻找特征和结果的关系。如果对了,就给这个特征加分。如果,错了,就给这个特征减分。<br/>
|
|
|
识别数字,也是一样。<br/>
|
|
|
比如在学习识别数字6的时候,它随机认为只要有一个圈圈特征,就是数字6。<br/>
|
|
|
验证其他样本时,发现这个随机特征是对的(不对就再换一个特征再试)。于是,它建立了一个模型:只要有圈这个特征,就是6。<br/>
|
|
|
后来,这个模型遇到了数字0。加入新样本后,人工智能发现,0也有圈,但它不是6,也有可能是0。得再找一个特征,于是,新增一条,有勾就是6。后来,它又遇到了9。那勾在上面的就是6。后来,它又遇到了字<br/>
|
|
|
母b……反正计算机有的是算力,能在很短的时间内完成这些学习。上面我是搂着说的,其实即便在32*32像素的小图片上,它随机上几十个特征去做验证,一点都不吃力。<br/>
|
|
|
这就是识别字符的原理。OCR字符的识别从来不难。难的是两头,比如开头的预处理,以及下面要说的后处理。<br/>
|
|
|
</p>
|
|
|
<span class="headline">4.恢复版面</span>
|
|
|
<p>识别出了字符,意义不大,有效地连接起来才能发挥作用。</p>
|
|
|
<p>我们期望的拆分和还原应该是下面这样:</p>
|
|
|
<p><img src="@/assets/images/szjj/u866.png" alt=""></p>
|
|
|
<p>“10+2= 4-3= 5+6=11”这些文本从数据结构上应该是一行。而且,“10+2=”从数据结构上是一个基本单位。因为,我们要对基本单位做运算和批改。这才叫还原,其实并不简单。</p>
|
|
|
<span class="headline">5.后处理文字</span>
|
|
|
<p>
|
|
|
OCR识别的最终目的,是要获得一份准确的、结构化的文本内容。<br/>
|
|
|
单个字符识别,其实是各自为战,前后不商量。<br/>
|
|
|
就比如,遇到一个圆圈形状的字符图片。OCR识别就犯了难,它是数字“0”?汉字“〇”?大写字母“O”?小写字母“o”?中文句号“。”?还是“Q”忘了加尾巴……<br/>
|
|
|
啥都对,啥都不对。<br/>
|
|
|
所以,需要矫正……校正。这两个词,都是高频词,尤其拼音打字jiaozheng,容易出错。其实,也好分辨。看语境,如果我前后提到了“文稿”,那么是“校正”的可能性就大。如果我刚刚说了“牙齿”、“视力”、“角度”啥的,那么基本上就应该是“矫正”了。
|
|
|
OCR识别的最后一步校正也是一样。如果无法确定是数字“0”还是字母“o”,可以观察它相邻的几个字符,下面一图胜千言<br/>
|
|
|
</p>
|
|
|
<p><img src="@/assets/images/szjj/u868.png" alt=""></p>
|
|
|
</el-scrollbar>
|
|
|
</el-dialog>
|
|
|
</div>
|
|
|
</div>
|
|
|
</template>
|
|
|
<script setup>
|
|
|
import {getToken,getUserInfo} from "@/utils/auth.js";
|
|
|
import * as commonality from "@/api/commonality.js";
|
|
|
const { proxy } = getCurrentInstance()
|
|
|
const textDialogVisible = ref(false);
|
|
|
const downloadShow = ref(false);
|
|
|
// 答题记录
|
|
|
const errorCount = ref(0);
|
|
|
const answerRecord=ref(0)
|
|
|
// 步骤一题目
|
|
|
const Step1 = ref([]);
|
|
|
const Step2 = ref([]);
|
|
|
const CharacterRecognitionCase=()=>{
|
|
|
commonality.getTextList({userId:JSON.parse(getUserInfo()).userId,model:'文字识别OCR'}).then(res=>{
|
|
|
if(Step1.value.length<1&&Step2.value.length<1){
|
|
|
Step1.value.push(res.data[0])
|
|
|
Step2.value.push(res.data[1])
|
|
|
}
|
|
|
Step1.value.forEach(item=>{
|
|
|
if(item.optionF!==null||item.optionF!==''){
|
|
|
textarea.value=item.optionF
|
|
|
}
|
|
|
if(item.studentAnswer!==''||item.studentAnswer!==null){
|
|
|
answerRecord.value=1
|
|
|
}
|
|
|
if(item.studentAnswer==null){
|
|
|
downloadShow.value=false
|
|
|
return
|
|
|
}
|
|
|
if(item.studentAnswer === item.correctAnswer){
|
|
|
downloadShow.value=true
|
|
|
}
|
|
|
})
|
|
|
|
|
|
Step2.value.forEach(item=>{
|
|
|
answerRecord.value=3
|
|
|
if(item.studentAnswer !== null){
|
|
|
item.studentAnswer=item.studentAnswer.split(',')
|
|
|
}else{
|
|
|
item.studentAnswer=[]
|
|
|
console.log( item.studentAnswer," item.studentAnswer");
|
|
|
}
|
|
|
})
|
|
|
})
|
|
|
}
|
|
|
// submitStepOne
|
|
|
const stepOneanswer=()=>{
|
|
|
answerRecord.value=1
|
|
|
commonality.submitStepOne({userId:JSON.parse(getUserInfo()).userId,answer:Step1.value[0].studentAnswer}).then(res=>{
|
|
|
if(res.msg.includes('错误')){
|
|
|
errorCount.value=1
|
|
|
proxy.$modal.msgError(res.msg);
|
|
|
}else{
|
|
|
proxy.$modal.msgSuccess('提交成功');
|
|
|
downloadShow.value=true
|
|
|
|
|
|
}
|
|
|
}).catch(error=>{
|
|
|
errorCount.value=1
|
|
|
})
|
|
|
}
|
|
|
const stepTwoanswer=()=>{
|
|
|
if(answerRecord.value<1){
|
|
|
return proxy.$modal.msgWarning("请先完成步骤一")
|
|
|
}else if(answerRecord.value < 2){
|
|
|
return proxy.$modal.msgWarning("请先下载文章图片再进行此操作")
|
|
|
}
|
|
|
answerRecord.value=3
|
|
|
commonality.submitStepTwo({userId:JSON.parse(getUserInfo()).userId,answer:Step2.value[0].studentAnswer.join(',')}).then(res=>{
|
|
|
if(res.msg.includes('错误')){
|
|
|
errorCount.value=2
|
|
|
proxy.$modal.msgError(res.msg);
|
|
|
}else{
|
|
|
proxy.$modal.msgSuccess('提交成功');
|
|
|
}
|
|
|
}).catch(err=>{
|
|
|
errorCount.value=2
|
|
|
})
|
|
|
}
|
|
|
// 下载文章
|
|
|
const downloadArticle=()=>{
|
|
|
if(answerRecord.value<1){
|
|
|
return proxy.$modal.msgWarning("请先完成上一步")
|
|
|
}
|
|
|
answerRecord.value=2
|
|
|
window.open(`http://118.31.7.2:9868/api/stu/experimental/downloadImg?userId=${JSON.parse(getUserInfo()).userId}&TOKEN=${getToken()}`)
|
|
|
}
|
|
|
|
|
|
// 识别文字
|
|
|
const textarea=ref('')
|
|
|
// 识别文字函数
|
|
|
const identifyText=()=>{
|
|
|
const params={
|
|
|
userId:JSON.parse(getUserInfo()).userId,
|
|
|
context:textarea.value
|
|
|
}
|
|
|
if(answerRecord.value<3){
|
|
|
return proxy.$modal.msgWarning("请先完成前面的题目再进行此操作")
|
|
|
}
|
|
|
answerRecord.value=4
|
|
|
commonality.submitStepThree(params).then(res=>{
|
|
|
proxy.$modal.msgSuccess(res.msg);
|
|
|
}).catch(err=>{
|
|
|
errorCount.value=3
|
|
|
})
|
|
|
}
|
|
|
const submit = () => {
|
|
|
if( textarea.value!==''&& answerRecord.value>2){
|
|
|
answerRecord.value=4
|
|
|
}
|
|
|
if(answerRecord.value < 4){
|
|
|
return proxy.$modal.msgWarning("请先完成实训")
|
|
|
}
|
|
|
commonality
|
|
|
.submitTask({module:'人工智能',numberOfErrors:errorCount.value>10?10:errorCount.value,taskName:'文字之别OCR',userId:JSON.parse(getUserInfo()).userId})
|
|
|
.then((response) => {
|
|
|
proxy.$modal.msgSuccess("提交成功");
|
|
|
textDialogVisible.value=false
|
|
|
})
|
|
|
.catch((error) => {
|
|
|
});
|
|
|
};
|
|
|
// 重新开始
|
|
|
const reStart=()=>{
|
|
|
textarea.value=''
|
|
|
Step1.value.forEach(item=>{
|
|
|
item.studentAnswer=''
|
|
|
})
|
|
|
Step2.value.forEach(item=>{
|
|
|
item.studentAnswer=[]
|
|
|
})
|
|
|
answerRecord.value=0
|
|
|
downloadShow.value=false
|
|
|
}
|
|
|
CharacterRecognitionCase()
|
|
|
</script>
|
|
|
<style lang="scss" scoped>
|
|
|
.text-centent {
|
|
|
display: flex;
|
|
|
.left {
|
|
|
position: relative;
|
|
|
width: 100%;
|
|
|
}
|
|
|
.right {
|
|
|
position: absolute;
|
|
|
right: 15px;
|
|
|
top: 150px;
|
|
|
width: 216px;
|
|
|
padding-left: 60px;
|
|
|
span {
|
|
|
display: inline-block;
|
|
|
width: 110px;
|
|
|
height: 143px;
|
|
|
background: url("@/assets/images/szjj/案例任务.png") no-repeat;
|
|
|
background-size: 100% 100%;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
div {
|
|
|
// p {
|
|
|
// font-size: 12px;
|
|
|
// }
|
|
|
.title-back {
|
|
|
width: 182px;
|
|
|
height: 66px;
|
|
|
line-height: 66px;
|
|
|
text-align: center;
|
|
|
background: url("@/assets/images/szjj/任务背景.png") no-repeat;
|
|
|
background-size: 100% 100%;
|
|
|
font-size: 20px;
|
|
|
font-family: Source Han Sans CN;
|
|
|
font-weight: bold;
|
|
|
}
|
|
|
.Step {
|
|
|
margin-bottom: 34px;
|
|
|
.Ellipse{
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
.Step-item {
|
|
|
width: 52px;
|
|
|
height: 25px;
|
|
|
color: #ffffff;
|
|
|
background: #5621f8;
|
|
|
border-radius: 13px;
|
|
|
font-size: 12px;
|
|
|
text-align: center;
|
|
|
line-height: 25px;
|
|
|
}
|
|
|
span {
|
|
|
display: inline-block;
|
|
|
margin-left: 10px !important;
|
|
|
font-size: 15px;
|
|
|
font-family: Source Han Sans CN;
|
|
|
font-weight: 600;
|
|
|
color: #FFFFFF;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
p {
|
|
|
width: calc(100% - 30%);
|
|
|
padding-left: 22px;
|
|
|
height: 36px;
|
|
|
background: #e9ebfc;
|
|
|
border-radius: 4px;
|
|
|
}
|
|
|
}
|
|
|
.--primary {
|
|
|
font-size: 12px;
|
|
|
padding-left: 40px;
|
|
|
padding-right: 34px;
|
|
|
border: none;
|
|
|
color: #ffffff;
|
|
|
background: url("@/assets/images/szjj/提交任务.png") no-repeat !important;
|
|
|
background-size: 100% 100%;
|
|
|
}
|
|
|
.el-button--primary:nth-child(2) {
|
|
|
width: 140px !important;
|
|
|
padding-left: 60px;
|
|
|
background: url("@/assets/images/szjj/重新开始.png") no-repeat !important;
|
|
|
background-size:cover;
|
|
|
}
|
|
|
.Identify {
|
|
|
margin-top: 20px;
|
|
|
width: calc(100% - 20%);
|
|
|
height: 420px;
|
|
|
background: #f7f7f7;
|
|
|
border: 1px solid #dcdcdc;
|
|
|
border-radius: 4px;
|
|
|
padding: 0px 18px;
|
|
|
.title {
|
|
|
height: 54px;
|
|
|
line-height: 54px;
|
|
|
span{
|
|
|
font-size: 12px;
|
|
|
font-family: Source Han Sans CN;
|
|
|
font-weight: 500;
|
|
|
line-height: 20px;
|
|
|
}
|
|
|
}
|
|
|
:deep(.el-textarea__inner) {
|
|
|
min-height: 282px !important;
|
|
|
}
|
|
|
.footer {
|
|
|
margin-top: 18px;
|
|
|
}
|
|
|
}
|
|
|
.foo_ter {
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
justify-content: center;
|
|
|
}
|
|
|
.dis{
|
|
|
display: none !important;
|
|
|
}
|
|
|
// .btn{
|
|
|
// // margin-left: 20px;
|
|
|
// }
|
|
|
}
|
|
|
</style>
|