luo-image-annotator 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +36 -0
- package/README.md +148 -0
- package/dist/index.d.ts +1 -0
- package/dist/luo-image-annotator.css +1 -0
- package/dist/luo-image-annotator.es.js +1152 -0
- package/dist/luo-image-annotator.umd.js +1 -0
- package/dist/vite.svg +1 -0
- package/package.json +49 -0
package/README.en.md
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# vue-image-annotator
|
|
2
|
+
|
|
3
|
+
#### Description
|
|
4
|
+
图片标组件
|
|
5
|
+
|
|
6
|
+
#### Software Architecture
|
|
7
|
+
Software architecture description
|
|
8
|
+
|
|
9
|
+
#### Installation
|
|
10
|
+
|
|
11
|
+
1. xxxx
|
|
12
|
+
2. xxxx
|
|
13
|
+
3. xxxx
|
|
14
|
+
|
|
15
|
+
#### Instructions
|
|
16
|
+
|
|
17
|
+
1. xxxx
|
|
18
|
+
2. xxxx
|
|
19
|
+
3. xxxx
|
|
20
|
+
|
|
21
|
+
#### Contribution
|
|
22
|
+
|
|
23
|
+
1. Fork the repository
|
|
24
|
+
2. Create Feat_xxx branch
|
|
25
|
+
3. Commit your code
|
|
26
|
+
4. Create Pull Request
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
#### Gitee Feature
|
|
30
|
+
|
|
31
|
+
1. You can use Readme\_XXX.md to support different languages, such as Readme\_en.md, Readme\_zh.md
|
|
32
|
+
2. Gitee blog [blog.gitee.com](https://blog.gitee.com)
|
|
33
|
+
3. Explore open source project [https://gitee.com/explore](https://gitee.com/explore)
|
|
34
|
+
4. The most valuable open source project [GVP](https://gitee.com/gvp)
|
|
35
|
+
5. The manual of Gitee [https://gitee.com/help](https://gitee.com/help)
|
|
36
|
+
6. The most popular members [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/)
|
package/README.md
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
# luo-image-annotator
|
|
2
|
+
|
|
3
|
+
一个基于 Vue 3 和 Canvas 的图片标注组件库,支持矩形、多边形、关键点、旋转矩形等多种标注类型,并提供批量标注和预览功能。
|
|
4
|
+
|
|
5
|
+
## 环境要求
|
|
6
|
+
|
|
7
|
+
- **Node.js**: >= 18.0.0
|
|
8
|
+
- **npm**: >= 9.0.0
|
|
9
|
+
- **Vue**: >= 3.2.0
|
|
10
|
+
|
|
11
|
+
## 开发与运行
|
|
12
|
+
|
|
13
|
+
如果你想本地开发或查看演示,请按照以下步骤操作:
|
|
14
|
+
|
|
15
|
+
1. **安装依赖**
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm install
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
2. **启动开发服务器**
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
npm run dev
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
启动后,访问控制台显示的本地地址(通常是 `http://localhost:5173/`)即可查看演示页面。
|
|
28
|
+
|
|
29
|
+
3. **构建组件库**
|
|
30
|
+
|
|
31
|
+
如果你需要打包组件库以发布到 npm:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
npm run build
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
构建产物将输出到 `dist` 目录。
|
|
38
|
+
|
|
39
|
+
## 在其他项目中使用
|
|
40
|
+
|
|
41
|
+
### 1. 安装
|
|
42
|
+
|
|
43
|
+
在你的 Vue 3 项目中安装本组件库:
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
npm install luo-image-annotator
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
由于本组件库依赖 `element-plus` 和 `@element-plus/icons-vue`,如果你的项目中尚未安装,请同时安装它们:
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
npm install element-plus @element-plus/icons-vue
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### 2. 全局引入 (推荐)
|
|
56
|
+
|
|
57
|
+
在 `main.ts` 或 `main.js` 中引入样式和组件:
|
|
58
|
+
|
|
59
|
+
```typescript
|
|
60
|
+
import { createApp } from 'vue'
|
|
61
|
+
import App from './App.vue'
|
|
62
|
+
|
|
63
|
+
// 1. 引入 Element Plus (必选依赖)
|
|
64
|
+
import ElementPlus from 'element-plus'
|
|
65
|
+
import 'element-plus/dist/index.css'
|
|
66
|
+
import * as ElementPlusIconsVue from '@element-plus/icons-vue'
|
|
67
|
+
|
|
68
|
+
// 2. 引入本组件库样式
|
|
69
|
+
import 'luo-image-annotator/dist/style.css'
|
|
70
|
+
|
|
71
|
+
// 3. 引入组件
|
|
72
|
+
import { BatchAnnotator } from 'luo-image-annotator'
|
|
73
|
+
|
|
74
|
+
const app = createApp(App)
|
|
75
|
+
|
|
76
|
+
app.use(ElementPlus)
|
|
77
|
+
// 注册图标
|
|
78
|
+
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
|
|
79
|
+
app.component(key, component)
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// 全局注册 BatchAnnotator 组件
|
|
83
|
+
app.component('BatchAnnotator', BatchAnnotator)
|
|
84
|
+
|
|
85
|
+
app.mount('#app')
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### 3. 局部引入
|
|
89
|
+
|
|
90
|
+
你也可以在单个 `.vue` 文件中局部引入:
|
|
91
|
+
|
|
92
|
+
```vue
|
|
93
|
+
<template>
|
|
94
|
+
<div style="height: 100vh;">
|
|
95
|
+
<BatchAnnotator
|
|
96
|
+
:images="images"
|
|
97
|
+
:labels="labels"
|
|
98
|
+
@export="handleExport"
|
|
99
|
+
/>
|
|
100
|
+
</div>
|
|
101
|
+
</template>
|
|
102
|
+
|
|
103
|
+
<script setup lang="ts">
|
|
104
|
+
import { ref } from 'vue';
|
|
105
|
+
import { BatchAnnotator } from 'luo-image-annotator';
|
|
106
|
+
import 'luo-image-annotator/dist/style.css'; // 别忘了引入样式
|
|
107
|
+
|
|
108
|
+
// 定义标签
|
|
109
|
+
const labels = [
|
|
110
|
+
{ id: '1', name: 'Person', color: '#FF0000', visible: true },
|
|
111
|
+
{ id: '2', name: 'Car', color: '#00FF00', visible: true }
|
|
112
|
+
];
|
|
113
|
+
|
|
114
|
+
// 定义图片数据
|
|
115
|
+
const images = ref([
|
|
116
|
+
{
|
|
117
|
+
imageUrl: 'https://example.com/image1.jpg',
|
|
118
|
+
annotations: [] // 初始标注为空
|
|
119
|
+
},
|
|
120
|
+
// ... 更多图片
|
|
121
|
+
]);
|
|
122
|
+
|
|
123
|
+
// 处理导出事件
|
|
124
|
+
const handleExport = (data) => {
|
|
125
|
+
console.log('导出的标注数据:', data);
|
|
126
|
+
};
|
|
127
|
+
</script>
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## 组件 API
|
|
131
|
+
|
|
132
|
+
### BatchAnnotator Props
|
|
133
|
+
|
|
134
|
+
| 属性名 | 类型 | 必填 | 描述 |
|
|
135
|
+
| --- | --- | --- | --- |
|
|
136
|
+
| `images` | `Array` | 是 | 图片列表,包含 `imageUrl` 和 `annotations` |
|
|
137
|
+
| `labels` | `Array` | 是 | 标签定义列表,包含 `id`, `name`, `color`, `visible` |
|
|
138
|
+
|
|
139
|
+
### BatchAnnotator Events
|
|
140
|
+
|
|
141
|
+
| 事件名 | 参数 | 描述 |
|
|
142
|
+
| --- | --- | --- |
|
|
143
|
+
| `export` | `data: Array` | 点击“导出”按钮时触发,返回最新的图片和标注数据 |
|
|
144
|
+
| `update:images` | `data: Array` | 当标注数据发生变化时触发,支持 `v-model:images` |
|
|
145
|
+
|
|
146
|
+
## 许可证
|
|
147
|
+
|
|
148
|
+
MIT
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
.annotation-container[data-v-d3dc5a63]{display:flex;height:100%;width:100%;border:1px solid #e0e0e0;background:#f5f5f5;overflow:hidden}.left-sidebar[data-v-d3dc5a63]{width:50px;background:#fff;border-right:1px solid #e0e0e0;display:flex;flex-direction:column;align-items:center;padding:8px 0;gap:8px;z-index:10}.tool-btn[data-v-d3dc5a63]{width:36px;height:36px;display:flex;align-items:center;justify-content:center;border:1px solid transparent;border-radius:4px;cursor:pointer;-webkit-user-select:none;user-select:none;font-size:18px;transition:all .2s}.tool-btn[data-v-d3dc5a63]:hover{background:#f0f0f0}.tool-btn.active[data-v-d3dc5a63]{background:#e3f2fd;border-color:#2196f3;color:#2196f3}.divider[data-v-d3dc5a63]{width:80%;height:1px;background:#ddd;margin:4px 0}.center-area[data-v-d3dc5a63]{flex:1;display:flex;flex-direction:column;position:relative;overflow:hidden}.top-bar[data-v-d3dc5a63]{height:50px;background:#fff;border-bottom:1px solid #e0e0e0;display:flex;align-items:center;padding:0 16px}.label-selector[data-v-d3dc5a63]{display:flex;align-items:center;gap:12px;width:100%;overflow-x:auto}.label-text[data-v-d3dc5a63]{font-size:14px;font-weight:700;color:#555;white-space:nowrap}.tags-row[data-v-d3dc5a63]{display:flex;gap:8px}.tag-chip[data-v-d3dc5a63]{padding:4px 12px;border-radius:16px;font-size:12px;color:#fff;cursor:pointer;border:2px solid transparent;opacity:.7;transition:all .2s;white-space:nowrap}.tag-chip.active[data-v-d3dc5a63]{opacity:1;transform:scale(1.05);box-shadow:0 2px 4px #0003}.canvas-wrapper[data-v-d3dc5a63]{flex:1;background:#333;position:relative;overflow:hidden}.batch-nav[data-v-d3dc5a63]{height:48px;background:#fff;border-top:1px solid #e0e0e0;display:flex;justify-content:center;align-items:center;gap:16px}.batch-nav button[data-v-d3dc5a63]{padding:6px 16px;background:#f5f5f5;border:1px solid #ddd;border-radius:4px;cursor:pointer}.batch-nav button[data-v-d3dc5a63]:hover:not(:disabled){background:#e0e0e0}.right-sidebar[data-v-d3dc5a63]{width:250px;background:#fff;border-left:1px solid #e0e0e0;display:flex;flex-direction:column;z-index:10}.sidebar-header[data-v-d3dc5a63]{padding:16px;border-bottom:1px solid #eee;display:flex;justify-content:space-between;align-items:center}.sidebar-header h3[data-v-d3dc5a63]{margin:0;font-size:16px}.add-btn[data-v-d3dc5a63]{padding:4px 8px;background:#2196f3;color:#fff;border:none;border-radius:4px;cursor:pointer;font-size:12px}.label-list[data-v-d3dc5a63]{flex:1;overflow-y:auto;padding:8px}.label-item[data-v-d3dc5a63]{margin-bottom:8px;padding:8px;background:#f9f9f9;border-radius:4px;border:1px solid #eee}.label-row[data-v-d3dc5a63]{display:flex;align-items:center;gap:8px}.eye-icon[data-v-d3dc5a63],.delete-icon[data-v-d3dc5a63]{cursor:pointer;font-size:16px;-webkit-user-select:none;user-select:none;opacity:.7}.eye-icon[data-v-d3dc5a63]:hover,.delete-icon[data-v-d3dc5a63]:hover{opacity:1}.color-picker[data-v-d3dc5a63]{width:24px;height:24px;padding:0;border:none;cursor:pointer;background:none}.name-input[data-v-d3dc5a63]{flex:1;border:1px solid transparent;background:transparent;font-size:14px;padding:2px 4px}.name-input[data-v-d3dc5a63]:focus{border-color:#2196f3;background:#fff;outline:none}.color-wrapper[data-v-d3dc5a63]{width:16px;height:16px;border-radius:50%;cursor:pointer;border:1px solid rgba(0,0,0,.1);flex-shrink:0}.label-name[data-v-d3dc5a63]{flex:1;font-size:14px;color:#333;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.action-icon[data-v-d3dc5a63]{cursor:pointer;font-size:16px;-webkit-user-select:none;user-select:none;width:24px;text-align:center;opacity:.6}.action-icon[data-v-d3dc5a63]:hover{opacity:1}.more-actions[data-v-d3dc5a63]{position:relative;display:flex;justify-content:center;align-items:center}.more-actions .delete-btn[data-v-d3dc5a63]{display:none;font-size:14px}.more-actions:hover .dots[data-v-d3dc5a63]{display:none}.more-actions:hover .delete-btn[data-v-d3dc5a63]{display:inline-block;color:#f44336}.modal-overlay[data-v-d3dc5a63]{position:fixed;top:0;left:0;width:100%;height:100%;background:#00000080;display:flex;justify-content:center;align-items:center;z-index:1000}.modal-content[data-v-d3dc5a63]{background:#fff;padding:20px;border-radius:8px;width:300px;box-shadow:0 4px 12px #00000026}.modal-content h3[data-v-d3dc5a63]{margin:0 0 16px;font-size:18px;color:#333}.form-group[data-v-d3dc5a63]{margin-bottom:16px}.form-group label[data-v-d3dc5a63]{display:block;margin-bottom:8px;font-size:14px;color:#666}.modal-input[data-v-d3dc5a63]{width:100%;padding:8px;border:1px solid #ddd;border-radius:4px;font-size:14px;box-sizing:border-box}.color-input-wrapper[data-v-d3dc5a63]{display:flex;align-items:center;gap:8px}.modal-color-picker[data-v-d3dc5a63]{width:40px;height:30px;padding:0;border:none;background:none;cursor:pointer}.modal-actions[data-v-d3dc5a63]{display:flex;justify-content:flex-end;gap:12px;margin-top:24px}.cancel-btn[data-v-d3dc5a63]{padding:6px 16px;background:#f5f5f5;border:1px solid #ddd;border-radius:4px;cursor:pointer;color:#666}.confirm-btn[data-v-d3dc5a63]{padding:6px 16px;background:#2196f3;border:none;border-radius:4px;cursor:pointer;color:#fff}.cancel-btn[data-v-d3dc5a63]:hover{background:#e0e0e0}.confirm-btn[data-v-d3dc5a63]:hover{background:#1976d2}.thumbnail-wrapper[data-v-78bcbe0c]{position:relative;width:100%;height:100%;overflow:hidden;border-radius:4px;background:#f0f0f0}.thumbnail-image[data-v-78bcbe0c]{width:100%;height:100%;object-fit:cover;display:block}.annotation-overlay[data-v-78bcbe0c]{position:absolute;top:0;left:0;width:100%;height:100%;pointer-events:none}.loading-placeholder[data-v-78bcbe0c]{position:absolute;top:0;left:0;width:100%;height:100%;display:flex;align-items:center;justify-content:center;color:#999;font-size:12px}.anno-label[data-v-78bcbe0c]{paint-order:stroke;stroke:#fff;stroke-width:2px;stroke-linecap:round;stroke-linejoin:round}.batch-annotator[data-v-4cf50076]{width:100%;height:100vh;display:flex;flex-direction:column;background:#f5f5f5}.gallery-view[data-v-4cf50076]{flex:1;display:flex;flex-direction:column;overflow:hidden;padding:20px;position:relative}.gallery-header[data-v-4cf50076]{margin-bottom:20px;display:flex;justify-content:space-between;align-items:center;flex-shrink:0}.gallery-header h3[data-v-4cf50076]{margin:0;font-size:20px;color:#333}.label-summary[data-v-4cf50076]{display:flex;gap:8px}.label-badge[data-v-4cf50076]{padding:4px 10px;border-radius:12px;color:#fff;font-size:12px;font-weight:700}.gallery-grid[data-v-4cf50076]{flex:1;display:grid;grid-template-columns:repeat(auto-fill,minmax(200px,1fr));gap:20px;overflow-y:auto;padding-bottom:80px}.gallery-item[data-v-4cf50076]{background:#fff;border-radius:8px;overflow:hidden;box-shadow:0 2px 8px #0000001a;cursor:pointer;transition:transform .2s,box-shadow .2s;display:flex;flex-direction:column;height:240px}.gallery-item[data-v-4cf50076]:hover{transform:translateY(-4px);box-shadow:0 4px 12px #00000026}.thumbnail-wrapper[data-v-4cf50076]{flex:1;overflow:hidden;position:relative}.img-meta[data-v-4cf50076]{padding:8px;font-size:12px;color:#666;display:flex;justify-content:space-between;background:#fff;border-top:1px solid #eee;height:32px;align-items:center}.bottom-bar[data-v-4cf50076]{position:absolute;bottom:0;left:0;width:100%;height:60px;background:#fff;border-top:1px solid #e0e0e0;display:flex;justify-content:space-between;align-items:center;padding:0 40px;box-shadow:0 -2px 10px #0000000d;z-index:100}.action-btn[data-v-4cf50076]{display:flex;align-items:center;gap:8px;padding:10px 24px;border:none;border-radius:4px;font-size:16px;cursor:pointer;transition:background .2s}.action-btn.primary[data-v-4cf50076]{background:#2196f3;color:#fff}.action-btn.primary[data-v-4cf50076]:hover{background:#1976d2}.action-btn.success[data-v-4cf50076]{background:#4caf50;color:#fff}.action-btn.success[data-v-4cf50076]:hover{background:#388e3c}.editor-view[data-v-4cf50076]{flex:1;display:flex;flex-direction:column;height:100%}.editor-header[data-v-4cf50076]{height:50px;background:#fff;border-bottom:1px solid #e0e0e0;display:flex;align-items:center;padding:0 16px;justify-content:space-between;flex-shrink:0}.header-left[data-v-4cf50076]{display:flex;align-items:center;gap:16px}.back-btn[data-v-4cf50076]{display:flex;align-items:center;gap:4px;background:transparent;border:1px solid #ddd;padding:6px 12px;border-radius:4px;cursor:pointer;font-size:14px;color:#666}.back-btn[data-v-4cf50076]:hover{background:#f5f5f5;color:#333}.editor-content[data-v-4cf50076]{flex:1;overflow:hidden;position:relative}
|