将两个图像与每个图像的两个点与 html5 canvas
Combine two image with two point of each image with html5 canvas
我尝试合并两个图像。
- 每张图片都有不同的大小和比例。
- 每张图片有两个匹配点。
- 一张图片作为背景,另一张在上面绘制。
我想像这张示例图片一样组合图像。 (黑色为背景)
我知道每个点的归一化值和每个图像的大小是这样的
- 黑色尺寸:500x500
- 黑色点数:[0.15, 0.25], [0.74, 0.5]
- 绿色尺寸:200x90
- 绿色点数:[0.05, 0.85], [0.86, 0.12]
我尝试结合angle函数,但是我用html5 canvas做的不好。
怎么才能匹配好这些点呢?
您首先需要计算要应用于绿色矩形的变换,以便它的第一个点与黑色的第一个点重叠。
const translate = {
x: black_points[ 0 ].x - green_points[ 0 ].x,
y: black_points[ 0 ].y - green_points[ 0 ].y
};
然后你需要找到由两点组成的绿色“线”与黑色“线”在同一方向的角度。
这可以通过找到两点之间的角度(使用 Math.atan2()
)并简单地减去两条线的结果来完成。
const angle = getRotation( black_points ) - getRotation( green_points );
最后,您需要找到将绿线缩放到与黑线一样大所需的比例。这只是简单地找到它们各自距离之间的比率,可以用 Math.hypot()
.
来计算
const scale = getDistance( black_points) / getDistance( green_points );
但是,请注意,由于我们计算了相对于第一个点的旋转和比例,因此在应用这些变换之前,我们需要将变换原点移动到该点。
const canvas = document.querySelector("canvas");
const ctx = canvas.getContext("2d");
// we'll start by converting our relative points to absolute coords
const black_rect = { width: 500, height: 500 };
const black_points = [
[0.15, 0.25],
[0.74, 0.5]
].map( ([x, y]) => new DOMPoint( x * black_rect.width, y * black_rect.height ) );
const green_rect = { width: 200, height: 90 };
const green_points = [
[0.05, 0.85],
[0.86, 0.12]
].map( ([x, y]) => new DOMPoint( x * green_rect.width, y * green_rect.height ) );
// move the first green point so it overlaps the black one
const translate = {
x: black_points[ 0 ].x - green_points[ 0 ].x,
y: black_points[ 0 ].y - green_points[ 0 ].y
};
// find the direction so that there is a single vector
const angle = getRotation( black_points ) - getRotation( green_points );
// by how much to stretch
const scale = getDistance( black_points) / getDistance( green_points );
// first we draw the "black" part untransformed
ctx.fillRect(0, 0, black_rect.width, black_rect.height);
ctx.fillStyle = "red";
black_points.forEach( ({x, y}) => ctx.arc( x, y, 5, 0, Math.PI * 2) );
ctx.fill();
// now we apply the transfrormation we found
// first make both first points overlap
ctx.translate( translate.x, translate.y );
// set the transformation origin on these first points
// because we calculated both the 'angle' and 'scale' relative to this position
ctx.translate( green_points[ 0 ].x, green_points[ 0 ].y );
ctx.rotate( angle );
ctx.scale( scale, scale );
// revert the transformation origin to top-left corner
ctx.translate( -green_points[ 0 ].x, -green_points[ 0 ].y );
// draw the "green" part
ctx.globalAlpha = .5;
ctx.fillStyle = "green";
ctx.fillRect( 0, 0, green_rect.width, green_rect.height );
ctx.beginPath();
ctx.fillStyle = "blue";
green_points.forEach( ({x, y}) => ctx.arc( x, y, 5, 0, Math.PI * 2 ));
ctx.fill();
function getRotation( [ point1, point2 ] ) {
const dx = point1.x - point2.x;
const dy = point1.y - point2.y;
return Math.atan2(dy, dx);
}
function getDistance( [ point1, point2 ] ) {
const dx = point1.x - point2.x;
const dy = point1.y - point2.y;
return Math.hypot( dy, dx );
}
<canvas width="800" height="800"></canvas>
我尝试合并两个图像。
- 每张图片都有不同的大小和比例。
- 每张图片有两个匹配点。
- 一张图片作为背景,另一张在上面绘制。
我想像这张示例图片一样组合图像。 (黑色为背景)
我知道每个点的归一化值和每个图像的大小是这样的
- 黑色尺寸:500x500
- 黑色点数:[0.15, 0.25], [0.74, 0.5]
- 绿色尺寸:200x90
- 绿色点数:[0.05, 0.85], [0.86, 0.12]
我尝试结合angle函数,但是我用html5 canvas做的不好。 怎么才能匹配好这些点呢?
您首先需要计算要应用于绿色矩形的变换,以便它的第一个点与黑色的第一个点重叠。
const translate = {
x: black_points[ 0 ].x - green_points[ 0 ].x,
y: black_points[ 0 ].y - green_points[ 0 ].y
};
然后你需要找到由两点组成的绿色“线”与黑色“线”在同一方向的角度。
这可以通过找到两点之间的角度(使用 Math.atan2()
)并简单地减去两条线的结果来完成。
const angle = getRotation( black_points ) - getRotation( green_points );
最后,您需要找到将绿线缩放到与黑线一样大所需的比例。这只是简单地找到它们各自距离之间的比率,可以用 Math.hypot()
.
const scale = getDistance( black_points) / getDistance( green_points );
但是,请注意,由于我们计算了相对于第一个点的旋转和比例,因此在应用这些变换之前,我们需要将变换原点移动到该点。
const canvas = document.querySelector("canvas");
const ctx = canvas.getContext("2d");
// we'll start by converting our relative points to absolute coords
const black_rect = { width: 500, height: 500 };
const black_points = [
[0.15, 0.25],
[0.74, 0.5]
].map( ([x, y]) => new DOMPoint( x * black_rect.width, y * black_rect.height ) );
const green_rect = { width: 200, height: 90 };
const green_points = [
[0.05, 0.85],
[0.86, 0.12]
].map( ([x, y]) => new DOMPoint( x * green_rect.width, y * green_rect.height ) );
// move the first green point so it overlaps the black one
const translate = {
x: black_points[ 0 ].x - green_points[ 0 ].x,
y: black_points[ 0 ].y - green_points[ 0 ].y
};
// find the direction so that there is a single vector
const angle = getRotation( black_points ) - getRotation( green_points );
// by how much to stretch
const scale = getDistance( black_points) / getDistance( green_points );
// first we draw the "black" part untransformed
ctx.fillRect(0, 0, black_rect.width, black_rect.height);
ctx.fillStyle = "red";
black_points.forEach( ({x, y}) => ctx.arc( x, y, 5, 0, Math.PI * 2) );
ctx.fill();
// now we apply the transfrormation we found
// first make both first points overlap
ctx.translate( translate.x, translate.y );
// set the transformation origin on these first points
// because we calculated both the 'angle' and 'scale' relative to this position
ctx.translate( green_points[ 0 ].x, green_points[ 0 ].y );
ctx.rotate( angle );
ctx.scale( scale, scale );
// revert the transformation origin to top-left corner
ctx.translate( -green_points[ 0 ].x, -green_points[ 0 ].y );
// draw the "green" part
ctx.globalAlpha = .5;
ctx.fillStyle = "green";
ctx.fillRect( 0, 0, green_rect.width, green_rect.height );
ctx.beginPath();
ctx.fillStyle = "blue";
green_points.forEach( ({x, y}) => ctx.arc( x, y, 5, 0, Math.PI * 2 ));
ctx.fill();
function getRotation( [ point1, point2 ] ) {
const dx = point1.x - point2.x;
const dy = point1.y - point2.y;
return Math.atan2(dy, dx);
}
function getDistance( [ point1, point2 ] ) {
const dx = point1.x - point2.x;
const dy = point1.y - point2.y;
return Math.hypot( dy, dx );
}
<canvas width="800" height="800"></canvas>