How to make an art website load at lightning speed using Cloudinary and Python

I collaborated with Ciaboga Studio to work with them on their new website. Ciaboga is an art studio and their website is packed with high quality images. The first issue I encountered was slow loading times and the second is that they wanted their images watermarked.

The website is hosted on Squarespace with more than 100 high-quality images (usually 15Mb per image), and apparently, when you upload an image, they don’t optimize it. I wanted to find a solution that can scale and work on all the website images.

I wanted to try Cloudinary service for a while now, and now I got a real chance to work with it. I signed up and did a few trials on their dashboard, and I realized they have what I need, and it can be faster than manipulating images by myself.

After playing with the Media Library, I moved into Transformations, which is the core of Cloudinary service, with the Transformations I was able to create a ‘template’ of particular transformation I want to make to my images.

I can use the full URL like this:
http://res.cloudinary.com/ciabogastudio/image/upload/if_ar_gt_1:1/c_scale,w_1024/c_scale,h_1024/if_else/q_86/if_end/c_scale,co_rgb:000000,g_south_east,l_logo-ciaboga,w_100,x_10,y_10/sample.jpg

Or, the named transformation:
http://res.cloudinary.com/ciabogastudio/image/upload/t_ciaboga_transformation_black/sample.jpg

You will immediately see your transformation results.


Transformations are necessary, especially for my use case where I wanted to change more than 100 pictures at the same time. To do so, I downloaded Cloudinary Python SDK and started coding (Using Python 3)

Using Cloudinary SDK is simple as downloading the pip package, configure your Cloudinary credentials, and upload an image:

$ pip install cloudinary
import cloudinary
# Initiate cloudinary sdk
cloudinary.config(
cloud_name='sample_user',
api_key='111122223333444'
api_secret='abcDEFgHij_KLMNopqrStuvWxYz'
)
# Upload an image
cloudinary.uploader.upload("my_picture.jpg")

With the help of Yakir, from Cloudinary support team, I was able to create a custom transformation for my exact needs, easily and seamlessly.
The transformation did the following:

  • If the image is landscape, it scales the width to 1024 pixels
  • If the image is portrait, it scales the height to 1024 pixels
  • Lower jpeg quality to 86%
  • Add an overlay image, Ciaboga studio logo, as a watermark on the lower right corner and ajust the size

I used Python’s os.walk to iterate through all of the website images I had locally on my computer and apply the transformation to them:

for root, subdirs, files in os.walk(input_directory):
for f in files:
if f.lower().endswith('.jpg') or f.lower().endswith('.png'):
image_path = os.path.join(root, f)
cloudinary.uploader.upload(image_path, named_transformation=['resize_and_watermark'])

All the images were soon available and ready to use from Cloudinary dashboard.

I encountered two problems at this stage

  1. The watermark on some of the images was not visible because it was black and the bottom corner was dark
  2. Squarespace won’t let me use image urls, and I had to upload the images directly to their service
Ciaboga image with a black corner (you can try to spot the black watermark)

Dealing with the colors of the corners was a little bit complicated. I used Pillow library for Python to analyze the image.

def get_main_color(image_path):
img = Image.open(image_path)
width = img.size[0]
height = img.size[1]
# Crop the south east corner of the image
img_cropped = img.crop(
(
width - 200,
height - 150,
width,
height
)
)
colors = img_cropped.getcolors(1024000)
max_occurence, most_present = 0, 0
for c in colors:
if c[0] > max_occurence:
(max_occurence, most_present) = c
return most_present

This function returned the most significant color, which in the above picture was (13, 8, 4) — Close to (0, 0, 0), which means black. It’s easy to it’s closer to black with the eyes, but I needed an easier way to do this with code. I converted the RGB colors to grayscale by computing the luminance with the following formula:

Y = 0.2126*R + 0.7152*G + 0.0722*B

Or in Python:

def get_watermark_color(image_path):
main_color = get_main_color(image_path)
Y = 0.2126 * main_color[0] + 0.7152 * main_color[1] + 0.0722 * main_color[2]
return 'white' if Y < 128 else 'black'
# Notice that this code returns 'white' if the corner is black and returns 'black' if the corner is white, because the name of the function is: get_watermark_color

Again, using Cloudinary support team, I was able to create two different transformations, one with the original black logo, and one that changed the logo to white (With the same black logo image). I changed the code above to something like this:

for f in files:
if f.lower().endswith('.jpg') or f.lower().endswith('.png'):
image_path = os.path.join(root, f)
watermark_color = get_watermark_color(image_path)
cloudinary_upload = None
if
watermark_color is 'black':
cloudinary_upload = cloudinary.uploader.upload(image_path, transformation=[black_watermark_transformation], use_filename=True, unique_filename=False)
        elif watermark_color is 'white':
cloudinary_upload = cloudinary.uploader.upload(image_path, transformation=[black_watermark_transformation], use_filename=True, unique_filename=False)

Dealing with Squarespace upload limitation was a bit easier, I had to download the image right after I upload it to Cloudinary. This made simple when using Cloudinary SDK because the upload function returns JSON as follows:

{
'public_id': '3wahoos',
'version': 1503588453,
'signature': '79cde65b42d279d432ff2bb0888f60ef43030c36',
'width': 1024,
'height': 768,
'format': 'jpg',
'resource_type': 'image',
'created_at': '2017-08-24T15:27:33Z',
'tags': [],
'bytes': 128085,
'type': 'upload',
'etag': 'e215c8853ded0687f47e3efde4395d73',
'url': 'http://res.cloudinary.com/username/image/upload/v1503588453/3wahoos.jpg',
'secure_url': 'https://res.cloudinary.com/username/image/upload/v1503588453/3wahoos.jpg',
'overwritten': True,
'original_filename': '3wahoos'
}

I could easily grab the ‘url’ value and download the image using Python’s urllib.request.urlretrieve (Didn’t want to add more dependencies using requests). I would recommend using their CDN instead of downloading if possible

def download_transformed_image(output_folder, output_file, cloudinary_upload):
output_path = os.path.join(output_folder, output_file)
transformed_url = cloudinary_upload['url']
print('Downloaded image')

The result

White watermark when corner is dark
Black watermark when corner is bright

Summary

I added an external configuration file to the project and also added some command line options. You can find the project here:

Using Cloudinary SDK was incredibly easy and saved me a lot of time handling image scaling and overlaying. Cloudinary support team was there for me 100% of the time and helped me get the best out of their service.

Ciaboga studio website is now much faster and works great, you can visit it here: www.ciabogastudio.com and check out their beautiful art.

You are more than welcome to download my code, submit issues and let me know how it went for you!


Michelle Brener @ Ciaboga Studio

Yakir Perlin @ Cloudinary Support Team