fbpx Skip to content

Aquent | DEV6

Capturing Camera Images with Angular

Written by: Chad Upton

Sometimes we want to capture images from one, or more, cameras that are built into many of our devices. This blog covers capturing images from those cameras using Angular 8.

There are many different scenarios that call for capturing images in an application, but two that I’ve implemented in Angular include capturing an image of a barcode (and decoding it) and capturing an image to upload it.

For the purpose of this blog, we’re going to show a live preview from the device camera and provide a button to capture the current image. Then we’ll display that image in the browser along with the live preview. Depending on your exact needs, you can take it from there and upload or process that image data as desired.

Getting images from a camera into your web browser wasn’t always so easy — in the old days, we had to use Flash to access a laptop’s “webcam”. But, modern browsers on mobile devices and laptops/desktops, support a JavaScript interface that (with permission) gives programmers access to a device’s Camera and/or Microphone without any plugins. That interface is called MediaDevices.

To start, generate a new Angular application using Angular CLI (no routing):

ng new capture

Then, change directory into that project and serve it with CLI’s webserver:

cd capture
ng serve

From here, we’re just going to overwrite what the CLI generated in the app-component’s html and ts files. In my example, I’ve also used bootstrap to make it look nicer, but that’s not necessary and you can skip any bootstrap CSS class references if you don’t need it to look just like my screenshots.

Here is the template file contents (app.component.html):

<div class="container vh-100">
   <div class="d-flex flex-column align-items-center">
      <div class="p-1">
         <video #video class="vid" autoplay></video>
      </div>
      <div class="pb-2">
         <button class="btn btn-primary" (click)="capture()">Capture Image</button>
      </div>
      <div class="p-1">
         <canvas #canvas class="vid"></canvas>
      </div>
   </div>
</div>

Most of this HTML is layout/styling but I want to draw your attention to the three functional HTML elements in this code:

  1. <video>
  2. <button>
  3. <canvas>

Here is the function that each will be used for:

video: display the live video data stream from the device camera

button: capture the current image from the live data stream

canvas: display the image data that was captured when the button was pressed

One additional styling point, you’ll notice the “vid” class on the video and canvas elements. You can add that to styles.scss/css file like so:

.vid {
    width: 100%;
    height: 100%;
}

We’ll get to creating that capture() method later, so you can remove it for now if your compiler is complaining.

That’s it for the template, now let’s make it do something!

Camera Preview

Let’s start by getting that live camera preview working. The first thing we need to do is create template references to the two components we’ll need to access from our code. You’ll add this in app.component.ts (make note of the “implements OnInit” on the first line, you’ll need to add that too):

export class AppComponent implements OnInit {
    @ViewChild('video', { static: true }) videoElement: ElementRef;
    @ViewChild('canvas', { static: true }) canvas: ElementRef;

Now, we’ll add the necessary imports at the top of app.component.ts:

import { Component, ElementRef, OnInit, Renderer2, ViewChild } from '@angular/core';

Next, we want to declare some configuration properties for our camera.

Since many devices have two cameras, one that faces the user and one that faces the “world” or “environment”, we sometimes need to select the appropriate camera for our application. Here we’re going to tell the browser to give us the “environment” facing camera (i.e. not the user-facing “selfie” cam).

We also want to set a maximum resolution for the video stream. This is more of a request than a guarantee though. You only get the highest resolution supported by the camera.  I have set it fairly high but consider setting it lower if high resolution images are not important.

In other words, use CSS to control the size of the video or canvas element that is displayed on the page and use the constraints request to set the (often larger) size of the image that you want to capture in your code.

 constraints = {
    video: {
        facingMode: "environment",
        width: { ideal: 4096 },
        height: { ideal: 2160 }
    }
};

Now, let’s add Angular’s Renderer2 class. This is the safest way to set values on our template elements and should be used whenever possible:

constructor(private renderer: Renderer2) {}

From there, we’ll add the following method to check for device cameras and request permission from the user:

startCamera() {
    if (!!(navigator.mediaDevices && navigator.mediaDevices.getUserMedia)) { 
 navigator.mediaDevices.getUserMedia(this.constraints).then(this.attachVideo.bind(this)).catch(this.handleError);
    } else {
        alert('Sorry, camera not available.');
    }
}

If camera access is permitted, the code above will start the camera, pass in our configuration constraints, and bind two functions: one for success and one for failure to start the camera.

On failure, we’ll simply log or display the error for this example.

handleError(error) {
    console.log('Error: ', error);
}

On success, we’ll attach the stream from the camera to the HTML video element in our template:

attachVideo(stream) {
    this.renderer.setProperty(this.videoElement.nativeElement, 'srcObject', stream);
}

Lastly, to see this live preview we need a way to kickoff this process. You can call startCamera() from a button or we can just add it to an init function:

ngOnInit() {
    this.startCamera();
}

Now, hit save and when your app restarts you should see a request for camera permissions. If you’ve already given camera permissions for that domain, then you should see a live preview video from your webcam:

Capturing an Image from the Stream

Now that we can see a preview from our camera, we’re going to snag a frame of that video and display it as a still picture.

First, we’ll add a listener to store the video’s height and width when the video starts. You don’t necessarily have to do this. You could just reference the video element’s dimensions directly when you need them, but this step shows how to hook into the play event which can trigger other useful actions, such as revealing the video preview pane that is otherwise hidden when the camera is not being used.

Let’s add some variables at the top of app.component.ts to store these values:

videoWidth = 0;
videoHeight = 0;

Next, lets add the listener to the attachVideo method:

attachVideo(stream) {
    this.renderer.setProperty(this.videoElement.nativeElement, 'srcObject', stream);
    this.renderer.listen(this.videoElement.nativeElement, 'play', (event) => {
        this.videoHeight = this.videoElement.nativeElement.videoHeight;
        this.videoWidth = this.videoElement.nativeElement.videoWidth;
    });
}

Lastly, we’ll add a capture method that the button will call:

capture() {
    this.renderer.setProperty(this.canvas.nativeElement, 'width', this.videoWidth);
    this.renderer.setProperty(this.canvas.nativeElement, 'height', this.videoHeight);
    this.canvas.nativeElement.getContext('2d').drawImage(this.videoElement.nativeElement, 0, 0);
}

The first two lines simply set the size of the canvas to the same size as the video that we’re capturing. This ensures that the captured images and the preview look the same.

The last line draws the image data from the video element to the canvas (starting at x,y coordinates (0,0) on the destination canvas).

If you removed the (click) handler from the button in the template earlier, be sure to add it back now:

<button class="btn btn-primary" (click)="capture()">Capture Image</button>

Save again, strike a pose and click the button to capture an image:

Cool! You should see your live video preview above the button and your captured image below the button. Congratulations, now you can preview a camera in your browser and capture images from it!

Before we go, I should mention that there is an npm library for doing this: Ngx-Webcam. But, as you can see, there’s not a lot of code needed to do the basics. So, you’ll have to consider whether it is necessary to use the library or if you can get away with potentially less code and implement it yourself.

The source is available below (run “npm install” after unzipping). Enjoy!