Here's the second of our three sketches in Processing. Open another new window and paste this code into it, then save it as ConvertBMPto8bit.

// ConvertBMPto8bit - Read and enlarge a modified 32x24 24-bit gray BMP file,
//                    write an upscaled 256x192 BMP image with a 256 color table.
// Ver. 2 - Fetch filenames and convert all suitable BMPs we find.
//          Builds sequences suitable for online animated GIF converters

import java.util.Date;

// BMP File Header, little end first
int BmpPSPHead[] = {
 0x42, 0x4D,             // "BM" in hex
 0x36, 0xC4, 0x00, 0x00, // File size, 50230
 0x00, 0x00,             // reserved for app data 1
 0x00, 0x00,             // reserved for app data 2
 0x36, 0x04, 0x00, 0x00  // Offset of pixel 0, 1078
};

// BMP 8-bit DIB Header, little end first
int DIBHeadPSP1[] = {
 0x28, 0x00, 0x00, 0x00,  // Header size, 40
 0x00, 0x01, 0x00, 0x00,  // pixel width, 256
 0xC0, 0x00, 0x00, 0x00,  // pixel height, 192
 0x01, 0x00,              // color planes, 1
 0x08, 0x00,              // bits per pixel, 8
 0x00, 0x00, 0x00, 0x00,  // Compression method, 0==none
 0x00, 0x00, 0x00, 0x00,  // Raw bitmap data size, dummy 0
 0x12, 0x0B, 0x00, 0x00,  // Pixels per meter H, 2834
 0x12, 0x0B, 0x00, 0x00,  // Pixels per meter V, 2834
 0x00, 0x00, 0x00, 0x00,  // Colors in palette, 0==default 2^n
 0x00, 0x00, 0x00, 0x00   // Number of important colors, 0
};

byte outBytes[], b[];     // Buffer for the input file bytes

PImage img;  // Declare variable of type PImage
int fileCount = 0, imageIndex = 0;
String[] filenames;

// "paletteChoice" selects a false color palette:
// 0 == Grayscale, white hot
// 1 == Ironbow
// 2 == Firebow
// 3 == Hot alarm
// 4 == Grayscale, black hot
int paletteChoice = 1;

void setup() {
  int i, j, x, y;
  String nameHead, nameTail;

  size(256, 192);  // Size must be the first statement
//  noStroke();
  frameRate(5);
  background(0);   // Clear the screen with a black background

  outBytes = new byte[50230]; // 54 header + 1K colors + 12K pixels

  String path = sketchPath() + "/data"; // Read from the "/data" subdirectory

  println("Listing filenames: ");
  filenames = listFileNames(path);
  println(filenames);
  fileCount = filenames.length;
  println(fileCount + " entries");

  if(fileCount < 1) {
    println("No images found.  Stopping.");
  } else {    // Filenames exist in the directory
    for(i = 0; i < fileCount; ++i) {   // Test each name
      nameHead = filenames[i].substring(0, 3);
      nameTail = filenames[i].substring(8);
      j = int(filenames[i].substring(3, 8));

      if(nameHead.equals("frm") && nameTail.equals(".bmp") && j != 0)   // Source "frm_____.bmp" found?
        enlarge8bit(i);    // Process and write an enlarged 8-bit version
    }
  }
  noLoop();
}

void draw() {
  int countX, countY;

  noSmooth();

  for(countY = 0; countY < 192; ++countY) {
    for(countX = 0; countX < 256; ++countX) {
      stroke(0xFF & outBytes[1078 + (countY * 256 + countX)]); // Color from BMP buffer 
      point(countX, 191 - countY);                             // Draw a pixel, bottom up
    }
  }
}

