2016-05-20 2 views
1

Ich möchte die Perspektive eines Bildes korrigieren, indem Sie 4 Punkte einer 3D-Darstellung eines Quadrats auswählen und diese auf Punkte auf ein 2D-Quadrat abbilden.Korrekte Perspektive durch Anwenden der projektiven Transformation auf CSS mit Javascript

ich zwei sehr reiche Beispielen folgte aber nicht gelungen, die gewünschten Ergebnisse zu reproduzieren, sind hier die Originale Beispiele sowie meine Anpassung von denen auf jsfiddle:

  • example 1 (article describing the solution): Dieses Beispiel zeigt (in CoffeeScript), wie man die Perspektive eines Videos korrigiert, also ist dieses Beispiel dem, was ich brauche, sehr ähnlich. Hier ist meine Anpassung davon, müssen Sie klicken Sie auf den vier Ecken mit der linken oberen Ecke des Schachbretts im Uhrzeigersinn ausgeteilt: my jsfiddle adaptation

    var srcImg, width, height, srcCvs, srcC, srcRect, dstCvs, dstC, widthToHeight; 
    var nbClicks = 0, coordinates = Array(8); 
    
    srcImg = document.getElementById('sourceImg'); 
    widthToHeight = srcImg.width/srcImg.height; 
    
    srcCvs = document.getElementById('sourceCanvas'); 
    srcC = srcCvs.getContext('2d'); 
    dstCvs = document.getElementById('destinationCanvas'); 
    dstC = dstCvs.getContext('2d'); 
    
    width = srcCvs.width = dstCvs.width = srcCvs.clientWidth; 
    height = srcCvs.height = dstCvs.height = srcCvs.clientWidth/widthToHeight; 
    srcRect = srcCvs.getBoundingClientRect(); 
    srcC.strokeStyle = '#0f0'; 
    srcC.drawImage(srcImg, 0, 0, width, height); 
    srcCvs.addEventListener('click', doClick, false); 
    
    function doClick(event) { 
        if (nbClicks < 4) { 
        coordinates[ nbClicks * 2 ] = (event.clientX - srcRect.left) * width/srcRect.width; 
        coordinates[ nbClicks * 2 + 1 ] = (event.clientY - srcRect.top) * height/srcRect.height; 
        srcC.strokeRect(coordinates[ nbClicks * 2 ] - 10, coordinates[ nbClicks * 2 + 1 ] - 10, 20, 20); 
        } 
        if (++nbClicks == 4) { 
        dstC.beginPath(); 
        dstC.moveTo(coordinates[ 0], coordinates[ 1 ]); 
        for(i = 1; i < 4; i ++) { 
         dstC.lineTo(coordinates[ i*2 ], coordinates[ i*2 + 1 ]); 
        } 
        dstC.closePath(); 
        dstC.clip(); 
    
        var t = getTransform(left, top, w, h); 
    
        dstCvs.style.visibility = 'visible'; 
        dstC.drawImage(srcImg, 0, 0, width, height); 
        var left = 0, top = 0, w = width, h = width; 
         srcC.strokeStyle = '#f00'; 
        srcC.strokeRect(left - 10, top - 10, 20, 20); 
        srcC.strokeRect(left + w - 10, top - 10, 20, 20); 
        srcC.strokeRect(left + w - 10, top + h - 10, 20, 20); 
        srcC.strokeRect(left - 10, top + h - 10, 20, 20); 
         alert('Clipped image is now drawn, going to apply transform after this alert.'); 
        var t = getTransform(left, top, w, w); 
        dstCvs.style.transform = t; 
        } 
    }; 
    
    function getTransform(left, top, w, h) { 
        var minX = Math.min(coordinates[ 0 ], coordinates[ 6 ]); 
        var minY = Math.min(coordinates[ 1 ], coordinates[ 3 ]); 
        var w = Math.max(Math.abs(coordinates[ 2 ] - coordinates[ 0 ]), Math.abs(coordinates[ 6 ] - coordinates[ 4 ])); 
        var h = Math.max(Math.abs(coordinates[ 3 ] - coordinates[ 1 ]), Math.abs(coordinates[ 7 ] - coordinates[ 5 ])); 
        var c = coordinates; 
        for (var i = 0; i < 4; i ++) { 
        c[ i * 2 ] = coordinates[ i ] - minX; 
        c[ i * 2 + 1 ] = coordinates[ i * 2 + 1 ] - minY; 
        } 
        var l=t=0; 
    
        var from = c; 
        var to = [ left, top, left + w, top, left + w, top + h, left, top + h ]; 
        A = []; 
        b = []; 
        for (var i = 0; i < 4; i ++) { 
        A.push([ from[ i * 2 ], from[ i * 2 + 1 ], 1, 0, 0, 0, -from[ i * 2 ] * to[ i * 2 ], -from[ i * 2 + 1 ] * to[ i * 2 ] ]); 
        A.push([ 0, 0, 0, from[ i * 2 ], from[ i * 2 + 1 ], 1, -from[ i * 2 ] * to[ i * 2 + 1 ], -from[ i * 2 + 1 ] * to[ i * 2 + 1 ] ]); 
        b.push(to[ i * 2 ]); 
        b.push(to[ i * 2 + 1 ]); 
        } 
        h = numeric.solve(A, b); 
        H = [[h[0], h[1], 0, h[2]], 
         [h[3], h[4], 0, h[5]], 
         [ 0, 0, 1, 0], 
         [h[6], h[7], 0, 1]];     
    
        return "matrix3d(" + H.join(", ") + ")"; 
    } 
    
  • example 2 (article describing the solution): Dieses Beispiel zeigt, wie Perspektive auf ein Element anzuwenden, hat keine, also ist mein Problem das Gegenteil davon. Es scheint jedoch, dass die allgemeine Transformation eine Reihe von Punkten auf eine andere überträgt, so dass ich davon ausgehe, dass dies gleichwertig sein sollte ... aber ich bin wahrscheinlich irgendwo falsch! Auch hier müssen Sie klicken auf die vier Ecken im Uhrzeigersinn mit der linken oberen Ecke des Schachbretts Start: my jsfiddle adaptation

    var srcImg, width, height, srcCvs, srcC, srcRect, dstCvs, dstC, widthToHeight; 
    var nbClicks = 0, coordinates = Array(8); 
    
    srcImg = document.getElementById('sourceImg'); 
    widthToHeight = srcImg.width/srcImg.height; 
    
    srcCvs = document.getElementById('sourceCanvas'); 
    srcC = srcCvs.getContext('2d'); 
    dstCvs = document.getElementById('destinationCanvas'); 
    dstC = dstCvs.getContext('2d'); 
    
    width = srcCvs.width = dstCvs.width = srcCvs.clientWidth; 
    height = srcCvs.height = dstCvs.height = srcCvs.clientWidth/widthToHeight; 
    srcRect = srcCvs.getBoundingClientRect(); 
    srcC.strokeStyle = '#0f0'; 
    srcC.drawImage(srcImg, 0, 0, width, height); 
    srcCvs.addEventListener('click', doClick, false); 
    
    function doClick(event) { 
        if (nbClicks < 4) { 
        coordinates[ nbClicks * 2 ] = (event.clientX - srcRect.left) * width/srcRect.width; 
        coordinates[ nbClicks * 2 + 1 ] = (event.clientY - srcRect.top) * height/srcRect.height; 
        srcC.strokeRect(coordinates[ nbClicks * 2 ] - 10, coordinates[ nbClicks * 2 + 1 ] - 10, 20, 20); 
        } 
        if (++nbClicks == 4) { 
        dstC.beginPath(); 
        dstC.moveTo(coordinates[ 0], coordinates[ 1 ]); 
        for(i = 1; i < 4; i ++) { 
         dstC.lineTo(coordinates[ i*2 ], coordinates[ i*2 + 1 ]); 
        } 
        dstC.closePath(); 
        dstC.clip(); 
        dstCvs.style.visibility = 'visible'; 
        dstC.drawImage(srcImg, 0, 0, width, height); 
        var left = 0, top = 0, w = width, h = width; 
         srcC.strokeStyle = '#f00'; 
        srcC.strokeRect(left - 10, top - 10, 20, 20); 
        srcC.strokeRect(left + w - 10, top - 10, 20, 20); 
        srcC.strokeRect(left + w - 10, top + h - 10, 20, 20); 
        srcC.strokeRect(left - 10, top + h - 10, 20, 20); 
         alert('Clipped image is now drawn, going to apply transform after this alert. On the left canvas, the positions of the mapped points are drawn in red.'); 
        var t = getTransform(left, top, w, h); 
        dstCvs.style.transform = t; 
        } 
    }; 
    
    function adj(m) { // Compute the adjugate of m 
        return [ 
        m[4]*m[8]-m[5]*m[7], m[2]*m[7]-m[1]*m[8], m[1]*m[5]-m[2]*m[4], 
        m[5]*m[6]-m[3]*m[8], m[0]*m[8]-m[2]*m[6], m[2]*m[3]-m[0]*m[5], 
        m[3]*m[7]-m[4]*m[6], m[1]*m[6]-m[0]*m[7], m[0]*m[4]-m[1]*m[3] 
        ]; 
    } 
    function multmm(a, b) { // multiply two matrices 
        var c = Array(9); 
        for (var i = 0; i != 3; ++i) { 
        for (var j = 0; j != 3; ++j) { 
         var cij = 0; 
         for (var k = 0; k != 3; ++k) { 
         cij += a[3*i + k]*b[3*k + j]; 
         } 
         c[3*i + j] = cij; 
        } 
        } 
        return c; 
    } 
    function multmv(m, v) { // multiply matrix and vector 
        return [ 
        m[0]*v[0] + m[1]*v[1] + m[2]*v[2], 
        m[3]*v[0] + m[4]*v[1] + m[5]*v[2], 
        m[6]*v[0] + m[7]*v[1] + m[8]*v[2] 
        ]; 
    } 
    function pdbg(m, v) { 
        var r = multmv(m, v); 
        return r + " (" + r[0]/r[2] + ", " + r[1]/r[2] + ")"; 
    } 
    function basisToPoints(x1, y1, x2, y2, x3, y3, x4, y4) { 
        var m = [ 
        x1, x2, x3, 
        y1, y2, y3, 
        1, 1, 1 
        ]; 
        var v = multmv(adj(m), [x4, y4, 1]); 
        return multmm(m, [ 
        v[0], 0, 0, 
        0, v[1], 0, 
        0, 0, v[2] 
        ]); 
    } 
    function general2DProjection(
        x1s, y1s, x1d, y1d, 
        x2s, y2s, x2d, y2d, 
        x3s, y3s, x3d, y3d, 
        x4s, y4s, x4d, y4d 
    ) { 
        var s = basisToPoints(x1s, y1s, x2s, y2s, x3s, y3s, x4s, y4s); 
        var d = basisToPoints(x1d, y1d, x2d, y2d, x3d, y3d, x4d, y4d); 
        return multmm(d, adj(s)); 
    } 
    function project(m, x, y) { 
        var v = multmv(m, [x, y, 1]); 
        return [v[0]/v[2], v[1]/v[2]]; 
    } 
    function getTransform(left, top, w, h) { 
        var x1 = coordinates[ 0 ], y1 = coordinates[ 1 ]; 
        var x2 = coordinates[ 2 ], y2 = coordinates[ 3 ]; 
        var x3 = coordinates[ 4 ], y3 = coordinates[ 5 ]; 
        var x4 = coordinates[ 6 ], y4 = coordinates[ 7 ]; 
    
        var t = general2DProjection 
        (left, top, x1, y1, w, top, x2, y2, w, h, x3, y3, left, h, x4, y4); 
        for(i = 0; i != 9; ++i) t[i] = t[i]/t[8]; 
        t = [t[0], t[3], 0, t[6], 
         t[1], t[4], 0, t[7], 
         0 , 0 , 1, 0 , 
         t[2], t[5], 0, t[8]]; 
        t = "matrix3d(" + t.join(", ") + ")"; 
        return t; 
    } 
    

