How to Boost Image Performance When Changing Brightness

Hello,

I’m loading large images into my Bokeh plot using p.image_rgba. I want to provide users with a way to increase or decrease the brightness of the image.

From my testing, I would subjectively conclude that adjusting the brightness using CustomJS is faster than changing the brightness on the backend.

Is it possible to change the brightness only on the client side and refresh the canvas without involving the backend? Is it possible to update the plot client side without calling source.change.emit()?

Additionally, I noticed the following message in the browser developer console:

Canvas2D: Multiple readback operations using getImageData are faster with the willReadFrequently attribute set to true. See: https://html.spec.whatwg.org/multipage/canvas.html#concept-canvas-will-read-frequently
This message is triggered here:
            _set_image_data_from_buffer(t, e) {
                (0,
                m.assert)(null != this.image_data);
                const i = this._get_or_create_canvas(t)
                  , s = i.getContext("2d")
                  , a = s.getImageData(0, 0, this.image_width[t], this.image_height[t]);
                a.data.set(e),
                s.putImageData(a, 0, 0),
                this.image_data[t] = i
            }

Are there other options to optimize or boost the performance of changing brightness for large images?

P.S. I noticed that in the documentation here: BokehJS — Bokeh 3.7.2 Documentation the “Try on CodePen” button does not work.

Please find attached my sample code where I compare changing brightness on the backend vs the client (using CustomJS). View the Demo here as well.

from bokeh.io import curdoc
from bokeh.layouts import column
from bokeh.models import ColumnDataSource, Slider, CustomJS
from bokeh.plotting import figure
import numpy as np

def create_image_data(height=2048, width = 2048):
    """
    Create a sample RGBA image data.

    Returns:
        np.ndarray: A 2D array of uint32 values representing the image.
    """
    # Generate a dynamic image of size 1024x1024
    
    image_data = np.zeros((height, width, 4), dtype=np.uint8)

    # Fill the image with dynamic pixel values
    for y in range(height):
        for x in range(width):
            image_data[y, x, 0] = x % 256  # Red channel
            image_data[y, x, 1] = y % 256  # Green channel
            image_data[y, x, 2] = (x + y) % 256  # Blue channel
            image_data[y, x, 3] = 255  # Alpha channel (fully opaque)

    # Convert the image to uint32 RGBA format
    image_data_uint32 = (
        (image_data[:, :, 0].astype(np.uint32) << 24) |  # Red
        (image_data[:, :, 1].astype(np.uint32) << 16) |  # Green
        (image_data[:, :, 2].astype(np.uint32) << 8) |   # Blue
        (image_data[:, :, 3].astype(np.uint32))          # Alpha
    )
    return image_data_uint32

def adjust_brightness(image, brightness_factor):
    """
    Adjust the brightness of a uint32 RGBA image.

    Parameters:
        image (np.ndarray): The input image as a 2D array of uint32 values.
        brightness_factor (float): The factor by which to adjust brightness (e.g., 1.0 = no change).

    Returns:
        np.ndarray: The modified image with adjusted brightness.
    """
    # Decode RGBA components from uint32
    r = (image >> 24) & 0xFF
    g = (image >> 16) & 0xFF
    b = (image >> 8) & 0xFF
    a = image & 0xFF  # Alpha channel remains unchanged

    # print (f"Original R: {r[x,y]}, G: {g[x,y]}, B: {b[x,y]}, A: {a[x,y]}")
    # Adjust brightness for RGB channels
    r = np.clip(r * brightness_factor, 0, 255).astype(np.uint8)
    g = np.clip(g * brightness_factor, 0, 255).astype(np.uint8)
    b = np.clip(b * brightness_factor, 0, 255).astype(np.uint8)
    # print (f"Adjusted R: {r[x,y]}, G: {g[x,y]}, B: {b[x,y]}, A: {a[x,y]}")
    # Recombine RGBA components into uint32
    updated_image = (
        (r.astype(np.uint32) << 24) |
        (g.astype(np.uint32) << 16) |
        (b.astype(np.uint32) << 8) |
        a.astype(np.uint32)
    )
    return updated_image


# Initialize the image
img = create_image_data()
backupImg= img.copy()
source = ColumnDataSource({'image': [img]})

# Create the Bokeh figure
p = figure(x_range=(0, 10), y_range=(0, 10))
p.image_rgba(image='image', x=0, y=0, dw=10, dh=10, source=source)

# Create a slider for brightness adjustment
brightness_slider = Slider(start=0.0, end=2.0, step=0.1, value=1.0, title="Brightness")

js_brightness_slider = Slider(start=0.1, end=2.0, step=0.1, value=1.0, title="JS Brightness")

