feat: 初步接入关系标注
This commit is contained in:
parent
dec3115831
commit
06d4378c73
|
@ -9,6 +9,7 @@ https://tekii.cn/ner
|
||||||
- [x] 命名实体识别
|
- [x] 命名实体识别
|
||||||
- [x] 文本分类
|
- [x] 文本分类
|
||||||
- [x] 人类反馈强化学习
|
- [x] 人类反馈强化学习
|
||||||
|
- [ ] 关系标注
|
||||||
- CV:
|
- CV:
|
||||||
- [x] 关键点
|
- [x] 关键点
|
||||||
|
|
||||||
|
|
|
@ -62,7 +62,13 @@
|
||||||
</span>
|
</span>
|
||||||
<text x="201" y="10" fill="red">关系一</text>
|
<text x="201" y="10" fill="red">关系一</text>
|
||||||
</svg> -->
|
</svg> -->
|
||||||
<div v-if="projectType === '命名实体识别'">
|
<div v-if="nerProjectType.indexOf(projectType) > -1">
|
||||||
|
<span class="word" v-for="(word, idx) in nowText" :key="idx" :id="idx" @contextmenu="stopPrev" @mousedown="startSelect(idx, $event)" @touchstart="startSelect(idx, $event)" @touchend="stopSelect()" @mousemove="pointWord(idx)" @touchmove="pointWordByTouch($event)"
|
||||||
|
>
|
||||||
|
{{ word }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div v-if="nerProjectType.indexOf(projectType) > -1">
|
||||||
<div class="word-rect-area">
|
<div class="word-rect-area">
|
||||||
<span class="rect" v-for="(word, idx) in nowNers" :key="idx">
|
<span class="rect" v-for="(word, idx) in nowNers" :key="idx">
|
||||||
<span v-for="(w, i) in word.name" :key="`${word}${w}${i}`">
|
<span v-for="(w, i) in word.name" :key="`${word}${w}${i}`">
|
||||||
|
@ -96,12 +102,6 @@
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="projectType === '命名实体识别'">
|
|
||||||
<span class="word" v-for="(word, idx) in nowText" :key="idx" :id="idx" @contextmenu="stopPrev" @mousedown="startSelect(idx, $event)" @touchstart="startSelect(idx, $event)" @touchend="stopSelect()" @mousemove="pointWord(idx)" @touchmove="pointWordByTouch($event)"
|
|
||||||
>
|
|
||||||
{{ word }}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div v-if="projectType === '文本分类'">
|
<div v-if="projectType === '文本分类'">
|
||||||
<template v-for="(word, idx) in nowText">
|
<template v-for="(word, idx) in nowText">
|
||||||
<span class="word" v-if="word !== '\n'" :key="idx" @contextmenu="stopPrev" @mousedown="startSelect(idx, $event)" @mousemove="pointWord(idx)">{{ word }}</span>
|
<span class="word" v-if="word !== '\n'" :key="idx" @contextmenu="stopPrev" @mousedown="startSelect(idx, $event)" @mousemove="pointWord(idx)">{{ word }}</span>
|
||||||
|
@ -110,6 +110,7 @@
|
||||||
</div>
|
</div>
|
||||||
<CVPoint v-if="projectType === '图片点标注'" :fileContent="nowText" :annoDetails="ners" :nowType="nowType" :types="types" :save="save"></CVPoint>
|
<CVPoint v-if="projectType === '图片点标注'" :fileContent="nowText" :annoDetails="ners" :nowType="nowType" :types="types" :save="save"></CVPoint>
|
||||||
<RLHF v-if="projectType === '人类反馈强化学习'" :fileContent="nowText" :annoDetails="ners" :nowType="nowType" :types="types" :save="save"></RLHF>
|
<RLHF v-if="projectType === '人类反馈强化学习'" :fileContent="nowText" :annoDetails="ners" :nowType="nowType" :types="types" :save="save"></RLHF>
|
||||||
|
<Rel v-if="projectType === '关系标注'" :relDetails="relDetails" :nowType="nowType" :save="save"></Rel>
|
||||||
</div>
|
</div>
|
||||||
<div class="page-btn-box">
|
<div class="page-btn-box">
|
||||||
<button class="page-btn" @click="changeIdx(-1, $event)" @mouseover="setFocus('page-up')" @mouseleave="setFocus('')">上一个 {{ fastTypeKey['page-up'] ? `【${fastTypeKey['page-up']}】` : '' }}</button>
|
<button class="page-btn" @click="changeIdx(-1, $event)" @mouseover="setFocus('page-up')" @mouseleave="setFocus('')">上一个 {{ fastTypeKey['page-up'] ? `【${fastTypeKey['page-up']}】` : '' }}</button>
|
||||||
|
@ -136,6 +137,7 @@
|
||||||
import { getColor } from '../../js/color.js'
|
import { getColor } from '../../js/color.js'
|
||||||
import CVPoint from '@/components/CV/point.vue'
|
import CVPoint from '@/components/CV/point.vue'
|
||||||
import RLHF from '@/components/NLP/rlhf.vue'
|
import RLHF from '@/components/NLP/rlhf.vue'
|
||||||
|
import Rel from '@/components/NLP/rel.vue'
|
||||||
|
|
||||||
// 是否是单机版
|
// 是否是单机版
|
||||||
const isLocal = false
|
const isLocal = false
|
||||||
|
@ -192,7 +194,8 @@ export default {
|
||||||
name: 'NER',
|
name: 'NER',
|
||||||
components: {
|
components: {
|
||||||
CVPoint,
|
CVPoint,
|
||||||
RLHF
|
RLHF,
|
||||||
|
Rel
|
||||||
},
|
},
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
|
@ -236,7 +239,10 @@ export default {
|
||||||
wordsOutType: [], // 文字对应是否放大
|
wordsOutType: [], // 文字对应是否放大
|
||||||
nersCache: { // 各个文件已标注内容的缓存
|
nersCache: { // 各个文件已标注内容的缓存
|
||||||
},
|
},
|
||||||
mode: '' // 如果鼠标在文字上按下了,就开始'select'模式,开始框选实体
|
mode: '', // 如果鼠标在文字上按下了,就开始'select'模式,开始框选实体
|
||||||
|
nerProjectType: ['命名实体识别', '关系标注'],
|
||||||
|
relStartIdx: '',
|
||||||
|
relDetails: []
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
@ -319,11 +325,15 @@ export default {
|
||||||
}
|
}
|
||||||
const that = this
|
const that = this
|
||||||
// 保存当前标注
|
// 保存当前标注
|
||||||
post('/v1/anno/create', {
|
let annoInfo = {
|
||||||
projectName: that.projectName,
|
projectName: that.projectName,
|
||||||
fileName: that.nowFile,
|
fileName: that.nowFile,
|
||||||
annoDetails: that.ners
|
annoDetails: that.ners
|
||||||
})
|
}
|
||||||
|
if (that.projectType === '关系标注') {
|
||||||
|
annoInfo.relDetails = that.relDetails
|
||||||
|
}
|
||||||
|
post('/v1/anno/create', annoInfo)
|
||||||
that.$set(that.nersCache, that.nowFile, [...that.ners])
|
that.$set(that.nersCache, that.nowFile, [...that.ners])
|
||||||
return true
|
return true
|
||||||
},
|
},
|
||||||
|
@ -419,7 +429,7 @@ export default {
|
||||||
// 如果没有选中文件,就不允许标注
|
// 如果没有选中文件,就不允许标注
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if (this.projectType === '命名实体识别') {
|
if (this.nerProjectType.indexOf(this.projectType) > -1) {
|
||||||
this.$set(this, 'nowType', type)
|
this.$set(this, 'nowType', type)
|
||||||
} else if (this.projectType === '文本分类') {
|
} else if (this.projectType === '文本分类') {
|
||||||
let typeIdx = -1
|
let typeIdx = -1
|
||||||
|
@ -536,17 +546,19 @@ export default {
|
||||||
}
|
}
|
||||||
this.$set(this, 'wordsOutType', this.wordsOutType)
|
this.$set(this, 'wordsOutType', this.wordsOutType)
|
||||||
},
|
},
|
||||||
delIdx: function (idx) {
|
getNerIdxByIdx: function (idx) {
|
||||||
const ners = this.ners
|
const ners = this.ners
|
||||||
for (let i = ners.length - 1; i >= 0; i -= 1) {
|
for (let i = ners.length - 1; i >= 0; i -= 1) {
|
||||||
const ner = ners[i]
|
const ner = ners[i]
|
||||||
if (idx >= ner.start && idx < ner.end) {
|
if (idx >= ner.start && idx < ner.end) {
|
||||||
this.del(i)
|
return i
|
||||||
this.flushWordsType()
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
delIdx: function (idx) {
|
||||||
|
const nerIdx = this.getNerIdxByIdx(idx)
|
||||||
|
this.del(nerIdx)
|
||||||
|
},
|
||||||
del: function (idx) {
|
del: function (idx) {
|
||||||
const ners = this.ners
|
const ners = this.ners
|
||||||
ners.splice(idx, 1)
|
ners.splice(idx, 1)
|
||||||
|
@ -554,7 +566,21 @@ export default {
|
||||||
this.save() // 删除时实时保存
|
this.save() // 删除时实时保存
|
||||||
},
|
},
|
||||||
startSelect: function (idx, event) {
|
startSelect: function (idx, event) {
|
||||||
if (this.projectType !== '命名实体识别') return
|
if (this.nerProjectType.indexOf(this.projectType) === -1) return
|
||||||
|
if (this.nowType.indexOf('关系-') > -1) {
|
||||||
|
if (typeof this.relStartIdx === 'number') {
|
||||||
|
console.log(this.relStartIdx, idx)
|
||||||
|
this.relDetails.push({
|
||||||
|
start: this.relStartIdx,
|
||||||
|
end: idx,
|
||||||
|
type: this.nowType
|
||||||
|
})
|
||||||
|
this.$set(this, 'relStartIdx', undefined)
|
||||||
|
} else {
|
||||||
|
this.$set(this, 'relStartIdx', idx)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
let that = this
|
let that = this
|
||||||
let isNeedDel = false
|
let isNeedDel = false
|
||||||
if (event.touches) {
|
if (event.touches) {
|
||||||
|
@ -652,13 +678,7 @@ export default {
|
||||||
outAllNers () {
|
outAllNers () {
|
||||||
if (!isLocal) {
|
if (!isLocal) {
|
||||||
// 非单机版,就直接通过url下载
|
// 非单机版,就直接通过url下载
|
||||||
if (this.projectType === '命名实体识别') {
|
window.open(`/v1/files/get_anno_json?projectName=${this.projectName}`, '_self')
|
||||||
window.open(`/v1/files/get_anno_json?projectName=${this.projectName}`, '_self')
|
|
||||||
} else if (this.projectType === '文本分类') {
|
|
||||||
window.open(`/v1/files/get_anno_json?projectName=${this.projectName}`, '_self')
|
|
||||||
} else {
|
|
||||||
window.open(`/v1/files/get_anno_json?projectName=${this.projectName}`, '_self')
|
|
||||||
}
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
this.nersCache[this.nowFile] = this.ners
|
this.nersCache[this.nowFile] = this.ners
|
||||||
|
@ -704,7 +724,7 @@ export default {
|
||||||
},
|
},
|
||||||
// 判断标签按钮是否需要被显示成按下
|
// 判断标签按钮是否需要被显示成按下
|
||||||
isTypeSelected (type) {
|
isTypeSelected (type) {
|
||||||
if (this.projectType === '命名实体识别') {
|
if (this.nerProjectType.indexOf(this.projectType) > -1) {
|
||||||
return this.nowType === type
|
return this.nowType === type
|
||||||
} else if (this.projectType === '文本分类') {
|
} else if (this.projectType === '文本分类') {
|
||||||
return this.ners.some((ner) => {
|
return this.ners.some((ner) => {
|
||||||
|
@ -717,7 +737,7 @@ export default {
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
ners: function () {
|
ners: function () {
|
||||||
if (this.projectType === '命名实体识别') {
|
if (this.nerProjectType.indexOf(this.projectType) > -1) {
|
||||||
// 给ner加上是否缩小的属性
|
// 给ner加上是否缩小的属性
|
||||||
let nowSmallAreaEnd
|
let nowSmallAreaEnd
|
||||||
for (let i = 0; i < this.ners.length; i++) {
|
for (let i = 0; i < this.ners.length; i++) {
|
||||||
|
@ -1097,7 +1117,6 @@ export default {
|
||||||
|
|
||||||
.word-rect-area .rect {
|
.word-rect-area .rect {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
pointer-events: none;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.color-input {
|
.color-input {
|
||||||
|
|
|
@ -0,0 +1,75 @@
|
||||||
|
<template>
|
||||||
|
<div class="rel-box">
|
||||||
|
<div v-for="(relDetail, idx) in relDetails" :key="idx" class="rel" @click="clickLine(idx)">
|
||||||
|
<div>{{relDetail}}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
export default ({
|
||||||
|
name: 'Rel',
|
||||||
|
props: {
|
||||||
|
nowType: {
|
||||||
|
type: String,
|
||||||
|
default: '1234',
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
relDetails: {
|
||||||
|
type: Array,
|
||||||
|
default: undefined,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
save: {
|
||||||
|
type: Function,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
points: [],
|
||||||
|
width: 0,
|
||||||
|
height: 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
log (...args) {
|
||||||
|
console.log(args)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
relDetails: {
|
||||||
|
handler (val) {
|
||||||
|
console.log(val)
|
||||||
|
},
|
||||||
|
deep: true
|
||||||
|
},
|
||||||
|
delRel: {
|
||||||
|
handler (val) {
|
||||||
|
if (!val) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.relDetails.splice(this.relDetails.indexOf(val), 1)
|
||||||
|
this.save()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
nowType: {
|
||||||
|
handler (val) {
|
||||||
|
if (!val) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (this.annoDetails.indexOf(val) === -1) {
|
||||||
|
this.annoDetails.push(val)
|
||||||
|
} else {
|
||||||
|
this.annoDetails.splice(this.annoDetails.indexOf(val), 1)
|
||||||
|
}
|
||||||
|
this.save()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
</style>
|
|
@ -29,6 +29,7 @@
|
||||||
<option value ="文本分类">文本分类</option>
|
<option value ="文本分类">文本分类</option>
|
||||||
<option value ="图片点标注">图片点标注</option>
|
<option value ="图片点标注">图片点标注</option>
|
||||||
<option value ="人类反馈强化学习">人类反馈强化学习</option>
|
<option value ="人类反馈强化学习">人类反馈强化学习</option>
|
||||||
|
<option value ="关系标注">关系标注</option>
|
||||||
</select>
|
</select>
|
||||||
</p>
|
</p>
|
||||||
<div class="title">
|
<div class="title">
|
||||||
|
|
Loading…
Reference in New Issue