void enlarge8bit(int fileNumber) {  // Read a small gray "frm" BMP image and write an enlarged colormapped "out" BMP
  int i, x, y;

  b = loadBytes(filenames[fileNumber]);   // Open a file and read its 8-bit data

  for(i = 0; i < 14; ++i)
    outBytes[i] = byte(BmpPSPHead[i] & 0xFF);        // Copy BMP header 1 into output buffer
  for(i = 0; i < 40; ++i)
    outBytes[i + 14] = byte(DIBHeadPSP1[i] & 0xFF);  // Copy header 2

  loadColorTable(paletteChoice, 54);  // Load color table, 54 byte BMP header offset

  for(y = 0; y < 23; ++y) {        // Bilinear interpolation, count the source pixels less one
    for(x = 0; x < 31; ++x) {
      for(int yLirp = 0; yLirp < 9; ++yLirp) {
        int corner0 = b[54 + ((32 * y + x) + 32) * 3] & 0xFF;
        int corner1 = b[54 + ((32 * y + x) +  0) * 3] & 0xFF;
        int pixLeft  = (corner0 * yLirp + corner1 * (8 - yLirp)) >> 3;  // Lirp 1 endpoint from 2 L pixels,

        int corner2 = b[54 + ((32 * y + x) + 33) * 3] & 0xFF;
        int corner3 = b[54 + ((32 * y + x) +  1) * 3] & 0xFF;
        int pixRight = (corner2 * yLirp + corner3 * (8 - yLirp)) >> 3;  // and the other from 2 R pixels

        for(int xLirp = 0; xLirp < 9; ++xLirp) {
          int pixMid = (pixRight * xLirp + pixLeft * (8 - xLirp)) >> 3; // Lirp between lirped endpoints, bilinear interp
          outBytes[1078 + y * 2048 + x * 8 + yLirp * 256 + xLirp + 771] = byte(pixMid & 0xFF);
        }
      }
    }
  }
  for(y = 0; y < 192; ++y) {   // Pad out the empty side pixels
    for(x = 0; x < 4; ++x) {
      outBytes[1078 + (3 - x) + 256 * y] = outBytes[1082 + 256 * y];
      outBytes[1330 +      x  + 256 * y] = outBytes[1329 + 256 * y];
    }
  }
  for(x = 0; x < 256; ++x) {   // Pad out the empty above/below pixels
    for(y = 0; y < 4; ++y) {
      outBytes[ 1078 + 256 * (3 - y) + x] = outBytes[ 2102 + x];
      outBytes[49206 + 256 *      y  + x] = outBytes[48950 + x];
    }
  }

  saveBytes("data/out" + filenames[fileNumber].substring(3), outBytes);   // Save a recolored 8-bit BMP as "out_____.bmp"
}

void loadColorTable(int choiceNum, int offset) {
  int i, x;

  switch(choiceNum) {
    case 1:     // Load 8-bit BMP color table with computed ironbow curves
      for(x = 0; x < 256; ++x) {
        float fleX = (float)x / 255.0;

        float fleG = 255.9 * (1.02 - (fleX - 0.72) * (fleX - 0.72) * 1.96);
        fleG = (fleG > 255.0) || (fleX > 0.75) ? 255.0 : fleG;  // Truncate curve
        i = (int)fleG;
        outBytes[offset + x * 4 + 2] = byte(i & 0xFF);    // Red vals

        fleG = fleX * fleX * 255.9;
        i = (int)fleG;
        outBytes[offset + x * 4 + 1] = byte(i & 0xFF);    // Grn vals

        fleG = 255.9 * (14.0 * (fleX * fleX * fleX) - 20.0 * (fleX * fleX) + 7.0 * fleX);
        fleG = fleG < 0.0 ? 0.0 : fleG;  // Truncate curve
        i = (int)fleG;
        outBytes[offset + x * 4 + 0] = byte(i & 0xFF);    // Blu vals
      }
      break;
    case 2:  // Compute quadratic "firebow" palette
      for(x = 0; x < 256; ++x) {
        float fleX = (float)x / 255.0;

        float fleG = 255.9 * (1.00 - (fleX - 1.0) * (fleX - 1.0));
        i = (int)fleG;
        outBytes[offset + x * 4 + 2] = byte(i & 0xFF);    // Red vals

        fleG = fleX < 0.25 ? 0.0 : (fleX - 0.25) * 1.3333 * 255.9;
        i = (int)fleG;
        outBytes[offset + x * 4 + 1] = byte(i & 0xFF);    // Grn vals

        fleG = fleX < 0.5 ? 0.0 : (fleX - 0.5) * (fleX - 0.5) * 1023.9;
        i = (int)fleG;
        outBytes[offset + x * 4 + 0] = byte(i & 0xFF);    // Blu vals
      }
      break;
    case 3:  // Compute "alarm" palette
      for(x = 0; x < 256; ++x) {
        float fleX = (float)x / 255.0;

        float fleG = 255.9 * (fleX < 0.875 ? 1.00 - (fleX * 1.1428) : 1.0);
        i = (int)fleG;
        outBytes[offset + x * 4 + 2] = byte(i & 0xFF);    // Red vals

        fleG = 255.9 * (fleX < 0.875 ? 1.00 - (fleX * 1.1428) : (fleX - 0.875) * 8.0);
        i = (int)fleG;
        outBytes[offset + x * 4 + 1] = byte(i & 0xFF);    // Grn vals

        fleG = 255.9 * (fleX < 0.875 ? 1.00 - (fleX * 1.1428) : 0.0);
        i = (int)fleG;
        outBytes[offset + x * 4 + 0] = byte(i & 0xFF);    // Blu vals
      }
      break;
    case 4:    // Grayscale, black hot 
      for(x = 0; x < 256; ++x) {
        outBytes[offset + x * 4 + 2] = byte(255 - x & 0xFF);    // Red vals
        outBytes[offset + x * 4 + 1] = byte(255 - x & 0xFF);    // Grn vals
        outBytes[offset + x * 4 + 0] = byte(255 - x & 0xFF);    // Blu vals
      }
      break;
    default:    // Grayscale, white hot 
      for(x = 0; x < 256; ++x) {
        outBytes[offset + x * 4 + 2] = byte(x & 0xFF);    // Red vals
        outBytes[offset + x * 4 + 1] = byte(x & 0xFF);    // Grn vals
        outBytes[offset + x * 4 + 0] = byte(x & 0xFF);    // Blu vals
      }
  }
}