# Define the CustomJS callback
js_brightness_callback = CustomJS(args=dict(source=source), code="""
    const brightness = cb_obj.value;  // Get the brightness factor from the slider
    const data = source.data['image'][0];  // Access the image data (uint32 array)

    // Use a static variable to store the original image data
    if (typeof window.original_image_data === 'undefined') {
        // If it doesn't exist, assign the current data to 'original_image_data'
        window.original_image_data = [...data];  // Create a copy of the data
    }

    const original_data = window.original_image_data;  // Use the original image data

    // Loop through each pixel and adjust brightness
    for (let i = 0; i < data.length; i++) {
        const r = (original_data[i] >> 24) & 0xFF;  // Extract Red
        const g = (original_data[i] >> 16) & 0xFF;  // Extract Green
        const b = (original_data[i] >> 8) & 0xFF;   // Extract Blue
        const a = original_data[i] & 0xFF;          // Extract Alpha

        // Adjust brightness for RGB channels
        const newR = Math.min(255, r * brightness);
        const newG = Math.min(255, g * brightness);
        const newB = Math.min(255, b * brightness);

        // Recombine RGBA components into uint32
        data[i] = (newR << 24) | (newG << 16) | (newB << 8) | a;
    }
    console.log("Brightness adjusted using JS callback");
    source.change.emit();  // Trigger a re-render
""")




# Define the Python callback for the brightness slider
def update_brightness(attr, old, new):
    brightness_factor = brightness_slider.value  # Get the brightness factor
    adjusted_img = adjust_brightness(backupImg.copy(), brightness_factor)
    source.data = {'image': [adjusted_img]}

# Attach the callbacks to the sliders
brightness_slider.on_change('value', update_brightness)
js_brightness_slider.js_on_change('value', js_brightness_callback)

# Add the layout to the document
layout = column(p, 
                js_brightness_slider,
                brightness_slider
                )
curdoc().add_root(layout)

Thank you!

In the Bokeh server case, the values of the CDS columns will always be kept in sync, so there is no way I can think of to avoid the communication overhead with an approach that changes the actual CDS columns.

It might be possible to set “base” image data in the CDS but then actually drive the image_rgba glyph using a CustomJSExpr derived from from that column. I am not aware that anyone has actually ever tried to use CustomJSExpr with anything other than basic 1d array columns, however, so I cannot promise that this approach would currently work.

Failing that, I think all I can suggest is making a feature request around this use case. That could mean expanding where CustomJSExpr functions if it doesn’t already work with image columns, or adding some additional options to image glyphs themselves (or something else entirely).

Canvas2D: Multiple readback operations using getImageData are faster with the willReadFrequently attribute set to true. See: HTML Standard

I’ve never seen this. There might be some optimization we can apply. If you can boil this down to a simple MRE than I’d suggest making a GitHub Issue to investigate.

P.S. I noticed that in the documentation here: BokehJS — Bokeh 3.7.2 Documentation the “Try on CodePen” button does not work.

Can you submit a GitHub Issue?

Your demo seems to run pretty smoothly when using the JS callback. Are you expecting larger images than in your demo?

chrome-capture-2025-4-23 (1)

Thanks @Bryan and @Maxxner for your reply.

I created the following github issues

@Maxxner I compared my sample with a pure HTML JavaScript page that uses a base64 image to change the brightness. It would be great to achieve the same smoothness when adjusting the brightness using Bokeh.

For simplicity, I have attached the HTML/JavaScript sample code with a much smaller base64 image to avoid any file size issues.

  1. save sample.html and sample.base64Data.js
  2. python3 -m http.server 8000
  3. run http://localhost:8000/sample.html

sample.html:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Adjust Image Brightness</title>
  <style>
    body {
      font-family: Arial, sans-serif;
      text-align: center;
      margin: 20px;
    }
    canvas {
      border: 1px solid #ccc;
      margin-top: 20px;
    }
    input[type="range"] {
      width: 300px;
    }
  </style>
</head>
<body>
  <input type="range" id="brightness-slider" min="0" max="2" step="0.01" value="1">
  <label for="brightness-slider">Brightness</label>
  <br>
  <canvas id="image-canvas"></canvas>

  <script type="module">
    import base64String from './sample.base64Data.js';


    const base64Image = `data:image/jpeg;base64,${base64String}`;

    // Base64 image string (replace this with your own base64 image)

    // Get references to the canvas and slider
    const canvas = document.getElementById('image-canvas');
    const ctx = canvas.getContext('2d');
    const slider = document.getElementById('brightness-slider');

    // Create an image element
    const img = new Image();
    img.src = base64Image;

    // Draw the image on the canvas when it loads
    img.onload = () => {
      canvas.width = img.width;
      canvas.height = img.height;
      ctx.drawImage(img, 0, 0);
    };

    // Function to adjust brightness
    function adjustBrightness(brightness) {
      // Clear the canvas
      ctx.clearRect(0, 0, canvas.width, canvas.height);

      // Draw the original image
      ctx.drawImage(img, 0, 0);

      // Get the image data
      const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
      const data = imageData.data;

      // Loop through each pixel and adjust brightness
      for (let i = 0; i < data.length; i += 4) {
        data[i] = data[i] * brightness;     // Red
        data[i + 1] = data[i + 1] * brightness; // Green
        data[i + 2] = data[i + 2] * brightness; // Blue
        // Alpha (data[i + 3]) remains unchanged
      }

      // Put the adjusted image data back on the canvas
      ctx.putImageData(imageData, 0, 0);
    }

    // Add an event listener to the slider
    slider.addEventListener('input', (event) => {
      const brightness = parseFloat(event.target.value);
      adjustBrightness(brightness);
    });
  </script>
</body>
</html>

sample.base64Data.js:

export const base64String = "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";
export default base64String;