Nur um zu versuchen, so klar wie möglich zu sein: in beiden meiner Anpassungen, ich auf die 4 Ecken des Schachbretts, die aufgrund der Perspektive verzerrt sind, der 4te Klick löst das Beschneiden dieses Vierecks aus, berechnet und wendet dann eine Transformation an, deren Ziel es ist, das verzerrte Viereck zurück in ein 2D-Quadrat zu bringen.

Danke, Tepp.

+0

PS: Ich bin nicht sehr erfahren mit SO, aber ich denke, dass ich den Autor "ping" kann von Beispiel 2, indem ich dies tue, hoffe ich, das ist nicht schlecht Etikette: @MvG – teppyogi

Antwort

0

Diese Antwort auf Stack-Überlauf scheint zu tun, was Sie wollen, dh eine „inverse perspektivische Transformation“: Redraw image from 3d perspective to 2d

Wie Sie sehen können, der Verfasser der Antwort (das gleiche Sie zum Beispiel zitiert 2) verwendet eine andere Gleichung für die inverse Transformation: C = A ∙ B⁻¹ anstelle von C = B ∙ A⁻¹

+0

Eigentlich, die Art, wie ich das Beispiel codiert, wechselte ich die Quelle und Zielkoordinaten, so dass die gleiche Sache (ich habe gerade überprüft) als die Verwendung der Coor diniert in der richtigen Reihenfolge, invertiert aber die Matrixmultiplikation. Aber du hast recht, es ist wichtig, darauf zu achten. – teppyogi

Verwandte Themen