0

AMG8833 - Big Red Clumps on TFT
Moderators: adafruit_support_bill, adafruit

Please be positive and constructive with your questions and comments.

AMG8833 - Big Red Clumps on TFT

by worn_out on Wed Sep 22, 2021 2:02 pm

AMG8833
Teensy 4.0
Arduino 1.8.16 (Teensyduino)

ILI9341 and ILI9341_t3
AMG88xx 2.1.1 and previous AMG88xx 1.2 and 1.1

Problem: Big Red Clumps and smaller one’s, too, only when using the Interpolate Code.
Otherwise, all codes work well (Graphics test codes and all my other codes work and all have the same Pinouts/hookup).

Thus, I suspect it’s the “Interpolate” code.

Are there other similar codes I could try that do similar to what the Interpolate code does (the general Thermalcam code presents only the 8x8 pixels making it a good test but, otherwise not useful).

Any other recommendations?
Attachments
Thermal1.mp4.gif
Thermal1.mp4.gif (999.86 KiB) Viewed 178 times

worn_out
 
Posts: 4
Joined: Wed Sep 22, 2021 1:24 pm

Re: AMG8833 - Big Red Clumps on TFT

by sj_remington on Wed Sep 22, 2021 6:03 pm

Yes, the bicubic interpolation algorithm can produce numbers that exceed the range of allowable output values, and will overflow 8 bits.

I rewrote it to write directly to a display (rather than an intermediate array), and to use 16 bit calculations. This runs on a Teensy 3.2 and a 480x320 display. It can handle arbitrary sized interpolated output arrays (integer multiples of the input array, of course), and can also output "pixels" as nxn boxes. Currently set up for the MLX90640 sensor and full interpolation of 32x24 to 320x240.

Code: Select all | TOGGLE FULL SIZE
#include <Adafruit_MLX90640.h>
Adafruit_MLX90640 mlx;
#include <Wire.h>
#include <SPI.h>
#include "Adafruit_GFX.h"
#include "Adafruit_HX8357.h"
// control pin definitions for HX8357 display
#define TFT_CS 10
#define TFT_DC 9
#define TFT_RST 8 // RST can be set to -1 if you tie it to Arduino's reset

// Use hardware SPI (on Uno, #13, #12, #11) and the above for CS/DC
Adafruit_HX8357 tft = Adafruit_HX8357(TFT_CS, TFT_DC, TFT_RST);

//heatmap color table 565 format
// 433 entries
 uint16_t ironbow[]=
{1,2,3,4,5,5,6,6,7,7,
8,8,9,9,10,10,10,11,11,11,
12,12,12,12,13,2061,2061,2062,2062,2062,
2062,2062,2062,4111,4111,4111,4111,4111,6159,6160,
6160,6160,8208,8208,8208,8208,10257,10257,10257,10257,
12305,12305,12305,12305,14353,14354,14354,14354,14354,14354,
16402,16402,16402,16402,16402,18450,18450,18450,18450,18450,
20498,20499,20499,20499,22547,22547,22547,22547,22547,24595,
24595,24595,24595,26643,26643,26643,26643,26643,28691,28691,
28691,28691,28691,30739,30739,30739,30739,30739,32787,32787,
32787,32787,32787,34835,34835,34835,34835,34835,36883,36883,
36883,36883,38931,38931,38931,38931,38931,38931,40979,40979,
40979,40979,40979,40979,43027,43027,43027,43027,43027,43027,
43027,45075,45075,45074,45074,45074,45074,45074,45074,45074,
47122,47154,47154,47154,47154,47154,47154,47154,47154,47154,
49202,49202,49234,49234,49233,49233,49233,49265,49265,49265,
49265,49265,51345,51344,51344,51344,51344,51376,51376,51376,
51376,51407,51407,51407,53455,53455,53486,53486,53486,53486,
53518,53517,53517,53517,53549,53548,53548,55596,55628,55628,
55627,55659,55659,55658,55658,55690,55689,55689,55688,55720,
55720,55719,57767,57798,57798,57798,57797,57829,57828,57828,
57828,57859,57859,57859,57891,57891,57890,57890,57922,57922,
59970,60002,60001,60001,60001,60001,60001,60033,60033,60033,
60033,60065,60065,60065,60097,60096,60096,60096,60128,60128,
60128,60128,60128,60160,60160,60160,62208,62240,62240,62240,
62240,62240,62272,62272,62272,62272,62272,62304,62304,62304,
62304,62336,62336,62336,62336,62368,62368,62368,62368,62400,
62400,62400,62432,62432,62432,62464,62464,62464,62464,62496,
62496,62496,64544,64576,64576,64576,64576,64576,64608,64608,
64608,64608,64608,64640,64640,64640,64640,64672,64672,64672,
64704,64704,64704,64736,64736,64736,64768,64768,64768,64768,
64800,64800,64800,64832,64832,64832,64864,64864,64864,64864,
64896,64896,64896,64896,64928,64928,64928,64960,64960,64960,
64960,64960,64992,64992,64992,65024,65024,65024,65024,65056,
65056,65056,65056,65088,65088,65088,65088,65088,65120,65120,
65120,65120,65120,65152,65152,65153,65185,65185,65185,65185,
65217,65217,65217,65217,65218,65250,65250,65250,65251,65251,
65251,65284,65284,65284,65284,65285,65317,65317,65318,65318,
65319,65319,65351,65352,65352,65353,65353,65386,65386,65386,
65387,65387,65388,65388,65421,65421,65422,65422,65423,65424,
65424,65425,65425,65458,65458,65459,65459,65460,65460,65461,
65461,65462,65462,65495,65495,65496,65496,65496,65497,65497,
65498,65498,65499,65531,65531,65532,65532,65533,65533,65533,
65534,65534,65534};

// bicubic interpolation templates
float get_point(float *p, uint16_t rows, uint16_t cols, int16_t x, int16_t y);
void set_point(float *p, uint16_t rows, uint16_t cols, int16_t x, int16_t y, float f);
void get_adjacents_1d(float *src, float *dest, uint16_t rows, uint16_t cols, int16_t x, int16_t y);
void get_adjacents_2d(float *src, float *dest, uint16_t rows, uint16_t cols, int16_t x, int16_t y);
float cubicInterpolate(float p[], float x);
float bicubicInterpolate(float p[], float x, float y);
void interpolate_image(float *src, uint16_t src_rows, uint16_t src_cols,
                       float *dest, uint16_t dest_rows, uint16_t dest_cols);

// 480x320 pixel
#define H_RES 480
#define V_RES   320
#define INPUT_COLS 32
#define INPUT_ROWS 24
// 10x10 interp
#define INTERPOLATED_COLS 320
#define INTERPOLATED_ROWS 240
// 1x1 box (single pixel) output
#define DISPLAY_COLS 320
#define DISPLAY_ROWS 240

float frame[32 * 24]; // buffer for full frame of temperatures
float dest_2d;  //output value for box fill
int boxsize; //pixel blocksize for output

void setup()
{
  tft.begin();
  tft.setRotation(1); // horizontal wide screen
  tft.fillScreen( HX8357_BLACK );

  //  tft.setCursor(0, 2);
  //  tft.setTextColor(HX8357_GREEN);  //tft.setTextSize(3);
  //  tft.setTextSize(1);
  //  tft.print("MLX90640");

  Serial.begin(115200);
  while (!Serial) delay(10);

  Serial.println("MLX90640 bicubic interpolate");
  while (! mlx.begin(MLX90640_I2CADDR_DEFAULT, &Wire)) {
    Serial.println("MLX90640 not found!");
  }
  Serial.println("Found MLX90640");

  //mlx.setMode(MLX90640_INTERLEAVED);
  mlx.setMode(MLX90640_CHESS);
  Serial.print("Current mode: ");
  if (mlx.getMode() == MLX90640_CHESS) {
    Serial.println("Chess");
  } else {
    Serial.println("Interleave");
  }

  mlx.setResolution(MLX90640_ADC_18BIT);
  Serial.print("Current resolution: ");
  mlx90640_resolution_t res = mlx.getResolution();
  switch (res) {
    case MLX90640_ADC_16BIT: Serial.println("16 bit"); break;
    case MLX90640_ADC_17BIT: Serial.println("17 bit"); break;
    case MLX90640_ADC_18BIT: Serial.println("18 bit"); break;
    case MLX90640_ADC_19BIT: Serial.println("19 bit"); break;
  }

  mlx.setRefreshRate(MLX90640_2_HZ);
  Serial.print("Current frame rate: ");
  mlx90640_refreshrate_t rate = mlx.getRefreshRate();
  switch (rate) {
    case MLX90640_0_5_HZ: Serial.println("0.5 Hz"); break;
    case MLX90640_1_HZ: Serial.println("1 Hz"); break;
    case MLX90640_2_HZ: Serial.println("2 Hz"); break;
    case MLX90640_4_HZ: Serial.println("4 Hz"); break;
    case MLX90640_8_HZ: Serial.println("8 Hz"); break;
    case MLX90640_16_HZ: Serial.println("16 Hz"); break;
    case MLX90640_32_HZ: Serial.println("32 Hz"); break;
    case MLX90640_64_HZ: Serial.println("64 Hz"); break;
  }
  boxsize = min(DISPLAY_COLS / INTERPOLATED_COLS, DISPLAY_ROWS / INTERPOLATED_ROWS);
  Serial.print("Box size = ");
  Serial.println(boxsize);
}

