图像标注的功能

This commit is contained in:
lSun 2025-09-29 18:22:00 +08:00
parent 5cc48fa229
commit 7e42679767
5 changed files with 278 additions and 106 deletions

View File

@ -0,0 +1,49 @@
import request from '@/utils/request'
import request_formdata from "@/utils/request_formdata";
// 查询标签列表
export function getSelectedAPI(data) {
return request({
url: '/image/caption/getSelected',
method: 'POST',
data,
})
}
// 查询左侧历史记录
export function getImageListAPI(data) {
return request({
url: '/image/caption/getImageList',
method: 'POST',
data,
})
}
//查询右侧的历史记录详情
export function getImageListDetailsAPI(data) {
return request({
url: '/image/caption/getImageListDetails',
method: 'POST',
data,
})
}
//新增标注
export function addImageInfoAPI(data) {
return request_formdata({
url: '/image/caption/addImageInfo',
method: 'POST',
data,
})
}
//修改图片
export function updateImageSureAPI(data) {
return request_formdata({
url: '/image/caption/updateImageSure',
method: 'POST',
data,
})
}

View File

@ -6,8 +6,10 @@
:upload-info="uploadInfo"
:image-results="imageResults"
:selected-images="selectedImages"
:is-sure="isSure"
@hand-click="handleHandClickFromChild"
@confirm-results="$emit('confirm-results')"
/>
</div>
@ -76,6 +78,10 @@ export default {
fileList: {
type: Array,
default: () => []
},
isSure: {
type: String,
default: ''
}
},
methods: {

View File

@ -20,14 +20,14 @@
>
<!-- 右上角的hand图标 -->
<img
v-if="selectedImages[index]"
v-if="selectedImages[index] && isSure == '0'"
src="@/assets/images/imageCaptioning/hand-yellow.png"
alt="selected"
class="icon-hand"
@click="$emit('hand-click', result, index)"
>
<img
v-else
v-else-if="!selectedImages[index] && isSure == '0'"
src="@/assets/images/imageCaptioning/hand.png"
alt="hand"
class="icon-hand"
@ -38,7 +38,7 @@
<!-- 操作按钮 -->
<div class="action-buttons">
<el-button type="primary" @click="$emit('confirm-results')">确认</el-button>
<el-button type="primary" @click="$emit('confirm-results')" v-if="isSure == '0'">确认</el-button>
</div>
</div>
</template>
@ -58,7 +58,20 @@ export default {
selectedImages: {
type: Object,
default: () => ({})
},
isSure: {
type: String,
default: '0'
}
},
watch: {
isSure(newValue) {
console.log('isSure changed to:', newValue);
}
},
mounted() {
// isSure
console.log('Initial isSure:', this.isSure);
}
}
</script>

View File

@ -1,22 +1,13 @@
<template>
<div class="tag-container">
<!-- <div-->
<!-- v-for="(tag, index) in visibleTags"-->
<!-- :key="index"-->
<!-- class="tag-item"-->
<!-- :class="{ active: selectedTag.includes(tag) }"-->
<!-- @click="$emit('toggle-tag', tag)"-->
<!-- >-->
<!-- {{ tag }}-->
<!-- </div>-->
<template v-for="(tag, index) in visibleTags">
<div
:key="'tag-' + index"
:key="'tag-' + tag.id"
class="tag-item"
:class="{ active: selectedTag.includes(tag) }"
:class="{ active: isSelected(tag) }"
@click="$emit('toggle-tag', tag)"
>
{{ tag }}
{{ tag.name }}
</div>
<span
v-if="index < visibleTags.length - 1"
@ -50,6 +41,12 @@ export default {
type: Array,
default: () => []
}
},
methods: {
//
isSelected(tag) {
return this.selectedTag.some(item => item.id === tag.id)
}
}
}
</script>

View File

