Interpolation of amg8834 sensor data

Status
Not open for further replies.

mash.m

Member
Hi,

i´d like to build a mini thermal imager arround the panasonic amg8834 sensor. i have the basic code running and now i would like to implement some features. first thing is to bicubic interpolate the 8x8 grid to 64x64 for example. i have found some usefull information about interpolation here:

http://paulbourke.net/miscellaneous/interpolation/

i think i understand the math behind the interpolation, but i have no idea how to implement in my code and upscale the 8x8 array. please, i need some help.

regards, markus


Code:
#include <SSD_13XX.h>
#include <Adafruit_GFX.h>
#include <SPI.h>
#include <Wire.h>
#include <Adafruit_AMG88xx.h>


#define __RST 8   )
#define __CS  10  )
#define __DC  9   )
SSD_13XX tft = SSD_13XX(__CS, __DC, __RST);

float   MINTEMP, MAXTEMP;
float pixels[AMG88xx_PIXEL_ARRAY_SIZE];
byte  displayPixelWidth = 8;
byte  displayPixelHeight = 8;

Adafruit_AMG88xx amg;

#define  BLACK           0x0000
#define BLUE            0x001F
#define RED             0xF800
#define GREEN           0x07E0
#define CYAN            0x07FF
#define MAGENTA         0xF81F
#define YELLOW          0xFFE0
#define WHITE           0xFFFF

//the colors we will be using
const uint16_t camColors[] = {0x480F,
                              0x400F, 0x400F, 0x400F, 0x4010, 0x3810, 0x3810, 0x3810, 0x3810, 0x3010, 0x3010,
                              0x3010, 0x2810, 0x2810, 0x2810, 0x2810, 0x2010, 0x2010, 0x2010, 0x1810, 0x1810,
                              0x1811, 0x1811, 0x1011, 0x1011, 0x1011, 0x0811, 0x0811, 0x0811, 0x0011, 0x0011,
                              0x0011, 0x0011, 0x0011, 0x0031, 0x0031, 0x0051, 0x0072, 0x0072, 0x0092, 0x00B2,
                              0x00B2, 0x00D2, 0x00F2, 0x00F2, 0x0112, 0x0132, 0x0152, 0x0152, 0x0172, 0x0192,
                              0x0192, 0x01B2, 0x01D2, 0x01F3, 0x01F3, 0x0213, 0x0233, 0x0253, 0x0253, 0x0273,
                              0x0293, 0x02B3, 0x02D3, 0x02D3, 0x02F3, 0x0313, 0x0333, 0x0333, 0x0353, 0x0373,
                              0x0394, 0x03B4, 0x03D4, 0x03D4, 0x03F4, 0x0414, 0x0434, 0x0454, 0x0474, 0x0474,
                              0x0494, 0x04B4, 0x04D4, 0x04F4, 0x0514, 0x0534, 0x0534, 0x0554, 0x0554, 0x0574,
                              0x0574, 0x0573, 0x0573, 0x0573, 0x0572, 0x0572, 0x0572, 0x0571, 0x0591, 0x0591,
                              0x0590, 0x0590, 0x058F, 0x058F, 0x058F, 0x058E, 0x05AE, 0x05AE, 0x05AD, 0x05AD,
                              0x05AD, 0x05AC, 0x05AC, 0x05AB, 0x05CB, 0x05CB, 0x05CA, 0x05CA, 0x05CA, 0x05C9,
                              0x05C9, 0x05C8, 0x05E8, 0x05E8, 0x05E7, 0x05E7, 0x05E6, 0x05E6, 0x05E6, 0x05E5,
                              0x05E5, 0x0604, 0x0604, 0x0604, 0x0603, 0x0603, 0x0602, 0x0602, 0x0601, 0x0621,
                              0x0621, 0x0620, 0x0620, 0x0620, 0x0620, 0x0E20, 0x0E20, 0x0E40, 0x1640, 0x1640,
                              0x1E40, 0x1E40, 0x2640, 0x2640, 0x2E40, 0x2E60, 0x3660, 0x3660, 0x3E60, 0x3E60,
                              0x3E60, 0x4660, 0x4660, 0x4E60, 0x4E80, 0x5680, 0x5680, 0x5E80, 0x5E80, 0x6680,
                              0x6680, 0x6E80, 0x6EA0, 0x76A0, 0x76A0, 0x7EA0, 0x7EA0, 0x86A0, 0x86A0, 0x8EA0,
                              0x8EC0, 0x96C0, 0x96C0, 0x9EC0, 0x9EC0, 0xA6C0, 0xAEC0, 0xAEC0, 0xB6E0, 0xB6E0,
                              0xBEE0, 0xBEE0, 0xC6E0, 0xC6E0, 0xCEE0, 0xCEE0, 0xD6E0, 0xD700, 0xDF00, 0xDEE0,
                              0xDEC0, 0xDEA0, 0xDE80, 0xDE80, 0xE660, 0xE640, 0xE620, 0xE600, 0xE5E0, 0xE5C0,
                              0xE5A0, 0xE580, 0xE560, 0xE540, 0xE520, 0xE500, 0xE4E0, 0xE4C0, 0xE4A0, 0xE480,
                              0xE460, 0xEC40, 0xEC20, 0xEC00, 0xEBE0, 0xEBC0, 0xEBA0, 0xEB80, 0xEB60, 0xEB40,
                              0xEB20, 0xEB00, 0xEAE0, 0xEAC0, 0xEAA0, 0xEA80, 0xEA60, 0xEA40, 0xF220, 0xF200,
                              0xF1E0, 0xF1C0, 0xF1A0, 0xF180, 0xF160, 0xF140, 0xF100, 0xF0E0, 0xF0C0, 0xF0A0,
                              0xF080, 0xF060, 0xF040, 0xF020, 0xF800,
                             };


void setup() {

  tft.begin();
  tft.setBrightness(15);

  amg.begin();
  Serial.begin(9600);
  delay(100); // Time for Sensor startup

}

void loop() {

  //read all the pixels
  amg.readPixels(pixels);

  //get min and max temp of pixeldata; get mintemp and maxtemp array possiton
  float MAXTEMP = 0;
  int max_i = 0;
  float MINTEMP = 200;
  int min_i = 0;

  for ( int i = 0; i < sizeof(pixels) / sizeof(pixels[0]); i++ )
  {
    if ( pixels[i] > MAXTEMP )
    {
      MAXTEMP = pixels[i];
      max_i = i;
    }
    if ( pixels[i] < MINTEMP )
    {
      MINTEMP = pixels[i];
      min_i = i;
    }
  }

  //map temp to color and pixel to the oled display
  for (int i = 0; i < AMG88xx_PIXEL_ARRAY_SIZE; i++) {
    uint8_t colorIndex = map(pixels[i], MINTEMP, MAXTEMP, 0, 255);
    colorIndex = constrain(colorIndex, 0, 255);

    //draw the pixels!
    tft.fillRect(displayPixelHeight * floor(i / 8), displayPixelWidth * (i % 8),
                 displayPixelHeight, displayPixelWidth, camColors[colorIndex]);

    // mark the hottest point at display
    tft.fillCircle(displayPixelHeight * floor (max_i / 8), displayPixelWidth * (max_i % 8), 4, WHITE);

    // mark the coldest point at display
    //    tft.fillCircle(displayPixelHeight * floor (min_i / 8), displayPixelWidth * (min_i % 8), 4, BLACK);


    //print some text on display for min and max messured temp
    tft.setTextColor(WHITE, BLACK);
    tft.setTextScale(1);
    tft.setCursor(65, 5 );
    tft.print("min");
    tft.setCursor(65, 15 );
    tft.println(MINTEMP);

    tft.setCursor(65, 25 );
    tft.print("max");
    tft.setCursor(65, 35 );
    tft.println(MAXTEMP);
  }
}
 
Bicubic interpolation is essentially the evaluation of
Code:
z(x,y) = C00       + C01*x       + C02*x*x       + C03*x*x*x
       + C10*y     + C11*x*y     + C12*x*x*y     + C13*x*x*x*y
       + C20*y*y   + C21*x*y*y   + C22*x*x*y*y   + C23*x*x*x*y*y
       + C30*y*y*y + C31*x*y*y*y + C32*x*x*y*y*y + C33*x*x*x*y*y*y
If you fit z(x,y) so that
Code:
z(-1,-1) = v00   z(0,-1) = v01   z(1,-1) = v02   z(2,-1) = v03
z(-1, 0) = v10   z(0, 0) = v11   z(1, 0) = v12   z(2, 0) = v13
z(-1, 1) = v20   z(0, 1) = v21   z(1, 1) = v22   z(2, 1) = v23
z(-1, 2) = v30   z(0, 2) = v31   z(1, 2) = v32   z(2, 2) = v33
you can use z(x,y) with x = [0, 1) and y = [0, 1) to interpolate the values, in a square region between v11, v12, v21, and v22 (with the values v00 through v33 being the original pixel values).