String[] listFileNames(String dir) {   // Return the filenames from a directory as an array of Strings
  File file = new File(dir);

  if (file.isDirectory()) {
    String names[] = file.list();
    return names;
  } else    // It's not a directory
    return null;
}

Sketch 3: ConvertBMPtoSeq01

Just one more sketch to go!  Do the same as before, saving this code as another new sketch named ConvertBMPtoSeq01.

// ConvertBMPtoSeq01 - Read and enlarge a modified 32x24 24-bit gray BMP file,
//                     saving 256x192 BMP images in 256 colors for converting to MOV.
// Ver. 1 - Fetch filenames and scan all suitable BMPs we find for their time/temp data,
//          to set the scale for graphing these numbers through the MOV.

import java.util.Date;

byte colorPal[], b[];     // Buffers for a color palette, and reading bytes from files

PImage img;
int i, fileCount = 0, frameTotal = 0, earlyFrame = 0, lastFrame = 0,
    hotLowFrame, hotHighFrame, coldLowFrame, coldHighFrame, targLowFrame, targHighFrame,
    framX1, framX2, coldY1, coldY2, targY1, targY2, hotY1, hotY2,
    offsetX = 153, offsetY = 6, numbersX = 40, numbersY = 30, graphX = 8, graphY = 342,
    histoX = 410, histoY = 342, histoH = 140, histoW = 64, BGcolor = 48;
float hottestLow, hottestHigh, coldestLow, coldestHigh, targetLow, targetHigh;
String[] filenames;



// Change the following values to customize the output images.
// "paletteChoice" selects a false color palette:
// 0 == Grayscale, white hot
// 1 == Ironbow
// 2 == Firebow
// 3 == Hot alarm
// 4 == Grayscale, black hot
int paletteChoice = 1;
boolean markersVisible = true, celsiusFlag = false, lirpSmoothing = true;