void loop(void)
{
  static int frame_number = 0;
  int n;
  if (mlx.getFrame(frame) != 0) {
    Serial.println("Failed");
    return;
  }

  Serial.print("frame "); Serial.println(frame_number++);

  // image stats, subtract min
  int npx = 32 * 24;
  float Tavg = 0.0;
  float Tmin = 1000.0;
  float Tmax = -1000.0;

  for (n = 0; n < npx; n++) {
    Tavg += frame[n];
    if (Tmin > frame[n]) Tmin = frame[n];
    if (Tmax < frame[n]) Tmax = frame[n];
  }
  Tavg = Tavg / npx;
  //  Serial.print("avg = "); Serial.println(Tavg);
  //  Serial.print("max = "); Serial.println(Tmax);
  //  Serial.print("min = "); Serial.println(Tmin);
  float scale = 425.0 / (Tmax - Tmin); //leave some headroom for bicubic interpolation (433 max)

  // scale image
  for (uint8_t h = 0; h < 24; h++) {
    for (uint8_t w = 0; w < 32; w++) {
      frame[h * 32 + w] = scale * (frame[h * 32 + w] - Tmin);
      //    Serial.print(frame[h*32+w],0); Serial.print(" ");
    }
    //   Serial.println();
  }

  interpolate_image(frame, INPUT_ROWS, INPUT_COLS, &dest_2d, INTERPOLATED_ROWS, INTERPOLATED_COLS);
}

// fetch the value at input array p(x, y) (cols X rows)
float get_point(float *p, uint16_t rows, uint16_t cols, int16_t x, int16_t y) {
  if (x < 0)        x = 0;
  if (y < 0)        y = 0;
  if (x >= cols)    x = cols - 1;
  if (y >= rows)    y = rows - 1;
  return p[y * cols + x];
}

// send blocks of points to destination array (rows X cols) (global boxsize)
void set_point(float *p, uint16_t rows, uint16_t cols, int16_t x, int16_t y, float f) {

  if ((x < 0) || (x >= cols)) return;
  if ((y < 0) || (y >= rows)) return;
  //    p[y * cols + x] = f;  //was, for output array

  // now, draw directly on screen

  int colorTemp = f;
  if (colorTemp < 0) colorTemp = 0;
  if (colorTemp > 432) colorTemp = 432;
  colorTemp = ironbow[colorTemp];

  if (boxsize == 1) tft.drawPixel(x, y, colorTemp);
  else   tft.fillRect(x * boxsize, y * boxsize, boxsize, boxsize, colorTemp);
}

