How to Create Thumbnail Images Using Face Detection with Cloudinary

Creating a gallery of images on a page can be trivial, but what if you don’t have control over how the images are created? Ending up with different sizes and formats can turn a good looking gallery into a mess. Instead, we can use Cloudinary image transformations and Face Detection to make sure we’re always optimally showing our media.

Table of Contents

YouTube Preview
View on YouTube

What is Cloudinary?

Cloudinary is a media platform that has a wide variety of features to host, manage, and deliver images and videos.

Part of that feature set includes being able to transform those images and videos, such as being able to resize, optimize, and crop them.

The issue with cropping though is it can be tricky depending on how much control over your images you have. You need to be confident that all images follow a typical layout, otherwise you may end up with awkward cropping or even cropping people out of an image unintentionally.

What is Face Detection?

Instead of cropping and hoping for the best, we can intelligently crop out images using Face Detection.

Cloudinary can determine if there’s a face in the image, figure out the coordinates of that face, and make sure the crop is focused on that location.

This is perfect if for instance, we have a gallery of people where we might not have much control (or no control) over the composition of those images.

What are we going to build?

We’re going to use Cloudinary to create thumbnails of our images that automatically crop based on where someone’s face is detected in that image.

To do this, we’ll use a demo Next.js app I created that has some sample content from the Stranger Things Wiki including a small image gallery with names.

Then, we’ll take advantage of the cloudinary-build-url package to configure our Cloudinary transformations and apply them to our images.

In order to follow along you’ll need a free Cloudinary account.

Disclaimer: I work for Cloudinary as a Developer Experience Engineer.

Step 0: Creating a new Next.js app from a demo starter

We’re going to start off with a new Next.js app using a Stranger Things Wiki demo I created.

Inside of your terminal, run:

yarn create next-app my-stranger-wiki -e https://github.com/colbyfayock/demo-stranger-things-wiki
# or
npx create-next-app my-stranger-wiki -e https://github.com/colbyfayock/demo-stranger-things-wiki

Note: feel free to use a different value than my-stranger-wiki as your project name!

Once installation has finished, you can navigate to that directory and start up your development server:

cd my-stranger-wiki

yarn dev
# or
npm run dev

And once loaded, you should now be able to open up your new app at http://localhost:3000!

Website with grid of characters from Stranger Things
New Next.js app with Stranger Things characters

Step 1: Installing and configuring Cloudinary Build URL from npm

The way that the Cloudinary image API works is it allows us to construct URLs with configured parameters allowing us to specify what settings or transformations we’d like to use.

https://res.cloudinary.com/demo/image/upload/c_crop,h_200,w_300/sample.jpg

In the above, this link from the Transformations demo is using the Crop feature along with a specified width and height. Cloudinary takes the original image, performs those transformations, and delivers the final image to the browser.

While we can do this manually using the URL like above, it can be a little tedious to manage all of the different settings in a long string. Instead, we can use Cloudinary Build URL to configure our URL right in JavaScript.

To get started, let’s install Cloudinary Build URL:

yarn add cloudinary-build-url
# or
npm install cloudinary-build-url

Next, we want to import our new dependencies into our application.

At the top of our homepage in pages/index.js add:

import { setConfig, buildImageUrl } from 'cloudinary-build-url'

Using setConfig we’ll be able to configure our Cloudinary account and using buildImageUrl we’ll be able to construct our image URL by passing in parameters.

To configure our account, we’ll need to make sure we have our Cloudinary Cloud Name.

Once logged into Cloudinary, you can find your Cloud Name right on the dashboard.

Highlighting location to find Cloud Name
Cloudinary Cloud Name

Note: Using my Cloud Name in the image above won’t work, you’ll need to make sure you use your own for the remainder of the tutorial!

Now with our Cloud Name, right above the Home function declaration let’s use setConfig to specify that Cloud Name:

setConfig({
  cloudName: '[Your Cloud Name]'
});

And next, we’ll be able to now use the buildImageUrl function to create our images!

Follow along with the commit!

In the app we just set up, we’re currently serving our images from imgur.com which is just a basic way for us to have our images publicly available, but we want to be able to take advantage of Cloudinary.

