How to run blur hash algorithm on firebase storage

1.1k views Asked by At

So, this is the problem. I have a social media type app where users upload images all the time. Right now, it has few users, but the number is growing by the day. I am using a package called blur hash to generate hashes (strings that represent a blurry version of the pic) as the picture is being uploaded to firebase storage. And these hashes are then uploaded to firebase rtdb or firestore.. Spending on the use case, while the image is saved on firebase storage. The problem, however is that generating these hashes is a heavy task and it makes the app freeze up for about 3 seconds per image. So, if the user is uploading 7 pics, that's an entire 21 seconds of just frozen app. Even the loading indicator stops moving. Nothing When the hash generation is done, it resumes working normally. If the images are really many, the app just crashes. On some phones that don't have that much ram, the app crashes the moment the user tries to upload images.

I have the idea of doing this hash generation on server side. Like how telegram does it. Or how whatsapp does it. Or medium. I'm sure they also do it server side. But, I cant seem to find anywhere how to generate blur hashes from the server side.

Any help please?

The package I am using to generate the blur hashes is this one.

https://pub.dev/packages/blurhash

And I am using the flutter_blur_ hash package to load the hashes and use them as placeholder images for the actual images. And it actually looks really nice. But the issue is in creating these hashes.

2

There are 2 answers

0
React Dev On

An alternative to this would be to send the image to a blurhashing service and let them do it for you!

const functions = require('firebase-functions');
const crypto = require('crypto');
const path = require('path');
const os = require('os');
const blurhash = require('blurhash_service');

blurhash.config({ apiKey: 'YOUR_API_KEY' });

exports.generateHash = functions.storage.bucket().object().onFinalize( async(uploadedObject) => {
  const filePath = uploadedObject.name;
  const bucket = admin.storage().bucket();
  const file = bucket.file(filePath);

  const randomFileName = crypto.randomBytes(20).toString('hex') + path.extname(filePath);
  const tempLocalFile = path.join(os.tmpdir(), randomFileName);

  await bucket.file(filePath).download({destination: tempLocalFile});

  const { data } = await blurhash.convertImageToHash({
    'pathToImage': filePath,
    'quality':     2,
  });

  console.log(data);
});

// example output
// {
//   message: 'Success',
//   id: '5a298b61-bcfa-4b3a-a647-b984e6e04364',
//   hash: 'AQI|Z}$%~Toy'
// }

Full disclosure, I am the developer behind blur-hash.com. You can register for free on the website or ping me and I can send you a trial api key :)

0
actuallymicah On

Use the blurhash node package in your firebase function. Here's is an example function.

const functions = require('firebase-functions');
const crypto = require('crypto');
const path = require('path');
const os = require('os');
const { Canvas } = require('canvas');
const { loadImage } = require('canvas');
const blurH = require("blurhash");

exports.generateHash = functions.storage.bucket().object().onFinalize( async(uploadedObject) => {
const filePath = uploadedObject.name;
const bucket = admin.storage().bucket();
const file = bucket.file(filePath);

const randomFileName = crypto.randomBytes(20).toString('hex') + path.extname(filePath);
const tempLocalFile = path.join(os.tmpdir(), randomFileName);
  
await bucket.file(filePath).download({destination: tempLocalFile});

const imageWidth = 1000;
const imageHeight = 1000;

const canvas = new Canvas(imageWidth, imageHeight);
const context = canvas.getContext('2d');
const myImg = await loadImage(tempLocalFile);
context.drawImage(myImg, 0, 0);
const imageData = context.getImageData(0, 0, imageWidth, imageHeight);
const hash = blurH.encode(imageData.data, imageWidth, imageHeight, 5, 5);
});

We assumed the image width and height to be 1000 each but for better results extract the dimensions from the metadata of the image and use that instead. You can use ImageMagick as shown in this firebase sample function