void setup() {
  int x, y;
  float fixedPoint[];
  String nameHead, nameTail;

  size(480, 360);      // Size must be the first statement
  background(BGcolor); // Clear the screen with a gray background
  noSmooth();

  colorPal = new byte[1024];        // Reserve a 1K color table
  loadColorTable(paletteChoice, 0); // Load color table
  fixedPoint = new float[5];        // Buffer for added fixed point values

  String path = sketchPath() + "/data";  // Read from the "/data" subdirectory

//  println("Listing filenames: ");
  filenames = listFileNames(path);
//  println(filenames);
  fileCount = filenames.length;
//  println(fileCount + " entries");

  if(fileCount < 1) {
    println("No images found.  Stopping.");
  } else {    // Filenames exist in the directory, convert what we can

// First pass: Read the embedded times/temps and find maxes/mins for graphing
    print("Counting through files: ");
    for(i = 0; i < fileCount; ++i) {   // Test each filename for conformity
      if((i & 0x3F) == 0)
        print(i + ", ");
      nameHead = filenames[i].substring(0, 3);
      nameTail = filenames[i].substring(8);

      if(nameHead.equals("frm") && nameTail.equals(".bmp") && int(filenames[i].substring(3, 8)) != 0) { // Source "frm_____.bmp" found?
        b = loadBytes(filenames[i]);   // Open a file and read its 8-bit data

        for(x = 0; x < 5; ++x) {  // Rebuild float values from next 4*n bytes in the file
          fixedPoint[x] = expandFloat(b[2360 + (x * 4) + 0], b[2360 + (x * 4) + 1],
                                      b[2360 + (x * 4) + 2], b[2360 + (x * 4) + 3]); // 2360 == headers + pixels + 2
        }
        y = ((b[2387] & 0xff) << 24) + ((b[2386] & 0xff) << 16)
          + ((b[2385] & 0xff) <<  8) +  (b[2384] & 0xff);   // Reassemble a uint32_t millis() stamp

        if(++frameTotal == 1) { // First frame found so far?
          coldestLow = coldestHigh = fixedPoint[0];
          targetLow  = targetHigh  = fixedPoint[2];  // Initialize all values
          hottestLow = hottestHigh = fixedPoint[4];
          hotLowFrame = hotHighFrame = coldLowFrame = coldHighFrame = targLowFrame = targHighFrame = earlyFrame = lastFrame = y;
        } else {   // Compare everything, update where necessary

          if(y < earlyFrame)
            earlyFrame = y;       // These will set the left and right bounds
          else if(y > lastFrame)  // of the temperature over time graphs
            lastFrame = y;

          if(fixedPoint[0] < coldestLow) {       // These will define the high and low bounds
            coldestLow = fixedPoint[0];
            coldLowFrame = y;
          } else if(fixedPoint[0] > coldestHigh) {
            coldestHigh = fixedPoint[0];
            coldHighFrame = y;
          }

          if(fixedPoint[2] < targetLow) {
            targetLow = fixedPoint[2];
            targLowFrame = y;
          } else if(fixedPoint[2] > targetHigh) {
            targetHigh = fixedPoint[2];
            targHighFrame = y;
          }

          if(fixedPoint[4] < hottestLow) {
            hottestLow = fixedPoint[4];
            hotLowFrame = y;
          } else if(fixedPoint[4] > hottestHigh) {
            hottestHigh = fixedPoint[4];
            hotHighFrame = y;
          }
        }
      }
    }
    println(i + ", done.\n");

// The high and low points of three datasets are found, display them
    println("Frame times " + earlyFrame + " to " + lastFrame + " totaling " + (lastFrame - earlyFrame));
    println("Cold values " + coldestLow + " at " + coldLowFrame + " to " + coldestHigh + " at " + coldHighFrame);
    println("Targ values " +  targetLow + " at " + targLowFrame + " to " +  targetHigh + " at " + targHighFrame);
    println("Hot values  " + hottestLow + " at " +  hotLowFrame + " to " + hottestHigh + " at " +  hotHighFrame);

    stroke(BGcolor + 48);
    for(y = 0; y <= 140; y += 35)
      line(graphX, graphY - y, graphX + 400, graphY - y);  // Draw a generic grid for the time graph
    for(x = 0; x <= 400; x += 40)
      line(graphX + x, graphY - 140, graphX + x, graphY);

    noStroke();     // Text labels for the top & bottom temp values of the graph
    textSize(10);
    fill(255);
    if(celsiusFlag) {
      text(hottestHigh, graphX + 402, graphY - 142);
      text(coldestLow,  graphX + 402, graphY +  12);
    } else {
      text(hottestHigh * 1.8 + 32.0, graphX + 402, graphY - 142);
      text(coldestLow * 1.8 + 32.0,  graphX + 402, graphY +  12);
    }

    fill(BGcolor + 128);           // Predraw 6 little high/low markers in the graph space
    rect(graphX + 400 * (coldLowFrame  - earlyFrame) / (lastFrame - earlyFrame) - 1,
         graphY - int((coldestLow -  coldestLow) / (coldestLow - hottestHigh) * 140.0) - 1, 3, 3);
    rect(graphX + 400 * (coldHighFrame - earlyFrame) / (lastFrame - earlyFrame) - 1,
         graphY - int((coldestLow - coldestHigh) / (coldestLow - hottestHigh) * 140.0) - 1, 3, 3);

    rect(graphX + 400 * (targLowFrame  - earlyFrame) / (lastFrame - earlyFrame) - 1,
         graphY - int((coldestLow -   targetLow) / (coldestLow - hottestHigh) * 140.0) - 1, 3, 3);
    rect(graphX + 400 * (targHighFrame - earlyFrame) / (lastFrame - earlyFrame) - 1,
         graphY - int((coldestLow -  targetHigh) / (coldestLow - hottestHigh) * 140.0) - 1, 3, 3);

    rect(graphX + 400 * (hotLowFrame   - earlyFrame) / (lastFrame - earlyFrame) - 1,
         graphY - int((coldestLow -  hottestLow) / (coldestLow - hottestHigh) * 140.0) - 1, 3, 3);
    rect(graphX + 400 * (hotHighFrame  - earlyFrame) / (lastFrame - earlyFrame) - 1,
         graphY - int((coldestLow - hottestHigh) / (coldestLow - hottestHigh) * 140.0) - 1, 3, 3);
  }
  i = 0;
}

