Last active
May 10, 2021 22:13
-
-
Save dglaude/33d0460427c0c082ed6151f2d29b723b to your computer and use it in GitHub Desktop.
mlx90640 thermal camera on CLUE with x2 bilinear scaler using ulab + x3 with displayio scale
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
### mlx90640_scalex2_clue.py + 64 | |
### same as mlx90640_scalex2_clue.py but with 64 colours rather than 32 => need one optimisation on memory size. | |
import time | |
import board | |
import busio | |
import adafruit_mlx90640 | |
import displayio | |
import terminalio | |
import bitmaptools | |
from adafruit_display_text.label import Label | |
from simpleio import map_range | |
import adafruit_fancyled.adafruit_fancyled as fancy | |
import gc | |
import ulab | |
gc.collect() | |
start_mem = gc.mem_free() | |
start_time = time.monotonic_ns() | |
# On PyPortal, the scale factor works from 3 to 9 | |
# On Clue the biggest scale is 7 | |
other_factor = 2 | |
scale_factor = 3 | |
total_factor = scale_factor * other_factor | |
text_x = (32 * scale_factor) - 30 | |
text_y = (24 * scale_factor) + 8 | |
gradian_y = 24 + int (20 / total_factor) | |
#gradian_y = int( (text_y + 8) / 2 ) | |
#number_of_colors = 128 # Number of color in the gradian | |
number_of_colors = 64 # Number of color in the gradian | |
#number_of_colors = 32 # Number of color in the gradian | |
last_color = number_of_colors-1 # Last color in palette | |
palette = displayio.Palette(number_of_colors) # Palette with all our colors | |
# gradian for fancyled palette generation | |
grad = [(0.00, fancy.CRGB(0, 0, 255)), # Blue | |
(0.25, fancy.CRGB(0, 255, 255)), | |
(0.50, fancy.CRGB(0, 255, 0)), # Green | |
(0.75, fancy.CRGB(255, 255, 0)), | |
(1.00, fancy.CRGB(255, 0, 0))] # Red | |
# create our palette using fancyled expansion of our gradian | |
fancy_palette = fancy.expand_gradient(grad, number_of_colors) | |
for c in range(number_of_colors): | |
palette[c] = fancy_palette[c].pack() | |
# Bitmap for colour coded thermal value | |
image_bitmap = displayio.Bitmap( 2*32-1, 2*24-1, number_of_colors ) | |
# Create a TileGrid using the Bitmap and Palette | |
image_tile= displayio.TileGrid(image_bitmap, pixel_shader=palette) | |
# Create a Group that scale 32*24 to 256*192 | |
image_group = displayio.Group(scale=scale_factor) | |
image_group.append(image_tile) | |
#^# scale_bitmap = displayio.Bitmap( number_of_colors, 1, number_of_colors ) | |
# Create a Group Scale | |
#^# scale_group = displayio.Group(scale=total_factor) | |
#^# scale_tile = displayio.TileGrid(scale_bitmap, pixel_shader=palette, x = 0, y = gradian_y) | |
#^# scale_group.append(scale_tile) | |
#^# for i in range(number_of_colors): | |
#^# scale_bitmap[i, 0] = i # Fill the scale with the palette gradian | |
# Create the super Group | |
group = displayio.Group() | |
#°# min_label = Label(terminalio.FONT, max_glyphs=10, color=palette[0], x=0, y=text_y) | |
#_# center_label = Label(terminalio.FONT, max_glyphs=10, color=palette[int(number_of_colors/2)], x=int (text_x/2), y=text_y) | |
#°# max_label = Label(terminalio.FONT, max_glyphs=10, color=palette[last_color], x=text_x, y=text_y) | |
# Indicator for the minimum and maximum location | |
#°# o_label = Label(terminalio.FONT, max_glyphs = 1, text = "o", color = 0xFFFFFF, x = 0, y = 0) | |
#°# x_label = Label(terminalio.FONT, max_glyphs = 1, text = "x", color = 0xFFFFFF, x = 0, y = 0) | |
# Add all the sub-group to the SuperGroup | |
group.append(image_group) | |
#^# group.append(scale_group) | |
#°# group.append(min_label) | |
#_# group.append(center_label) | |
#°# group.append(max_label) | |
#°# group.append(o_label) | |
#°# group.append(x_label) | |
# Add the SuperGroup to the Display | |
board.DISPLAY.show(group) | |
min_t = 20 # Initial minimum temperature range, before auto scale | |
max_t = 37 # Initial maximum temperature range, before auto scale | |
i2c = busio.I2C(board.SCL, board.SDA, frequency=800000) | |
mlx = adafruit_mlx90640.MLX90640(i2c) | |
print("MLX addr detected on I2C") | |
#mlx.refresh_rate = adafruit_mlx90640.RefreshRate.REFRESH_2_HZ | |
mlx.refresh_rate = adafruit_mlx90640.RefreshRate.REFRESH_4_HZ | |
####frame = [0] * 768 | |
npframe = ulab.zeros(768) | |
gc.collect() | |
mid_mem = gc.mem_free() | |
print("Memory now:", mid_mem - start_mem) | |
# Allocate once, not in the loop. | |
a = ulab.zeros((2*24-1, 2*32-1)) # the upscaled image | |
while True: | |
#for i in range(10): | |
stamp = time.monotonic() | |
try: | |
# mlx.getFrame(frame) | |
mlx.getFrame(npframe) | |
except ValueError: # these happen, no biggie - retry | |
continue | |
print("Time for data aquisition: %0.2f s" % (time.monotonic()-stamp)) | |
stamp = time.monotonic() | |
# Convert in ulab.array, find the min and max and normalize to int from 0 to last_color. | |
# npframe=ulab.array(frame) | |
min_t=ulab.numerical.min(npframe) | |
max_t=ulab.numerical.max(npframe) | |
b = npframe.reshape((24,32)) | |
# It is possible to build a 24x32 array with ulab too: | |
## b = inta.reshape((24,32)) | |
# a = ulab.zeros((2*24-2, 2*32-2)) # the upscaled image | |
#/# a = ulab.zeros((2*32-1, 2*24-1)) # the upscaled image | |
# Allocate once, not in the loop. | |
#^# a = ulab.zeros((2*24-1, 2*32-1)) # the upscaled image | |
# Some old magic by @v923z | |
# a[::2,::2] = b | |
# a[1::2,::2] = 0.5 * (b[:-1,:] + b[1:, :]) | |
# a[::2,1::2] = 0.5 * (b[:,1:] + b[:,:-1]) | |
# a[1::2,1::2] = 0.25 * (b[:-1,1:] + b[1:,:-1] + b[1:,1:] + b[:-1,:-1]) | |
a[::2,::2] = b | |
if False: # Readable arithmetic => more memory, max: n=65 | |
a[1::2,::2] = 0.5 * (b[:-1,:] + b[1:, :]) | |
a[::,1::2]=0.5 * (a[::,:-1:2] + a[::,2::2]) | |
else: # Splitting into simple operation => less memory max: n=87 | |
a[1::2,::2] = b[:-1,:] | |
a[1::2,::2] += b[1:, :] | |
a[1::2,::2] /= 2 | |
a[::,1::2]=a[::,:-1:2] | |
a[::,1::2]+=a[::,2::2] | |
a[::,1::2]/=2 | |
factor=last_color/(max_t-min_t) | |
inta=ulab.array((a-min_t)*factor,dtype=ulab.int8) | |
#°# minx = 0 | |
#°# miny = 0 | |
#°# maxx = 23 | |
#°# maxy = 31 | |
print("ulab processing: %0.2f s" % (time.monotonic()-stamp)) | |
stamp_copy = time.monotonic() | |
bitmaptools.arrayblit(image_bitmap, inta) | |
print("Copy from uLab to bitmap: %0.2f s" % (time.monotonic()-stamp_copy)) | |
# min and max temperature indicator | |
#°# min_label.text="%0.2f" % (min_t) | |
#°# max_label.text="%0.2f" % (max_t) | |
# Compute average_center temperature of the middle of sensor and convert to palette color | |
#_# center_average = (frame[11*32 + 15] + frame[12*32 + 15] + frame[11*32 + 16] + frame[12*32 + 16]) / 4 | |
#_# center_color = int(map_range(center_average, min_t, max_t, 0, last_color )) | |
#_# center_label.text = "%0.2f" % (center_average) | |
#_# center_label.color = palette[center_color] | |
# Set the location of X for lowest temperature | |
#°# x_label.x = maxx * total_factor | |
#°# x_label.y = (23-maxy) * total_factor | |
# Set the location of O for highest temperature | |
#°# o_label.x = minx * total_factor | |
#°# o_label.y = (23-miny) * total_factor | |
# print("Total time for aquisition and display %0.2f s" % (time.monotonic()-stamp)) | |
stop_time = time.monotonic_ns() | |
gc.collect() | |
stop_mem = gc.mem_free() | |
print(stop_time - start_time, stop_mem - start_mem) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
### mlx90640_scalex2_clue.py | |
import time | |
import board | |
import busio | |
import adafruit_mlx90640 | |
import displayio | |
import terminalio | |
import bitmaptools | |
from adafruit_display_text.label import Label | |
from simpleio import map_range | |
import adafruit_fancyled.adafruit_fancyled as fancy | |
import gc | |
import ulab | |
gc.collect() | |
start_mem = gc.mem_free() | |
start_time = time.monotonic_ns() | |
# On PyPortal, the scale factor works from 3 to 9 | |
# On Clue the biggest scale is 7 | |
other_factor = 2 | |
scale_factor = 3 | |
total_factor = scale_factor * other_factor | |
text_x = (32 * scale_factor) - 30 | |
text_y = (24 * scale_factor) + 8 | |
gradian_y = 24 + int (20 / total_factor) | |
#gradian_y = int( (text_y + 8) / 2 ) | |
number_of_colors = 32 # Number of color in the gradian | |
last_color = number_of_colors-1 # Last color in palette | |
palette = displayio.Palette(number_of_colors) # Palette with all our colors | |
# gradian for fancyled palette generation | |
grad = [(0.00, fancy.CRGB(0, 0, 255)), # Blue | |
(0.25, fancy.CRGB(0, 255, 255)), | |
(0.50, fancy.CRGB(0, 255, 0)), # Green | |
(0.75, fancy.CRGB(255, 255, 0)), | |
(1.00, fancy.CRGB(255, 0, 0))] # Red | |
# create our palette using fancyled expansion of our gradian | |
fancy_palette = fancy.expand_gradient(grad, number_of_colors) | |
for c in range(number_of_colors): | |
palette[c] = fancy_palette[c].pack() | |
# Bitmap for colour coded thermal value | |
image_bitmap = displayio.Bitmap( 2*32-1, 2*24-1, number_of_colors ) | |
# Create a TileGrid using the Bitmap and Palette | |
image_tile= displayio.TileGrid(image_bitmap, pixel_shader=palette) | |
# Create a Group that scale 32*24 to 256*192 | |
image_group = displayio.Group(scale=scale_factor) | |
image_group.append(image_tile) | |
#^# scale_bitmap = displayio.Bitmap( number_of_colors, 1, number_of_colors ) | |
# Create a Group Scale | |
#^# scale_group = displayio.Group(scale=total_factor) | |
#^# scale_tile = displayio.TileGrid(scale_bitmap, pixel_shader=palette, x = 0, y = gradian_y) | |
#^# scale_group.append(scale_tile) | |
#^# for i in range(number_of_colors): | |
#^# scale_bitmap[i, 0] = i # Fill the scale with the palette gradian | |
# Create the super Group | |
group = displayio.Group() | |
#°# min_label = Label(terminalio.FONT, max_glyphs=10, color=palette[0], x=0, y=text_y) | |
#_# center_label = Label(terminalio.FONT, max_glyphs=10, color=palette[int(number_of_colors/2)], x=int (text_x/2), y=text_y) | |
#°# max_label = Label(terminalio.FONT, max_glyphs=10, color=palette[last_color], x=text_x, y=text_y) | |
# Indicator for the minimum and maximum location | |
#°# o_label = Label(terminalio.FONT, max_glyphs = 1, text = "o", color = 0xFFFFFF, x = 0, y = 0) | |
#°# x_label = Label(terminalio.FONT, max_glyphs = 1, text = "x", color = 0xFFFFFF, x = 0, y = 0) | |
# Add all the sub-group to the SuperGroup | |
group.append(image_group) | |
#^# group.append(scale_group) | |
#°# group.append(min_label) | |
#_# group.append(center_label) | |
#°# group.append(max_label) | |
#°# group.append(o_label) | |
#°# group.append(x_label) | |
# Add the SuperGroup to the Display | |
board.DISPLAY.show(group) | |
min_t = 20 # Initial minimum temperature range, before auto scale | |
max_t = 37 # Initial maximum temperature range, before auto scale | |
i2c = busio.I2C(board.SCL, board.SDA, frequency=800000) | |
mlx = adafruit_mlx90640.MLX90640(i2c) | |
print("MLX addr detected on I2C") | |
#mlx.refresh_rate = adafruit_mlx90640.RefreshRate.REFRESH_2_HZ | |
#Time for data aquisition: 1.36 s | |
#ulab processing: 0.05 s | |
#Copy from uLab to bitmap: 0.00 s | |
mlx.refresh_rate = adafruit_mlx90640.RefreshRate.REFRESH_4_HZ | |
frame = [0] * 768 | |
####npframe = ulab.zeros(768) | |
gc.collect() | |
mid_mem = gc.mem_free() | |
print("Memory now:", mid_mem - start_mem) | |
# Allocate once, not in the loop. | |
a = ulab.zeros((2*24-1, 2*32-1)) # the upscaled image | |
while True: | |
#for i in range(10): | |
stamp = time.monotonic() | |
try: | |
mlx.getFrame(frame) | |
except ValueError: # these happen, no biggie - retry | |
continue | |
print("Time for data aquisition: %0.2f s" % (time.monotonic()-stamp)) | |
stamp = time.monotonic() | |
# Convert in ulab.array, find the min and max and normalize to int from 0 to last_color. | |
npframe=ulab.array(frame) | |
min_t=ulab.numerical.min(npframe) | |
max_t=ulab.numerical.max(npframe) | |
b = npframe.reshape((24,32)) | |
# It is possible to build a 24x32 array with ulab too: | |
## b = inta.reshape((24,32)) | |
# a = ulab.zeros((2*24-2, 2*32-2)) # the upscaled image | |
#/# a = ulab.zeros((2*32-1, 2*24-1)) # the upscaled image | |
# Allocate once, not in the loop. | |
#^# a = ulab.zeros((2*24-1, 2*32-1)) # the upscaled image | |
# Some old magic by @v923z | |
# a[::2,::2] = b | |
# a[1::2,::2] = 0.5 * (b[:-1,:] + b[1:, :]) | |
# a[::2,1::2] = 0.5 * (b[:,1:] + b[:,:-1]) | |
# a[1::2,1::2] = 0.25 * (b[:-1,1:] + b[1:,:-1] + b[1:,1:] + b[:-1,:-1]) | |
a[::2,::2] = b | |
if False: # Readable arithmetic => more memory, max: n=65 | |
a[1::2,::2] = 0.5 * (b[:-1,:] + b[1:, :]) | |
a[::,1::2]=0.5 * (a[::,:-1:2] + a[::,2::2]) | |
else: # Splitting into simple operation => less memory max: n=87 | |
a[1::2,::2] = b[:-1,:] | |
a[1::2,::2] += b[1:, :] | |
a[1::2,::2] /= 2 | |
a[::,1::2]=a[::,:-1:2] | |
a[::,1::2]+=a[::,2::2] | |
a[::,1::2]/=2 | |
factor=last_color/(max_t-min_t) | |
inta=ulab.array((a-min_t)*factor,dtype=ulab.int8) | |
#°# minx = 0 | |
#°# miny = 0 | |
#°# maxx = 23 | |
#°# maxy = 31 | |
print("ulab processing: %0.2f s" % (time.monotonic()-stamp)) | |
stamp_copy = time.monotonic() | |
bitmaptools.arrayblit(image_bitmap, inta) | |
print("Copy from uLab to bitmap: %0.2f s" % (time.monotonic()-stamp_copy)) | |
# min and max temperature indicator | |
#°# min_label.text="%0.2f" % (min_t) | |
#°# max_label.text="%0.2f" % (max_t) | |
# Compute average_center temperature of the middle of sensor and convert to palette color | |
#_# center_average = (frame[11*32 + 15] + frame[12*32 + 15] + frame[11*32 + 16] + frame[12*32 + 16]) / 4 | |
#_# center_color = int(map_range(center_average, min_t, max_t, 0, last_color )) | |
#_# center_label.text = "%0.2f" % (center_average) | |
#_# center_label.color = palette[center_color] | |
# Set the location of X for lowest temperature | |
#°# x_label.x = maxx * total_factor | |
#°# x_label.y = (23-maxy) * total_factor | |
# Set the location of O for highest temperature | |
#°# o_label.x = minx * total_factor | |
#°# o_label.y = (23-miny) * total_factor | |
# print("Total time for aquisition and display %0.2f s" % (time.monotonic()-stamp)) | |
stop_time = time.monotonic_ns() | |
gc.collect() | |
stop_mem = gc.mem_free() | |
print(stop_time - start_time, stop_mem - start_mem) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I am merging all of my thermal camera with interpolation into a single source file: https://gist.github.com/dglaude/cdd4ede9e43fe620637a2199e05ba8cb