If you want to play with the above, the coefficients are, as reported by Maple,
Code:
C00 = v11
C01 = -1/2*v11-1/3*v10+v12-1/6*v13
C02 = -v11+1/2*v10+1/2*v12
C03 = 1/2*v11-1/6*v10+1/6*v13-1/2*v12
C10 = -1/2*v11-1/3*v01+v21-1/6*v31
C11 = 1/9*v00+1/6*v01-1/3*v02+1/18*v03+1/6*v10+1/4*v11-1/2*v12+1/12*v13-1/3*v20-1/2*v21+v22-1/6*v23+1/18*v30+1/12*v31-1/6*v32+1/36*v33
C12 = -1/6*v00+1/3*v01-1/6*v02-1/4*v10+1/2*v11-1/4*v12+1/2*v20-v21+1/2*v22-1/12*v30+1/6*v31-1/12*v32
C13 = 1/18*v00-1/6*v01+1/6*v02-1/18*v03+1/12*v10-1/4*v11+1/4*v12-1/12*v13-1/6*v20+1/2*v21-1/2*v22+1/6*v23+1/36*v30-1/12*v31+1/12*v32-1/36*v33
C20 = -v11+1/2*v01+1/2*v21
C21 = -1/6*v00-1/4*v01+1/2*v02-1/12*v03+1/3*v10+1/2*v11-v12+1/6*v13-1/6*v20-1/4*v21+1/2*v22-1/12*v23
C22 = v11-1/2*v01+1/4*v02-1/2*v12-1/2*v10+1/4*v00+1/4*v22-1/2*v21+1/4*v20
C23 = -1/12*v00+1/4*v01-1/4*v02+1/12*v03+1/6*v10-1/2*v11+1/2*v12-1/6*v13-1/12*v20+1/4*v21-1/4*v22+1/12*v23
C30 = 1/2*v11-1/6*v01+1/6*v31-1/2*v21
C31 = 1/18*v00+1/12*v01-1/6*v02+1/36*v03-1/6*v10-1/4*v11+1/2*v12-1/12*v13+1/6*v20+1/4*v21-1/2*v22+1/12*v23-1/18*v30-1/12*v31+1/6*v32-1/36*v33
C32 = -1/2*v11+1/6*v01-1/12*v02+1/4*v12-1/6*v31+1/12*v32+1/4*v10-1/12*v00+1/12*v30-1/4*v22+1/2*v21-1/4*v20
C33 = 1/36*v00-1/12*v01+1/12*v02-1/36*v03-1/12*v10+1/4*v11-1/4*v12+1/12*v13+1/12*v20-1/4*v21+1/4*v22-1/12*v23-1/36*v30+1/12*v31-1/12*v32+1/36*v33

You can use z(ix/N, iy/N) to calculate the scaled pixels. This scales a W by H -pixel image to (W-1)N+1 by (H-1)N+1 -pixel image, with all original W by H pixel values repeated at z(0,0) in the new pixels.

(If you use z(ix/N+1/2, iy/N+1/2) to calculate the scaled pixels, you get a (W-1)N by (H-1)N -pixel image, but none of the pixels in the old image are copied to the new one.)

