In the first part of this series, we explored the foundational principles of color correction and explained the importance of working with colors when editing images. In this second part, we dive into more advanced concepts, such as the differences between the RGB and LAB color models, as well as the process of extracting palettes from images and using them to create unique visual styles.

As we saw in the previous article, some colors and palettes displayed differently than we initially expected. This is due to the fact that we used the simplest tools — the RGB model and Euclidean distance calculation. For more advanced processes, however, we need something a little more sophisticated.

But first, a bit of theory.

The RGB Model

The two most popular color models — RGB and LAB — are fundamentally different in how they represent and process colors.

The RGB model uses the Red, Green, and Blue channels to represent colors. Each channel can have values ranging from 0 to 255, resulting in over 16 million possible colors. This model is ideal for working with images displayed on screens, as computer monitors, TVs, and smartphones all use this model.

RGB Key Features:

The LAB Model

The LAB model represents color in three channels: L (lightness), A (a color spectrum from green to red), and B (a color spectrum from blue to yellow). This model is closer to how humans perceive color and accounts for both the color and the brightness of an image.

LAB Key Features:

In the LAB model, brightness is separated from color, making it possible to adjust the visual parameters of an image without impacting its color balance. This results in more accurate and natural color corrections.

Color Calculation Algorithm and Using LAB

To calculate and apply colors, our application utilizes the colorjs.io library, which efficiently works with colors in the LAB model. Integrating this library into the project allows us to perform accurate calculations that help achieve the desired results when processing images.

Here’s an example of the code used in our app to work with colors in the LAB model:

// Sample code for using colorjs.io in LAB model
const findClosestColor = (r, g, b) => {
  if (mode === "lab") {
    const color1 = new Color({ coords: [r / 255, g / 255, b / 255], space: "srgb" }).to("lab");
    let minDist = Infinity;
    let closest = [r, g, b];
    for (let i = 0; i < paletteLAB.length; i++) {
      const color2 = paletteLAB[i];
      const dist = color1.distance(color2);  // Calculating the distance between colors in LAB space
      if (dist < minDist) {
        minDist = dist;
        closest = palette[i];
      }
    }
    return closest;
  } else {
    // RGB calculations
    let minDist = Infinity;
    let closest = [r, g, b];
    for (let [pr, pg, pb] of palette) {
      const dist = Math.sqrt((r - pr) ** 2 + (g - pg) ** 2 + (b - pb) ** 2);
      if (dist < minDist) {
        minDist = dist;
        closest = [pr, pg, pb];
      }
    }
    return closest;
  }
};

This block of code handles the process of finding the closest color in a given palette by calculating the color distance between the target color and the colors in the palette. The calculation method is based on the selected mode, either RGB or LAB.

How This Works in Practice:

In professional color grading and image processing, matching colors based on LAB is highly advantageous because it mimics how the human eye perceives colors, allowing for more natural and visually appealing results. Using this method, the application can achieve better consistency when applying color palettes to images, making it easier to match colors to desired styles.

Optimization Challenge: LAB Computation

Working with the LAB color model requires significantly more computational resources than working with RGB. This is because, in LAB, each color is represented not just by three channels (RGB), but also involves additional calculations to obtain the color coordinates and their distances.

To optimize this process, we introduced palette caching to avoid redundant calculations. Palettes created in LAB mode are stored in memory, and if the image has not changed, the system will not recalculate the palette. This helps to significantly speed up the process, especially when applying palettes to multiple images, by reusing the precomputed data without having to perform the costly calculations again.

// Optimization with palette caching
const extractPalette = () => {
  if (!image || image === lastExtractedImageRef.current) return;  // Check for changes

  const img = new Image();
  img.src = image;
  img.onload = () => {
    const canvas = document.createElement("canvas");
    const ctx = canvas.getContext("2d");
    canvas.width = img.width;
    canvas.height = img.height;
    ctx.drawImage(img, 0, 0);

    const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height).data;
    const colorMap = new Map();

    // Creating a color map
    for (let i = 0; i < imageData.length; i += 4) {
      const key = `${imageData[i]},${imageData[i + 1]},${imageData[i + 2]}`;
      colorMap.set(key, (colorMap.get(key) || 0) + 1);
    }

    const sorted = Array.from(colorMap.entries()).sort((a, b) => b[1] - a[1]);
    const topRaw = sorted.map(([str]) => str.split(",").map(Number));
    const topColors = filterColors(topRaw).slice(0, 10);

    const randomId = Math.floor(10000 + Math.random() * 90000);
    const name = `Palette_${randomId}`;

    setPalettes(prev => ({ ...prev, [name]: topColors }));
    setSelectedPalette(name);
    lastExtractedImageRef.current = image;  // Saving cache
  };
};

