Skip to content

Crop the area that should be scanned #594

@rubimadi

Description

@rubimadi

Does anybody have an solution, that would work with the latest version of the package?

Great package odahcam!

Thank you NilsEngelbach very helpful!!!.

Using your code was the only way for me to read the PDF147 with an iPhone.

Everyone else, you need to create a canvas and then play the video on the canvas before calling.

BrowserCodeReader.createBinaryBitmapFromCanvas = function (canvas: HTMLCanvasElement): BinaryBitmap.

I also had to wait for the video to load and wait for it to have a size (using setInterval and setTimeout).

Good Luck :)

I did it like this .


in HTML 

   <canvas id="canvas" class="canvas" hidden></canvas>

    <zxing-scanner [torch]="torchEnabled" [device]="deviceCurrent" (deviceChange)="onDeviceChange($event)"

                   (scanSuccess)="onCodeResult($event)"

                   [formats]="formatsEnabled" [tryHarder]="tryHarder" (permissionResponse)="onHasPermission($event)"

                   (camerasFound)="onCamerasFound($event)" (torchCompatible)="onTorchCompatible($event)"

                   (scanError)="onScanError($event)"

                   style="max-height: 60%;   width: auto;  object-fit: contain;"></zxing-scanner>`



scss

 .canvas {

  position: absolute;

  top: 60px;

  left: 20px;

   height: 438px;

   width: 768px;

}





in TS

  ngOnInit(): void {

    this.loadingInterval = setInterval(() => {

      this.loading = this.zXingScannerComponent?.isAutostarting ?? true;

      if (!this.loading) {

        this.croppingBox()

        console.log('this.croppingBox()')

        clearInterval(this.loadingInterval);

      }

    }, 100);

  }



  onCamerasFound(devices: MediaDeviceInfo[]): void {

    this.availableDevices = devices;

    this.hasDevices = Boolean(devices && devices.length);

    this.zXingScannerComponent.getAnyVideoDevice = (): Promise<MediaStream> => {

      return navigator.mediaDevices.getUserMedia({

        audio: false,

        video: {

          width: {min: 640, ideal: 1920},

          height: {min: 400, ideal: 1080},

          aspectRatio: {ideal: 1.7777777778}

        }

      });

    };



    BrowserCodeReader.prototype.decodeFromVideoDevice = async function (

      deviceId: string | undefined,

      previewElem: string | HTMLVideoElement | undefined,

      callbackFn: any,

    ): Promise<IScannerControls> {



      // We had to comment this out because it is a private method...

      // BrowserCodeReader.checkCallbackFnOrThrow(callbackFn);



      let videoConstraints: MediaTrackConstraints;



      if (!deviceId) {

        videoConstraints = {facingMode: 'environment'};

      } else {

        videoConstraints = {

          deviceId: {exact: deviceId},

          width: {min: 640, ideal: 1920},

          height: {min: 400, ideal: 1080},

          aspectRatio: {ideal: 1.7777777778}

        };

      }

      const constraints: MediaStreamConstraints = {video: videoConstraints};

      return await this.decodeFromConstraints(constraints, previewElem, callbackFn);

    }

  }



drawImage(canvas, video, width, height) {

    canvas.getContext('2d', {alpha: false}).drawImage(video, 0, 0, width, height);

  }



  croppingBox() {

    // https://github.com/zxing-js/ngx-scanner/issues/365

    let squareShape = false

    let timeout

    let canvas = <HTMLCanvasElement>document.getElementById("canvas");

    let video = (document.querySelector('zxing-scanner video') as any)

    const fps = 60;

    const width = 1280;

    const height = 720;

    let canvasInterval = null;

    canvasInterval = window.setInterval(() => {

      this.drawImage(canvas, video, width, height);

    }, 1000 / fps);





    video.onpause = function () {

      clearInterval(canvasInterval);

    };

    video.onended = function () {

      clearInterval(canvasInterval);

    };

    video.onplay = function () {

      clearInterval(canvasInterval);

      canvasInterval = window.setInterval(() => {

        this.drawImage(canvas, video, width, height);

      }, 1000 / fps);

    };



    BrowserCodeReader.createBinaryBitmapFromCanvas = function (canvas: HTMLCanvasElement): BinaryBitmap {



      const cameraCanvasImageWidth = canvas.width;

      const cameraCanvasImageHeight = canvas.height;



      let videoElementHeight = (document.querySelector('zxing-scanner video') as any).offsetHeight;

      let videoElementWidth = (document.querySelector('zxing-scanner video') as any).offsetWidth;

      while (videoElementHeight < 1) {

        let tries = 0

        timeout = setTimeout(() => {

          tries++

          videoElementHeight = (document.querySelector('zxing-scanner video') as any).offsetHeight;

          videoElementWidth = (document.querySelector('zxing-scanner video') as any).offsetWidth;

          console.log('tries', tries)

          if (tries > 100) {

            clearTimeout(timeout)

          }

        }, 5000);

      }





      let factor = 1;

      if (videoElementWidth > videoElementHeight) {

        factor = cameraCanvasImageWidth / videoElementWidth;

      } else {

        factor = cameraCanvasImageHeight / videoElementHeight;

      }



      // console.log('factor', factor)

      const width = Math.floor((squareShape ? 250 : 450) * factor);

      const height = Math.floor((squareShape ? 250 : 200) * factor);



      const left = (cameraCanvasImageWidth - width) / 2;

      const top = (cameraCanvasImageHeight - height) / 2;



      const croppedCanvas = document.createElement('canvas');

      croppedCanvas.width = width;

      croppedCanvas.height = height;

      const croppedCanvasContext = croppedCanvas.getContext('2d');

      croppedCanvasContext.rect(0, 0, width, height);

      croppedCanvasContext.fillStyle = 'red';

      croppedCanvasContext.fill();

      console.log('drawImage', canvas, left, top, width, height)

      croppedCanvasContext.drawImage(canvas, left, top, width, height, 0, 0, width, height);



      // These lines can be used to show the cropped part of the image stream that is used

      // to find the code and check if the highlighted area matches the cropped image

      // document.getElementById('croppedSvg').innerHTML = '';

      // const span = document.createElement('span');

      // span.textContent = `${cameraCanvasImageWidth} x ${cameraCanvasImageHeight} | ${videoElementWidth} x ${videoElementHeight}`;

      // span.style.position = 'absolute';

      // span.style.right = `0`;

      // span.style.color = 'black';

      // span.style.display = 'block';

      // document.getElementById('croppedSvg').appendChild(span);

      // croppedCanvas.style.marginTop = '20px';

      // croppedCanvas.style.transform = `scale(1)`;

      // croppedCanvas.style.transformOrigin = `right top`;

      // document.getElementById('croppedSvg').appendChild(croppedCanvas);



      let luminanceSource = new HTMLCanvasElementLuminanceSource(croppedCanvas); // .invert() to support inverted codes



      const hybridBinarizer = new HybridBinarizer(luminanceSource);



      return new BinaryBitmap(hybridBinarizer);

    };



  }



Originally posted by @NexPlex in #365

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions