Pixels, size, and colors

This tutorial illustrates how we deal with Arbalet's height, width and pixel colors. We'll create a rainbow picture with a white frame as an example.


In [ ]:
from arbalet.core import Arbalet
from __future__ import print_function

In [ ]:
arbalet = Arbalet(hardware=False)

In the first tutorial we changed the color of all pixels at a time, but setting a new color to a single pixel is achieved with Model.set_pixel(). Let's draw a center horizontal line in cyan above the pink background. We browse all columns (from 0 to the end) with the same row number (the middle of the height) and change the color of each pixel:


In [ ]:
for w in range(arbalet.width):
    arbalet.user_model.set_pixel(int(arbalet.height/2), w, 'cyan')

In the previous cell, we had to cast height/2 into integer because the result 15/2 = 7.5 is a decimal value (float). Note: in python 2.7 dividing an integer with an integer already results in an integer but it is good to explicit the cast though.

Now let's print in the terminal what color are all the pixels on the centered vertical column this time, getting a pixel color is achieved with get_pixel():


In [ ]:
for h in range(arbalet.height):
    w = int(arbalet.width/2)
    print("Color of pixel row ", h, 'column', w, 'is', arbalet.user_model.get_pixel(h, w))

We first observe that the color are encoded as a 3-tuple. Each of them represents of much red, green, and blue are composing this color. Each color component is a float value between 0.0 and 1.0.

You can think these values as 3 percentages. For instance, the color [1.0, 0.5, 0.0] means 100% of red, 50% of green, 0% of blue. This color is orange, do you wanna try it out?


In [ ]:
for w in range(arbalet.width):
    arbalet.user_model.set_pixel(int(arbalet.height/2)-1, w, [1.0, 0.5, 0.0])

We have been using set_pixel with 2 different color representation: a color name (a string, e.g. "cyan") or a list of red, green and blue percentages (a 3-tuple, e.g. [0, 1, 1]). The SDK actually encodes colors using the latest. It is compatible with color name for convenience but the SDK always resolves the name and stores only its corresponding 3-tuple, like our "cyan" row 6 column 5 that we have been printing here above, which has been converted into [0, 1, 1].

The magic with colors is that we can add them or multiply them by some percentage. Have you ever dreamed of seeing what is 50% of cyan and 50% of orange? Let's multiply the middle vertical line by 0.5:


In [ ]:
for h in range(arbalet.height):
    color = arbalet.user_model.get_pixel(h, int(arbalet.width/2))
    color = color*0.5
    arbalet.user_model.set_pixel(h, int(arbalet.width/2), color)

Multiplying a color by 50% actually just makes it darker.

Now let's switch off the LEDs. This amounts to setting color "black" (e.g. 3-tuple [0, 0, 0]) to all:


In [ ]:
arbalet.user_model.set_all('black')

The red, green blue (RGB) color representation though, even the most used within computers, is not the more natural for humans. What if I want to display a rainbow for instance? This is trivially achievable using another color representation: the hue, saturation, value (HSV) representation. In this representation:

  • Hue is the actual color from 0.0 (red) to 1.0 (also red because it's a wheel). 0.5 is cyan, 0.25 is green, 0.75 is purple...
  • Saturation is its intensity from 0.0 to 1.0
  • Value is its brightness from 0.0 (dark = black) to 1.0 (bright)

Browsing all the colors of a rainbow amounts to browse only hue from 0.0 to 1.0 with saturation = 1.0 and value = 1.0. While with RGB we would need a complex combination of the 3 values red, green, blue.

In arbalet, the function hsv_to_rgb allows to take an HSV representation in input. Let's display a full amtrix of red (i.e. hue = 0):


In [ ]:
from arbalet.colors import hsv_to_rgb
arbalet.user_model.set_all(hsv_to_rgb(0, 1, 1))  # HSV color [0, 1, 1]

In [ ]:
arbalet.user_model.set_all('black')

Since hue is a wheel, [1, 1, 1] is the same as [0, 1, 1], a perfect red:


In [ ]:
arbalet.user_model.set_all(hsv_to_rgb(1, 1, 1))  # HSV color [1, 1, 1]

Let's switch off the LEDs again, and try to display a rainbow by changing only the hue value:


In [ ]:
for w in range(arbalet.width):
    hue = float(w)/arbalet.height
    saturation = 1
    value = 1
    print('Column', w, 'has hue =', hue)
    for h in range(arbalet.height):
        arbalet.user_model.set_pixel(h, w, hsv_to_rgb(hue, saturation, value))

Geometry: height and width

It is sometimes difficult to remember what side the height and the width are, since some programs will run in landscape, some others in portrait, some tables may be square, ... Exchanging height and width will lead to exceptions or less understandable bugs. The rule of the thumb is that whatever its size is, any Arbalet is vertical (in portrait mode), thus:

  • height represents rows
  • width represents columns
  • height >= width

In Arbalet apps, coordinates are often called h and w instead of terms row and column and so that they can be easily associated with width and height. But these name might be changed in the future for a better understanding.

The center of the coordinate system, is on the top left in the default configuration, let's draw the point [0, 0] in black to identify the origin:


In [ ]:
arbalet.user_model.set_pixel(0, 0, 'black')

To practice with colors and geometry, let's draw now a white frame to our previous rainbow. We are going to draw 4 borders: top, bottom, left and right.


In [ ]:
# For the top border, we set all columns of the first row (h = 0) to white
h = 0
for w in range(arbalet.width):
    arbalet.user_model.set_pixel(h, w, 'white')

In [ ]:
# For the bottom border, we set all columns of the last row (h = Arbalet's height minus 1) to white
h = arbalet.height-1
for w in range(arbalet.width):
    arbalet.user_model.set_pixel(h, w, 'white')

In [ ]:
# For the left border, we set all columns of the first column (w = 0) to white
w = 0
for h in range(arbalet.height):
    arbalet.user_model.set_pixel(h, w, 'white')

In [ ]:
# For the right border, we set all columns of the last column (w = Arbalet's width minus 1) to white
w = arbalet.width-1
for h in range(arbalet.height):
    arbalet.user_model.set_pixel(h, w, 'white')

Congratulations, you finished this tutorial about pixels, size and color operations, don't forget to close resources cleanly before leaving, see you in the next tuto!


In [ ]:
arbalet.close()