// Second pass: Read each frame again, plot color mapped enlarged image, temperature values and graph, save each frame
void draw() {
  int x, y, histogram[];
  float tempY, fixedPoint[];
  String nameHead, nameTail;

  noSmooth();
  fixedPoint = new float[5];   // Buffer for appended fixed point values
  histogram  = new int[256];   // Buffer for color histogram
  for(x = 0; x < 256; ++x)
    histogram[x] = 0;          // Initialize histogram

  if(i < fileCount) {   // Test each filename for conformity
    nameHead = filenames[i].substring(0, 3);
    nameTail = filenames[i].substring(8);

    if(nameHead.equals("frm") && nameTail.equals(".bmp") && int(filenames[i].substring(3, 8)) != 0) { // Source "frm_____.bmp" found?
      b = loadBytes(filenames[i]);   // Open a file and read its 8-bit data
//      println(i + " " + filenames[i]);
      enlarge8bitColor();            // Place colored enlarged image on screen

      for(x = 0; x < 5; ++x) {  // Rebuild float values from next 4*n bytes in the file
        fixedPoint[x] = expandFloat(b[2360 + (x * 4) + 0], b[2360 + (x * 4) + 1],
                                    b[2360 + (x * 4) + 2], b[2360 + (x * 4) + 3]);
      }
      y = ((b[2387] & 0xff) << 24) + ((b[2386] & 0xff) << 16)
        + ((b[2385] & 0xff) <<  8) +  (b[2384] & 0xff);       // Reassemble a milliseconds time stamp

      smooth();
      framX2 = graphX + 400 * (y - earlyFrame) / (lastFrame - earlyFrame);
      coldY2 = graphY - int((coldestLow - fixedPoint[0]) / (coldestLow - hottestHigh) * 140.0); // Map data values into graph space
      targY2 = graphY - int((coldestLow - fixedPoint[2]) / (coldestLow - hottestHigh) * 140.0);
      hotY2  = graphY - int((coldestLow - fixedPoint[4]) / (coldestLow - hottestHigh) * 140.0);

      if(i == 0) {
        framX1 = framX2;  // Set starting points for 3 graphs 
        coldY1 = coldY2;
        targY1 = targY2;
        hotY1 = hotY2;
      }

      stroke(128, 128, 255);
      line(framX1, coldY1, framX2, coldY2);  // Graph cold data point
      stroke(255, 200, 64);
      line(framX1, targY1, framX2, targY2);  // Graph center data point
      stroke(255, 128, 64);
      line(framX1,  hotY1, framX2,  hotY2);  // Graph hot data point

      framX1 = framX2;  // Remember endpoints of graphed lines 
      coldY1 = coldY2;
      targY1 = targY2;
      hotY1  = hotY2;

      noStroke();          // Print key values onscreen for current frame
      fill(BGcolor);
      rect(numbersX, numbersY, 82, 152);  // Erase number region

      fill(BGcolor + 32);  // A color to highlight any extreme values
      if(y == hotLowFrame || y == hotHighFrame)
        rect(numbersX, numbersY + 95, 80, 16);
      if(y == targLowFrame || y == targHighFrame)
        rect(numbersX, numbersY + 115, 80, 16);
      if(y == coldLowFrame || y == coldHighFrame)
        rect(numbersX, numbersY + 135, 80, 16);

      textSize(10);
      fill(255);
      text(filenames[i], numbersX + 5, numbersY + 40); // Show current filename

      if(celsiusFlag)
        text("Frame\n\n\nElapsed sec\n\nDegrees C", numbersX + 5, numbersY + 8);
      else
        text("Frame\n\n\nElapsed sec\n\nDegrees F", numbersX + 5, numbersY + 8);

      textSize(15);
      text(i, numbersX + 5, numbersY + 25);                          // Print frame number
      text(float(y - earlyFrame) * 0.001, numbersX, numbersY + 74);  // Print elapsed time

      if(celsiusFlag) {      // Print temps in Celsius
        fill(255, 128, 64);
        text(fixedPoint[4], numbersX, numbersY + 108);
        fill(255, 200, 64);
        text(fixedPoint[2], numbersX, numbersY + 128);
        fill(128, 128, 255);
        text(fixedPoint[0], numbersX, numbersY + 148);
      } else {               // or print them in Farenheit
        fill(255, 128, 64);
        text(fixedPoint[4] * 1.8 + 32.0, numbersX, numbersY + 108);
        fill(255, 200, 64);
        text(fixedPoint[2] * 1.8 + 32.0, numbersX, numbersY + 128);
        fill(128, 128, 255);
        text(fixedPoint[0] * 1.8 + 32.0, numbersX, numbersY + 148);
      }

      for(x = 0; x < 768; ++x)
        ++histogram[b[54 + 3 * x] & 0xFF];  // Count all colors
      framX2 = histogram[0];
      for(x = 1; x < 256; ++x) {            // Find most numerous color
        if(histogram[x] > framX2) {
          framX2 = histogram[x];
          targY2 = x;
        }
      }

      fill(BGcolor);
      rect(histoX, histoY - 140, histoW, histoH + 1);  // Erase histogram region

      for(y = 0; y < 256; ++y) {
        if(histogram[y] > 0) {
          tempY = float(y) * (fixedPoint[3] - fixedPoint[1]) / 255.0 + fixedPoint[1];    // Convert a 8-bit value to a temperature
          tempY = float(histoH) * (coldestLow - tempY) / (coldestLow - hottestHigh);     // Position it on the graph Y axis
          stroke(colorPal[4 * y + 2] & 0xFF, colorPal[4 * y + 1] & 0xFF, colorPal[4 * y + 0] & 0xFF);  // Color map the stroke
          line(histoX, histoY - int(tempY), histoX + (histoW - 1) * histogram[y] / framX2, histoY - int(tempY)); // Draw a line proportional to the pixel count  
        }

        noStroke();
        noSmooth();
        textSize(10);
        if(targY2 < 0x80) // Histogram peak in the dark side?
          fill(255);      // Set contrasting test to white
        else
          fill(0);

        tempY = float(targY2) * (fixedPoint[3] - fixedPoint[1]) / 255.0 + fixedPoint[1];    // Convert a 8-bit value to a temperature
        if(celsiusFlag)    // Print the Y-positioned float value in C?
          text(tempY, histoX, histoY + 3 - int(float(histoH) * (coldestLow - tempY) / (coldestLow - hottestHigh)));
        else
          text(tempY * 1.8 + 32.0, histoX, histoY + 3 - int(float(histoH) * (coldestLow - tempY) / (coldestLow - hottestHigh)));
      }
      saveFrame("mov#####.jpg");  // Save the image into a sequence for Movie Maker
    }
    ++i;
  }
}

