'Unable to positioned the webgl canvas while capturing image

I'm accessing the webcam and trying to take a picture. this is what it looks like before capturing the pic.

enter image description here

But this is what I'm getting after taking the picture.

enter image description here

Mask is the canvas and Someone helped me detecting the face to put mask on it but whn i capture it doesn't capture as expected.

here's the code when i click the snap button

  const glCanvas = document.getElementById('faceCanvas') as HTMLCanvasElement;
    const webglContext = glCanvas.getContext('webgl', {
      preserveDrawingBuffer: true,
    });
    const canvas2d = document.getElementById(
      'faceCanvas2'
    ) as HTMLCanvasElement;
    canvas2d.width = 1400;  
    canvas2d.height = 1400;  
    const ctx = canvas2d.getContext('2d');
    ctx.drawImage(glCanvas, 0, 0);
    ctx.drawImage(this.video.nativeElement, 0, 0); 

    const image = canvas2d.toDataURL('image/png');

Here's the Html code

<div class="main" style="overflow: hidden; position: relative">
  <div class="ar">
    <video #video muted id="webcam" class="visible"></video>
    <canvas
      #faceCanvas
      id="faceCanvas"
      tabindex="1"
      style="position: fixed; z-index: 99"
    ></canvas>
    <canvas
      #faceCanvas2
      id="faceCanvas2"
      tabindex="1"
      style="z-index: 98; position: fixed"
    ></canvas>

      <span>Webcam visible</span>
    </button>

    <a id="artist" target="_blank"> Mar flag </a>
    <button id="snap" (click)="capture()" style="z-index: 999">Snap</button> 
  </div>
</div>

if anyone can give me some pointer it would be really helpful. I've never worked on this so i'm bit lost.



Solution 1:[1]

The problem you face is that the video element that shows your webcam is stretched to fill the full screen. The "original" video element is much smaller. It is this "original" dimension that you capture, hence you see a smaller image.

So in order to have the captured image look the same as the what you see on screen, you will have to do some calculations. You should take the original video element's dimensions, compare these to the actual rendered size of the video and when drawing the canvas, stretch the image so it looks the same as the stretched video you actually see on screen.

I adjusted your code to make it less complicated. The most important part is calculating the width and height, like so:

const width = (originalVideoWidth / videoActualWidth  ) * targetDims.width;
const height = (originalVideoHeigth / videoActualHeight ) * targetDims.height;

var video = document.querySelector("#video");

if (navigator.mediaDevices.getUserMedia) {
  navigator.mediaDevices.getUserMedia({ video: true })
    .then(function (stream) {
      video.srcObject = stream;
    })
    .catch(function (err0r) {
      console.log("Something went wrong!");
    });
}
function capture(){
    const canvas2d = document.getElementById(
              'faceCanvas2'
            );
    canvas2d.width = 1400;  
    canvas2d.height = 1400;  
    //const glCanvas = document.getElementById('faceCanvas2');
    // const targetSquare = document.getElementsByClassName('profile-box')[0];
     const targetDims = canvas2d.getBoundingClientRect()
    const originalVideoWidth = video.videoWidth;
    const originalVideoHeigth = video.videoHeight; 
    const videoActualWidth = video.getBoundingClientRect().width;
    const videoActualHeight = video.getBoundingClientRect().height;
    const videoContainer = document.getElementsByClassName('ar')[0];
    const containerWidth = videoContainer.getBoundingClientRect().width;
    const containerHeight = videoContainer.getBoundingClientRect().height;
    let videoOffsetLeft = 0;
    let videoOffsetTop = 0;
    if(containerWidth === videoActualWidth){
        videoOffsetTop = ((videoActualHeight - containerHeight) / 2) * (originalVideoHeigth / videoActualHeight)
    }
    else{
        videoOffsetLeft = ((videoActualWidth - containerWidth) / 2) * (originalVideoWidth / videoActualWidth)
    }
    const left = videoOffsetLeft + ((originalVideoWidth / videoActualWidth) * targetDims.left);
    const top = videoOffsetTop + ((originalVideoHeigth / videoActualHeight) * targetDims.top);
    const width = (originalVideoWidth / videoActualWidth  ) * targetDims.width;
    const height = (originalVideoHeigth / videoActualHeight ) * targetDims.height;
    
    
    
    const ctx = canvas2d.getContext('2d');
    ctx.drawImage(video, 0,0, width, height, 0, 0, canvas2d.width,canvas2d.height); 
    video.style.display = "none";
}
html,
body,
.main,
.ar,
video{
    width:100%;
    height:100%;
}
.ar{
    position:relative;
}
#artist,
#snap{
    position:absolute;
    top:50px;
    left:50px;
}
<div class="main" style="overflow: hidden; position: relative">
  <div class="ar">
    <video id="video" muted autoplay allowfullscreen class="visible"></video>
    <canvas
      
      id="faceCanvas"
      tabindex="1"
      style="position: fixed; z-index: 99"
    ></canvas>
    <canvas
      
      id="faceCanvas2"
      tabindex="1"
      style="z-index: 98; position: fixed"
    ></canvas>

      <span>Webcam visible</span>
    </button>

    <a id="artist" target="_blank"> Mar flag </a>
    <button id="snap" onclick="capture()" style="z-index: 999">Snap</button> 
  </div>
</div>

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1