gpt4 book ai didi

javascript - 图像处理 - 在精确位置添加带 Angular 的图像

转载 作者:行者123 更新时间:2023-11-28 04:10:17 25 4
gpt4 key购买 nike

我有一个图像,它是一个包含如下方框区域的背景:

Distorted trapezium

我知道那个形状的 Angular 的确切位置,我想在其中放置另一个图像。 (所以它似乎在盒子里)。

我知道 HTML5 Canvas 的 drawImage 方法,但它似乎只支持 x、y、宽度、高度参数而不是精确坐标。我如何在一组特定坐标处将图像绘制到 Canvas 上,理想情况下让浏览器本身处理拉伸(stretch)图像。

最佳答案

四边形变换

解决此问题的一种方法是使用四边形变换。它们与 3D 变换不同,允许您在 Canvas 上绘图以备导出结果。

此处显示的示例已简化,并在渲染本身上使用了基本的分割和“作弊”——也就是说,它绘制在一个小正方形中而不是分割单元格的形状,但由于尺寸小且在许多非极端情况下,我们可以避免重叠。

正确的方法是将形状分成两个三 Angular 形,然后在目标位图中逐像素扫描,将目标三 Angular 形中的点映射到源三 Angular 形。如果位置值是分数,您将使用它来确定像素插值(例如双线性 2x2 或双三次 4x4)。

我不打算在这个答案中涵盖所有这些,因为它很快就会超出 SO 格式的范围,但该方法可能适用于这种情况,除非您需要对其进行动画处理(它的性能不够如果你想要高分辨率)。

方法

让我们从初始的四边形开始:

Initial quad

第一步是在每个柱状图 C1-C4 和 C2-C3 上插入 Y 位置。我们需要当前位置和下一个位置。我们将为此使用线性插值(“lerp”),使用 t 的归一化值:

y1current = lerp( C1, C4, y / height)
y2current = lerp( C2, C3, y / height)

y1next = lerp(C1, C4, (y + step) / height)
y2next = lerp(C2, C3, (y + step) / height)

这为我们在外部垂直条之间和沿线提供了一条新线。

http://i.imgur.com/qAWUK4B.png

接下来我们需要该行上的 X 位置,包括当前位置和下一个位置。这将为我们提供四个位置,我们将用当前像素填充,可以按原样或对其进行插值(此处未显示):

p1 = lerp(y1current, y2current, x / width)
p2 = lerp(y1current, y2current, (x + step) / width)
p3 = lerp(y1next, y2next, (x + step) / width)
p4 = lerp(y1next, y2next, x / width)

xy 将使用整数值作为源图像中的位置。

Iterating x/y

我们可以在循环中使用此设置,循环遍历源位图中的每个像素。

演示

演示可以在答案的底部找到。四处移动圆形 handle 以变换并调整步长值以查看其对性能和结果的影响。

演示有波纹和其他伪影,但如前所述,这将是另一天的话题。

演示快照:

Demo snapshot

替代方法

您还可以使用 WebGL 或 Three.js 设置 3D 环境并渲染到 Canvas 。这是后一种解决方案的链接:

以及如何使用纹理贴图表面的示例:

使用这种方法还可以将结果导出到 Canvas 或图像,但为了提高性能,客户端需要 GPU。

如果您不需要导出或操作结果,我建议使用其他答案中所示的简单 CSS 3D 转换。

/* Quadrilateral Transform - (c) Ken Nilsen, CC3.0-Attr */
var img = new Image(); img.onload = go;
img.src = "https://i.imgur.com/EWoZkZm.jpg";

function go() {
var me = this,
stepEl = document.querySelector("input"),
stepTxt = document.querySelector("span"),
c = document.querySelector("canvas"),
ctx = c.getContext("2d"),
corners = [
{x: 100, y: 20}, // ul
{x: 520, y: 20}, // ur
{x: 520, y: 380}, // br
{x: 100, y: 380} // bl
],
radius = 10, cPoint, timer, // for mouse handling
step = 4; // resolution

update();

// render image to quad using current settings
function render() {

var p1, p2, p3, p4, y1c, y2c, y1n, y2n,
w = img.width - 1, // -1 to give room for the "next" points
h = img.height - 1;

ctx.clearRect(0, 0, c.width, c.height);

for(y = 0; y < h; y += step) {
for(x = 0; x < w; x += step) {
y1c = lerp(corners[0], corners[3], y / h);
y2c = lerp(corners[1], corners[2], y / h);
y1n = lerp(corners[0], corners[3], (y + step) / h);
y2n = lerp(corners[1], corners[2], (y + step) / h);

// corners of the new sub-divided cell p1 (ul) -> p2 (ur) -> p3 (br) -> p4 (bl)
p1 = lerp(y1c, y2c, x / w);
p2 = lerp(y1c, y2c, (x + step) / w);
p3 = lerp(y1n, y2n, (x + step) / w);
p4 = lerp(y1n, y2n, x / w);

ctx.drawImage(img, x, y, step, step, p1.x, p1.y, // get most coverage for w/h:
Math.ceil(Math.max(step, Math.abs(p2.x - p1.x), Math.abs(p4.x - p3.x))) + 1,
Math.ceil(Math.max(step, Math.abs(p1.y - p4.y), Math.abs(p2.y - p3.y))) + 1)
}
}
}

function lerp(p1, p2, t) {
return {
x: p1.x + (p2.x - p1.x) * t,
y: p1.y + (p2.y - p1.y) * t}
}

/* Stuff for demo: -----------------*/
function drawCorners() {
ctx.strokeStyle = "#09f";
ctx.lineWidth = 2;
ctx.beginPath();
// border
for(var i = 0, p; p = corners[i++];) ctx[i ? "lineTo" : "moveTo"](p.x, p.y);
ctx.closePath();
// circular handles
for(i = 0; p = corners[i++];) {
ctx.moveTo(p.x + radius, p.y);
ctx.arc(p.x, p.y, radius, 0, 6.28);
}
ctx.stroke()
}

function getXY(e) {
var r = c.getBoundingClientRect();
return {x: e.clientX - r.left, y: e.clientY - r.top}
}

function inCircle(p, pos) {
var dx = pos.x - p.x,
dy = pos.y - p.y;
return dx*dx + dy*dy <= radius * radius
}

// handle mouse
c.onmousedown = function(e) {
var pos = getXY(e);
for(var i = 0, p; p = corners[i++];) {if (inCircle(p, pos)) {cPoint = p; break}}
}
window.onmousemove = function(e) {
if (cPoint) {
var pos = getXY(e);
cPoint.x = pos.x; cPoint.y = pos.y;
cancelAnimationFrame(timer);
timer = requestAnimationFrame(update.bind(me))
}
}
window.onmouseup = function() {cPoint = null}

stepEl.oninput = function() {
stepTxt.innerHTML = (step = Math.pow(2, +this.value));
update();
}

function update() {render(); drawCorners()}
}
body {margin:20px;font:16px sans-serif}
canvas {border:1px solid #000;margin-top:10px}
<label>Step: <input type=range min=0 max=5 value=2></label><span>4</span><br>
<canvas width=620 height=400></canvas>

关于javascript - 图像处理 - 在精确位置添加带 Angular 的图像,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36372692/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com