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.
Page last edited January 20, 2025
Text editor powered by tinymce.