javascriptnode.jsgoogle-cloud-mlgoogle-cloud-vision

Node JS | Google Vision API | "Bad image data" when sending base64 to Google Cloud Vision


I'm experimenting with Google Cloud Vision API and trying to extract colors from an image. The problem occurs when I try to send a cropped image to the Vision API and I get an "Bad image data" error message back.

Could it have something with some CrossOrigin problem? But then I don't get why I can't draw it to an canvas later on when sent to the client.

Thanks in advance for the help!

The function for requesting and sending the Vision API request out of the cropped image

// Imports the Google Cloud client library
const vision = require("@google-cloud/vision");
const client = new vision.ImageAnnotatorClient();

const fetch = require("../functions/Fetch");
const imageEdit = require("../functions/ImageEdit");
exports.colorDetection = async (req, res) => {
  // Then you 'get' your image like so:
  await fetch.getImage(req.headers.imageurl, async function (err, data) {
    // Handle the error if there was an error getting the image.
    if (err) {
      throw new Error(err);
    }
    // Crop Image
    const cropSettings = JSON.parse(req.headers.cropsettings);
    const croppedImage = await imageEdit.CropImage(data, cropSettings);
    const stringify = JSON.stringify(croppedImage);

    const request = {
      image: {
        content: Buffer.from(croppedImage).toString("base64"),
      },
    };

    // // Performs label detection on the image file
    const [result] = await client.imageProperties(request);
    console.log(result) 
    const colors = result.imagePropertiesAnnotation;
    // const stringify = JSON.stringify(colors);
    await res.json(stringify);
  });
};

The crop image function

const { createCanvas, Image } = require("canvas");

exports.CropImage = (data, cropSettings) => {
  "user strict";
  // Settings
  let cropLeft = cropSettings.startX,
    cropTop = cropSettings.startY,
    cropWidth = cropSettings.width,
    cropHeight = cropSettings.height;

  // Canvas
  let resize_canvas = createCanvas(cropWidth / 2, cropHeight / 2);

  //Image
  let crop_img = new Image();
  crop_img.src = `data:image/png;base64,${data.toString("base64")}`;

  function Crop() {
    const ctx = resize_canvas.getContext("2d");
    ctx.drawImage(
      crop_img,
      cropLeft,
      cropTop,
      cropWidth * 2,
      cropHeight * 2,
      0,
      0,
      cropWidth,
      cropHeight
    );
  }

  if (crop_img && crop_img.complete) {
    Crop();
    try {
      const base64Img = resize_canvas.toDataURL("image/png", 1.0);
      return base64Img
    } catch (e) {
      console.log(e);
    }
  } else {
    crop_img.onload = function () {
      Crop();
      try {
        const base64Img = resize_canvas.toDataURL("image/png", 1.0);
        return base64Img
      } catch (e) {
        console.log(e);
      }
    };
  }
};

Solution

  • You can use my code given below. Basically you have to convert the image into base64 and remove 'data:image/png;base64' and pass the value into content key.

    const getBase64FromUrl = async (url) => {
        const data = await fetch(url);
        const blob = await data.blob();
        return new Promise((resolve) => {
          const reader = new FileReader();
          reader.readAsDataURL(blob);
          reader.onloadend = () => {
            const base64data = reader.result;
            resolve(base64data);
          };
        });
      };
    const base64Image = getBase64FromUrl('url').split(',')[1]
    
          image: {
                  content: base64Image.split(",")[1],
                },
    

    This should work perfectly!