void enlarge8bitColor() {  // Convert a small gray BMP array and plot an enlarged colormapped version
  int x, y;

  if(lirpSmoothing) {           // Bilinear interpolation?
    for(y = 0; y < 23; ++y) {   // Count the source pixels less one
      for(x = 0; x < 31; ++x) {
        for(int yLirp = 0; yLirp < 9; ++yLirp) {
          int corner0 = b[54 + ((32 * y + x) + 32) * 3] & 0xFF;
          int corner1 = b[54 + ((32 * y + x) +  0) * 3] & 0xFF;
          int pixLeft  = (corner0 * yLirp + corner1 * (8 - yLirp)) >> 3;  // Lirp 1 endpoint from 2 L pixels,

          int corner2 = b[54 + ((32 * y + x) + 33) * 3] & 0xFF;
          int corner3 = b[54 + ((32 * y + x) +  1) * 3] & 0xFF;
          int pixRight = (corner2 * yLirp + corner3 * (8 - yLirp)) >> 3;  // and the other from 2 R pixels

          for(int xLirp = 0; xLirp < 9; ++xLirp) {
            int pixMid = (pixRight * xLirp + pixLeft * (8 - xLirp)) >> 3;         // Lirp between lirped endpoints, bilinear interp
            stroke(colorPal[4 * pixMid + 2] & 0xFF, colorPal[4 * pixMid + 1] & 0xFF, colorPal[4 * pixMid + 0] & 0xFF);
            point(offsetX + 4 + 8 * x + xLirp, offsetY + 188 - (8 * y + yLirp));  // Draw a pixel, bottom up
          }
        }
      }
    }

    for(y = 0; y < 192; ++y) {   // Pad out the empty side pixels
      stroke(get(offsetX + 4, offsetY + y));
      line(offsetX + 0, offsetY + y, offsetX + 3, offsetY + y);
      stroke(get(offsetX + 252, offsetY + y));
      line(offsetX + 253, offsetY + y, offsetX + 255, offsetY + y);
    }
    for(x = 0; x < 256; ++x) {
      stroke(get(offsetX + x, offsetY + 4));
      line(offsetX + x, offsetY + 0, offsetX + x, offsetY + 3);
      stroke(get(offsetX + x, offsetY + 188));
      line(offsetX + x, offsetY + 189, offsetX + x, offsetY + 191);
    }
  } else {    // Plain square pixels
    noStroke();

    for(y = 0; y < 24; ++y) {   // Count all source pixels
      for(x = 0; x < 32; ++x) {
        int pixMid = b[54 + ((32 * y + x) +  0) * 3] & 0xFF;
        fill(colorPal[4 * pixMid + 2] & 0xFF, colorPal[4 * pixMid + 1] & 0xFF, colorPal[4 * pixMid + 0] & 0xFF);  // Get color from table
        rect(offsetX + 8 * x, offsetY + 8 * (23 - y), 8, 8);  // Draw a pixel, bottom up
      }
    }
  }

  if(markersVisible) {  // Show the green marker crosses?
    stroke(0, 192, 0);  // Deep green

    y = ((b[2381] & 0xff) <<  8) +  (b[2380] & 0xff);   // Reassemble 16-bit addresses of cold / hot pixels
    line(offsetX + 8 * (y & 31) + 1, offsetY + 188 - 8 * (y >> 5), offsetX + 8 * (y & 31) + 7, offsetY + 188 - 8 * (y >> 5));
    line(offsetX + 8 * (y & 31) + 4, offsetY + 185 - 8 * (y >> 5), offsetX + 8 * (y & 31) + 4, offsetY + 191 - 8 * (y >> 5));

    y = ((b[2383] & 0xff) <<  8) +  (b[2382] & 0xff);
    line(offsetX + 8 * (y & 31) + 1, offsetY + 188 - 8 * (y >> 5), offsetX + 8 * (y & 31) + 7, offsetY + 188 - 8 * (y >> 5));
    line(offsetX + 8 * (y & 31) + 4, offsetY + 185 - 8 * (y >> 5), offsetX + 8 * (y & 31) + 4, offsetY + 191 - 8 * (y >> 5));

    y = 400;
    line(offsetX + 8 * (y & 31) + 1, offsetY + 188 - 8 * (y >> 5), offsetX + 8 * (y & 31) + 7, offsetY + 188 - 8 * (y >> 5));
    line(offsetX + 8 * (y & 31) + 4, offsetY + 185 - 8 * (y >> 5), offsetX + 8 * (y & 31) + 4, offsetY + 191 - 8 * (y >> 5));
  }
}

