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.