justadudewhohacks / face-api.js

JavaScript API for face detection and face recognition in the browser and nodejs with tensorflow.js
MIT License
16.7k stars 3.72k forks source link

Save detected face into jpg #659

Open logypaser opened 4 years ago

logypaser commented 4 years ago

I am trying to capture the detected face into a jpg, is there a way for me to save detected face into jpg extension?

anhtuan0030 commented 4 years ago

I want the same, any solution to capture face detected on camera

msvargas commented 4 years ago

share a little function to do that:


const getFaceAndDescriptor = (canvas, type = 'base64') => {
  if (!canvas) return Promise.reject(new Error('Foto no válida'));

  return new Promise(async (resolve, reject) => {
    try {
      const photo = {
        thumbnail: {
          b64: null,
          blob: null,
        },
        faces: [],
      };
      resizeToMax(canvas); //<- reduce canvas height and width if it necesary
      const result = await faceapi.detectAllFaces(canvas, faceDetectionOptionsFiles);
      if (!result) return reject(new Error('Cara no encontrada'));
      if (result.length > 1) {
        return reject(new Error(`Foto no válida, se encontraron ${result.length} rostros`));
      }
      if (result.length === 0) {
        return reject(new Error('La imagen no contiene un rostro o se encuentra muy lejos'));
      }

      let { box } = result[0];
      let region = new faceapi.Rect(box._x, box._y, box._width, box._height);
      const boxFace = box;
      let face = await faceapi.extractFaces(canvas, [region]);

      const landmarks = await faceapi.detectFaceLandmarksTiny(face[0]);
      box = landmarks.align();
      region = new faceapi.Rect(box._x, box._y, box._width, box._height);
      face = await faceapi.extractFaces(face[0], [region]);
      canvas2gray(face[0]); // Convierte el canvas a escala de grises
      const blobFace = await canvas2blob(face[0]);
      let descriptor = await faceapi.computeFaceDescriptor(face[0]); // get Float32Array
      // Encode descriptor to send a server
      descriptor =  btoa(String.fromCharCode.apply(null, new Uint8Array(descriptor.buffer)))
      // Push blob of face and descriptor
      photo.faces.push({
        blob: blobFace,
        descriptor,
      });

      box = boxFace;
      if (canvas.height > 240 && canvas.width > 320) {
        let x;
        let y;
        let h = canvas.height * 0.9;
        let w = box.width;
        const centerX = box.width / 2;
        const centerY = box.height / 2;
        x = box.x - centerX;
        if (x < 0) x = box.x;
        else w = box.width * 2;
        y = box.y - centerY;
        if (y < 0) y = 0;
        if (h - y < canvas.height) h -= y;
        else h = box.height;
        // Get thumbnail
        region = new faceapi.Rect(x, y, w, h); // region center imagen
      } else {
        region = new faceapi.Rect(box.x, box.y, box.width, box.height);
      }

      const thumbnail = await faceapi.extractFaces(canvas, [region]);
      const blobThumbnail = await canvas2blob(thumbnail[0]);
      photo.thumbnail = {
        blob: blobThumbnail,
      };
      if (type === 'base64') photo.thumbnail.b64 = thumbnail[0].toDataURL('image/jpeg');

      resolve(photo);
    } catch (error) {
      console.error(error);
      reject(error);
    }
  });
};

function canvas2blob(can, type = 'image/jpeg', quality = 0.97) {
  return Promise.try(() => {
    if (!can) throw new Error('Empty canvas');
    return new Promise((resolve, reject) => {
      can.toBlob(resolve, type, quality);
    });
  });
}

function resizeToMax(can, max_size = 640) {
  if (!can || !can.width || !can.height) return;
  let { width } = can;
  let { height } = can;
  if (width <= max_size && height <= max_size) return;
  if (width > height) {
    if (width > max_size) {
      height *= max_size / width;
      width = max_size;
    }
  } else if (height > max_size) {
    width *= max_size / height;
    height = max_size;
  }
  can.width = width;
  can.height = height;
}

function canvas2gray(c) {
  if (!c || !c.width || !c.height) return;
  const ctx = c.getContext('2d');

  const idataSrc = ctx.getImageData(0, 0, c.width, c.height); // original
  const idataTrg = ctx.createImageData(c.width, c.height); // empty data
  const dataSrc = idataSrc.data; // reference the data itself
  const dataTrg = idataTrg.data;
  const len = dataSrc.length;
  let i = 0;
  let luma;

  // convert by iterating over each pixel each representing RGBA
  for (; i < len; i += 4) {
    // calculate luma, here using Rec 709
    luma = dataSrc[i] * 0.2126 + dataSrc[i + 1] * 0.7152 + dataSrc[i + 2] * 0.0722;

    // update target's RGB using the same luma value for all channels
    dataTrg[i] = dataTrg[i + 1] = dataTrg[i + 2] = luma;
    dataTrg[i + 3] = dataSrc[i + 3]; // copy alpha
  }
  // put back luma data so we can save it as image
  ctx.putImageData(idataTrg, 0, 0);
}

You can pass a canvas element reference


canvasEl = .... // React.useRef o vainilla javascript

getFaceAndDescriptor(canvasEl).then((result)=>console.log(result))
anhtuan0030 commented 4 years ago

Thanks @punisher97

ExpErgio commented 2 years ago

cabe la posibilidad de que puedas adjuntar el código entero por favor?