void loadColorTable(int choiceNum, int offset) {
  int i, x;

  switch(choiceNum) {
    case 1:     // Load 8-bit BMP color table with computed ironbow curves
      for(x = 0; x < 256; ++x) {
        float fleX = (float)x / 255.0;

        float fleG = 255.9 * (1.02 - (fleX - 0.72) * (fleX - 0.72) * 1.96);
        fleG = (fleG > 255.0) || (fleX > 0.75) ? 255.0 : fleG;  // Truncate curve
        i = (int)fleG;
        colorPal[offset + x * 4 + 2] = byte(i & 0xFF);    // Red vals

        fleG = fleX * fleX * 255.9;
        i = (int)fleG;
        colorPal[offset + x * 4 + 1] = byte(i & 0xFF);    // Grn vals

        fleG = 255.9 * (14.0 * (fleX * fleX * fleX) - 20.0 * (fleX * fleX) + 7.0 * fleX);
        fleG = fleG < 0.0 ? 0.0 : fleG;  // Truncate curve
        i = (int)fleG;
        colorPal[offset + x * 4 + 0] = byte(i & 0xFF);    // Blu vals
      }
      break;
    case 2:  // Compute quadratic "firebow" palette
      for(x = 0; x < 256; ++x) {
        float fleX = (float)x / 255.0;

        float fleG = 255.9 * (1.00 - (fleX - 1.0) * (fleX - 1.0));
        i = (int)fleG;
        colorPal[offset + x * 4 + 2] = byte(i & 0xFF);    // Red vals

        fleG = fleX < 0.25 ? 0.0 : (fleX - 0.25) * 1.3333 * 255.9;
        i = (int)fleG;
        colorPal[offset + x * 4 + 1] = byte(i & 0xFF);    // Grn vals

        fleG = fleX < 0.5 ? 0.0 : (fleX - 0.5) * (fleX - 0.5) * 1023.9;
        i = (int)fleG;
        colorPal[offset + x * 4 + 0] = byte(i & 0xFF);    // Blu vals
      }
      break;
    case 3:  // Compute "alarm" palette
      for(x = 0; x < 256; ++x) {
        float fleX = (float)x / 255.0;

        float fleG = 255.9 * (fleX < 0.875 ? 1.00 - (fleX * 1.1428) : 1.0);
        i = (int)fleG;
        colorPal[offset + x * 4 + 2] = byte(i & 0xFF);    // Red vals

        fleG = 255.9 * (fleX < 0.875 ? 1.00 - (fleX * 1.1428) : (fleX - 0.875) * 8.0);
        i = (int)fleG;
        colorPal[offset + x * 4 + 1] = byte(i & 0xFF);    // Grn vals

        fleG = 255.9 * (fleX < 0.875 ? 1.00 - (fleX * 1.1428) : 0.0);
        i = (int)fleG;
        colorPal[offset + x * 4 + 0] = byte(i & 0xFF);    // Blu vals
      }
      break;
    case 4:    // Grayscale, black hot 
      for(x = 0; x < 256; ++x) {
        colorPal[offset + x * 4 + 2] = byte(255 - x & 0xFF);    // Red vals
        colorPal[offset + x * 4 + 1] = byte(255 - x & 0xFF);    // Grn vals
        colorPal[offset + x * 4 + 0] = byte(255 - x & 0xFF);    // Blu vals
      }
      break;
    default:    // Grayscale, white hot 
      for(x = 0; x < 256; ++x) {
        colorPal[offset + x * 4 + 2] = byte(x & 0xFF);    // Red vals
        colorPal[offset + x * 4 + 1] = byte(x & 0xFF);    // Grn vals
        colorPal[offset + x * 4 + 0] = byte(x & 0xFF);    // Blu vals
      }
  }
}

// Rebuild a float from a fixed point decimal value encoded in 4 bytes
float expandFloat(byte m1, byte m2, byte e1, byte e2) {
  int fracPart;
  float floatPart;

  fracPart = ((e2 & 0xff) << 8) + (e1 & 0xff);   // Reassemble 16-bit value
  floatPart = (float)fracPart / 49152.0;         // Convert into fractional portion of float
  fracPart = ((m2 & 0xff) << 8) + (m1 & 0xff);   // Reassemble 16-bit value
  return ((float)fracPart + floatPart) - 1000.0; // Complete reconstructing original float
}

String[] listFileNames(String dir) {   // Return the filenames from a directory as an array of Strings
  File file = new File(dir);

  if (file.isDirectory()) {
    String names[] = file.list();
    return names;
  } else    // It's not a directory
    return null;
}

Almost there!  Our next step is to bring in the thermal image data from the camera so we can do cool things with it.

This guide was first published on Mar 27, 2020. It was last updated on Mar 27, 2020.

This page (More Processing Sketches) was last updated on Jan 31, 2020.

Text editor powered by tinymce.