Search code examples
node.jsamazon-web-servicesreact-nativeamazon-rekognition

Amazon Rekogntion Image: error InvalidImageFormatException: Request has invalid image format


I am trying to compare faces calling AWS Rekognition from a Node.Js application. When comparing two images on a S3 bucket, all went fine, but when i tried to upload a local image from the client (React Native/Expo app) to compare with another image stored on this bucket, i got the error InvalidImageFormatException: Request has invalid image format.

This image is a jpeg 250px square and was sent as a valid base64 string (atob tested). Aparently, it meets the requisites presented here: https://docs.aws.amazon.com/rekognition/latest/dg/limits.html.

Below, some code snippets:

Capturing the image:

const takeImgHandler = async () => {
    const img = await ImagePicker.launchCameraAsync(getImgProps);
    editImg(img);
};

Editing the image:

const editImg = async img => {
   ...
    const actions = [
      { resize: { 250, 250 } },
    ];

    const saveOptions = {      
      base64: true,
    };

    const edited = await ImageManipulator.manipulateAsync(img.uri, actions, saveOptions);
    setState({ ...state, img: edited });    
};

Setting the detectFaces call to my server:

// sourceImg is appState.img.base64
const compareImagesHandler = async sourceImg => {
    const targetImage = {
      S3Object: {
        Bucket: 'my-bucket-name',
        Name: 'image-name.jpg',
      },
    }; 

    const sourceImage = {
      Bytes: sourceImg,
};

const comparison = await ajax({ method: 'POST', url: `url-to-server-route`, data: { sourceImage, targetImage }});
    console.log('comparison: >>>>>> ', comparison);
    return comparison;
};

The server controler runs this function:

const awsConfig = () => {
  const config = new AWS.Config({
    accessKeyId: process.env.AWS_ACCESS_KEY_ID,
    secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
    region: process.env.AWS_DEFAULT_REGION,
  });
  AWS.config.update(config);
};

const compareImages = async (SourceImage, TargetImage, cb) => {
  const client = new AWS.Rekognition();

  // Logging the base64 string to validate it, externally, just to make
     sure that it´s valid!
  console.log('sourceImag.Bytes: >>>>>> ', SourceImage.Bytes);

  const params = {
    SourceImage,
    TargetImage,
    SimilarityThreshold: 50,
  };

  client.compareFaces(params, (err, response) => {
    if (err) {
      console.log('err: >>>>>> ', err);
      return cb({ err });
    }

    if (!response.FaceMatches.length) {
      return cb({ err: 'Face not recongized' });
    }

    response.FaceMatches.forEach(data => {
      const position = data.Face.BoundingBox;
      const similarity = data.Similarity;
      console.log(`The face at: ${position.Left}, ${position.Top} matches with ${similarity} % confidence`);
      return cb({ success: data.Similarity });
    });
  });
};

Solution

  • Solved!

    Two tweaks are needed. first, encode the sourceImg file using encodeURIComponent:

    const sourceImage = encodeURIComponent(sourceImg);

    On the server, I should create a Buffer, instead of sending the base64 string:

    const imageBuffer = Buffer.from(decodeURIComponent(SourceImage), 'base64');

    So, the body sent to AWS should be:

    const params = {    
        SourceImage: {
          Bytes: imageBuffer,
        }
        TargetImage,
        SimilarityThreshold: 50,
    };