Search code examples
imageionic-frameworkcameraresizecompression

IONIC - capacitor/camera - Change image size or compress image


I take a picture with the @capacitor/camera library using this code:

let capturedPhoto:any = await Camera
    .getPhoto({
      resultType: CameraResultType.Uri,
      source: CameraSource.Camera,
      quality: 30,
      height: 150,
      allowEditing: false,
      direction: CameraDirection.Front
    })
    .catch(err => {

    });

And when I use the app with this code in an Android or a iOS, the file of picture is too large (near to 4MB). This file a need to store in Firebase Storage and I need to save space because the cost in a large number of users can by a problem.

So a don't know how I can do to resize the pictures or compress the image?? I need some algorithm in typescript to implement without canvas or HTML usage. Is that's possible?

My idea is use the "capturedPhoto" and prosess it in a some way, but all the options that I can find use canvas or HTML element and is not working or I don't know how I must to implement.

The capturePhoto have this format and when a try to put jpg, the "options" for the Camera.getPhoto, don't let me.

{
    "webPath": "blob:http://localhost:8100/df29b43e-da65-4345-8bbb-704a8745d483",
    "format": "png",
    "saved": false
}

Solution

  • Finally, I was able to resolve my problem.

    I was can find and merge various solutions of different platform and languages, to generate my own function for resize pictures (using canvas, yes, but without interactions with the HTML page).

    The first test was go very good and the photo taken with the computer camera have it 285KB and the result ended was 2.75KB (in a extrembly reduction of size), in plus the convertion of png to jpg.

    I present the function next (resizeImage), but integrated in a service with all other functions what I need to use in my project.

    import { Injectable } from '@angular/core';
    // NATIVE CAMERA
    import { Camera, CameraDirection, CameraResultType, CameraSource, Photo } from '@capacitor/camera';
    import {decode} from "base64-arraybuffer";
    // https://ionicframework.com/docs/native/camera
    // https://www.npmjs.com/package/@capacitor/camera
    
    import * as moment from 'moment'
    
    @Injectable({
      providedIn: 'root'
    })
    export class PhotoService {
      base64ResizedImage: string = null;
      constructor(
      ) { }
    
      async takePicture() {
        return await Camera
          .getPhoto({
            resultType: CameraResultType.Uri,
            source: CameraSource.Camera,
            quality: 30,
            height: 150,
            allowEditing: false,
            direction: CameraDirection.Front
          })
          .catch(err => {
            return undefined;
          });
      }
    
      base64StringToFile(base64String: string, format: string) {
        const blob = new Blob([new Uint8Array(decode(base64String))], {
          type: `image/${format}`,
        });
        let file:any = new File([blob], "Name", {
          lastModified: moment().unix(),
          type: blob.type,
        });
    
        return file;
      }
    
      base64Tobase64String(base64: string) {
        console.log("photo.service.ts: base64Tobase64String => base64", base64);
        let result = base64.split(';base64,')[1];
        console.log("photo.service.ts: base64Tobase64String => base64String", result);
        return result;
      }
    
      getTypeBase64(base64: string) {
        let type = base64.substring("data:image/".length, base64.indexOf(";base64"));
        console.log("photo.service.ts: getTypeBase64 => type", type);
        return type;
      }
    
      resizeImage(base64image: string, width: number, height: number) {
        return new Promise((resolve, reject) => {
          console.log("photo.service.ts: resizeImage => entered");
          let img = new Image();
          img.src = base64image;
    
          var that = this;
          img.onload = function () {
            console.log("photo.service.ts: resizeImage => img.onload");
    
            // Check if the image require resize at all
            if(img.height <= height && img.width <= width) {
              that.base64ResizedImage = base64image;
    
              // TODO: Call method to do something with the resize image
              console.log("photo.service.ts: resizeImage => not require resize");
              resolve (that.base64ResizedImage);
            }
            else {
              // Make sure the width and height preserve the original aspect ratio and adjust if needed
              if(img.height > img.width) {
                width = Math.floor(height * (img.width / img.height));
              }
              else {
                height = Math.floor(width * (img.height / img.width));
              }
    
              let resizingCanvas: HTMLCanvasElement = document.createElement('canvas');
              let resizingCanvasContext = resizingCanvas.getContext("2d");
    
              // Start with original image size
              resizingCanvas.width = img.width;
              resizingCanvas.height = img.height;
    
              // Draw the original image on the (temp) resizing canvas
              resizingCanvasContext.drawImage(img, 0, 0, resizingCanvas.width, resizingCanvas.height);
    
              let curImageDimensions = {
                width: Math.floor(img.width),
                height: Math.floor(img.height)
              };
    
              let halfImageDimensions = {
                width: 0,
                height: 0
              };
    
              // Quickly reduce the dize by 50% each time in few iterations until the size is less then
              // 2x time the target size - the motivation for it, is to reduce the aliasing that would have been
              // created with direct reduction of very big image to small image
              while (curImageDimensions.width * 0.5 > width) {
                // Reduce the resizing canvas by half and refresh the image
                halfImageDimensions.width = Math.floor(curImageDimensions.width * 0.5);
                halfImageDimensions.height = Math.floor(curImageDimensions.height * 0.5);
    
                resizingCanvasContext.drawImage(resizingCanvas, 0, 0, curImageDimensions.width, curImageDimensions.height, 0, 0, halfImageDimensions.width, halfImageDimensions.height);
    
                curImageDimensions.width = halfImageDimensions.width;
                curImageDimensions.height = halfImageDimensions.height;
              }
    
              // Now do final resize for the resizingCanvas to meet the dimension requirments
              // directly to the output canvas, that will output the final image
              let outputCanvas: HTMLCanvasElement = document.createElement('canvas');
              let outputCanvasContext = outputCanvas.getContext("2d");
    
              outputCanvas.width = width;
              outputCanvas.height = height;
    
              outputCanvasContext.drawImage(resizingCanvas, 0, 0, curImageDimensions.width, curImageDimensions.height, 0, 0, width, height);
    
              // output the canvas pixels as an image. params: format, quality
              that.base64ResizedImage = outputCanvas.toDataURL('image/jpeg', 0.85);
    
              // TODO: Call method to do something with the resize image
              console.log("photo.service.ts: resizeImage => resize OK");
              resolve (that.base64ResizedImage);
            }
          }
          img.onerror = reject;
        });
      }
    }
    

    Now, my next step is test the resize function in a phone (Android or iOS), to see the result with the finally size of photos.