Tip: you don’t need to have your images already externally hosted to use Cloudinary, you can simply upload your images directly to Cloudinary’s asset manager!

In Step 3, we’ll get to how we can use Cloudinary to transform our images, but before we get there, we simply benefit from serving our images from Cloudinary such as automated Image Optimization.

Note: typically you need to pass in parameters for specific optimizations, but Cloudinary Build URL appends a few by default!

So to start, let’s simply configure each of our images to be served from Cloudinary.

Inside of our map statement which is looping through all of our characters, add the following before the return statement:

const image = buildImageUrl(character.image, {
  cloud: {
    storageType: 'fetch'
  }
});
console.log(image);

Here we’re passing in our character’s image URL along with a storage type of “fetch” which is telling Cloudinary we want it to download our external image and serve it from the Cloudinary CDN.

If we look our browser’s web console, we should see a few URLs logged out.

Browser with web console showing Cloudinary URLs
Image URLs generated for Cloudinary

You should notice all of the URLs are from Cloudinary, and if we open any of them, it should look exactly the same at this point!

Now we can swap those image URLs into our UI.

Update the image tag to use our new image variable:

<img width="280" src={image} alt={character.title} />

If we look in the browser again, we should still notice no changes.

Stranger Things characters with no changes
Unchanged image grid

The cool thing though, is we’re getting those mentioned optimizations out of the box, including converting those images to a better image compression method such as AVIF if your browser supports it!

Highlighted network tab showing images of type AVIF
Images being served as AVIF

This will help our images load faster for our visitors!

Next, we’ll learn how to now transform those images using Face Detection to make sure we’re always clearly showing our character’s faces.

Follow along with the commit!

Step 3: Automatically creating cropped thumbnails based on detected faces with Cloudinary transformations

Part of the issue with our current solution is our images are coming in a variety of sizes. This makes the UI look disorganized, but it also means some of the characters appear bigger than others due to how the images are resized.

Now that we’re hooked into Cloudinary for serving our images, we can take advantage of transformations to shape our images exactly how we want.

Particularly, we’re going to do 2 things:

  • Resize and crop our images to be square
  • Use Face Detection to automatically center the image on the character’s face

To get started, let’s first crop our images. We can do this by creating a new transformations property in our buildImageUrl options argument, right after our cloud settings:

const image = buildImageUrl(character.image, {
  cloud: {
    storageType: 'fetch'
  },
  transformations: {
    resize: {
      width: 280,
      height: 280,
    }
  }
});

This will resize our images to a 280px square.

If we look in our browser though, we’ll notice all of our images are each a square, but they are now skewed, which isn’t a great look! ????

Character images showing as square but skewed in ratio
Skewed character images due to resizing

To fix this, we can add the fill property which will tell Cloudinary to use the image to fill the space with the right ratio.

transformations: {
  resize: {
    type: 'fill',
    width: 280,
    height: 280,
  }
}

Which works a lot better.

Character images showing as square with correct aspect ratio inside frame
Images with correct aspect ratio inside cropped frame

But another issue we see, is our character’s faces don’t seem to be consistently shown in the images. Particularly, Eleven is getting cut off!

To fix this, we can use the gravity property which will allow us to tell Cloudinary to find the face in the image, then center the crop based on that face.

transformations: {
  resize: {
    type: 'fill',
    width: 280,
    height: 280,
  },
  gravity: 'face'
}
Character images showing square with faces not cut off in frame
Character faces centered in image

The cool thing is we can even take more control of this, where if we wanted to make sure that we’re only cropping on the face itself, to be even more consistent, we could use a different resize “type”, such as:

transformations: {
  resize: {
    type: 'thumb',
    width: 280,
    height: 280,
  },
  gravity: 'face'
}

Which will then give us a crop of the face itself in the image frame!

Character images cropped to the character's face
Images cropped zoomed in to face

What else can we do?

Adjust image cropping with zoom and other transformations

We only covered a few basic transformations, but there are a wide variety of other options we can use to manage our images.

For instance, we can use the zoom transformation to control how close to the character’s face we zoom in on the thumbnail.

Image Transformations for Developers

Add dynamic text on top of images

In addition to working with the original image, we can add text on top of that image. That’s how we can do things like create dynamically generated social media images based on a page title or text.

Add a text to an image