【问题标题】:Image transformation matrix图像变换矩阵
【发布时间】:2017-05-04 10:42:56
【问题描述】:

我想练习使用矩阵进行像素操作以从另一个图像中提取图像。

这是我对 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) 比预期的太小。你能帮我找出问题吗?

说真的, 矩阵好奇心。

【问题讨论】:

  • 这个矩阵是用于直接变换 L->R 还是返回一个 R->L?为什么矩阵是 4x4 而不是 3x3 进行 2D 转换?移位组件(12/-17?)看起来太小了。
  • 交叉参考:请参阅math.stackexchange.com/a/339033/35416,了解给定四个点及其图像的矩阵查找步骤。答案和 cmets 包括一些有效的 JavaScript/CSS 演示。

标签: image math matrix pixel transformation


【解决方案1】:

这些是齐次坐标。所以给定一些 [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 demoanswer a question about finding the transform 时,我特意设置了transform-origin: 0 0;(以及这个的各种供应商前缀版本)以避免此类问题。

【讨论】:

    【解决方案2】:

    非常感谢 MvG。

    我点击你的链接,我找到了我想要的 [https://math.stackexchange.com/a/339033] 只是一件事,我必须反转 C 矩阵才能找到与像素相关的 L

    我分享我的代码,让您了解您必须做什么 您可以在 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>
    

    【讨论】:

      猜你喜欢
      • 2021-12-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-03-09
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多