@ -50,6 +50,7 @@
:visible-tags="visibleTags"
:selected-tag="selectedTag"
:file-list="fileList"
:is-sure="isSure"
@hand-click="handleHandClick"
@confirm-results="confirmResults"
@toggle-tag="toggleTag"
@ -63,10 +64,18 @@
</template>
<script>
import {
getSelectedAPI,
getImageListAPI,
getImageListDetailsAPI,
addImageInfoAPI,
updateImageSureAPI
} from "@/api/imageCaptioning/imageCaptioning";
import Sidebar from "./components/Sidebar";
import TagSelector from "./components/TagSelector";
import FileUploader from "./components/FileUploader";
import HistoryView from "./components/HistoryView";
export default {
name: "imageCaptioning",
components: {
@ -79,21 +88,14 @@ export default {
return {
showNewAnnotation: true, //
showNewAnnotationAdd: false, //
//
tags: ['安全帽', '绝缘子', '人', '电线杆', '变压器', '电缆'],
visibleTags: ['安全帽', '绝缘子', '人'], // 3
visibleTagsOld: [],
selectedTag: [],
showAllTags: false,
tags: [],//
visibleTags: [], // 3
selectedTag: [], //
showAllTags: false, //
//
labelList: [
{id: 1, date: '2025-09-13', name: '安全帽、绝缘子、虎视眈眈、去去去、滴滴滴、'},
{id: 2, date: '2025-09-10', name: '安全帽'}
],
labelList: [],
selectedItem: null,
//
fileList: [],
showImageResults: false,
@ -103,14 +105,58 @@ export default {
imageResults: [],
selectedImages: {}, //
isSidebarVisible: true, // /
isSure:'', // 0- 1-
}
},
created() {
//
this.getSelectedAPI()
//
this.getImageListAPI()
},
methods: {
createNewLabel() {
// visibleTagsOld
if (this.visibleTagsOld && this.visibleTagsOld.length > 0) {
this.visibleTags = this.visibleTagsOld;
async getSelectedAPI() {
try {
const res = await getSelectedAPI()
//
if (res.code === 200 && res.data && Array.isArray(res.data)) {
// id name
this.tags = res.data.map(item => ({
id: item.id,
name: item.name
}))
this.visibleTags = this.tags.slice(0, 3);
} else {
console.error('获取标签列表失败:', res)
this.tags = []
}
} catch (error) {
console.error('获取标签列表异常:', error)
this.tags = []
}
},
async getImageListAPI() {
try {
const res = await getImageListAPI({operaType: 1})
//
if (res.code === 200 && res.data && Array.isArray(res.data)) {
this.labelList = res.data.map(item => ({
id: item.id,
date: item.operaTime,
name: item.operaName
}))
}
} catch (error) {
console.error('获取列表异常:', error)
}
},
createNewLabel() {
this.selectedItem = null;
this.fileList = [];
this.selectedTag = [];
@ -128,15 +174,17 @@ export default {
this.$message.warning('请选择需要标注的内容标签')
return
}
//
const selectedTagIds = this.selectedTag.map(tag => tag.id).join(',');
const selectedTagNames = this.selectedTag.map(tag => tag.name).join(',');
console.log('选中的标签IDs:', selectedTagIds);
console.log('选中的标签名称:', selectedTagNames);
if (this.fileList.length === 0) {
this.$message.warning('请上传图片')
return
}
// selectedImages
this.selectedImages = {};
//
const loading = this.$loading({
lock: true,
@ -144,39 +192,71 @@ export default {
spinner: 'el-icon-loading',
background: 'rgba(0, 0, 0, 0.7)'
});
//
const originalFileList = [...this.fileList];
//
this.fileList = [];
//
// FormData
const formData = new FormData();
//
originalFileList.forEach(file => {
formData.append('files', file.raw); // 使 raw
});
formData.append('param', selectedTagNames);
//IDsname
addImageInfoAPI(formData)
.then(res => {
loading.close();
if (res.code === 200 && res.data && Array.isArray(res.data)) {
//
const record = res.data[0]; //
const totalCount = record.imageNum || originalFileList.length;
const recognizedCount = record.bzNum || 0;
const unrecognizedCount = record.wbzNum || (totalCount - recognizedCount);
this.uploadInfo = `上传${totalCount}张图片,可识别标注${recognizedCount}张,${unrecognizedCount}张无法识别标注。已识别标注图片如下,请确认。`;
//
this.imageResults = record.fileVoList.map(item => ({
url: item.bjUrl || "未找到图片地址",
id: item.imageId || item.id,
name: item.originalName,
contentImage: item.contentImage,
fileSize: item.fileSize,
operId: res.data[0].id , // ID
}));
this.isSure = record.isSure;
//
this.refreshSidebarLabel(record.operaName, record.id);
this.showImageResults = true;
this.showNewAnnotation = false;
this.showNewAnnotationAdd = true;
this.$message.success('上传和识别完成');
} else {
this.$message.error('上传失败');
}
})
.catch(error => {
loading.close();
console.error('上传异常:', error);
this.$message.error('上传过程中出现错误');
});
},
refreshSidebarLabel(operaName, recordId) {
this.getImageListAPI()
setTimeout(() => {
loading.close();
//
const recognizedCount = Math.floor(originalFileList.length * 0.7);
const unrecognizedCount = originalFileList.length - recognizedCount;
this.uploadInfo = `上传${originalFileList.length}张图片,可识别标注${recognizedCount}张,${unrecognizedCount}张无法识别标注。已识别标注图片如下,请确认。`;
// status
this.imageResults = originalFileList.map((file, index) => ({
url: file.url,
id: index,
// status: 1 0
status: index < recognizedCount ? 1 : 0
}));
console.log(this.imageResults);
this.showImageResults = true;
//
this.showNewAnnotation = false;
this.showNewAnnotationAdd = true;
this.$message.success('上传和识别完成');
}, 1500); // 1.5
const index = this.labelList.findIndex(item => item.id === recordId);
if (index !== -1) {
this.labelList[index].name = operaName;
}
//
this.selectedItem = recordId;
}, 500); //
},
selectItem(item) {
@ -189,29 +269,26 @@ export default {
//
const record = this.labelList.find(r => r.id === recordId);
if (record) {
this.showNewAnnotation = false;
this.showNewAnnotationAdd = true;
//
this.fileList = []; //
this.selectedTag = []; //
this.showImageResults = true; //
this.selectedImages = {}; //
//
//
this.generateTestData(recordId);
}
},
toggleTag(tag) {
const index = this.selectedTag.indexOf(tag)
const index = this.selectedTag.findIndex(item => item.id === tag.id)
if (index === -1) {
this.selectedTag.push(tag)
} else {
this.selectedTag.splice(index, 1)
}
// visibleTags
if (!this.showAllTags) {
this.updateVisibleTagsInCollapsedState();
}
@ -222,16 +299,15 @@ export default {
const defaultTags = this.tags.slice(0, 3)
if (selectedTags.length > 0) {
//
if (selectedTags.length >= 3) {
this.visibleTags = selectedTags.slice(0, 3)
} else {
// 3
const tags = defaultTags.filter(tag => !selectedTags.includes(tag)).slice(0, 3 - selectedTags.length)
const tags = defaultTags.filter(tag =>
!selectedTags.some(selected => selected.id === tag.id))
.slice(0, 3 - selectedTags.length)
this.visibleTags = [...selectedTags, ...tags]
}
} else {
// 3
this.visibleTags = defaultTags
}
},
@ -239,28 +315,21 @@ export default {
toggleAllTags() {
this.showAllTags = !this.showAllTags
if (this.showAllTags) {
//
this.visibleTags = this.tags
} else {
//
const selectedTags = this.selectedTag || []
const defaultTags = this.tags.slice(0, 3)
if (selectedTags.length > 0) {
//
// 3
// 3
if (selectedTags.length <= 3) {
// 3
const allTags = [...selectedTags, ...defaultTags]
const uniqueTags = [...new Set(allTags)]
const uniqueTags = allTags.filter((tag, index, self) =>
index === self.findIndex(t => t.id === tag.id))
this.visibleTags = uniqueTags.slice(0, 3)
} else {
// 33
this.visibleTags = selectedTags
}
} else {
// 3
this.visibleTags = defaultTags
}
}
@ -287,6 +356,7 @@ export default {
//
handleHandClick(result, index) {
console.log(result)
console.log("点击了图片:", index)
// 使
if (this.selectedImages[index]) {
@ -298,38 +368,75 @@ export default {
}
},
generateTestData(recordId) {
//
const testData = {
id: recordId,
date: '2025-09-13',
name: '安全帽、绝缘子...',
uploadInfo: `上传10张图片可识别标注6张4张无法识别标注。已识别标注图片如下请确认。`,
imageResults: [
{
url: 'https://fastly.picsum.photos/id/830/200/200.jpg?hmac=3ce7zNUn5yg_XKy7dHgIHta7t_0vghPQnAGUSGJuBZE',
name: 'image1.jpg',
},
{
url: 'https://example.com/image2.jpg',
name: 'image1.jpg',
},
],
selectedTags: ['安全帽', '绝缘子', '电线杆']
};
async generateTestData(recordId) {
//
try {
const res = await getImageListDetailsAPI({id: recordId, operaType: 1})
//
if (res.code === 200 && res.data && Array.isArray(res.data)) {
//
this.imageResults = res.data[0].fileVoList.map((item, index) => ({
url: item.bjUrl || item.url, // 使 bjUrl URL
id: item.imageId || index, // 使 imageId ID
name: item.originalName, //
contentImage: item.contentImage, //
fileSize: item.fileSize, //
operId: res.data[0].id , // ID
}));
this.selectedTag = testData.selectedTags;
this.uploadInfo = testData.uploadInfo;
this.imageResults = testData.imageResults;
this.isSure = res.data[0].isSure;
//
this.visibleTagsOld = this.visibleTags;
this.visibleTags = testData.selectedTags;
const totalCount = res.data[0].imageNum || 0;
const recognizedCount = res.data[0].bzNum || 0;
const unrecognizedCount = res.data[0].wbzNum ;
this.uploadInfo = `上传${totalCount}张图片,可识别标注${recognizedCount}张,${unrecognizedCount}张无法识别标注。已识别标注图片如下,请确认。`;
}
} catch (error) {
console.error('获取列表异常:', error)
}
},
confirmResults(){
console.log("确定")
confirmResults() {
console.log('当前 selectedImages 状态:', this.selectedImages);
// index
const selectedItems = [];
for (let index in this.selectedImages) {
const idx = parseInt(index);
if (this.selectedImages[idx]) {
selectedItems.push(this.imageResults[idx]); // index imageResults
}
}
if (selectedItems.length === 0) {
this.$message.warning('请至少选择一张图片');
return;
}
// operId imageId
const imageIds = selectedItems.map(item => item.id);
const operId = selectedItems[0].operId; // operId
//
this.handleConfirm(operId,imageIds);
},
async handleConfirm(operId,imageIds) {
try {
//
const res = await updateImageSureAPI({
id: operId,
imageId:imageIds
});
if (res.code === 200) {
this.$message.success('确认成功');
//
await this.generateTestData(operId);
} else {
this.$message.error('确认失败');
}
} catch (error) {
console.error('确认异常:', error);
this.$message.error('确认过程中出现错误');
}
},
toggleSidebar() {
this.isSidebarVisible = !this.isSidebarVisible;
}