// src is the input array src_rows * src_cols
// dest is a pre-allocated output array, dest_rows*dest_cols
void interpolate_image(float *src, uint16_t src_rows, uint16_t src_cols,
                       float *dest, uint16_t dest_rows, uint16_t dest_cols) {
  float mu_x = (src_cols - 1.0) / (dest_cols - 1.0);
  float mu_y = (src_rows - 1.0) / (dest_rows - 1.0);

  float adj_2d[16]; // 4x4 matrix for storing adjacents
  float frac_x, frac_y, out;

  for (uint16_t y_idx = 0; y_idx < dest_rows; y_idx++) {
    for (uint16_t x_idx = 0; x_idx < dest_cols; x_idx++) {
      float x = x_idx * mu_x;
      float y = y_idx * mu_y;
 
      get_adjacents_2d(src, adj_2d, src_rows, src_cols, x, y);

      frac_x = x - (int)x; // we only need the ~delta~ between the points
      frac_y = y - (int)y; // we only need the ~delta~ between the points
      out = bicubicInterpolate(adj_2d, frac_x, frac_y);
      set_point(dest, dest_rows, dest_cols, x_idx, y_idx, out);
    }
  }
}

// p is a list of 4 points, 2 to the left, 2 to the right
float cubicInterpolate(float p[], float x) {
  float r = p[1] + (0.5 * x * (p[2] - p[0] + x * (2.0 * p[0] - 5.0 * p[1] + 4.0 * p[2] - p[3] + x * (3.0 * (p[1] - p[2]) + p[3] - p[0]))));

  return r;
}

// p is a 16-point 4x4 array of the 2 rows & columns left/right/above/below
float bicubicInterpolate(float p[], float x, float y) {
  float arr[4] = {0, 0, 0, 0};
  arr[0] = cubicInterpolate(p + 0, x);
  arr[1] = cubicInterpolate(p + 4, x);
  arr[2] = cubicInterpolate(p + 8, x);
  arr[3] = cubicInterpolate(p + 12, x);
  return cubicInterpolate(arr, y);
}

// src is rows*cols and dest is a 4-point array passed in already allocated!
void get_adjacents_1d(float *src, float *dest, uint16_t rows, uint16_t cols, int16_t x, int16_t y) {
  //    Serial.print("adj_1d("); Serial.print(x); Serial.print(", "); Serial.print(y); Serial.println(")");
  // pick two items to the left
  dest[0] = get_point(src, rows, cols, x - 1, y);
  dest[1] = get_point(src, rows, cols, x, y);
  // pick two items to the right
  dest[2] = get_point(src, rows, cols, x + 1, y);
  dest[3] = get_point(src, rows, cols, x + 2, y);
}


// src is rows*cols and dest is a 16-point array passed in, already allocated.
void get_adjacents_2d(float *src, float *dest, uint16_t rows, uint16_t cols, int16_t x, int16_t y) {
  //    Serial.print("adj_2d("); Serial.print(x); Serial.print(", "); Serial.print(y); Serial.println(")");
  for (int16_t delta_y = -1; delta_y < 3; delta_y++) { // -1, 0, 1, 2
    float *row = dest + 4 * (delta_y + 1); // index into each chunk of 4
    for (int16_t delta_x = -1; delta_x < 3; delta_x++) { // -1, 0, 1, 2
      row[delta_x + 1] = get_point(src, rows, cols, x + delta_x, y + delta_y);
    }
  }
}

sj_remington
 
Posts: 147
Joined: Mon Jul 27, 2020 4:51 pm

Re: AMG8833 - Big Red Clumps on TFT

by worn_out on Thu Sep 23, 2021 3:21 pm

Thank you for your quick response.

Not yet ready to step-up to MLX90640 sensor so, focusing on getting the best out of the AMG8833 on Teensy.

I grabbed Kasprzak’s code for thermal AMG8833 (screenshot attached). It works but, also has the Clumps...

Did not use the ili9341_t3. Instead, I tweaked the code to use default ili9341.h

Not sure if you’re inferring I could tweak the code/array to eliminate the clumps... If so, I’m not sure where to start (and, most likely is too much for my little, very old brain).

If not suggesting I tweak code/array but to get MLX90640, I’ll need to chew on that a bit...

FYI - also trying to do it on Raspberry pi Pico (using MicroPython). Got the Display working (ili9341), now need to find code/lib for the AMG8833
Attachments
Screen Shot 2021-09-23 at 12.13.07 PM.png
Screen Shot 2021-09-23 at 12.13.07 PM.png (599.16 KiB) Viewed 156 times

worn_out
 
Posts: 4
Joined: Wed Sep 22, 2021 1:24 pm

