图像变换矩阵
Image transformation matrix
我想练习使用矩阵进行像素操作以从另一个图像中提取图像。
这是我对 css 变换矩阵所做的:
https://www.noelshack.com/2017-18-1493893008-capture-2.png
对于左图 'L' 我在图像周围放置了 4 个点,在右图 'R' 我找到了变换的内容。
为此,我使用 css 的 属性 变换,但我想手动进行操作。
CSS 版本:
matrix3d(1.5456325781948308,1.6561987730956724,0,0.0012239101773909712,-0.4663849104791486,2.218793881308064,0,0.0009095626603861196,0,0,1,0,12.247969030166722,-17.754955132517754,0,0.9951722722714726)
矩阵'M':
[[1.5456325781948308, 1.6561987730956724, 0, 0.0012239101773909712],
[-0.4663849104791486, 2.218793881308064, 0, 0.0009095626603861196],
[0, 0, 1, 0],
[12.247969030166722, -17.754955132517754, 0, 0.9951722722714726]]
我想知道图像 R 中的每个像素在图像 L 中的像素相关位置是什么。
例如R中的(0,0)是R中的(52,203)。
为此,我做了这个计算。
M * P = P'
P为R图中的像素位置
P'是L图中的像素位置
P矩阵定义如下:
[[x],
[y],
[0],
[1]]
所以对于 0,0 位置,我这样做:
[[1.5456325781948308, 1.6561987730956724, 0, 0.0012239101773909712],
[-0.4663849104791486, 2.218793881308064, 0, 0.0009095626603861196],
[0, 0, 1, 0],
[12.247969030166722, -17.754955132517754, 0, 0.9951722722714726]]
X
[[0],
[0],
[0],
[1]]
=
[[0.0012239101773909712],
[0.0009095626603861196],
[0],
[0.9951722722714726]]
这是结果,但是第 2 个组成部分:
(0.0012239101773909712, 0.0009095626603861196)
比预期的要小。你能帮我找到问题吗
诚恳地,
矩阵好奇心。
这些是齐次坐标。所以给定一些 [x1, y1, z1, 1] 作为输入你得到一些 [x2, y2, z2, w2] 但他们描述的实际位置是 [x2/w2, y2/w2, z2/w2],即你必须除以最后一个坐标。
但这并没有达到您预期的结果。也没有用它的附属(或等效的逆)替换矩阵,也没有它的转置。这两个都是容易出错的约定,因此无需过多考虑您实际拥有和应该拥有哪个版本,尝试所有四种选择(有和没有附加,有和没有转置)解决了大量的琐碎问题.
但不是你的。所以我下一个最好的选择是你期望的坐标是从图像的某个角落测量的,而 CSS 属性 transform-origin 的初始值是 50% 50% 0
所以坐标系的原点其实是在物体的中心。
实际上为此共享 HTML 和 CSS 可能让我验证了这个假设。现在你必须检查这是否适用于你。我记得上次创建 a projective image transformation demo to answer a question about finding the transform 时,我特意设置了 transform-origin: 0 0;
(以及各种带有供应商前缀的版本)以避免此类问题。
非常感谢 MvG。
我关注你的link,我找到了我想要的[https://math.stackexchange.com/a/339033]
只有一件事,我必须反转 C 矩阵才能找到与像素相关的 L<-R
我分享我的代码让你知道你必须做什么
您可以在函数 computeMat()
中找到我的实现
<style>
body {
touch-action: none;
overflow-y: hidden;
}
#canvas_toeic
{
position:absolute;
top:0;
left:0;
}
</style>
<script type="text/javascript" src="http://cdnjs.cloudflare.com/ajax/libs/mathjs/3.12.2/math.min.js"></script>
</head>
<body>
<canvas id="canvas_toeic" width="600" height="400">
</canvas>
<script type="text/javascript">
var image = new Image();
image.src = 'image.jpg';
image.onload = function() {
var c = document.getElementById("canvas_toeic");
var ratio = image.width / image.height;
var canvasWidth = document.body.clientWidth;
var canvasHeight = canvasWidth / ratio;
if(document.body.clientHeight < canvasHeight)
{
canvasHeight = document.body.clientHeight;
canvasWidth = canvasHeight * ratio;
}
var canvasLargeur = canvasWidth;
var canvasLongueur = canvasHeight;
if(canvasLargeur < canvasHeight) {
canvasLargeur = canvasHeight;
canvasLongueur = canvasWidth;
}
var canvasPixelRatio = canvasLargeur / image.width;
c.setAttribute("width", canvasWidth);
c.setAttribute("height", canvasHeight);
var ctx = c.getContext("2d");
var idPoint = -1;
var points = [];
for(var i = 0; i < 4; i++)
points[i] = {x:0, y:0};
var marginImage = Math.round(40 * canvasPixelRatio);
points[0].x = marginImage;
points[0].y = marginImage;
points[1].x = marginImage;
points[1].y = canvasHeight - marginImage;
points[2].x = canvasWidth - marginImage;
points[2].y = canvasHeight - marginImage;
points[3].x = canvasWidth - marginImage;
points[3].y = marginImage;
function draw(points) {
console.log("draw");
// Fond
ctx.fillStyle = "#222";
ctx.fillRect(0, 0, canvasWidth, canvasHeight);
ctx.drawImage(image, marginImage, marginImage, canvasWidth - marginImage * 2, canvasHeight - marginImage * 2); // this fait référence à l'objet courant (=image)
if(idPoint == -1)
ctx.lineWidth = 3 * canvasPixelRatio;
else
ctx.lineWidth = 5 * canvasPixelRatio;
ctx.beginPath(); // Début du chemin
ctx.lineJoin = "round";
ctx.lineCap = "round";
ctx.strokeStyle = "rgba(64, 128, 255, 0.5)";
ctx.moveTo(points[0].x, points[0].y); // Le tracé part du point 50,50
for(var i = 0; i < 4; i++)
ctx.lineTo(points[i].x, points[i].y); // Un segment est ajouté vers 200,200
ctx.closePath(); // Fermeture du chemin (facultative)
ctx.stroke();
for(var i = 0; i < 4; i++)
{
var radius = 30 * canvasPixelRatio;
if(idPoint == i)
radius = 60 * canvasPixelRatio;
ctx.beginPath();
ctx.arc(points[i].x, points[i].y, radius, 0, Math.PI*2, true);
ctx.strokeStyle = "#FF8800";
ctx.fillStyle = "rgba(255, 128, 0, 0.5)";
ctx.fill();
ctx.stroke();
}
if(idPoint != -1)
{
var zoomWidth = canvasWidth / 3;
var zoomHeight = canvasHeight / 3;
var zoomMargin = 5;
var zoomAroundWidth = 50;
var zoomAroundHeight = zoomAroundWidth / ratio;
var positionMouse = points[idPoint];
var imagePositionX = (positionMouse.x - marginImage) / (canvasWidth - marginImage * 2) * image.width;
var imagePositionY = (positionMouse.y - marginImage) / (canvasHeight - marginImage * 2) * image.height;
var zoomX = 0;
var zoomY = 0;
if(imagePositionX < image.width / 2)
zoomX = canvasWidth - zoomWidth;
if(imagePositionY < image.height / 2)
zoomY = canvasHeight - zoomHeight;
ctx.fillStyle = "#F08";
ctx.fillRect(zoomX, zoomY, zoomWidth, zoomHeight);
ctx.drawImage(image, imagePositionX - zoomAroundWidth, imagePositionY - zoomAroundHeight, zoomAroundWidth * 2, zoomAroundHeight * 2, zoomX + zoomMargin, zoomY + zoomMargin, zoomWidth - zoomMargin * 2, zoomHeight - zoomMargin * 2);
ctx.lineWidth = 3 * canvasPixelRatio;
ctx.beginPath();
ctx.lineJoin = "round";
ctx.lineCap = "round";
ctx.strokeStyle = "rgba(255, 0, 0, 0.5)";
ctx.moveTo(zoomX, zoomY + zoomHeight / 2);
ctx.lineTo(zoomX + zoomWidth, zoomY + zoomHeight / 2);
ctx.moveTo(zoomX + zoomWidth / 2, zoomY);
ctx.lineTo(zoomX + zoomWidth / 2, zoomY + zoomHeight);
ctx.closePath();
ctx.stroke();
}
}
function nearPoint(points, x, y)
{
var radiusDetection = 60 * canvasPixelRatio;
var distances = [];
for(i = 0; i < 4; i++) {
var mx = x - points[i].x;
var my = y - points[i].y;
distances[i] = Math.sqrt(mx * mx + my * my);
}
minI = 0;
minD = distances[0];
for(i = 1; i < 4; i++)
{
if(minD > distances[i])
{
minD = distances[i];
minI = i;
}
}
if(minD <= radiusDetection)
return minI;
return -1;
}
function getTouchPosition(e)
{
var target = null;
var mouse = null;
if(e.changedTouches != undefined)
{
var touches = e.changedTouches;
mouse = touches[0];
target = touches[0].target;
}
else if(e.originalTarget != undefined)
{
mouse = e;
target = e.originalTarget;
}
var coordX = 0;
var coordY = 0;
if(mouse.layerX != undefined)
{
coordX = mouse.layerX;
coordY = mouse.layerY;
}
else
{
coordX = mouse.pageX;
coordY = mouse.pageY;
}
var x = coordX - target.offsetLeft;
var y = coordY - target.offsetTop;
if(x < 0) x = 0;
if(y < 0) y = 0;
if(x >= canvasWidth) x = canvasWidth - 1;
if(y >= canvasHeight) y = canvasHeight - 1;
return {'x':x, 'y':y};
}
function mouseDown(e)
{
var position = getTouchPosition(e);
idPoint = nearPoint(points, position.x, position.y);
if(idPoint == -1)
{
if(position.x < marginImage * 3 && position.y < marginImage * 3)
{
computeMat();
}
}
}
function mouseUp(e)
{
if(idPoint != -1)
{
idPoint = -1;
draw(points);
}
}
function mouseMove(e)
{
if(idPoint != -1)
{
var position = getTouchPosition(e);
points[idPoint].x = position.x;
points[idPoint].y = position.y;
draw(points);
}
}
function cancelDefault(e)
{
e.preventDefault();
}
function matStep12(pts)
{
var matP = [
[pts[0].x, pts[1].x, pts[2].x],
[pts[0].y, pts[1].y, pts[2].y],
[1, 1, 1]
];
var vecP = [[pts[3].x], [pts[3].y], [1]];
var matPi = math.inv(matP);
var vecPi = math.multiply(matPi, vecP);
var result = [
[pts[0].x * vecPi[0][0], pts[1].x * vecPi[1][0], pts[2].x * vecPi[2][0]],
[pts[0].y * vecPi[0][0], pts[1].y * vecPi[1][0], pts[2].y * vecPi[2][0]],
[vecPi[0][0], vecPi[1][0], vecPi[2][0]]
];
return result;
}
function distance(a, b)
{
var mx = b.x - a.x;
var my = b.y - a.y;
return Math.sqrt(mx * mx + my * my);
}
function computeMat()
{
var pts = getPointRelativePosition();
var widthT = distance(pts[0], pts[3]);
var widthB = distance(pts[1], pts[2]);
var heightL = distance(pts[0], pts[1]);
var heightR = distance(pts[2], pts[3]);
var maxWidth = (widthT > widthB) ? widthT : widthB;
var maxHeight = (heightL > heightR) ? heightL : heightR;
var imgWidth = Math.round(maxWidth);
var imgHeight = Math.round(maxHeight);
var matA = matStep12(pts);
var matB = matStep12([{x:0,y:0}, {x:0,y:maxHeight}, {x:maxWidth,y:maxHeight}, {x:maxWidth,y:0}]);
var matC = math.multiply(matB, math.inv(matA));
var matCi = math.inv(matC);
console.log('width:' + imgWidth + ', height:' + imgHeight);
printMat(matC);
// construct image with transformation matrice
imageData = ctx.createImageData(imgWidth, imgHeight);
var tempCanvas = document.createElement('canvas');
var tempCtx = tempCanvas.getContext('2d');
tempCanvas.width = image.width;
tempCanvas.height = image.height;
tempCtx.drawImage(image, 0, 0, image.width, image.height);
var imageDataSrc = tempCtx.getImageData(0, 0, image.width, image.height);
var mz = [matCi[0][2], matCi[1][2], matCi[2][2]];
for(var y = 0; y < imgHeight; y++)
{
var my = [matCi[0][1] * y, matCi[1][1] * y, matCi[2][1] * y];
var offsetY = y * imgWidth;
for(var x = 0; x < imgWidth; x++)
{
var mx = [matCi[0][0] * x, matCi[1][0] * x, matCi[2][0] * x];
var cx = mx[0] + my[0] + mz[0];
var cy = mx[1] + my[1] + mz[1];
var cz = mx[2] + my[2] + mz[2];
var px = Math.round(cx / cz);
var py = Math.round(cy / cz);
if(px < 0.0 || py < 0.0 || px >= image.width || py >= image.height)
{
imageData.data[pixelIndex] = 0;
imageData.data[pixelIndex + 1] = 255;
imageData.data[pixelIndex + 2] = 0;
imageData.data[pixelIndex + 3] = 255;
}
else
{
var pixelIndex = (offsetY + x) * 4;
var pixelIndexSrc = (py * image.width + px) * 4;
imageData.data[pixelIndex] = imageDataSrc.data[pixelIndexSrc];
imageData.data[pixelIndex + 1] = imageDataSrc.data[pixelIndexSrc + 1];
imageData.data[pixelIndex + 2] = imageDataSrc.data[pixelIndexSrc + 2];
imageData.data[pixelIndex + 3] = 255;
}
}
}
// here to do, image analysis
}
function getPointRelativePosition()
{
var pointOrigin = [];
for(i = 0; i < 4; i++)
{
pointOrigin[i] = {x:(points[i].x - marginImage) * image.width / (canvasWidth - marginImage * 2), y:(points[i].y - marginImage) * image.height / (canvasHeight - marginImage * 2)};
}
return pointOrigin;
}
function getPointPosition()
{
var pointOrigin = [];
for(i = 0; i < 4; i++)
{
pointOrigin[i] = {x:(points[i].x - marginImage) / (canvasWidth - marginImage * 2), y:(points[i].y - marginImage) / (canvasHeight - marginImage * 2)};
}
return pointOrigin;
}
function printPoint(pts)
{
var result = '';
for(var i = 0; i < 4; i++)
{
result += "{x:" + pts[i].x + ", y:" + pts[i].y + "},\n";
}
console.log(result);
}
function printMat(mat)
{
var result = '';
for(var i = 0; i < mat.length; i++)
{
result += "[";
for(var j = 0; j < mat[i].length; j++)
{
result += mat[i][j] + ", ";
}
result += "],\n";
}
console.log(result);
}
function canvasResize()
{
if(canvasWidth != document.body.clientWidth && canvasHeight != document.body.clientHeight)
{
var transformPoint = getPointPosition();
ratio = image.width / image.height;
canvasWidth = document.body.clientWidth;
canvasHeight = canvasWidth / ratio;
if(document.body.clientHeight < canvasHeight)
{
canvasHeight = document.body.clientHeight;
canvasWidth = canvasHeight * ratio;
}
canvasLargeur = canvasWidth;
canvasLongueur = canvasHeight;
if(canvasLargeur < canvasHeight) {
canvasLargeur = canvasHeight;
canvasLongueur = canvasWidth;
}
canvasPixelRatio = canvasLargeur / image.width;
c.setAttribute("width", canvasWidth);
c.setAttribute("height", canvasHeight);
marginImage = Math.round(40 * canvasPixelRatio);
for(i = 0; i < 4; i++)
{
points[i].x = transformPoint[i].x * (canvasWidth - marginImage * 2) + marginImage;
points[i].y = transformPoint[i].y * (canvasHeight - marginImage * 2) + marginImage;
}
draw(points);
}
}
c.addEventListener("mousedown", mouseDown, false);
c.addEventListener("mouseup", mouseUp, false);
c.addEventListener("mousemove", mouseMove, false);
c.addEventListener("touchstart", mouseDown, false);
c.addEventListener("touchend", mouseUp, false);
c.addEventListener("touchmove", mouseMove, false);
document.addEventListener("touchstart", cancelDefault, true);
document.addEventListener("touchend", cancelDefault, true);
document.addEventListener("touchmove", cancelDefault, true);
setInterval(canvasResize, 30);
draw(points);
};
</script>
我想练习使用矩阵进行像素操作以从另一个图像中提取图像。
这是我对 css 变换矩阵所做的: https://www.noelshack.com/2017-18-1493893008-capture-2.png
对于左图 'L' 我在图像周围放置了 4 个点,在右图 'R' 我找到了变换的内容。
为此,我使用 css 的 属性 变换,但我想手动进行操作。
CSS 版本:
matrix3d(1.5456325781948308,1.6561987730956724,0,0.0012239101773909712,-0.4663849104791486,2.218793881308064,0,0.0009095626603861196,0,0,1,0,12.247969030166722,-17.754955132517754,0,0.9951722722714726)
矩阵'M':
[[1.5456325781948308, 1.6561987730956724, 0, 0.0012239101773909712],
[-0.4663849104791486, 2.218793881308064, 0, 0.0009095626603861196],
[0, 0, 1, 0],
[12.247969030166722, -17.754955132517754, 0, 0.9951722722714726]]
我想知道图像 R 中的每个像素在图像 L 中的像素相关位置是什么。
例如R中的(0,0)是R中的(52,203)。 为此,我做了这个计算。
M * P = P'
P为R图中的像素位置 P'是L图中的像素位置
P矩阵定义如下:
[[x],
[y],
[0],
[1]]
所以对于 0,0 位置,我这样做:
[[1.5456325781948308, 1.6561987730956724, 0, 0.0012239101773909712],
[-0.4663849104791486, 2.218793881308064, 0, 0.0009095626603861196],
[0, 0, 1, 0],
[12.247969030166722, -17.754955132517754, 0, 0.9951722722714726]]
X
[[0],
[0],
[0],
[1]]
=
[[0.0012239101773909712],
[0.0009095626603861196],
[0],
[0.9951722722714726]]
这是结果,但是第 2 个组成部分: (0.0012239101773909712, 0.0009095626603861196) 比预期的要小。你能帮我找到问题吗
诚恳地, 矩阵好奇心。
这些是齐次坐标。所以给定一些 [x1, y1, z1, 1] 作为输入你得到一些 [x2, y2, z2, w2] 但他们描述的实际位置是 [x2/w2, y2/w2, z2/w2],即你必须除以最后一个坐标。
但这并没有达到您预期的结果。也没有用它的附属(或等效的逆)替换矩阵,也没有它的转置。这两个都是容易出错的约定,因此无需过多考虑您实际拥有和应该拥有哪个版本,尝试所有四种选择(有和没有附加,有和没有转置)解决了大量的琐碎问题.
但不是你的。所以我下一个最好的选择是你期望的坐标是从图像的某个角落测量的,而 CSS 属性 transform-origin 的初始值是 50% 50% 0
所以坐标系的原点其实是在物体的中心。
实际上为此共享 HTML 和 CSS 可能让我验证了这个假设。现在你必须检查这是否适用于你。我记得上次创建 a projective image transformation demo to answer a question about finding the transform 时,我特意设置了 transform-origin: 0 0;
(以及各种带有供应商前缀的版本)以避免此类问题。
非常感谢 MvG。
我关注你的link,我找到了我想要的[https://math.stackexchange.com/a/339033] 只有一件事,我必须反转 C 矩阵才能找到与像素相关的 L<-R
我分享我的代码让你知道你必须做什么 您可以在函数 computeMat()
中找到我的实现<style>
body {
touch-action: none;
overflow-y: hidden;
}
#canvas_toeic
{
position:absolute;
top:0;
left:0;
}
</style>
<script type="text/javascript" src="http://cdnjs.cloudflare.com/ajax/libs/mathjs/3.12.2/math.min.js"></script>
</head>
<body>
<canvas id="canvas_toeic" width="600" height="400">
</canvas>
<script type="text/javascript">
var image = new Image();
image.src = 'image.jpg';
image.onload = function() {
var c = document.getElementById("canvas_toeic");
var ratio = image.width / image.height;
var canvasWidth = document.body.clientWidth;
var canvasHeight = canvasWidth / ratio;
if(document.body.clientHeight < canvasHeight)
{
canvasHeight = document.body.clientHeight;
canvasWidth = canvasHeight * ratio;
}
var canvasLargeur = canvasWidth;
var canvasLongueur = canvasHeight;
if(canvasLargeur < canvasHeight) {
canvasLargeur = canvasHeight;
canvasLongueur = canvasWidth;
}
var canvasPixelRatio = canvasLargeur / image.width;
c.setAttribute("width", canvasWidth);
c.setAttribute("height", canvasHeight);
var ctx = c.getContext("2d");
var idPoint = -1;
var points = [];
for(var i = 0; i < 4; i++)
points[i] = {x:0, y:0};
var marginImage = Math.round(40 * canvasPixelRatio);
points[0].x = marginImage;
points[0].y = marginImage;
points[1].x = marginImage;
points[1].y = canvasHeight - marginImage;
points[2].x = canvasWidth - marginImage;
points[2].y = canvasHeight - marginImage;
points[3].x = canvasWidth - marginImage;
points[3].y = marginImage;
function draw(points) {
console.log("draw");
// Fond
ctx.fillStyle = "#222";
ctx.fillRect(0, 0, canvasWidth, canvasHeight);
ctx.drawImage(image, marginImage, marginImage, canvasWidth - marginImage * 2, canvasHeight - marginImage * 2); // this fait référence à l'objet courant (=image)
if(idPoint == -1)
ctx.lineWidth = 3 * canvasPixelRatio;
else
ctx.lineWidth = 5 * canvasPixelRatio;
ctx.beginPath(); // Début du chemin
ctx.lineJoin = "round";
ctx.lineCap = "round";
ctx.strokeStyle = "rgba(64, 128, 255, 0.5)";
ctx.moveTo(points[0].x, points[0].y); // Le tracé part du point 50,50
for(var i = 0; i < 4; i++)
ctx.lineTo(points[i].x, points[i].y); // Un segment est ajouté vers 200,200
ctx.closePath(); // Fermeture du chemin (facultative)
ctx.stroke();
for(var i = 0; i < 4; i++)
{
var radius = 30 * canvasPixelRatio;
if(idPoint == i)
radius = 60 * canvasPixelRatio;
ctx.beginPath();
ctx.arc(points[i].x, points[i].y, radius, 0, Math.PI*2, true);
ctx.strokeStyle = "#FF8800";
ctx.fillStyle = "rgba(255, 128, 0, 0.5)";
ctx.fill();
ctx.stroke();
}
if(idPoint != -1)
{
var zoomWidth = canvasWidth / 3;
var zoomHeight = canvasHeight / 3;
var zoomMargin = 5;
var zoomAroundWidth = 50;
var zoomAroundHeight = zoomAroundWidth / ratio;
var positionMouse = points[idPoint];
var imagePositionX = (positionMouse.x - marginImage) / (canvasWidth - marginImage * 2) * image.width;
var imagePositionY = (positionMouse.y - marginImage) / (canvasHeight - marginImage * 2) * image.height;
var zoomX = 0;
var zoomY = 0;
if(imagePositionX < image.width / 2)
zoomX = canvasWidth - zoomWidth;
if(imagePositionY < image.height / 2)
zoomY = canvasHeight - zoomHeight;
ctx.fillStyle = "#F08";
ctx.fillRect(zoomX, zoomY, zoomWidth, zoomHeight);
ctx.drawImage(image, imagePositionX - zoomAroundWidth, imagePositionY - zoomAroundHeight, zoomAroundWidth * 2, zoomAroundHeight * 2, zoomX + zoomMargin, zoomY + zoomMargin, zoomWidth - zoomMargin * 2, zoomHeight - zoomMargin * 2);
ctx.lineWidth = 3 * canvasPixelRatio;
ctx.beginPath();
ctx.lineJoin = "round";
ctx.lineCap = "round";
ctx.strokeStyle = "rgba(255, 0, 0, 0.5)";
ctx.moveTo(zoomX, zoomY + zoomHeight / 2);
ctx.lineTo(zoomX + zoomWidth, zoomY + zoomHeight / 2);
ctx.moveTo(zoomX + zoomWidth / 2, zoomY);
ctx.lineTo(zoomX + zoomWidth / 2, zoomY + zoomHeight);
ctx.closePath();
ctx.stroke();
}
}
function nearPoint(points, x, y)
{
var radiusDetection = 60 * canvasPixelRatio;
var distances = [];
for(i = 0; i < 4; i++) {
var mx = x - points[i].x;
var my = y - points[i].y;
distances[i] = Math.sqrt(mx * mx + my * my);
}
minI = 0;
minD = distances[0];
for(i = 1; i < 4; i++)
{
if(minD > distances[i])
{
minD = distances[i];
minI = i;
}
}
if(minD <= radiusDetection)
return minI;
return -1;
}
function getTouchPosition(e)
{
var target = null;
var mouse = null;
if(e.changedTouches != undefined)
{
var touches = e.changedTouches;
mouse = touches[0];
target = touches[0].target;
}
else if(e.originalTarget != undefined)
{
mouse = e;
target = e.originalTarget;
}
var coordX = 0;
var coordY = 0;
if(mouse.layerX != undefined)
{
coordX = mouse.layerX;
coordY = mouse.layerY;
}
else
{
coordX = mouse.pageX;
coordY = mouse.pageY;
}
var x = coordX - target.offsetLeft;
var y = coordY - target.offsetTop;
if(x < 0) x = 0;
if(y < 0) y = 0;
if(x >= canvasWidth) x = canvasWidth - 1;
if(y >= canvasHeight) y = canvasHeight - 1;
return {'x':x, 'y':y};
}
function mouseDown(e)
{
var position = getTouchPosition(e);
idPoint = nearPoint(points, position.x, position.y);
if(idPoint == -1)
{
if(position.x < marginImage * 3 && position.y < marginImage * 3)
{
computeMat();
}
}
}
function mouseUp(e)
{
if(idPoint != -1)
{
idPoint = -1;
draw(points);
}
}
function mouseMove(e)
{
if(idPoint != -1)
{
var position = getTouchPosition(e);
points[idPoint].x = position.x;
points[idPoint].y = position.y;
draw(points);
}
}
function cancelDefault(e)
{
e.preventDefault();
}
function matStep12(pts)
{
var matP = [
[pts[0].x, pts[1].x, pts[2].x],
[pts[0].y, pts[1].y, pts[2].y],
[1, 1, 1]
];
var vecP = [[pts[3].x], [pts[3].y], [1]];
var matPi = math.inv(matP);
var vecPi = math.multiply(matPi, vecP);
var result = [
[pts[0].x * vecPi[0][0], pts[1].x * vecPi[1][0], pts[2].x * vecPi[2][0]],
[pts[0].y * vecPi[0][0], pts[1].y * vecPi[1][0], pts[2].y * vecPi[2][0]],
[vecPi[0][0], vecPi[1][0], vecPi[2][0]]
];
return result;
}
function distance(a, b)
{
var mx = b.x - a.x;
var my = b.y - a.y;
return Math.sqrt(mx * mx + my * my);
}
function computeMat()
{
var pts = getPointRelativePosition();
var widthT = distance(pts[0], pts[3]);
var widthB = distance(pts[1], pts[2]);
var heightL = distance(pts[0], pts[1]);
var heightR = distance(pts[2], pts[3]);
var maxWidth = (widthT > widthB) ? widthT : widthB;
var maxHeight = (heightL > heightR) ? heightL : heightR;
var imgWidth = Math.round(maxWidth);
var imgHeight = Math.round(maxHeight);
var matA = matStep12(pts);
var matB = matStep12([{x:0,y:0}, {x:0,y:maxHeight}, {x:maxWidth,y:maxHeight}, {x:maxWidth,y:0}]);
var matC = math.multiply(matB, math.inv(matA));
var matCi = math.inv(matC);
console.log('width:' + imgWidth + ', height:' + imgHeight);
printMat(matC);
// construct image with transformation matrice
imageData = ctx.createImageData(imgWidth, imgHeight);
var tempCanvas = document.createElement('canvas');
var tempCtx = tempCanvas.getContext('2d');
tempCanvas.width = image.width;
tempCanvas.height = image.height;
tempCtx.drawImage(image, 0, 0, image.width, image.height);
var imageDataSrc = tempCtx.getImageData(0, 0, image.width, image.height);
var mz = [matCi[0][2], matCi[1][2], matCi[2][2]];
for(var y = 0; y < imgHeight; y++)
{
var my = [matCi[0][1] * y, matCi[1][1] * y, matCi[2][1] * y];
var offsetY = y * imgWidth;
for(var x = 0; x < imgWidth; x++)
{
var mx = [matCi[0][0] * x, matCi[1][0] * x, matCi[2][0] * x];
var cx = mx[0] + my[0] + mz[0];
var cy = mx[1] + my[1] + mz[1];
var cz = mx[2] + my[2] + mz[2];
var px = Math.round(cx / cz);
var py = Math.round(cy / cz);
if(px < 0.0 || py < 0.0 || px >= image.width || py >= image.height)
{
imageData.data[pixelIndex] = 0;
imageData.data[pixelIndex + 1] = 255;
imageData.data[pixelIndex + 2] = 0;
imageData.data[pixelIndex + 3] = 255;
}
else
{
var pixelIndex = (offsetY + x) * 4;
var pixelIndexSrc = (py * image.width + px) * 4;
imageData.data[pixelIndex] = imageDataSrc.data[pixelIndexSrc];
imageData.data[pixelIndex + 1] = imageDataSrc.data[pixelIndexSrc + 1];
imageData.data[pixelIndex + 2] = imageDataSrc.data[pixelIndexSrc + 2];
imageData.data[pixelIndex + 3] = 255;
}
}
}
// here to do, image analysis
}
function getPointRelativePosition()
{
var pointOrigin = [];
for(i = 0; i < 4; i++)
{
pointOrigin[i] = {x:(points[i].x - marginImage) * image.width / (canvasWidth - marginImage * 2), y:(points[i].y - marginImage) * image.height / (canvasHeight - marginImage * 2)};
}
return pointOrigin;
}
function getPointPosition()
{
var pointOrigin = [];
for(i = 0; i < 4; i++)
{
pointOrigin[i] = {x:(points[i].x - marginImage) / (canvasWidth - marginImage * 2), y:(points[i].y - marginImage) / (canvasHeight - marginImage * 2)};
}
return pointOrigin;
}
function printPoint(pts)
{
var result = '';
for(var i = 0; i < 4; i++)
{
result += "{x:" + pts[i].x + ", y:" + pts[i].y + "},\n";
}
console.log(result);
}
function printMat(mat)
{
var result = '';
for(var i = 0; i < mat.length; i++)
{
result += "[";
for(var j = 0; j < mat[i].length; j++)
{
result += mat[i][j] + ", ";
}
result += "],\n";
}
console.log(result);
}
function canvasResize()
{
if(canvasWidth != document.body.clientWidth && canvasHeight != document.body.clientHeight)
{
var transformPoint = getPointPosition();
ratio = image.width / image.height;
canvasWidth = document.body.clientWidth;
canvasHeight = canvasWidth / ratio;
if(document.body.clientHeight < canvasHeight)
{
canvasHeight = document.body.clientHeight;
canvasWidth = canvasHeight * ratio;
}
canvasLargeur = canvasWidth;
canvasLongueur = canvasHeight;
if(canvasLargeur < canvasHeight) {
canvasLargeur = canvasHeight;
canvasLongueur = canvasWidth;
}
canvasPixelRatio = canvasLargeur / image.width;
c.setAttribute("width", canvasWidth);
c.setAttribute("height", canvasHeight);
marginImage = Math.round(40 * canvasPixelRatio);
for(i = 0; i < 4; i++)
{
points[i].x = transformPoint[i].x * (canvasWidth - marginImage * 2) + marginImage;
points[i].y = transformPoint[i].y * (canvasHeight - marginImage * 2) + marginImage;
}
draw(points);
}
}
c.addEventListener("mousedown", mouseDown, false);
c.addEventListener("mouseup", mouseUp, false);
c.addEventListener("mousemove", mouseMove, false);
c.addEventListener("touchstart", mouseDown, false);
c.addEventListener("touchend", mouseUp, false);
c.addEventListener("touchmove", mouseMove, false);
document.addEventListener("touchstart", cancelDefault, true);
document.addEventListener("touchend", cancelDefault, true);
document.addEventListener("touchmove", cancelDefault, true);
setInterval(canvasResize, 30);
draw(points);
};
</script>