Skip to content

Commit 650f46e

Browse files
committed
Convert to TileGrid to avoid alpha blend. Not much faster
1 parent 674a016 commit 650f46e

1 file changed

Lines changed: 88 additions & 57 deletions

File tree

pycamera_amg88xx/code.py

Lines changed: 88 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -50,39 +50,72 @@
5050
SENSOR_MAX_C = 80
5151

5252
# Allocate all buffers
53-
thermal_overlay = displayio.Bitmap(pycam.camera.width, pycam.camera.height, 65535)
54-
combined_view = displayio.Bitmap(pycam.camera.width, pycam.camera.height, 65535)
53+
THERMAL_BLOCK_SIZE = 30 # Prefer even numbers
54+
THERMAL_COLORS = 64
55+
THERMAL_FADE = 0.1
5556
thermal_raw = displayio.Bitmap(SENSOR_SIZE_X,SENSOR_SIZE_Y,65535)
56-
57-
# Without interpolation, thermal_mapped is the same size
58-
thermal_mapped = displayio.Bitmap(thermal_raw.width,thermal_raw.height,65535)
59-
60-
# Build a color lookup table that covers the color range commonly
57+
thermal_blockmap = displayio.Bitmap(THERMAL_COLORS*THERMAL_BLOCK_SIZE,THERMAL_BLOCK_SIZE,THERMAL_COLORS)
58+
thermal_palette = displayio.Palette(THERMAL_COLORS, dither=False)
59+
thermal_grid = displayio.TileGrid(bitmap=thermal_blockmap,
60+
pixel_shader=thermal_palette,
61+
width = pycam.display.width//THERMAL_BLOCK_SIZE,
62+
height = pycam.display.height//THERMAL_BLOCK_SIZE,
63+
tile_width = THERMAL_BLOCK_SIZE,
64+
tile_height = THERMAL_BLOCK_SIZE)
65+
66+
viewfinder_bitmap = displayio.Bitmap(pycam.camera.width, pycam.camera.height, 65535)
67+
viewfinder_grid = displayio.TileGrid(viewfinder_bitmap,
68+
pixel_shader=displayio.ColorConverter(input_colorspace=displayio.Colorspace.RGB565_SWAPPED),
69+
x = (pycam.display.width-pycam.camera.width)//2,
70+
y = (pycam.display.height-pycam.camera.height)//2)
71+
72+
display_group = displayio.Group()
73+
display_group.append(viewfinder_grid)
74+
display_group.append(thermal_grid)
75+
pycam.display.root_group = display_group
76+
77+
# Without interpolation, thermal_mapped is the same size as thermal_raw
78+
thermal_mapped = displayio.Bitmap(thermal_raw.width,thermal_raw.height,THERMAL_COLORS)
79+
80+
# Build a color palette that covers the color range commonly
6181
# used to convey temperature, conveniently half of hue wheel in
6282
# HSV (hue/saturation/value) colorspace.
6383
#
6484
# Coolest = blue -> purple -> red -> orange -> yellow = hottest
6585
print("Building color lookup table...")
86+
for color in range(THERMAL_COLORS):
87+
color_fraction = color/THERMAL_COLORS
88+
if color_fraction < THERMAL_FADE:
89+
# Fade from blue to black
90+
hue = -1/3
91+
saturation = 1.0
92+
value = (color_fraction/THERMAL_FADE)
93+
elif color_fraction > (1-THERMAL_FADE):
94+
# Glow from yellow to white
95+
hue = 1/6
96+
fade_fraction = color_fraction - (1-THERMAL_FADE)
97+
saturation = (THERMAL_FADE-fade_fraction)/THERMAL_FADE
98+
value = 1.0
99+
else:
100+
# Full saturation and full value, but with hue somewhere in the range
101+
# of blue -> purple -> red -> orange -> yellow
102+
# We want hue from +1/6 to -1/3
103+
# Scale number to between 0.5 and 0.0
104+
# Then drop by 1/3
105+
hue_range = 1-(THERMAL_FADE*2)
106+
hue = ((color_fraction-THERMAL_FADE)/(hue_range*2))-(1/3)
107+
saturation = 1.0
108+
value = 1.0
66109

67-
# With an 8x8 sensor array, and in the absence of interpolation, we would
68-
# never need more than 8*8=64 distinct colors.
69-
COLORS=64
70-
color_lookup=[] # Can I preallocate it to be COLORS in size?
71-
72-
for color in range(COLORS):
73-
# We want hue from +1/6 to -1/3
74-
# Scale number to between 0.5 and 0.0
75-
# Then drop by 1/3
76-
hue = (color/(COLORS*2))-(1/3)
77110
# Obtain hue from HSV spectrum, then convert to RGB with pack()
78-
rgb = fancy.CHSV(hue).pack()
79-
# Extract each color channel and drop lower bits
80-
red = (rgb & 0xFF0000) >> 19
81-
green_h3 = (rgb & 0x00FF00) >> 13
82-
green_l3 = (rgb & 0x003800) >> 11
83-
blue = (rgb & 0x0000FF) >> 3
84-
# Pack bits into RGB565_SWAPPED format
85-
color_lookup.append((red << 3) + (green_h3) + (green_l3 << 13) + (blue << 8))
111+
thermal_palette[color] = fancy.CHSV(hue, saturation, value).pack()
112+
thermal_palette.make_transparent(0)
113+
114+
# Draw tile set that will be used for overlay, using just-built palette
115+
thermal_blockmap.fill(0)
116+
for y in range(0,thermal_blockmap.height,2):
117+
for x in range(0,thermal_blockmap.width,2):
118+
thermal_blockmap[x,y] = x//THERMAL_BLOCK_SIZE
86119

87120
print("Starting!")
88121

@@ -104,38 +137,36 @@
104137
scan_y += 1
105138
range_now = max_now - min_now
106139

107-
# Clear overlay to black
108-
thermal_overlay.fill(0)
109-
110-
# Proceed only if we actually have a range of sensor values.
111-
if (range_now > 0):
112-
# TODO: interplate 8x8 thermal_raw array to something bigger
113-
# https://learn.adafruit.com/adafruit-amg8833-8x8-thermal-camera-sensor/raspberry-pi-thermal-camera
140+
# TODO: interplate 8x8 thermal_raw array to something bigger
141+
# https://learn.adafruit.com/adafruit-amg8833-8x8-thermal-camera-sensor/raspberry-pi-thermal-camera
114142

115-
# Given the min/max values, we can map raw values across range of
116-
# available values in color lookup table
117-
for y in range(thermal_raw.height):
118-
for x in range(thermal_raw.width):
143+
# Given the min/max values, we can map raw values across range of
144+
# available values in color lookup table
145+
for y in range(thermal_raw.height):
146+
for x in range(thermal_raw.width):
147+
if range_now > 100:
119148
raw = thermal_raw[x,y]
120149
raw = raw - min_now
121-
raw = raw/range_now
122-
mapped = math.floor(raw*(COLORS-1))
123-
thermal_mapped[x,y] = color_lookup[mapped]
124-
125-
# Transfer thermal data, mapped via color table, into thermal overlay.
126-
y_offset = math.floor((pycam.display.height - pycam.camera.height)/2)
127-
x_offset = math.floor((pycam.display.width - pycam.camera.width)/2)
128-
x_block_size = math.floor(pycam.display.width/SENSOR_SIZE_X)
129-
y_block_size = math.floor(pycam.display.height/SENSOR_SIZE_Y)
130-
for y in range(0,pycam.camera.height,4):
131-
for x in range(0,pycam.camera.width,4):
132-
# Adjust for physical sensor orientation and field of view
133-
x_lookup = (y+y_offset)//y_block_size
134-
y_lookup = SENSOR_SIZE_X-1-(x//x_block_size)
135-
thermal_overlay[x,y] = thermal_mapped[x_lookup,y_lookup]
136-
137-
# Blend camera view with thermal overlay, and blit to screen
138-
bitmaptools.alphablend(
139-
combined_view, pycam.continuous_capture(), thermal_overlay, displayio.Colorspace.RGB565_SWAPPED
140-
)
141-
pycam.blit(combined_view)
150+
raw = raw * THERMAL_COLORS
151+
raw = raw // range_now
152+
if raw < 1:
153+
raw = 1
154+
elif raw >= THERMAL_COLORS:
155+
raw = THERMAL_COLORS-1
156+
thermal_mapped[x,y] = raw
157+
else:
158+
thermal_mapped[x,y] = 1
159+
160+
# Transfer thermal data, mapped via color table, into thermal overlay.
161+
x_block_size = pycam.display.width//thermal_mapped.width
162+
y_block_size = pycam.display.height//thermal_mapped.height
163+
for y in range(thermal_grid.height):
164+
for x in range(thermal_grid.width):
165+
# Adjust for physical sensor orientation and field of view
166+
x_lookup = (y*THERMAL_BLOCK_SIZE)//y_block_size
167+
y_lookup = thermal_mapped.width-1-((x*THERMAL_BLOCK_SIZE)//x_block_size)
168+
169+
thermal_grid[x,y]=thermal_mapped[x_lookup,y_lookup]
170+
171+
bitmaptools.blit(viewfinder_bitmap, pycam.continuous_capture(), 0, 0)
172+
pycam.display.refresh()

0 commit comments

Comments
 (0)