前言
我们的图像标注系统中,标注的时候,每人需要一个容器(docker),需要把数据推到去LabelStudio里面去标注,利用webhook回推标注好的数据。
Canvas的使用,当选中某个矩形框的时候,会变成蓝色。
画布大小
设置的是,图片有多大,画布就有多大,然后以画布的中心点对齐。默认是左上角对齐
this.ctx.drawImage(this.image, -this.image.width / 2, -this.image.height / 2);
需要注意的是drawImage三种方法分别如下:
drawImage(image, dx, dy) 在画布指定位置绘制原图
drawImage(image, dx, dy, dw, dh) 在画布指定位置上按原图大小绘制指定大小的图
drawImage(image, sx, sy, sw, sh, dx, dy, dw, dh) 剪切图像,并在画布上定位被剪切的部分
描述 | 参数 |
---|---|
image | 规定要使用的图像、画布或视频 |
dx | 在画布上放置图像的 x 坐标位置 |
dy | 在画布上放置图像的 y 坐标位置 |
sx | 可选。开始剪切图片的 x 坐标位置 |
sy | 可选。开始剪切图片的 y 坐标位置 |
dw | 可选。要使用的图像的宽度(就是裁剪之后的图片高度,放大或者缩放) |
dh | 可选。要使用的图像的高度(就是裁剪之后的图片高度,放大或者缩放) |
sw | 可选。被剪切图像的宽度(就是裁剪之前的图片宽度,这里的宽度若小于图片的原宽。则图片多余部分被剪掉;若大于,则会以空白填充) |
sh | 可选。被剪切图像的高度(就是裁剪之前的图片高度) |
具体细节请点击官网查看
图片的旋转
调用rotate
函数即可,this.rotation
代表的是角度,例如this.rotation = 90,表示画布表示反转90度,即图片翻卷90度,Math.PI / 180将度数转换为弧度。
this.ctx.rotate(this.rotation * Math.PI / 180);
图片的缩放和移动
图片的缩放
缩放比较简单,一行代码就可以解决。第一个参数代表x轴缩放,第二个参数代表y轴缩放,绘图上下文会沿 x 轴和 y 轴都按 this.scale 的比例进行缩放。假设this.scale = 2,那么整体图片会被放大两倍
this.ctx.scale(this.scale, this.scale);
图片的移动
图片的移动相对比较困难,思想:
- 右击代表是移动图片
- 计算拖动的距离
- 更新偏移量
- 限制偏移量(不能超过画布的大小)
- 记录当前鼠标的位置
- 重新绘制图像
核心代码:
const dx = event.clientX - this.lastX;
const dy = event.clientY - this.lastY;
this.offsetX += dx;
this.offsetY += dy;
this.limitOffset();
this.lastX = event.clientX;
this.lastY = event.clientY;
this.draw();
limitOffset() {
const canvas = this.canvas.nativeElement;
//计算缩放后的图像宽度和高度
const scaledWidth = this.image.width * this.scale;
const scaledHeight = this.image.height * this.scale;
//计算偏移范围
const minOffsetX = - (scaledWidth - canvas.width) / 2;
const maxOffsetX = (scaledWidth - canvas.width) / 2;
const minOffsetY = - (scaledHeight - canvas.height) / 2;
const maxOffsetY = (scaledHeight - canvas.height) / 2;
//图像的实际偏移量
this.offsetX = Math.max(minOffsetX, Math.min(maxOffsetX, this.offsetX));
this.offsetY = Math.max(minOffsetY, Math.min(maxOffsetY, this.offsetY));
}
选中矩形框
只需要判断当前鼠标的位置再某个矩形框内,点击后即可被选中。因为是矩形框,只需要判断起始位和结束位置即可。
注意:必须是对象,而不是里面的坐标值,因为,当你更改被选中的矩形框后,annotations也会进行相应的更改。
核心代码:
findSelectedShape(x: number, y: number) {
for (const annotation of this.annotations) {
if (x >= annotation.x1 && x <= annotation.x2 && y >= annotation.y1 && y <= annotation.y2) {
return annotation;
}
}
return null;
}
矩形框的移动、缩放和删除
移动
计算鼠标移动的距离,对选中的举行框进行修改,第一个参数是选中的矩形框,第二个是沿着水平线(x轴)的偏移量,第三个是垂直线(y轴)的偏移量。
moveShape(shape: { x1: number, y1: number, x2: number, y2: number }, dx: number, dy: number) {
shape.x1 += dx;
shape.y1 += dy;
shape.x2 += dx;
shape.y2 += dy;
}
缩放
缩放相比移动要比较复杂点,当选中一个矩形框后,点击矩形四个角的其中一个角,进行拖拽,达到缩放效果。
第一个参数,是当前的矩形,第二个和第三个是当前鼠标的位置。
resizeShape(shape: { x1: number, y1: number, x2: number, y2: number }, x: number, y: number) {
switch (this.resizeCorner) {
case 'top-left':
shape.x1 = x;
shape.y1 = y;
break;
case 'top-right':
shape.x2 = x;
shape.y1 = y;
break;
case 'bottom-left':
shape.x1 = x;
shape.y2 = y;
break;
case 'bottom-right':
shape.x2 = x;
shape.y2 = y;
break;
}
}
删除
删除很简单,从矩形框数组中过滤掉被选中矩形框即可。
onDelete() {
if (this.selectedShape) {
this.annotations = this.annotations.filter(annotation => annotation !== this.selectedShape);
this.selectedShape = null;
this.draw();
}
}
总结
相对来说基础的实现了labelStudio的标注功能,还有一写功能,还需后续实现,比如自定义矩形框的颜色、值。
希望这篇文章对你有一定的帮助
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。