Re: AMG8833 - Big Red Clumps on TFT

by sj_remington on Thu Sep 23, 2021 11:40 pm

It should be just a few minutes of work to change my code to accept the AMG8833 8x8 sensor, but since that was where I started, here it is:

It uses a completely different processor and display, of course, and if I recall correctly, this version DOES NOT fix the overflow due to 8 bit math in the interpolation step.

Code: Select all | TOGGLE FULL SIZE
// This version interpolates 8x8 up to 64x64
// OLED display is 128x128, so use 4x4 pixel blocks
//For Pololu A* Mini LV
// connections to OLED:
// TX pin 0
// RX pin 1
// R pin 7 (reset)
// + 5V
// - GND
#define INPUT_COLS 8
#define INPUT_ROWS 8
#define INPUT_PIXEL_ARRAY_SIZE (INPUT_ROWS*INPUT_COLS)
#define INTERPOLATED_COLS 64
#define INTERPOLATED_ROWS 64
#define DISPLAY_COLS 128
#define DISPLAY_ROWS 128

#include <AStar32U4.h>
#include <Wire.h>
#include <Adafruit_AMG88xx.h>
Adafruit_AMG88xx amg;

float pixels[AMG88xx_PIXEL_ARRAY_SIZE];
int npix = AMG88xx_PIXEL_ARRAY_SIZE;
float dest_2d;  //output value for box fill
int boxsize; //pixel blocksize for output
int numColors; //number of entries in color lookup table
float TempMin=1000.0, TempMax=-1000.0;  //heatmap scaling min and max
float TempDif,TempAvg;

#include "oled160drv.h"
#include "interp.h"

char powerup = 1; //OLED display powered up

// timeout set to four minutes
#define SCREENSAVER 4*60*1000UL

void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
  while (!Serial);
  Serial.println(F("AMG image interpolate/display"));
  Serial1.begin(115200);
  OLED_Init();
  OLED_Clear();  //Note: have to set font size first or row spacing doesn't work properly
  OLED_Pensize(0); //  fill rectangles
  bool status;

  // initialize thermal sensor with defaults
  status = amg.begin();
  if (!status) {
    Serial.println(F("Could not initialize thermal sensor"));
    while (1);
  }

  boxsize = min(DISPLAY_COLS / INTERPOLATED_COLS, DISPLAY_ROWS / INTERPOLATED_COLS);
  Serial.print(F("Box size = "));
  Serial.println(boxsize);
  numColors = sizeof(camColors)/sizeof(camColors[0]);
  Serial.print(F("Color Table entries = "));
  Serial.println(numColors);
  delay(100); // sensor is still booting
}


void loop() {
static int i, counter = 0;
  counter++;

  if ( (millis() > SCREENSAVER) && powerup) {
    Serial.println(F("screensaver activated, power down"));
    powerup = 0;
    OLED_PowerDn();
    while (1);
  }
  Serial.print(F("Image "));
  Serial.println(counter);

//read all the pixels, bottom to top
  amg.readPixels(pixels);
// scale image
  float t;
  for (i = 0; i < npix; i++) {
    t = pixels[i];
    if ( TempMax < t) TempMax = t; //get max
    if ( TempMin > t) TempMin = t; //get min
    TempAvg += t; //calc average
  }
 
  TempDif = TempMax - TempMin; //for heatmap scaling
  TempAvg /= (float)npix;
  Serial.print(F("Min, Max, Avg: "));
  Serial.print(TempMin);
  Serial.print("\t");
  Serial.print(TempMax);
  Serial.print("\t");
  Serial.println(TempAvg),

  interpolate_image(pixels, INPUT_ROWS, INPUT_COLS, &dest_2d, INTERPOLATED_ROWS, INTERPOLATED_COLS);

}


interp.h

