Scopesยถ

Introductionยถ

Scopes refers to a set of nodes:

  • ColorStats doing the color analysis of a given textures and storing it into a data block

  • DrawWaveform and DrawHistogram reading this data block and representing this data in a fancy way

This document explains how to work with them, and in particular how to exploit the data block or build your own representation of the data.

Scopes

This screenshot comes from the scopes scene from the nope.demos repository.

Requirementsยถ

ColorStats relies on compute shaders and SSBO, which are available pretty much anywhere nowadays, but there are a few exceptions to keep in mind. MacOS OpenGL support is not sufficient and you will need to enable the Metal backend (through Vulkan/MoltenVK) to benefit from it. Also, some Android devices officially support them, but experience showed that reality begs to disagree.

So while effort has been made such that ColorStats runs as fast as possible and on all backends, your mileage may vary.

DrawHistogramยถ

The histogram (bottom-right in the screenshot) is a 2-dimensional representation of the distribution of color in the input texture:

  • the x-axis represents the intensity of the color: absence of light on the left, maximum light on the right

  • the y-axis represents how much of each intensity is present in the texture

Display the histogram of a media
Code
Graph
from pynopegl_utils.misc import load_media

import pynopegl as ngl


@ngl.scene()
def histogram(cfg: ngl.SceneCfg):
    image = load_media("rooster")

    stats = ngl.ColorStats(texture=ngl.Texture2D(data_src=ngl.Media(image.filename)))
    return ngl.DrawHistogram(stats, mode="luma_only")
graph

DrawWaveformยถ

The waveform spectrum (top-right and bottom-left in the screenshot) is a 3-dimensional representation of the distribution of color in the input texture:

  • the x-axis is spatial: each column of pixels in the waveform is a mini-histogram of the same column of pixels on the input texture

  • the y-axis represents the intensity of the color: absence of light on the bottom maximum light on the top; it is equivalent to the y-axis in DrawHistogram

  • the z-axis represents how much of each intensity is present, and is communicated using the color intensity where black means 0 intensity and white the maximum intensity; it is an alternative representation of the y-axis in DrawHistogram

A simple way of understanding the representation is to consider that every column of pixels is like looking at an histogram from above (a peak in the histogram would be a bright color in the waveform)

Display the waveform spectrum of a media
Code
Graph
from pynopegl_utils.misc import load_media

import pynopegl as ngl


@ngl.scene()
def waveform(cfg: ngl.SceneCfg):
    image = load_media("rooster")

    stats = ngl.ColorStats(texture=ngl.Texture2D(data_src=ngl.Media(image.filename)))
    return ngl.DrawWaveform(stats, mode="luma_only")
graph

ColorStatsยถ

The ColorStats interface is essentially the same as a Block. This means that in addition of being read by DrawHistogram and DrawWaveform, it can be attached to Draw vertex or fragment resources, or Compute resources, and read in the corresponding shaders.

The following fields are accessible from the shader:

Name

Type

Length

Description

max_rgb

uvec2

Single

Maximum RGB value of summary and data

max_luma

uvec2

Single

Maximum luma value of summary and data

depth

uint

Single

Bit depth (256 for 8-bit, 1024 for 10-bit, โ€ฆ)

length_minus1

uint

Single

Length of the image minus 1

summary

uivec4

depth

Global histogram data

data

uivec4

depth ร— length

length rows of histogram of size depth (waveform data)

Additional details:

  • We use the term โ€œlengthโ€ instead of โ€œwidthโ€ to because in the future it may correspond to the height.

  • summary and data are 4-component long for R, G, B and luma