In OP's case, scaling 8×8 pixels to 64×64 pixels, means 9×9 sub-pixels (since 9×7+1 = 64). I personally would use a fixed 9×9×4×4 = 1296-element array of coefficients,
Code:
static const float coeff[9][9][4][4] = {
  { { {               0.0,               0.0,               0.0,               0.0 },
      {               0.0,               1.0,               0.0,               0.0 },
      {               0.0,               0.0,               0.0,               0.0 },
      {               0.0,               0.0,               0.0,               0.0 } },
    { {               0.0,        -68/2187.0,               0.0,               0.0 },
      {               0.0,         680/729.0,               0.0,               0.0 },
      {               0.0,         +85/729.0,               0.0,               0.0 },
      {               0.0,        -40/2187.0,               0.0,               0.0 } },
    { {               0.0,       -112/2187.0,               0.0,               0.0 },
      {               0.0,         616/729.0,               0.0,               0.0 },
      {               0.0,        +176/729.0,               0.0,               0.0 },
      {               0.0,        -77/2187.0,               0.0,               0.0 } },
    { {               0.0,           -5/81.0,               0.0,               0.0 },
      {               0.0,           20/27.0,               0.0,               0.0 },
      {               0.0,          +10/27.0,               0.0,               0.0 },
      {               0.0,           -4/81.0,               0.0,               0.0 } },
    { {               0.0,       -140/2187.0,               0.0,               0.0 },
      {               0.0,         455/729.0,               0.0,               0.0 },
      {               0.0,        +364/729.0,               0.0,               0.0 },
      {               0.0,       -130/2187.0,               0.0,               0.0 } },
    { {               0.0,       -130/2187.0,               0.0,               0.0 },
      {               0.0,         364/729.0,               0.0,               0.0 },
      {               0.0,        +455/729.0,               0.0,               0.0 },
      {               0.0,       -140/2187.0,               0.0,               0.0 } },
    { {               0.0,           -4/81.0,               0.0,               0.0 },
      {               0.0,           10/27.0,               0.0,               0.0 },
      {               0.0,          +20/27.0,               0.0,               0.0 },
      {               0.0,           -5/81.0,               0.0,               0.0 } },
    { {               0.0,        -77/2187.0,               0.0,               0.0 },
      {               0.0,         176/729.0,               0.0,               0.0 },
      {               0.0,        +616/729.0,               0.0,               0.0 },
      {               0.0,       -112/2187.0,               0.0,               0.0 } },
    { {               0.0,        -40/2187.0,               0.0,               0.0 },
      {               0.0,          85/729.0,               0.0,               0.0 },
      {               0.0,        +680/729.0,               0.0,               0.0 },
      {               0.0,        -68/2187.0,               0.0,               0.0 } } },
  { { {               0.0,               0.0,               0.0,               0.0 },
      {        -68/2187.0,         680/729.0,         +85/729.0,        -40/2187.0 },
      {               0.0,               0.0,               0.0,               0.0 },
      {               0.0,               0.0,               0.0,               0.0 } },
    { {    4624/4782969.0,  -46240/1594323.0,   -5780/1594323.0,   +2720/4782969.0 },
      {  -46240/1594323.0,  +462400/531441.0,   +57800/531441.0,  -27200/1594323.0 },
      {   -5780/1594323.0,   +57800/531441.0,    +7225/531441.0,   -3400/1594323.0 },
      {   +2720/4782969.0,  -27200/1594323.0,   -3400/1594323.0,   +1600/4782969.0 } },
    { {    7616/4782969.0,  -76160/1594323.0,   -9520/1594323.0,   +4480/4782969.0 },
      {  -41888/1594323.0,  +418880/531441.0,   +52360/531441.0,  -24640/1594323.0 },
      {  -11968/1594323.0,  +119680/531441.0,   +14960/531441.0,   -7040/1594323.0 },
      {   +5236/4782969.0,  -52360/1594323.0,   -6545/1594323.0,   +3080/4782969.0 } },
    { {      340/177147.0,     -3400/59049.0,      -425/59049.0,     +200/177147.0 },
      {     -1360/59049.0,    +13600/19683.0,     +1700/19683.0,      -800/59049.0 },
      {      -680/59049.0,     +6800/19683.0,      +850/19683.0,      -400/59049.0 },
      {     +272/177147.0,     -2720/59049.0,      -340/59049.0,     +160/177147.0 } },
    { {    9520/4782969.0,  -95200/1594323.0,  -11900/1594323.0,   +5600/4782969.0 },
      {  -30940/1594323.0,  +309400/531441.0,   +38675/531441.0,  -18200/1594323.0 },
      {  -24752/1594323.0,  +247520/531441.0,   +30940/531441.0,  -14560/1594323.0 },
      {   +8840/4782969.0,  -88400/1594323.0,  -11050/1594323.0,   +5200/4782969.0 } },
    { {    8840/4782969.0,  -88400/1594323.0,  -11050/1594323.0,   +5200/4782969.0 },
      {  -24752/1594323.0,  +247520/531441.0,   +30940/531441.0,  -14560/1594323.0 },
      {  -30940/1594323.0,  +309400/531441.0,   +38675/531441.0,  -18200/1594323.0 },
      {   +9520/4782969.0,  -95200/1594323.0,  -11900/1594323.0,   +5600/4782969.0 } },
    { {      272/177147.0,     -2720/59049.0,      -340/59049.0,     +160/177147.0 },
      {      -680/59049.0,     +6800/19683.0,      +850/19683.0,      -400/59049.0 },
      {     -1360/59049.0,    +13600/19683.0,     +1700/19683.0,      -800/59049.0 },
      {     +340/177147.0,     -3400/59049.0,      -425/59049.0,     +200/177147.0 } },
    { {    5236/4782969.0,  -52360/1594323.0,   -6545/1594323.0,   +3080/4782969.0 },
      {  -11968/1594323.0,  +119680/531441.0,   +14960/531441.0,   -7040/1594323.0 },
      {  -41888/1594323.0,  +418880/531441.0,   +52360/531441.0,  -24640/1594323.0 },
      {   +7616/4782969.0,  -76160/1594323.0,   -9520/1594323.0,   +4480/4782969.0 } },
    { {    2720/4782969.0,  -27200/1594323.0,   -3400/1594323.0,   +1600/4782969.0 },
      {   -5780/1594323.0,   +57800/531441.0,    +7225/531441.0,   -3400/1594323.0 },
      {  -46240/1594323.0,  +462400/531441.0,   +57800/531441.0,  -27200/1594323.0 },
      {   +4624/4782969.0,  -46240/1594323.0,   -5780/1594323.0,   +2720/4782969.0 } } },
  { { {               0.0,               0.0,               0.0,               0.0 },
      {       -112/2187.0,         616/729.0,        +176/729.0,        -77/2187.0 },
      {               0.0,               0.0,               0.0,               0.0 },
      {               0.0,               0.0,               0.0,               0.0 } },
    { {    7616/4782969.0,  -41888/1594323.0,  -11968/1594323.0,   +5236/4782969.0 },
      {  -76160/1594323.0,  +418880/531441.0,  +119680/531441.0,  -52360/1594323.0 },
      {   -9520/1594323.0,   +52360/531441.0,   +14960/531441.0,   -6545/1594323.0 },
      {   +4480/4782969.0,  -24640/1594323.0,   -7040/1594323.0,   +3080/4782969.0 } },
    { {   12544/4782969.0,  -68992/1594323.0,  -19712/1594323.0,   +8624/4782969.0 },
      {  -68992/1594323.0,  +379456/531441.0,  +108416/531441.0,  -47432/1594323.0 },
      {  -19712/1594323.0,  +108416/531441.0,   +30976/531441.0,  -13552/1594323.0 },
      {   +8624/4782969.0,  -47432/1594323.0,  -13552/1594323.0,   +5929/4782969.0 } },
    { {      560/177147.0,     -3080/59049.0,      -880/59049.0,     +385/177147.0 },
      {     -2240/59049.0,    +12320/19683.0,     +3520/19683.0,     -1540/59049.0 },
      {     -1120/59049.0,     +6160/19683.0,     +1760/19683.0,      -770/59049.0 },
      {     +448/177147.0,     -2464/59049.0,      -704/59049.0,     +308/177147.0 } },
    { {   15680/4782969.0,  -86240/1594323.0,  -24640/1594323.0,  +10780/4782969.0 },
      {  -50960/1594323.0,  +280280/531441.0,   +80080/531441.0,  -35035/1594323.0 },
      {  -40768/1594323.0,  +224224/531441.0,   +64064/531441.0,  -28028/1594323.0 },
      {  +14560/4782969.0,  -80080/1594323.0,  -22880/1594323.0,  +10010/4782969.0 } },
    { {   14560/4782969.0,  -80080/1594323.0,  -22880/1594323.0,  +10010/4782969.0 },
      {  -40768/1594323.0,  +224224/531441.0,   +64064/531441.0,  -28028/1594323.0 },
      {  -50960/1594323.0,  +280280/531441.0,   +80080/531441.0,  -35035/1594323.0 },
      {  +15680/4782969.0,  -86240/1594323.0,  -24640/1594323.0,  +10780/4782969.0 } },
    { {      448/177147.0,     -2464/59049.0,      -704/59049.0,     +308/177147.0 },
      {     -1120/59049.0,     +6160/19683.0,     +1760/19683.0,      -770/59049.0 },
      {     -2240/59049.0,    +12320/19683.0,     +3520/19683.0,     -1540/59049.0 },
      {     +560/177147.0,     -3080/59049.0,      -880/59049.0,     +385/177147.0 } },
    { {    8624/4782969.0,  -47432/1594323.0,  -13552/1594323.0,   +5929/4782969.0 },
      {  -19712/1594323.0,  +108416/531441.0,   +30976/531441.0,  -13552/1594323.0 },
      {  -68992/1594323.0,  +379456/531441.0,  +108416/531441.0,  -47432/1594323.0 },
      {  +12544/4782969.0,  -68992/1594323.0,  -19712/1594323.0,   +8624/4782969.0 } },
    { {    4480/4782969.0,  -24640/1594323.0,   -7040/1594323.0,   +3080/4782969.0 },
      {   -9520/1594323.0,   +52360/531441.0,   +14960/531441.0,   -6545/1594323.0 },
      {  -76160/1594323.0,  +418880/531441.0,  +119680/531441.0,  -52360/1594323.0 },
      {   +7616/4782969.0,  -41888/1594323.0,  -11968/1594323.0,   +5236/4782969.0 } } },
  { { {               0.0,               0.0,               0.0,               0.0 },
      {           -5/81.0,           20/27.0,          +10/27.0,           -4/81.0 },
      {               0.0,               0.0,               0.0,               0.0 },
      {               0.0,               0.0,               0.0,               0.0 } },
    { {      340/177147.0,     -1360/59049.0,      -680/59049.0,     +272/177147.0 },
      {     -3400/59049.0,    +13600/19683.0,     +6800/19683.0,     -2720/59049.0 },
      {      -425/59049.0,     +1700/19683.0,      +850/19683.0,      -340/59049.0 },
      {     +200/177147.0,      -800/59049.0,      -400/59049.0,     +160/177147.0 } },
    { {      560/177147.0,     -2240/59049.0,     -1120/59049.0,     +448/177147.0 },
      {     -3080/59049.0,    +12320/19683.0,     +6160/19683.0,     -2464/59049.0 },
      {      -880/59049.0,     +3520/19683.0,     +1760/19683.0,      -704/59049.0 },
      {     +385/177147.0,     -1540/59049.0,      -770/59049.0,     +308/177147.0 } },
    { {         25/6561.0,       -100/2187.0,        -50/2187.0,        +20/6561.0 },
      {       -100/2187.0,        +400/729.0,        +200/729.0,        -80/2187.0 },
      {        -50/2187.0,        +200/729.0,        +100/729.0,        -40/2187.0 },
      {        +20/6561.0,        -80/2187.0,        -40/2187.0,        +16/6561.0 } },
    { {      700/177147.0,     -2800/59049.0,     -1400/59049.0,     +560/177147.0 },
      {     -2275/59049.0,     +9100/19683.0,     +4550/19683.0,     -1820/59049.0 },
      {     -1820/59049.0,     +7280/19683.0,     +3640/19683.0,     -1456/59049.0 },
      {     +650/177147.0,     -2600/59049.0,     -1300/59049.0,     +520/177147.0 } },
    { {      650/177147.0,     -2600/59049.0,     -1300/59049.0,     +520/177147.0 },
      {     -1820/59049.0,     +7280/19683.0,     +3640/19683.0,     -1456/59049.0 },
      {     -2275/59049.0,     +9100/19683.0,     +4550/19683.0,     -1820/59049.0 },
      {     +700/177147.0,     -2800/59049.0,     -1400/59049.0,     +560/177147.0 } },
    { {         20/6561.0,        -80/2187.0,        -40/2187.0,        +16/6561.0 },
      {        -50/2187.0,        +200/729.0,        +100/729.0,        -40/2187.0 },
      {       -100/2187.0,        +400/729.0,        +200/729.0,        -80/2187.0 },
      {        +25/6561.0,       -100/2187.0,        -50/2187.0,        +20/6561.0 } },
    { {      385/177147.0,     -1540/59049.0,      -770/59049.0,     +308/177147.0 },
      {      -880/59049.0,     +3520/19683.0,     +1760/19683.0,      -704/59049.0 },
      {     -3080/59049.0,    +12320/19683.0,     +6160/19683.0,     -2464/59049.0 },
      {     +560/177147.0,     -2240/59049.0,     -1120/59049.0,     +448/177147.0 } },
    { {      200/177147.0,      -800/59049.0,      -400/59049.0,     +160/177147.0 },
      {      -425/59049.0,     +1700/19683.0,      +850/19683.0,      -340/59049.0 },
      {     -3400/59049.0,    +13600/19683.0,     +6800/19683.0,     -2720/59049.0 },
      {     +340/177147.0,     -1360/59049.0,      -680/59049.0,     +272/177147.0 } } },
  { { {               0.0,               0.0,               0.0,               0.0 },
      {       -140/2187.0,         455/729.0,        +364/729.0,       -130/2187.0 },
      {               0.0,               0.0,               0.0,               0.0 },
      {               0.0,               0.0,               0.0,               0.0 } },
    { {    9520/4782969.0,  -30940/1594323.0,  -24752/1594323.0,   +8840/4782969.0 },
      {  -95200/1594323.0,  +309400/531441.0,  +247520/531441.0,  -88400/1594323.0 },
      {  -11900/1594323.0,   +38675/531441.0,   +30940/531441.0,  -11050/1594323.0 },
      {   +5600/4782969.0,  -18200/1594323.0,  -14560/1594323.0,   +5200/4782969.0 } },
    { {   15680/4782969.0,  -50960/1594323.0,  -40768/1594323.0,  +14560/4782969.0 },
      {  -86240/1594323.0,  +280280/531441.0,  +224224/531441.0,  -80080/1594323.0 },
      {  -24640/1594323.0,   +80080/531441.0,   +64064/531441.0,  -22880/1594323.0 },
      {  +10780/4782969.0,  -35035/1594323.0,  -28028/1594323.0,  +10010/4782969.0 } },
    { {      700/177147.0,     -2275/59049.0,     -1820/59049.0,     +650/177147.0 },
      {     -2800/59049.0,     +9100/19683.0,     +7280/19683.0,     -2600/59049.0 },
      {     -1400/59049.0,     +4550/19683.0,     +3640/19683.0,     -1300/59049.0 },
      {     +560/177147.0,     -1820/59049.0,     -1456/59049.0,     +520/177147.0 } },
    { {   19600/4782969.0,  -63700/1594323.0,  -50960/1594323.0,  +18200/4782969.0 },
      {  -63700/1594323.0,  +207025/531441.0,  +165620/531441.0,  -59150/1594323.0 },
      {  -50960/1594323.0,  +165620/531441.0,  +132496/531441.0,  -47320/1594323.0 },
      {  +18200/4782969.0,  -59150/1594323.0,  -47320/1594323.0,  +16900/4782969.0 } },
    { {   18200/4782969.0,  -59150/1594323.0,  -47320/1594323.0,  +16900/4782969.0 },
      {  -50960/1594323.0,  +165620/531441.0,  +132496/531441.0,  -47320/1594323.0 },
      {  -63700/1594323.0,  +207025/531441.0,  +165620/531441.0,  -59150/1594323.0 },
      {  +19600/4782969.0,  -63700/1594323.0,  -50960/1594323.0,  +18200/4782969.0 } },
    { {      560/177147.0,     -1820/59049.0,     -1456/59049.0,     +520/177147.0 },
      {     -1400/59049.0,     +4550/19683.0,     +3640/19683.0,     -1300/59049.0 },
      {     -2800/59049.0,     +9100/19683.0,     +7280/19683.0,     -2600/59049.0 },
      {     +700/177147.0,     -2275/59049.0,     -1820/59049.0,     +650/177147.0 } },
    { {   10780/4782969.0,  -35035/1594323.0,  -28028/1594323.0,  +10010/4782969.0 },
      {  -24640/1594323.0,   +80080/531441.0,   +64064/531441.0,  -22880/1594323.0 },
      {  -86240/1594323.0,  +280280/531441.0,  +224224/531441.0,  -80080/1594323.0 },
      {  +15680/4782969.0,  -50960/1594323.0,  -40768/1594323.0,  +14560/4782969.0 } },
    { {    5600/4782969.0,  -18200/1594323.0,  -14560/1594323.0,   +5200/4782969.0 },
      {  -11900/1594323.0,   +38675/531441.0,   +30940/531441.0,  -11050/1594323.0 },
      {  -95200/1594323.0,  +309400/531441.0,  +247520/531441.0,  -88400/1594323.0 },
      {   +9520/4782969.0,  -30940/1594323.0,  -24752/1594323.0,   +8840/4782969.0 } } },
  { { {               0.0,               0.0,               0.0,               0.0 },
      {       -130/2187.0,         364/729.0,        +455/729.0,       -140/2187.0 },
      {               0.0,               0.0,               0.0,               0.0 },
      {               0.0,               0.0,               0.0,               0.0 } },
    { {    8840/4782969.0,  -24752/1594323.0,  -30940/1594323.0,   +9520/4782969.0 },
      {  -88400/1594323.0,  +247520/531441.0,  +309400/531441.0,  -95200/1594323.0 },
      {  -11050/1594323.0,   +30940/531441.0,   +38675/531441.0,  -11900/1594323.0 },
      {   +5200/4782969.0,  -14560/1594323.0,  -18200/1594323.0,   +5600/4782969.0 } },
    { {   14560/4782969.0,  -40768/1594323.0,  -50960/1594323.0,  +15680/4782969.0 },
      {  -80080/1594323.0,  +224224/531441.0,  +280280/531441.0,  -86240/1594323.0 },
      {  -22880/1594323.0,   +64064/531441.0,   +80080/531441.0,  -24640/1594323.0 },
      {  +10010/4782969.0,  -28028/1594323.0,  -35035/1594323.0,  +10780/4782969.0 } },
    { {      650/177147.0,     -1820/59049.0,     -2275/59049.0,     +700/177147.0 },
      {     -2600/59049.0,     +7280/19683.0,     +9100/19683.0,     -2800/59049.0 },
      {     -1300/59049.0,     +3640/19683.0,     +4550/19683.0,     -1400/59049.0 },
      {     +520/177147.0,     -1456/59049.0,     -1820/59049.0,     +560/177147.0 } },
    { {   18200/4782969.0,  -50960/1594323.0,  -63700/1594323.0,  +19600/4782969.0 },
      {  -59150/1594323.0,  +165620/531441.0,  +207025/531441.0,  -63700/1594323.0 },
      {  -47320/1594323.0,  +132496/531441.0,  +165620/531441.0,  -50960/1594323.0 },
      {  +16900/4782969.0,  -47320/1594323.0,  -59150/1594323.0,  +18200/4782969.0 } },
    { {   16900/4782969.0,  -47320/1594323.0,  -59150/1594323.0,  +18200/4782969.0 },
      {  -47320/1594323.0,  +132496/531441.0,  +165620/531441.0,  -50960/1594323.0 },
      {  -59150/1594323.0,  +165620/531441.0,  +207025/531441.0,  -63700/1594323.0 },
      {  +18200/4782969.0,  -50960/1594323.0,  -63700/1594323.0,  +19600/4782969.0 } },
    { {      520/177147.0,     -1456/59049.0,     -1820/59049.0,     +560/177147.0 },
      {     -1300/59049.0,     +3640/19683.0,     +4550/19683.0,     -1400/59049.0 },
      {     -2600/59049.0,     +7280/19683.0,     +9100/19683.0,     -2800/59049.0 },
      {     +650/177147.0,     -1820/59049.0,     -2275/59049.0,     +700/177147.0 } },
    { {   10010/4782969.0,  -28028/1594323.0,  -35035/1594323.0,  +10780/4782969.0 },
      {  -22880/1594323.0,   +64064/531441.0,   +80080/531441.0,  -24640/1594323.0 },
      {  -80080/1594323.0,  +224224/531441.0,  +280280/531441.0,  -86240/1594323.0 },
      {  +14560/4782969.0,  -40768/1594323.0,  -50960/1594323.0,  +15680/4782969.0 } },
    { {    5200/4782969.0,  -14560/1594323.0,  -18200/1594323.0,   +5600/4782969.0 },
      {  -11050/1594323.0,   +30940/531441.0,   +38675/531441.0,  -11900/1594323.0 },
      {  -88400/1594323.0,  +247520/531441.0,  +309400/531441.0,  -95200/1594323.0 },
      {   +8840/4782969.0,  -24752/1594323.0,  -30940/1594323.0,   +9520/4782969.0 } } },
  { { {               0.0,               0.0,               0.0,               0.0 },
      {           -4/81.0,           10/27.0,          +20/27.0,           -5/81.0 },
      {               0.0,               0.0,               0.0,               0.0 },
      {               0.0,               0.0,               0.0,               0.0 } },
    { {      272/177147.0,      -680/59049.0,     -1360/59049.0,     +340/177147.0 },
      {     -2720/59049.0,     +6800/19683.0,    +13600/19683.0,     -3400/59049.0 },
      {      -340/59049.0,      +850/19683.0,     +1700/19683.0,      -425/59049.0 },
      {     +160/177147.0,      -400/59049.0,      -800/59049.0,     +200/177147.0 } },
    { {      448/177147.0,     -1120/59049.0,     -2240/59049.0,     +560/177147.0 },
      {     -2464/59049.0,     +6160/19683.0,    +12320/19683.0,     -3080/59049.0 },
      {      -704/59049.0,     +1760/19683.0,     +3520/19683.0,      -880/59049.0 },
      {     +308/177147.0,      -770/59049.0,     -1540/59049.0,     +385/177147.0 } },
    { {         20/6561.0,        -50/2187.0,       -100/2187.0,        +25/6561.0 },
      {        -80/2187.0,        +200/729.0,        +400/729.0,       -100/2187.0 },
      {        -40/2187.0,        +100/729.0,        +200/729.0,        -50/2187.0 },
      {        +16/6561.0,        -40/2187.0,        -80/2187.0,        +20/6561.0 } },
    { {      560/177147.0,     -1400/59049.0,     -2800/59049.0,     +700/177147.0 },
      {     -1820/59049.0,     +4550/19683.0,     +9100/19683.0,     -2275/59049.0 },
      {     -1456/59049.0,     +3640/19683.0,     +7280/19683.0,     -1820/59049.0 },
      {     +520/177147.0,     -1300/59049.0,     -2600/59049.0,     +650/177147.0 } },
    { {      520/177147.0,     -1300/59049.0,     -2600/59049.0,     +650/177147.0 },
      {     -1456/59049.0,     +3640/19683.0,     +7280/19683.0,     -1820/59049.0 },
      {     -1820/59049.0,     +4550/19683.0,     +9100/19683.0,     -2275/59049.0 },
      {     +560/177147.0,     -1400/59049.0,     -2800/59049.0,     +700/177147.0 } },
    { {         16/6561.0,        -40/2187.0,        -80/2187.0,        +20/6561.0 },
      {        -40/2187.0,        +100/729.0,        +200/729.0,        -50/2187.0 },
      {        -80/2187.0,        +200/729.0,        +400/729.0,       -100/2187.0 },
      {        +20/6561.0,        -50/2187.0,       -100/2187.0,        +25/6561.0 } },
    { {      308/177147.0,      -770/59049.0,     -1540/59049.0,     +385/177147.0 },
      {      -704/59049.0,     +1760/19683.0,     +3520/19683.0,      -880/59049.0 },
      {     -2464/59049.0,     +6160/19683.0,    +12320/19683.0,     -3080/59049.0 },
      {     +448/177147.0,     -1120/59049.0,     -2240/59049.0,     +560/177147.0 } },
    { {      160/177147.0,      -400/59049.0,      -800/59049.0,     +200/177147.0 },
      {      -340/59049.0,      +850/19683.0,     +1700/19683.0,      -425/59049.0 },
      {     -2720/59049.0,     +6800/19683.0,    +13600/19683.0,     -3400/59049.0 },
      {     +272/177147.0,      -680/59049.0,     -1360/59049.0,     +340/177147.0 } } },
  { { {               0.0,               0.0,               0.0,               0.0 },
      {        -77/2187.0,         176/729.0,        +616/729.0,       -112/2187.0 },
      {               0.0,               0.0,               0.0,               0.0 },
      {               0.0,               0.0,               0.0,               0.0 } },
    { {    5236/4782969.0,  -11968/1594323.0,  -41888/1594323.0,   +7616/4782969.0 },
      {  -52360/1594323.0,  +119680/531441.0,  +418880/531441.0,  -76160/1594323.0 },
      {   -6545/1594323.0,   +14960/531441.0,   +52360/531441.0,   -9520/1594323.0 },
      {   +3080/4782969.0,   -7040/1594323.0,  -24640/1594323.0,   +4480/4782969.0 } },
    { {    8624/4782969.0,  -19712/1594323.0,  -68992/1594323.0,  +12544/4782969.0 },
      {  -47432/1594323.0,  +108416/531441.0,  +379456/531441.0,  -68992/1594323.0 },
      {  -13552/1594323.0,   +30976/531441.0,  +108416/531441.0,  -19712/1594323.0 },
      {   +5929/4782969.0,  -13552/1594323.0,  -47432/1594323.0,   +8624/4782969.0 } },
    { {      385/177147.0,      -880/59049.0,     -3080/59049.0,     +560/177147.0 },
      {     -1540/59049.0,     +3520/19683.0,    +12320/19683.0,     -2240/59049.0 },
      {      -770/59049.0,     +1760/19683.0,     +6160/19683.0,     -1120/59049.0 },
      {     +308/177147.0,      -704/59049.0,     -2464/59049.0,     +448/177147.0 } },
    { {   10780/4782969.0,  -24640/1594323.0,  -86240/1594323.0,  +15680/4782969.0 },
      {  -35035/1594323.0,   +80080/531441.0,  +280280/531441.0,  -50960/1594323.0 },
      {  -28028/1594323.0,   +64064/531441.0,  +224224/531441.0,  -40768/1594323.0 },
      {  +10010/4782969.0,  -22880/1594323.0,  -80080/1594323.0,  +14560/4782969.0 } },
    { {   10010/4782969.0,  -22880/1594323.0,  -80080/1594323.0,  +14560/4782969.0 },
      {  -28028/1594323.0,   +64064/531441.0,  +224224/531441.0,  -40768/1594323.0 },
      {  -35035/1594323.0,   +80080/531441.0,  +280280/531441.0,  -50960/1594323.0 },
      {  +10780/4782969.0,  -24640/1594323.0,  -86240/1594323.0,  +15680/4782969.0 } },
    { {      308/177147.0,      -704/59049.0,     -2464/59049.0,     +448/177147.0 },
      {      -770/59049.0,     +1760/19683.0,     +6160/19683.0,     -1120/59049.0 },
      {     -1540/59049.0,     +3520/19683.0,    +12320/19683.0,     -2240/59049.0 },
      {     +385/177147.0,      -880/59049.0,     -3080/59049.0,     +560/177147.0 } },
    { {    5929/4782969.0,  -13552/1594323.0,  -47432/1594323.0,   +8624/4782969.0 },
      {  -13552/1594323.0,   +30976/531441.0,  +108416/531441.0,  -19712/1594323.0 },
      {  -47432/1594323.0,  +108416/531441.0,  +379456/531441.0,  -68992/1594323.0 },
      {   +8624/4782969.0,  -19712/1594323.0,  -68992/1594323.0,  +12544/4782969.0 } },
    { {    3080/4782969.0,   -7040/1594323.0,  -24640/1594323.0,   +4480/4782969.0 },
      {   -6545/1594323.0,   +14960/531441.0,   +52360/531441.0,   -9520/1594323.0 },
      {  -52360/1594323.0,  +119680/531441.0,  +418880/531441.0,  -76160/1594323.0 },
      {   +5236/4782969.0,  -11968/1594323.0,  -41888/1594323.0,   +7616/4782969.0 } } },
  { { {               0.0,               0.0,               0.0,               0.0 },
      {        -40/2187.0,          85/729.0,        +680/729.0,        -68/2187.0 },
      {               0.0,               0.0,               0.0,               0.0 },
      {               0.0,               0.0,               0.0,               0.0 } },
    { {    2720/4782969.0,   -5780/1594323.0,  -46240/1594323.0,   +4624/4782969.0 },
      {  -27200/1594323.0,   +57800/531441.0,  +462400/531441.0,  -46240/1594323.0 },
      {   -3400/1594323.0,    +7225/531441.0,   +57800/531441.0,   -5780/1594323.0 },
      {   +1600/4782969.0,   -3400/1594323.0,  -27200/1594323.0,   +2720/4782969.0 } },
    { {    4480/4782969.0,   -9520/1594323.0,  -76160/1594323.0,   +7616/4782969.0 },
      {  -24640/1594323.0,   +52360/531441.0,  +418880/531441.0,  -41888/1594323.0 },
      {   -7040/1594323.0,   +14960/531441.0,  +119680/531441.0,  -11968/1594323.0 },
      {   +3080/4782969.0,   -6545/1594323.0,  -52360/1594323.0,   +5236/4782969.0 } },
    { {      200/177147.0,      -425/59049.0,     -3400/59049.0,     +340/177147.0 },
      {      -800/59049.0,     +1700/19683.0,    +13600/19683.0,     -1360/59049.0 },
      {      -400/59049.0,      +850/19683.0,     +6800/19683.0,      -680/59049.0 },
      {     +160/177147.0,      -340/59049.0,     -2720/59049.0,     +272/177147.0 } },
    { {    5600/4782969.0,  -11900/1594323.0,  -95200/1594323.0,   +9520/4782969.0 },
      {  -18200/1594323.0,   +38675/531441.0,  +309400/531441.0,  -30940/1594323.0 },
      {  -14560/1594323.0,   +30940/531441.0,  +247520/531441.0,  -24752/1594323.0 },
      {   +5200/4782969.0,  -11050/1594323.0,  -88400/1594323.0,   +8840/4782969.0 } },
    { {    5200/4782969.0,  -11050/1594323.0,  -88400/1594323.0,   +8840/4782969.0 },
      {  -14560/1594323.0,   +30940/531441.0,  +247520/531441.0,  -24752/1594323.0 },
      {  -18200/1594323.0,   +38675/531441.0,  +309400/531441.0,  -30940/1594323.0 },
      {   +5600/4782969.0,  -11900/1594323.0,  -95200/1594323.0,   +9520/4782969.0 } },
    { {      160/177147.0,      -340/59049.0,     -2720/59049.0,     +272/177147.0 },
      {      -400/59049.0,      +850/19683.0,     +6800/19683.0,      -680/59049.0 },
      {      -800/59049.0,     +1700/19683.0,    +13600/19683.0,     -1360/59049.0 },
      {     +200/177147.0,      -425/59049.0,     -3400/59049.0,     +340/177147.0 } },
    { {    3080/4782969.0,   -6545/1594323.0,  -52360/1594323.0,   +5236/4782969.0 },
      {   -7040/1594323.0,   +14960/531441.0,  +119680/531441.0,  -11968/1594323.0 },
      {  -24640/1594323.0,   +52360/531441.0,  +418880/531441.0,  -41888/1594323.0 },
      {   +4480/4782969.0,   -9520/1594323.0,  -76160/1594323.0,   +7616/4782969.0 } },
    { {    1600/4782969.0,   -3400/1594323.0,  -27200/1594323.0,   +2720/4782969.0 },
      {   -3400/1594323.0,    +7225/531441.0,   +57800/531441.0,   -5780/1594323.0 },
      {  -27200/1594323.0,   +57800/531441.0,  +462400/531441.0,  -46240/1594323.0 },
      {   +2720/4782969.0,   -5780/1594323.0,  -46240/1594323.0,   +4624/4782969.0 } }
} };
so that you can calculate all but the rightmost/bottommost new pixels using
Code:
for (ox = 0; ox < 7; ox++) {
    for (oy = 0; ox < 7; oy++) {
        for (ny = 0; ny < 9; ny++) {
            for (nx = 0; nx < 9; nx++) {
                value = 0.0f;
                for (sy = 0; sy < 4; sy++)
                    for (sx = 0; sx < 4; sx++)
                        value += coeff[ny][nx][sy][sx] * old_pixel[CLAMP(oy+sy-1)][CLAMP(ox+sx-1)];
                new_pixel[oy*9+ny][ox*9+nx] = value;
            }
        }
    }
}