Code: Select all | TOGGLE FULL SIZE
#ifndef interp_H
#define interp_H
//ironbow palette from FLIR 433 entries, converted from 24 bit to 565
const uint16_t camColors[] =
{ 1, 2, 3, 4, 5, 5, 6, 6, 7, 7,
  8, 8, 9, 9, 10, 10, 10, 11, 11, 11,
  12, 12, 12, 12, 13, 2061, 2061, 2062, 2062, 2062,
  2062, 2062, 2062, 4111, 4111, 4111, 4111, 4111, 6159, 6160,
  6160, 6160, 8208, 8208, 8208, 8208, 10257, 10257, 10257, 10257,
  12305, 12305, 12305, 12305, 14353, 14354, 14354, 14354, 14354, 14354,
  16402, 16402, 16402, 16402, 16402, 18450, 18450, 18450, 18450, 18450,
  20498, 20499, 20499, 20499, 22547, 22547, 22547, 22547, 22547, 24595,
  24595, 24595, 24595, 26643, 26643, 26643, 26643, 26643, 28691, 28691,
  28691, 28691, 28691, 30739, 30739, 30739, 30739, 30739, 32787, 32787,
  32787, 32787, 32787, 34835, 34835, 34835, 34835, 34835, 36883, 36883,
  36883, 36883, 38931, 38931, 38931, 38931, 38931, 38931, 40979, 40979,
  40979, 40979, 40979, 40979, 43027, 43027, 43027, 43027, 43027, 43027,
  43027, 45075, 45075, 45074, 45074, 45074, 45074, 45074, 45074, 45074,
  47122, 47154, 47154, 47154, 47154, 47154, 47154, 47154, 47154, 47154,
  49202, 49202, 49234, 49234, 49233, 49233, 49233, 49265, 49265, 49265,
  49265, 49265, 51345, 51344, 51344, 51344, 51344, 51376, 51376, 51376,
  51376, 51407, 51407, 51407, 53455, 53455, 53486, 53486, 53486, 53486,
  53518, 53517, 53517, 53517, 53549, 53548, 53548, 55596, 55628, 55628,
  55627, 55659, 55659, 55658, 55658, 55690, 55689, 55689, 55688, 55720,
  55720, 55719, 57767, 57798, 57798, 57798, 57797, 57829, 57828, 57828,
  57828, 57859, 57859, 57859, 57891, 57891, 57890, 57890, 57922, 57922,
  59970, 60002, 60001, 60001, 60001, 60001, 60001, 60033, 60033, 60033,
  60033, 60065, 60065, 60065, 60097, 60096, 60096, 60096, 60128, 60128,
  60128, 60128, 60128, 60160, 60160, 60160, 62208, 62240, 62240, 62240,
  62240, 62240, 62272, 62272, 62272, 62272, 62272, 62304, 62304, 62304,
  62304, 62336, 62336, 62336, 62336, 62368, 62368, 62368, 62368, 62400,
  62400, 62400, 62432, 62432, 62432, 62464, 62464, 62464, 62464, 62496,
  62496, 62496, 64544, 64576, 64576, 64576, 64576, 64576, 64608, 64608,
  64608, 64608, 64608, 64640, 64640, 64640, 64640, 64672, 64672, 64672,
  64704, 64704, 64704, 64736, 64736, 64736, 64768, 64768, 64768, 64768,
  64800, 64800, 64800, 64832, 64832, 64832, 64864, 64864, 64864, 64864,
  64896, 64896, 64896, 64896, 64928, 64928, 64928, 64960, 64960, 64960,
  64960, 64960, 64992, 64992, 64992, 65024, 65024, 65024, 65024, 65056,
  65056, 65056, 65056, 65088, 65088, 65088, 65088, 65088, 65120, 65120,
  65120, 65120, 65120, 65152, 65152, 65153, 65185, 65185, 65185, 65185,
  65217, 65217, 65217, 65217, 65218, 65250, 65250, 65250, 65251, 65251,
  65251, 65284, 65284, 65284, 65284, 65285, 65317, 65317, 65318, 65318,
  65319, 65319, 65351, 65352, 65352, 65353, 65353, 65386, 65386, 65386,
  65387, 65387, 65388, 65388, 65421, 65421, 65422, 65422, 65423, 65424,
  65424, 65425, 65425, 65458, 65458, 65459, 65459, 65460, 65460, 65461,
  65461, 65462, 65462, 65495, 65495, 65496, 65496, 65496, 65497, 65497,
  65498, 65498, 65499, 65531, 65531, 65532, 65532, 65533, 65533, 65533,
  65534, 65534, 65534
};