This improvement helps speed up the application’s performance, especially when processing multiple similar images repeatedly.

Palette Extraction and Application

Palette extraction is the process of identifying the most characteristic and vibrant colors from an image, which can then be used to create filters or visual styles. We extract the top-10 colors (ensuring diversity) using an algorithm that takes into account both the frequency of color occurrence and its luminance, ensuring that the palette doesn’t include colors that are too similar to each other.

The main mechanism for extracting the palette is as follows:

const extractPalette = () => {
  // Check for image changes
  if (!image || image === lastExtractedImageRef.current) return;

  const img = new Image();
  img.src = image;
  img.onload = () => {
    const canvas = document.createElement("canvas");
    const ctx = canvas.getContext("2d");
    canvas.width = img.width;
    canvas.height = img.height;
    ctx.drawImage(img, 0, 0);

    const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height).data;
    const colorMap = new Map();

    // Counting the frequency of occurrence of each color
    for (let i = 0; i < imageData.length; i += 4) {
      const key = `${imageData[i]},${imageData[i + 1]},${imageData[i + 2]}`;
      colorMap.set(key, (colorMap.get(key) || 0) + 1);
    }

    // Sorting by frequency and filtering similar colors
    const sorted = Array.from(colorMap.entries()).sort((a, b) => b[1] - a[1]);
    const topRaw = sorted.map(([str]) => str.split(",").map(Number));
    const topColors = filterColors(topRaw).slice(0, 10);  // Segmentation and filtering

    const randomId = Math.floor(10000 + Math.random() * 90000);
    const name = `Palette_${randomId}`;

    // Saving the palette
    setPalettes(prev => ({ ...prev, [name]: topColors }));
    setSelectedPalette(name);
    lastExtractedImageRef.current = image;
  };
};

This code is used to extract a palette from an image, where colors are filtered by brightness and difference. With this mechanism, you can quickly gather the most representative colors for further application.

We added automatic name generation for extracted palettes to avoid cluttering the user interface with unnecessary buttons.

Custom Palettes

One of the tools in our application is the custom palette creation block. This feature allows users to create palettes suitable for various projects, providing full control over colors.

The process starts with selecting a color through the HexColorPicker interface. The user can choose a color from the image or an external source. Each selected color is automatically added to the user’s color list, from which a palette is created.

After selecting a few colors, the palette can be saved with a custom name. Later, it can be applied to the uploaded image to achieve a specific visual style.

Color Neutralization with Color Palettes

Color neutralization is an important aspect of color grading and plays a critical role in film post-production. It is the process where individual scenes or shots, taken under different lighting conditions, can be brought to a common color tone. This is usually done to ensure a unified visual style throughout the film and to make the scenes visually consistent, regardless of the shooting conditions.

Let’s explore how palettes are applied through two iconic examples of cinematic visual language.

Palettes in the Movie “The Matrix”

The film “The Matrix” (1999), directed by the Wachowski siblings, became iconic due to its unique visual style, which includes a specific greenish color palette. This color style was designed to visualize the digital reality in which the main characters exist. The visual effects of the film were created to convey the feeling of virtuality, where cold and mechanical colors dominate.

Main Colors: Green, black, metallic.

Palette Application:

Palettes in Wes Anderson’s Works

Wes Anderson is known for his vivid, colorful, and symmetrical visual solutions. His works, such as “The Grand Budapest Hotel”, “Moonrise Kingdom”, and “The French Dispatch”, all have his signature style, which features rich pastel colors and carefully selected palettes. One key element of his films is the harmony and contrast in color design.

Main Colors: Pastel shades, bright contrasting colors, red, yellow, turquoise, green.

Palette Application:

Now that we can extract palettes from references, it’s time for some experimentation!

Since we are using palettes limited in the number of colors and running our application in the browser (which means using limited computational resources), we shouldn’t expect outstanding results. However, this is enough to demonstrate the core principles of how palettes work in color grading.

Here are examples of processed images in both RGB and LAB modes.

Extracting and applying palettes is a crucial aspect of creating visual styles, such as the “Matrix” style, Wes Anderson’s, or Denis Villeneuve’s. Our app provides a visual tool for working with colors that allows users to extract palettes, apply them, and create unique visual effects. With it, you can easily stylize images and create visual looks similar to those seen in popular films.

In the final part, we will focus on fine-tuning and optimizing colors.