new_pixel[63][63] = old_pixel[7][7];

/* Rightmost column of pixels */
ox = 7; nx = 0;
for (oy = 0; oy < 7; oy++) {
    for (ny = 0; ny < 9; ny++) {
        value = 0.0f;
        for (sy = 0; sy < 4; sy++)
            for (sx = 0; sx < 4; sx++)
                value += coeff[ny][nx][sy][sx] * old_pixel[CLAMP(oy+sy-1)][CLAMP(ox+sx-1)];
        new_pixel[oy*9+ny][ox*9+nx] = value;
    }
}

/* Bottommost row of pixels */
oy = 7; ny = 0;
for (ox = 0; ox < 7; ox ++) {
    for (nx = 0; nx < 9; nx++) {
        value = 0.0f;
        for (sy = 0; sy < 4; sy++)
            for (sx = 0; sx < 4; sx++)
                value += coeff[ny][nx][sy][sx] * old_pixel[CLAMP(oy+sy-1)][CLAMP(ox+sx-1)];
        new_pixel[oy*9+ny][ox*9+nx] = value;
    }
}
where the CLAMP macro clamps the argument to 0 to 7, inclusive, so that the lookup does not undeflow/overflow.

Because cubic interpolation can overshoot the values, the new pixel values may exceed the range in the old pixels. So, if you are working with a limited range, you might wish to clamp value to your range (before assigning to the new image).