float get_point(float *p, uint8_t rows, uint8_t cols, int8_t x, int8_t y);
void set_point(float *p, uint8_t rows, uint8_t cols, int8_t x, int8_t y, float f);
void get_adjacents_1d(float *src, float *dest, uint8_t rows, uint8_t cols, int8_t x, int8_t y);
void get_adjacents_2d(float *src, float *dest, uint8_t rows, uint8_t cols, int8_t x, int8_t y);
float cubicInterpolate(float p[], float x);
float bicubicInterpolate(float p[], float x, float y);
void interpolate_image(float *src, uint8_t src_rows, uint8_t src_cols,
                       float *dest, uint8_t dest_rows, uint8_t dest_cols);


float get_point(float *p, uint8_t rows, uint8_t cols, int8_t x, int8_t y) {
  if (x < 0)        x = 0;
  if (y < 0)        y = 0;
  if (x >= cols)    x = cols - 1;
  if (y >= rows)    y = rows - 1;
  return p[y * cols + x];
}

void set_point(float *p, uint8_t rows, uint8_t cols, int8_t x, int8_t y, float f) {
  //    p[y * cols + x] = f; } //was, for output array (SJR)

  // Now, send blocks of points to display (global boxsize)
  uint16_t colorTemp, colorIndex;
  uint8_t x1, y1, x2, y2;

  if ((x < 0) || (x >= cols)) return;
  if ((y < 0) || (y >= rows)) return;

  x1 = x * boxsize;
  x2 = x1 + boxsize - 1;
  y1 = y * boxsize;
  y2 = y1 + boxsize - 1;

  colorIndex = float(numColors) * (f - TempMin) / TempDif;
  colorIndex = constrain(colorIndex, 0, numColors - 1);
  colorTemp = camColors[colorIndex];
  OLED_DrawRectangle(x1, y1, x2, y2, colorTemp);  //fill must be set by pensize()
}

// src is a grid src_rows * src_cols
// dest is a pre-allocated grid, dest_rows*dest_cols
void interpolate_image(float *src, uint8_t src_rows, uint8_t src_cols,
                       float *dest, uint8_t dest_rows, uint8_t dest_cols) {

  float mu_x = (src_cols - 1.0) / (dest_cols - 1.0);
  float mu_y = (src_rows - 1.0) / (dest_rows - 1.0);

  float adj_2d[16]; // 4x4 matrix for storing adjacents
  float frac_x, frac_y, out;

  for (uint8_t y_idx = 0; y_idx < dest_rows; y_idx++) {
    for (uint8_t x_idx = 0; x_idx < dest_cols; x_idx++) {
      float x = x_idx * mu_x;
      float y = y_idx * mu_y;
      get_adjacents_2d(src, adj_2d, src_rows, src_cols, x, y);
      frac_x = x - (int)x; // we only need the ~delta~ between the points
      frac_y = y - (int)y; // we only need the ~delta~ between the points
      out = bicubicInterpolate(adj_2d, frac_x, frac_y);
      //       Serial.print("\tInterp: "); Serial.println(out);
      set_point(dest, dest_rows, dest_cols, x_idx, y_idx, out);
    }
  }
}

// p is a list of 4 points, 2 to the left, 2 to the right
float cubicInterpolate(float p[], float x) {
  float r = p[1] + (0.5 * x * (p[2] - p[0] + x * (2.0 * p[0] - 5.0 * p[1] + 4.0 * p[2] - p[3] + x * (3.0 * (p[1] - p[2]) + p[3] - p[0]))));
  return r;
}

// p is a 16-point 4x4 array of the 2 rows & columns left/right/above/below
float bicubicInterpolate(float p[], float x, float y) {
  float arr[4] = {0, 0, 0, 0};
  arr[0] = cubicInterpolate(p + 0, x);
  arr[1] = cubicInterpolate(p + 4, x);
  arr[2] = cubicInterpolate(p + 8, x);
  arr[3] = cubicInterpolate(p + 12, x);
  return cubicInterpolate(arr, y);
}

// src is rows*cols and dest is a 4-point array passed in already allocated!
void get_adjacents_1d(float *src, float *dest, uint8_t rows, uint8_t cols, int8_t x, int8_t y) {
  // pick two items to the left
  dest[0] = get_point(src, rows, cols, x - 1, y);
  dest[1] = get_point(src, rows, cols, x, y);
  // pick two items to the right
  dest[2] = get_point(src, rows, cols, x + 1, y);
  dest[3] = get_point(src, rows, cols, x + 2, y);
}

// src is rows*cols and dest is a 16-point array passed in, already allocated.
void get_adjacents_2d(float *src, float *dest, uint8_t rows, uint8_t cols, int8_t x, int8_t y) {
  for (int8_t delta_y = -1; delta_y < 3; delta_y++) { // -1, 0, 1, 2
    float *row = dest + 4 * (delta_y + 1); // index into each chunk of 4
    for (int8_t delta_x = -1; delta_x < 3; delta_x++) { // -1, 0, 1, 2
      row[delta_x + 1] = get_point(src, rows, cols, x + delta_x, y + delta_y);
    }
  }
}
#endif

sj_remington
 
Posts: 147
Joined: Mon Jul 27, 2020 4:51 pm

Re: AMG8833 - Big Red Clumps on TFT

by sj_remington on Fri Sep 24, 2021 10:55 am

In thinking about the overflow problem in the color mapping, this line in the code that I posted immediately above is wrong:

Code: Select all | TOGGLE FULL SIZE
  colorIndex = float(numColors) * (f - TempMin) / TempDif;

The bicubic interpolation procedure can produce values of "f" that exceed the duly recorded TempMax in the image.

To map the interpolated temperatures to the full extent of the color scale takes a bit more work, but one approach is simply to reduce the scale factor by a small percentage, and cap the resulting index.

sj_remington
 
Posts: 147
Joined: Mon Jul 27, 2020 4:51 pm

Re: AMG8833 - Big Red Clumps on TFT

by worn_out on Tue Sep 28, 2021 1:09 pm

Following up...

Spent far too much time on this to arrive at the following:

• Did this with:
ESP32 (two different brands)
Raspberry Pi Pico via Arduino IDE
Raspberry Pi Pico via Thonny, Python IDE
Teensy 4.0

• Results:
ESP32 not quite enough speed and had clumps
Pico via Thonny has too many quirks
Pico via Arduino - I2C issues but got close to working (shortcomming’s of Arduino’s IDE and Pico I2C. Getting better but, the interface/lib’s need work)
Teensy 4.0 Works the best

Efforts:
(• Main problem is the large Clumps)
• I was able to eliminate the small clumps (they were one or two interpolated pixels in size and are now gone)

• Did a detailed experiment, tracking code changes and results on singular and combined parameters/changes

Final Result:
Video Coffee-Cup, shows clear image (except for .GIF quality, but, still has big clumps (no matter what I changed in the code). They are now cleaner clumps but, clumps, nontheless!

Thought about moving up to MLX90640 but I’m reluctant to do it - thinking the same issues just at higher cost...
Attachments
Thermal2x.mp4.gif
Thermal2x.mp4.gif (539.59 KiB) Viewed 84 times

worn_out
 
Posts: 4
Joined: Wed Sep 22, 2021 1:24 pm

Re: AMG8833 - Big Red Clumps on TFT

by sj_remington on Tue Sep 28, 2021 4:01 pm

It is a software problem, not a hardware problem.

You will not understand it until you print out the actual interpolated values, determine how they are used to index the color lookup table, and how that determines which color values are sent to the display.

In the code I posted first, that error has been fixed.

sj_remington
 
Posts: 147
Joined: Mon Jul 27, 2020 4:51 pm

Re: AMG8833 - Big Red Clumps on TFT

by worn_out on Sat Oct 09, 2021 1:48 pm

** SOLVED **

Following up...

The problem was solved by rewiring the TFT, after that worked, I rewired it back to previous but the problem did Not re-occur. Odd problems and I suspect it's the Teensy4.0 (seems to also have problem with the second i2c but, that's a different matter...)

Anyway, all is now good and all thermal codes work.
Attachments
Thermal_Cam.mp4.gif
Thermal_Cam.mp4.gif (292.63 KiB) Viewed 43 times

worn_out
 
Posts: 4
Joined: Wed Sep 22, 2021 1:24 pm

Please be positive and constructive with your questions and comments.