The bottom rightmost pixel is the same as the bottom rightmost pixel in the original image; this is the reason for the +1 in the new size in pixels. (Another way to think about is, is that we interpolate in the spaces between pixels, so there are W-1 by H-1 such spaces in an W by H image. Because the coefficient array has the original pixel value in the initial cell, we can do one extra row and column of pixels. The CLAMP() macro kind-of duplicates the edge pixels in the old image, so that we don't need to specially handle the outermost pixels.)

(It is also quite possible to write eg. an awk script that generates C code that uses only literal floating-point or integer coefficients, to calculate each pixel value separately without any loops, but I think that would be quite overkill, especially because very few of the coefficients are zero.)
 
Hi Nominal Animal,

thanks for you replay. i will try to understand what you wrote next days and Play with the code, but it seem´s quite difficult to undestand for me. i´m an electronic engineer not a programmer. i just start to learn about it. i already had some Projects with arduinos, but only easy things with some Basic functions.

regards, markus
 
Well, first thing to understand is that bicubic interpolation uses 4×4 pixels from the original image to calculate each pixel in the scaled image.

At the borders, mathematically, we duplicate the edge pixels. For example, if the source image was 5×5, we actually use a 7×7 region,
Code:
A A B C D E E
A A B C D E E     A B C D E
F F G H I J J     F G H I J
K K L M N O O  <- K L M N O
P P Q R S T T     P Q R S T
U U V W X Y Y     U V W X Y
U U V W X Y Y
The CLAMP() macro handled this in my original post; below, this is handled by the old_pixel() function.

Weight of each of the sixteen source pixels depends on the fractional coordinates.

If zero fractional coordinates produces the value of an original pixel, then we should add an extra row, and an extra column, of pixels to the new image, where one of the fractional coordinates is zero (x for the added column, y for the added row); this makes the resulting image a symmetric copy of the original. (That is, if you rotate or mirror the initial image, the result is the same as if you just generated one scaled image and then rotated that. If you do not add the extra column and row of pixels, this will not hold.)

As an example, if we scale each pixel to N×N pixels, then we use A A B C, F F G H, K K L M, and P P Q R to calculate the ones in the square bracketed by A B F G, with A in the upper left corner (in the scaled image).

The C00 .. C33 coefficients shown in my post are the correct ones, if those sixteen pixels are labeled v00 v01 v02 v03, v10 v11 v12 v13, v20 v21 v22 v23, v30 v31 v32 and v33.

We can write the code in my previous post in a more generic fashion, although it will become quite, quite slow:
Code:
static inline float old_pixel(int x, int y)
{
    if (x < 0)
        x = 0;
    else
    if (x > 7)
        x = 7;

    if (y < 0)
        y = 0;
    else
    if (y > 7)
        y = 7;

    return pixel[x + 8*y];
}

static float new_pixel(const float x, const float y)
{
    const int ix = (int)x;
    const int iy = (int)y;
    const float fx = x - (float)ix;
    const float fy = y - (float)iy;

    const float v[4][4] = {
        { old_pixel(ix-1, iy-1), old_pixel(ix, iy-1), old_pixel(ix+1, iy-1), old_pixel(ix+2, iy-1) },
        { old_pixel(ix-1, iy),   old_pixel(ix, iy),   old_pixel(ix+1, iy),   old_pixel(ix+2, iy)   },
        { old_pixel(ix-1, iy+1), old_pixel(ix, iy+1), old_pixel(ix+1, iy+1), old_pixel(ix+2, iy+1) },
        { old_pixel(ix-1, iy+2), old_pixel(ix, iy+2), old_pixel(ix+1, iy+2), old_pixel(ix+2, iy+2) }
    };

    const float c[4][4] = {
        {                                v[1][1],
          -1.0f/3.0f*v[1][0] -1.0f/2.0f*v[1][1] +          v[1][2] -1.0f/6.0f*v[1][3],
           1.0f/2.0f*v[1][0] -          v[1][1] +1.0f/2.0f*v[1][2],
          -1.0f/6.0f*v[1][0] +1.0f/2.0f*v[1][1] -1.0f/2.0f*v[1][2] +1.0f/6.0f*v[1][3]
        },
        {                     -1.0f/3.0f*v[0][1] -1.0f/2.0f*v[1][1]                                                                                                                      +          v[2][1]                                                           -1.0f/ 6.0f*v[3][1],
           1.0f/ 9.0f*v[0][0] +1.0f/6.0f*v[0][1] -1.0f/3.0f*v[0][2] +1.0f/18.0f*v[0][3] +1.0f/ 6.0f*v[1][0] +1.0f/4.0f*v[1][1] -1.0f/2.0f*v[1][2] +1.0f/12.0f*v[1][3] -1.0f/3.0f*v[2][0] -1.0f/2.0f*v[2][1] +          v[2][2] -1.0f/6.0f*v[2][3] +1.0f/18.0f*v[3][0] +1.0f/12.0f*v[3][1] -1.0f/ 6.0f*v[3][2] +1.0f/36.0f*v[3][3],
          -1.0f/ 6.0f*v[0][0] +1.0f/3.0f*v[0][1] -1.0f/6.0f*v[0][2]                     -1.0f/ 4.0f*v[1][0] +1.0f/2.0f*v[1][1] -1.0f/4.0f*v[1][2]                     +1.0f/2.0f*v[2][0] -          v[2][1] +1.0f/2.0f*v[2][2]                    -1.0f/12.0f*v[3][0] +1.0f/ 6.0f*v[3][1] -1.0f/12.0f*v[3][2],
           1.0f/18.0f*v[0][0] -1.0f/6.0f*v[0][1] +1.0f/6.0f*v[0][2] -1.0f/18.0f*v[0][3] +1.0f/12.0f*v[1][0] -1.0f/4.0f*v[1][1] +1.0f/4.0f*v[1][2] -1.0f/12.0f*v[1][3] -1.0f/6.0f*v[2][0] +1.0f/2.0f*v[2][1] -1.0f/2.0f*v[2][2] +1.0f/6.0f*v[2][3] +1.0f/36.0f*v[3][0] -1.0f/12.0f*v[3][1] +1.0f/12.0f*v[3][2] -1.0f/36.0f*v[3][3]
        },
        {                     +1.0f/2.0f*v[0][1]                                                           -          v[1][1]                                                           +1.0f/2.0f*v[2][1],
          -1.0f/ 6.0f*v[0][0] -1.0f/4.0f*v[0][1] +1.0f/2.0f*v[0][2] -1.0f/12.0f*v[0][3] +1.0f/3.0f*v[1][0] +1.0f/2.0f*v[1][1] -          v[1][2] +1.0f/6.0f*v[1][3] -1.0f/ 6.0f*v[2][0] -1.0f/4.0f*v[2][1] +1.0f/2.0f*v[2][2] -1.0f/12.0f*v[2][3],
          +1.0f/ 4.0f*v[0][0] -1.0f/2.0f*v[0][1] +1.0f/4.0f*v[0][2]                     -1.0f/2.0f*v[1][0] +          v[1][1] -1.0f/2.0f*v[1][2]                    +1.0f/ 4.0f*v[2][0] -1.0f/2.0f*v[2][1] +1.0f/4.0f*v[2][2],
          -1.0f/12.0f*v[0][0] +1.0f/4.0f*v[0][1] -1.0f/4.0f*v[0][2] +1.0f/12.0f*v[0][3] +1.0f/6.0f*v[1][0] -1.0f/2.0f*v[1][1] +1.0f/2.0f*v[1][2] -1.0f/6.0f*v[1][3] -1.0f/12.0f*v[2][0] +1.0f/4.0f*v[2][1] -1.0f/4.0f*v[2][2] +1.0f/12.0f*v[2][3]
        },
        {                     -1.0f/ 6.0f*v[0][1]                                                             +1.0f/2.0f*v[1][1]                                                            -1.0f/2.0f*v[2][1]                                                            +1.0f/ 6.0f*v[3][1],
          +1.0f/18.0f*v[0][0] +1.0f/12.0f*v[0][1] -1.0f/ 6.0f*v[0][2] +1.0f/36.0f*v[0][3] -1.0f/ 6.0f*v[1][0] -1.0f/4.0f*v[1][1] +1.0f/2.0f*v[1][2] -1.0f/12.0f*v[1][3] +1.0f/ 6.0f*v[2][0] +1.0f/4.0f*v[2][1] -1.0f/2.0f*v[2][2] +1.0f/12.0f*v[2][3] -1.0f/18.0f*v[3][0] -1.0f/12.0f*v[3][1] +1.0f/ 6.0f*v[3][2] -1.0f/36.0f*v[3][3],
          -1.0f/12.0f*v[0][0] +1.0f/ 6.0f*v[0][1] -1.0f/12.0f*v[0][2]                     +1.0f/ 4.0f*v[1][0] -1.0f/2.0f*v[1][1] +1.0f/4.0f*v[1][2]                     -1.0f/ 4.0f*v[2][0] +1.0f/2.0f*v[2][1] -1.0f/4.0f*v[2][2]                     +1.0f/12.0f*v[3][0] -1.0f/ 6.0f*v[3][1] +1.0f/12.0f*v[3][2],
          +1.0f/36.0f*v[0][0] -1.0f/12.0f*v[0][1] +1.0f/12.0f*v[0][2] -1.0f/36.0f*v[0][3] -1.0f/12.0f*v[1][0] +1.0f/4.0f*v[1][1] -1.0f/4.0f*v[1][2] +1.0f/12.0f*v[1][3] +1.0f/12.0f*v[2][0] -1.0f/4.0f*v[2][1] +1.0f/4.0f*v[2][2] -1.0f/12.0f*v[2][3] -1.0f/36.0f*v[3][0] +1.0f/12.0f*v[3][1] -1.0f/12.0f*v[3][2] +1.0f/36.0f*v[3][3]
        }
    };

    return     c[0][0] + fx*(c[0][1] + fx*(c[0][2] + fx*c[0][3]))
         + fy*(c[1][0] + fx*(c[1][1] + fx*(c[1][2] + fx*c[1][3]))
         + fy*(c[2][0] + fx*(c[2][1] + fx*(c[2][2] + fx*c[2][3]))
         + fy*(c[3][0] + fx*(c[3][1] + fx*(c[3][2] + fx*c[3][3])) )));
}
With the above two functions, to scale the 8×8 image to W pixels wide and H pixels tall, you can use
Code:
int x, y;
for (y = 0; y < H; y++) {
    const float yf = (float)y * 8.0f / (float)(H - 1);
    for (x = 0; x < W; x++) {
        const float xf = (float)x * 8.0f / (float)(H - 1);
        /* Value at (x,y) = new_pixel(xf, yf); */
    }
}
Note the H-1 and W-1 in the floating-point coordinate calculations. These effectively add the "extra" row and column of pixels to the right side and bottom of the image, because there yf = H and xf = H. (That is, this slow method should produce correctly scaled images: if the original image is rotated or mirrored before scaling, the resulting image is the same scaled image rotated or mirrored.)

Note that I haven't tested the actual code with images, so if you have any issues, ping me so I can verify and fix.
 
Well, first thing to understand is that bicubic interpolation uses 4×4 pixels from the original image to calculate each pixel in the scaled image.

Understand

At the borders, mathematically, we duplicate the edge pixels. For example, if the source image was 5×5, we actually use a 7×7 region
Understand, this is done to have 4x4 pixels for calculate a new pixel.

The CLAMP() macro handled this in my original post; below, this is handled by the old_pixel() function.
Ok, in arduino ide this is done by constrain or via your function

Weight of each of the sixteen source pixels depends on the fractional coordinates.

If zero fractional coordinates produces the value of an original pixel, then we should add an extra row, and an extra column, of pixels to the new image, where one of the fractional coordinates is zero (x for the added column, y for the added row); this makes the resulting image a symmetric copy of the original. (That is, if you rotate or mirror the initial image, the result is the same as if you just generated one scaled image and then rotated that. If you do not add the extra column and row of pixels, this will not hold.)

ok

As an example, if we scale each pixel to N×N pixels, then we use A A B C, F F G H, K K L M, and P P Q R to calculate the ones in the square bracketed by A B F G, with A in the upper left corner (in the scaled image).

The C00 .. C33 coefficients shown in my post are the correct ones, if those sixteen pixels are labeled v00 v01 v02 v03, v10 v11 v12 v13, v20 v21 v22 v23, v30 v31 v32 and v33.
ok, understand

for my understanding the flow is:
- get original pixels ( done by amg.readPixels(pixels); so we have an array[pixels] with 64 values
- put this values in a 2d array x & y
- rotate or mirror the original image
- add an extra row
- calculate new pixels and write them to a new array


Code:
static inline float old_pixel(int x, int y)
{
    if (x < 0)
        x = 0;
    else
    if (x > 7)
        x = 7;

    if (y < 0)
        y = 0;
    else
    if (y > 7)
        y = 7;

    return pixel[x + 8*y];
}
How is this adding a new row to the boarders? you set x to 0 if it´s smaller then 0, also set it to 7 if it will be bigger. the same with y.



With the above two functions, to scale the 8×8 image to W pixels wide and H pixels tall, you can use
Code:
int x, y;
for (y = 0; y < H; y++) {
    const float yf = (float)y * 8.0f / (float)(H - 1);
    for (x = 0; x < W; x++) {
        const float xf = (float)x * 8.0f / (float)(H - 1);
        /* Value at (x,y) = new_pixel(xf, yf); */
    }
}
ok, but i don´t understand where the scaling occur. first you declare x & y. then you call a loop as long as y is 0 or < H and increase y by 1. then yf is calculated and a sub loop starts for x. i know that these are basic´s, and it will be easy for you to implement this in my code, but i like to understand what to do for upscaling. so any help is welcome.

thanks, markus
 
for my understanding the flow is:
- get original pixels ( done by amg.readPixels(pixels); so we have an array[pixels] with 64 values
- put this values in a 2d array x & y
- rotate or mirror the original image
No. Let's look at an example. Let's say you have a 3×3 image,
Code:
A B C
D E F
G H I
that you want to scale to 5×5; I'll use uppercase for original pixel values, and lowercase for interpolated values:
Code:
A a B b C
d e f g h
D i E j F
k l m n o
G p H q I
Here, each source pixel generates 2×2 pixels. Yet, we don't get a 6×6 image, but a 5×5 one. The 2×2 pixel blocks are A a d e, B b f g, D i k l, and E j m n. The rightmost pixel row, C h F o, and the bottommost pixel row, G p H q, are "special", because they are a single row and column of pixels. The bottom rightmost pixel I, is copied from the source image. If you were to rotate the original image by 90 or 180 or 270 degrees, and/or mirror it horizontally, you get the same result if you just rotated the already scaled version.

But, if you just create 2×2 pixel blocks from the original image, leading to 4×4 or 6×6 image, it will be "wrong": if you first rotate or mirror the small image, and then scale it up, the result is different than if you scale first, and then rotate. This is a common bug when scaling/interpolating data, often hard to understand how and why it occurs, and I just wanted to make sure you avoid it.

How is this adding a new row to the boarders?
If we call old_pixel(-1.0f, y), we get the same result as calling old_pixel(0.0f, y); similarly, if we call old_pixel(x, 8.0f), we get the same result as calling old_pixel(x, 7.0f). It is important that old_pixel() handles the coordinates outside the original image by clamping to the original image area.

The new_pixel() function does call old_pixel() with coordinates just beyond the original image. The effect is the same as if the original image had extra pixels copied around its border.

If we look at the above example, for the A b d e pixels, new_pixel() uses old_pixel() to find the values of A A B C, A A B C, D D E F, and G G H I. If old_pixel() ignored the coordinate checks, then almost half that data would be garbage (* * * *, * A B C, * D E F, * G H I).

ok, but i don´t understand where the scaling occur.
x and y are in new image coordinates, and xf and yf are in old image coordinates. For example, xf can be 2.25, meaning one quarter from 2 towards 3.

The new_pixel(xf, yf) function uses old_pixel() to obtain the 4×4 block from the original image, and applies bicubic interpolation to find the value in the fractional coordinates near the center of that block. (The integral part of the xf and yf coordinates specify the location of the second pixel from top, second pixel from left.)

The new_pixel(xf, yf) function is slow, because it recalculates the coefficients for each new pixel, where it really only needs to do so for every old pixel instead. (So, scaling from 8×8 to 64×64 means it does 64 times the calculations compared to my original code.)

On the other hand, because new_pixel(xf, yf) calculates the coefficients every time, you can use any scale factor; that is, W (scaled image width) and H (scaled image height) can be any positive integers.

You could, for example, set W to the full width (in pixels) of your display, and H height, and instead of saving the scaled image in some array, send the color data directly to the display module. I don't know if it is fast enough for practice, or if the update is noticeably slow (visible "sweep"), though.
 
I took a look at the AMG88xx library sources, and it looks like the sensor actually provides signed 13-bit integer data (12-bit magnitude and a sign bit). I believe the bicubic scaling code and the AMG8xx library could be merged, so that scaling up to 225×225 (7*N+1 , with N an integer) would take less than 0.1 seconds (the sensor has 1 Hz and 10 Hz update modes) on Teensy 3.5 and 3.6, using 32-bit integer math only.

Unfortunately, I don't have the sensor, nor a 3.5/3.6 to use it with right now.
 
But, if you just create 2×2 pixel blocks from the original image, leading to 4×4 or 6×6 image, it will be "wrong": if you first rotate or mirror the small image, and then scale it up, the result is different than if you scale first, and then rotate. This is a common bug when scaling/interpolating data, often hard to understand how and why it occurs, and I just wanted to make sure you avoid it.

Ah, now i understood!

If we call old_pixel(-1.0f, y), we get the same result as calling old_pixel(0.0f, y); similarly, if we call old_pixel(x, 8.0f), we get the same result as calling old_pixel(x, 7.0f). It is important that old_pixel() handles the coordinates outside the original image by clamping to the original image area.

The new_pixel() function does call old_pixel() with coordinates just beyond the original image. The effect is the same as if the original image had extra pixels copied around its border.
easy to understand now

x and y are in new image coordinates, and xf and yf are in old image coordinates. For example, xf can be 2.25, meaning one quarter from 2 towards 3.

The new_pixel(xf, yf) function uses old_pixel() to obtain the 4×4 block from the original image, and applies bicubic interpolation to find the value in the fractional coordinates near the center of that block. (The integral part of the xf and yf coordinates specify the location of the second pixel from top, second pixel from left.)

so it have to be:
- get original pixels ( done by amg.readPixels(pixels); so we have an array[pixels] with 64 values
- put this 64 values [pixels] in a static 8x8 array [xf] & [yf]
- set possition with

Code:
int x, y;
for (y = 0; y < H; y++) {
    const float yf = (float)y * 8.0f / (float)(H - 1);
    for (x = 0; x < W; x++) {
        const float xf = (float)x * 8.0f / (float)(H - 1);
        /* Value at (x,y) = new_pixel(xf, yf); */
    }
}

- and finaly send x, y, to display

I believe the bicubic scaling code and the AMG8xx library could be merged, so that scaling up to 225×225 (7*N+1 , with N an integer) would take less than 0.1 seconds (the sensor has 1 Hz and 10 Hz update modes) on Teensy 3.5 and 3.6, using 32-bit integer math only.
Unfortunately, I don't have the sensor, nor a 3.5/3.6 to use it with right now.
That would be quite usefull for others too. if i order next time from mouser i can oder a addidional sensor if you would like to play with it...

Regards, Markus
 
Hi,

i wrote a basic script to understand the implementation to my code.
- Read temperature values from the Sensor (readpixelfromsensor [arraysize])
- sort this array into a 2D array

and then? How do i implement the function to interpolate readpixelfromsensor [arraysize]

Thanks, Markus

Code:
byte H  = 64;
byte W = 64;
float  pixel[64];
const int  arraysize = 64;
int i = -1;
float pixels_old[8][8];
float readpixelfromsensor[arraysize];


void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);


}

void loop() {


  float readpixelfromsensor [arraysize] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 62, 62, 63, 64};


  for (int k = 0; k <= 7; k++) {

    for (int l = 0; l <= 7; l++) {
      i++;
      pixels_old[k][l] = readpixelfromsensor[i];
      if (i >= 63) {
        i = -1;
      }
        Serial.println(pixels_old[k][l]);
    }
  }


  Serial.println();
  delay(30000);
}

static inline float old_pixel(int x, int y)
{
    if (x < 0)
        x = 0;
    else
    if (x > 7)
        x = 7;

    if (y < 0)
        y = 0;
    else
    if (y > 7)
        y = 7;

    return pixel[x + 8*y];
}


static float new_pixel(const float x, const float y)
{
  const int ix = (int)x;
  const int iy = (int)y;
  const float fx = x - (float)ix;
  const float fy = y - (float)iy;

  const float v[4][4] = {
    { old_pixel(ix - 1, iy - 1), old_pixel(ix, iy - 1), old_pixel(ix + 1, iy - 1), old_pixel(ix + 2, iy - 1) },
    { old_pixel(ix - 1, iy),   old_pixel(ix, iy),   old_pixel(ix + 1, iy),   old_pixel(ix + 2, iy)   },
    { old_pixel(ix - 1, iy + 1), old_pixel(ix, iy + 1), old_pixel(ix + 1, iy + 1), old_pixel(ix + 2, iy + 1) },
    { old_pixel(ix - 1, iy + 2), old_pixel(ix, iy + 2), old_pixel(ix + 1, iy + 2), old_pixel(ix + 2, iy + 2) }
  };

  const float c[4][4] = {
    { v[1][1],
      -1.0f / 3.0f * v[1][0] - 1.0f / 2.0f * v[1][1] +          v[1][2] - 1.0f / 6.0f * v[1][3],
      1.0f / 2.0f * v[1][0] -          v[1][1] + 1.0f / 2.0f * v[1][2],
      -1.0f / 6.0f * v[1][0] + 1.0f / 2.0f * v[1][1] - 1.0f / 2.0f * v[1][2] + 1.0f / 6.0f * v[1][3]
    },
    { -1.0f / 3.0f * v[0][1] - 1.0f / 2.0f * v[1][1]                                                                                                                      +          v[2][1]                                                           - 1.0f / 6.0f * v[3][1],
      1.0f / 9.0f * v[0][0] + 1.0f / 6.0f * v[0][1] - 1.0f / 3.0f * v[0][2] + 1.0f / 18.0f * v[0][3] + 1.0f / 6.0f * v[1][0] + 1.0f / 4.0f * v[1][1] - 1.0f / 2.0f * v[1][2] + 1.0f / 12.0f * v[1][3] - 1.0f / 3.0f * v[2][0] - 1.0f / 2.0f * v[2][1] +          v[2][2] - 1.0f / 6.0f * v[2][3] + 1.0f / 18.0f * v[3][0] + 1.0f / 12.0f * v[3][1] - 1.0f / 6.0f * v[3][2] + 1.0f / 36.0f * v[3][3],
      -1.0f / 6.0f * v[0][0] + 1.0f / 3.0f * v[0][1] - 1.0f / 6.0f * v[0][2]                     - 1.0f / 4.0f * v[1][0] + 1.0f / 2.0f * v[1][1] - 1.0f / 4.0f * v[1][2]                     + 1.0f / 2.0f * v[2][0] -          v[2][1] + 1.0f / 2.0f * v[2][2]                    - 1.0f / 12.0f * v[3][0] + 1.0f / 6.0f * v[3][1] - 1.0f / 12.0f * v[3][2],
      1.0f / 18.0f * v[0][0] - 1.0f / 6.0f * v[0][1] + 1.0f / 6.0f * v[0][2] - 1.0f / 18.0f * v[0][3] + 1.0f / 12.0f * v[1][0] - 1.0f / 4.0f * v[1][1] + 1.0f / 4.0f * v[1][2] - 1.0f / 12.0f * v[1][3] - 1.0f / 6.0f * v[2][0] + 1.0f / 2.0f * v[2][1] - 1.0f / 2.0f * v[2][2] + 1.0f / 6.0f * v[2][3] + 1.0f / 36.0f * v[3][0] - 1.0f / 12.0f * v[3][1] + 1.0f / 12.0f * v[3][2] - 1.0f / 36.0f * v[3][3]
    },
    { +1.0f / 2.0f * v[0][1]                                                           -          v[1][1]                                                           + 1.0f / 2.0f * v[2][1],
      -1.0f / 6.0f * v[0][0] - 1.0f / 4.0f * v[0][1] + 1.0f / 2.0f * v[0][2] - 1.0f / 12.0f * v[0][3] + 1.0f / 3.0f * v[1][0] + 1.0f / 2.0f * v[1][1] -          v[1][2] + 1.0f / 6.0f * v[1][3] - 1.0f / 6.0f * v[2][0] - 1.0f / 4.0f * v[2][1] + 1.0f / 2.0f * v[2][2] - 1.0f / 12.0f * v[2][3],
      +1.0f / 4.0f * v[0][0] - 1.0f / 2.0f * v[0][1] + 1.0f / 4.0f * v[0][2]                     - 1.0f / 2.0f * v[1][0] +          v[1][1] - 1.0f / 2.0f * v[1][2]                    + 1.0f / 4.0f * v[2][0] - 1.0f / 2.0f * v[2][1] + 1.0f / 4.0f * v[2][2],
      -1.0f / 12.0f * v[0][0] + 1.0f / 4.0f * v[0][1] - 1.0f / 4.0f * v[0][2] + 1.0f / 12.0f * v[0][3] + 1.0f / 6.0f * v[1][0] - 1.0f / 2.0f * v[1][1] + 1.0f / 2.0f * v[1][2] - 1.0f / 6.0f * v[1][3] - 1.0f / 12.0f * v[2][0] + 1.0f / 4.0f * v[2][1] - 1.0f / 4.0f * v[2][2] + 1.0f / 12.0f * v[2][3]
    },
    { -1.0f / 6.0f * v[0][1]                                                             + 1.0f / 2.0f * v[1][1]                                                            - 1.0f / 2.0f * v[2][1]                                                            + 1.0f / 6.0f * v[3][1],
      +1.0f / 18.0f * v[0][0] + 1.0f / 12.0f * v[0][1] - 1.0f / 6.0f * v[0][2] + 1.0f / 36.0f * v[0][3] - 1.0f / 6.0f * v[1][0] - 1.0f / 4.0f * v[1][1] + 1.0f / 2.0f * v[1][2] - 1.0f / 12.0f * v[1][3] + 1.0f / 6.0f * v[2][0] + 1.0f / 4.0f * v[2][1] - 1.0f / 2.0f * v[2][2] + 1.0f / 12.0f * v[2][3] - 1.0f / 18.0f * v[3][0] - 1.0f / 12.0f * v[3][1] + 1.0f / 6.0f * v[3][2] - 1.0f / 36.0f * v[3][3],
      -1.0f / 12.0f * v[0][0] + 1.0f / 6.0f * v[0][1] - 1.0f / 12.0f * v[0][2]                     + 1.0f / 4.0f * v[1][0] - 1.0f / 2.0f * v[1][1] + 1.0f / 4.0f * v[1][2]                     - 1.0f / 4.0f * v[2][0] + 1.0f / 2.0f * v[2][1] - 1.0f / 4.0f * v[2][2]                     + 1.0f / 12.0f * v[3][0] - 1.0f / 6.0f * v[3][1] + 1.0f / 12.0f * v[3][2],
      +1.0f / 36.0f * v[0][0] - 1.0f / 12.0f * v[0][1] + 1.0f / 12.0f * v[0][2] - 1.0f / 36.0f * v[0][3] - 1.0f / 12.0f * v[1][0] + 1.0f / 4.0f * v[1][1] - 1.0f / 4.0f * v[1][2] + 1.0f / 12.0f * v[1][3] + 1.0f / 12.0f * v[2][0] - 1.0f / 4.0f * v[2][1] + 1.0f / 4.0f * v[2][2] - 1.0f / 12.0f * v[2][3] - 1.0f / 36.0f * v[3][0] + 1.0f / 12.0f * v[3][1] - 1.0f / 12.0f * v[3][2] + 1.0f / 36.0f * v[3][3]
    }
  };

  return     c[0][0] + fx * (c[0][1] + fx * (c[0][2] + fx * c[0][3]))
             + fy * (c[1][0] + fx * (c[1][1] + fx * (c[1][2] + fx * c[1][3]))
                     + fy * (c[2][0] + fx * (c[2][1] + fx * (c[2][2] + fx * c[2][3]))
                             + fy * (c[3][0] + fx * (c[3][1] + fx * (c[3][2] + fx * c[3][3])) )));
}
 
As I mentioned, I don't have the hardware. Also, I normally use raw metal and not the Arduino environment. So, I'm not sure if I am the right person to look over your code to see what changes are needed to make it work.

I think it would make most sense if you first get a nearest-neighbor-scaled version working, duplicating each original sample 8×8 times.

Then, I would fork the Adafruit AMG88xx library, optimizing it a bit better for 32-bit microcontrollers (like Teensy LC, 3.x), and adding some new functions that instead of just reading the 64 samples, actually provide a bicubic-interpolated array of bytes (0-255) or 16-bit color values taken from a lookup table (can be any size). Essentially, instead of using amg.readPixels(buffer), you'd call something like amg.readScaledBytes(array, width, height) or amg.readScaledLookup16(buffer, width, height, colorLookupTable, colorCount).

The Adafruit_AMG88xx::readScaledBytes() would use bicubic scaling to scale the sensor samples, with 0 corresponding to minimum sample value (in the current 8x8 set), and 255 to the maximum, with width*height bytes saved to the buffer.

The Adafruit_AMG88xx::readScaledLookup16() would use bicubic scaling, with the current sensor sample range mapped linearly to the full color lookup table, and the 16-bit color values (2*width*height bytes) saved to the buffer. This should be fast to blit to the OLED display, so that even if the interpolation takes longer than 0.1s, the entire image changes at once, and not in a visible sweep.

I did play a bit further with the cubic interpolation code itself (in C, not C++, and in Linux, not on a microcontroller), and it definitely looks like the AMG88xx sensor data could be mapped to bytes or a color lookup table (of, say, up to 1024 values or so), using integer arithmetic. I do have a Teensy 3.1 or two somewhere, so I could try and see how fast it can scale the 12-bit samples (from a pseudo-random number generator instead of an AMG88xx) to eg. 64×64.
 
Hi,

i will try to get the nearest-neibhor version working. it will be fine to see an updated version of the adafruit library. maybe i can help with testing your versions.

markus
 
(Apologies, I missed your last post for some reason.)

maybe i can help with testing your versions.

That would be extremely useful.

I found a better parametrization, that mathematically should yield the same scaling results, but is cheaper to compute, and requires less precision for the temporaries. I still need to verify it with a test program, but if I haven't made any silly mistakes, it should work for both fixed-point and float bicubic interpolation.

Are you using Teensy 3.x? Or Teensy LC?
 
Status
Not open for further replies.
Back
Top