Skip to content

Commit 295055e

Browse files
authored
Merge pull request adafruit#1061 from adafruit/TheKitty-patch-120
Add code for new Thermal Imager guide
2 parents 1a3e5e9 + 33cdf5c commit 295055e

File tree

4 files changed

+1395
-0
lines changed

4 files changed

+1395
-0
lines changed
Lines changed: 332 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,332 @@
1+
// ConvertBMPinspector01 - Read and enlarge a modified 32x24 24-bit gray BMP file,
2+
// display an upscaled 256x192 BMP image in 256 colors.
3+
// Ver. 1 - Fetch filenames and display BMPs in sequence.
4+
// Add nav buttons and mouseover pixel temperatures
5+
// This sketch does no checking for file compatibility.
6+
// Only frm_____.bmp images from the thermal camera sketch will work.
7+
// Any other files in the data folder will fail.
8+
9+
import java.util.Date;
10+
11+
byte b[], colorPal[]; // Buffers for input file bytes and for colors
12+
13+
int i, fileCount = 0, BGcolor = 48, colorMap = 1,
14+
butnsX = 30, butnsY = 290,
15+
offsetX = 153, offsetY = 6, // These value pairs control where the onscreen features appear
16+
numbersX = 40, numbersY = 48,
17+
probeX = 190, probeY = 210;
18+
boolean celsiusFlag = false;
19+
float fixedPoint[];
20+
String[] filenames;
21+
22+
void setup() {
23+
24+
size(480, 360); // Size must be the first statement
25+
background(BGcolor); // Clear the screen with a gray background
26+
27+
colorPal = new byte[1024]; // Prepare a 1K color table
28+
loadColorTable(colorMap, 0); // Load color table, 1 == ironbow palette
29+
fixedPoint = new float[5]; // A buffer for appended fixed point values
30+
31+
String path = sketchPath() + "/data"; // Read from the "/data" subdirectory
32+
33+
filenames = listFileNames(path);
34+
fileCount = filenames.length;
35+
36+
i = 0;
37+
if(fileCount < 1) {
38+
println("No files found. Stopping.");
39+
noLoop();
40+
} else {
41+
loadBMPscreen(i); // Read in the first frame for inspection
42+
}
43+
}
44+
45+
void draw() {
46+
int sampleX, sampleY, pixelVal;
47+
float sampleTemp;
48+
49+
sampleX = (mouseX - offsetX) >> 3; // Map mouse position to BMP pixel space
50+
sampleY = 23 - ((mouseY - offsetY) >> 3);
51+
52+
noStroke();
53+
smooth();
54+
fill(BGcolor + 16);
55+
rect(probeX, probeY, 180, 40); // Clear the interactive window space
56+
57+
if((sampleX >= 0) && (sampleX < 32) && (sampleY >= 0) && (sampleY < 24)) { // Mouse within BMP image bounds?
58+
pixelVal = b[54 + (32 * sampleY + sampleX) * 3] & 0xff; // Read the 8-bit pixel value
59+
60+
fill(colorPal[4 * pixelVal + 2] & 0xFF, colorPal[4 * pixelVal + 1] & 0xFF, colorPal[4 * pixelVal + 0] & 0xFF);
61+
rect(probeX, probeY, 180, 40);
62+
fill(BGcolor);
63+
rect(probeX + 10, probeY + 10, 160, 20); // Draw a colorized frame for the interactive temp readout
64+
65+
sampleTemp = (float(pixelVal) + 1.0) / 257.0 * (fixedPoint[3] - fixedPoint[1]) + fixedPoint[1];
66+
if(!celsiusFlag)
67+
sampleTemp = sampleTemp * 1.8 + 32.0;
68+
69+
fill(255); // Ready to display white interactive text
70+
textSize(11);
71+
text(sampleX, probeX + 154, probeY + 19); // Display X Y position
72+
text(sampleY, probeX + 154, probeY + 29);
73+
textSize(15);
74+
text(sampleTemp, probeX + 60, probeY + 25); // Display temperature
75+
76+
if(pixelVal == 0 && fixedPoint[0] < fixedPoint[1]) // Pixel values clipped at bottom limit?
77+
text("<", probeX + 40, probeY + 25); // Show out-of-range indicator
78+
if(pixelVal == 255 && fixedPoint[4] > fixedPoint[3]) // Clipped at top?
79+
text(">", probeX + 40, probeY + 25); // Same
80+
}
81+
82+
noSmooth(); // Clear any highlighted buttons
83+
stroke(0);
84+
noFill();
85+
for(sampleX = 0; sampleX < 8; ++sampleX)
86+
rect(butnsX + sampleX * 52, butnsY, 51, 24);
87+
88+
sampleX = mouseX - butnsX;
89+
sampleY = mouseY - butnsY;
90+
if(sampleX >=0 && sampleX < 416 && sampleY >= 0 && sampleY < 24) { // Mouse over buttons?
91+
sampleX = sampleX / 52; // Map mouse X to button X space
92+
stroke(BGcolor + 64);
93+
rect(butnsX + sampleX * 52, butnsY, 51, 24); // Highlight border around a button
94+
}
95+
}
96+
97+
void keyPressed() { // Load a different thermal BMP image based on keystroke
98+
switch(key) {
99+
case '.': // Next image
100+
i = (i + 1) % fileCount;
101+
break;
102+
case ',': // Prev Image
103+
i = (i + fileCount - 1) % fileCount;
104+
break;
105+
case '>': // 16 images forward
106+
i = i + 16 < fileCount ? i + 16 : fileCount - 1;
107+
break;
108+
case '<': // 16 images back
109+
i = i - 16 < 0 ? 0 : i - 16;
110+
break;
111+
case '/': // Last image
112+
i = fileCount - 1;
113+
break;
114+
case 'm': // First image
115+
i = 0;
116+
break;
117+
}
118+
loadBMPscreen(i);
119+
}
120+
121+
void mousePressed() {
122+
int sampleX, sampleY;
123+
124+
sampleX = mouseX - butnsX;
125+
sampleY = mouseY - butnsY;
126+
if(sampleX >=0 && sampleX < 416 && sampleY >= 0 && sampleY < 24) { // Is mouse over button row?
127+
sampleX = sampleX / 52; // Map mouse X to button X space
128+
129+
switch(sampleX) {
130+
case 1: // First image
131+
i = 0;
132+
break;
133+
case 2: // 16 images back
134+
i = i - 16 < 0 ? 0 : i - 16;
135+
break;
136+
case 3: // Prev Image
137+
i = (i + fileCount - 1) % fileCount;
138+
break;
139+
case 4: // Next image
140+
i = (i + 1) % fileCount;
141+
break;
142+
case 5: // 16 images forward
143+
i = i + 16 < fileCount ? i + 16 : fileCount - 1;
144+
break;
145+
case 6: // Last image
146+
i = fileCount - 1;
147+
break;
148+
case 7: // Change color map
149+
loadColorTable(colorMap = (colorMap + 1) % 5, 0); // Load color table
150+
break;
151+
default: // Toggle C/F
152+
celsiusFlag = !celsiusFlag;
153+
break;
154+
}
155+
loadBMPscreen(i);
156+
}
157+
}
158+
159+
void loadBMPscreen(int fileIndex) {
160+
int x, y;
161+
162+
b = loadBytes(filenames[fileIndex]); // Open a file and read its 8-bit data
163+
background(BGcolor); // Clear screen
164+
enlarge8bitColor(); // Place colored enlarged image on screen
165+
166+
for(x = 0; x < 5; ++x) { // Rebuild 5 float values from next 4*n bytes in the file
167+
fixedPoint[x] = expandFloat(b[2360 + (x * 4) + 0], b[2360 + (x * 4) + 1],
168+
b[2360 + (x * 4) + 2], b[2360 + (x * 4) + 3]);
169+
}
170+
y = ((b[2387] & 0xff) << 24) + ((b[2386] & 0xff) << 16)
171+
+ ((b[2385] & 0xff) << 8) + (b[2384] & 0xff); // Reassemble a milliseconds time stamp
172+
173+
textSize(10); // Print text labels for the frame stats
174+
smooth();
175+
fill(255);
176+
text(filenames[fileIndex], numbersX + 5, numbersY + 40); // Show current filename
177+
178+
if(celsiusFlag)
179+
text("Frame\n\n\nSeconds\n\nDegrees C", numbersX + 5, numbersY + 8);
180+
else
181+
text("Frame\n\n\nSeconds\n\nDegrees F", numbersX + 5, numbersY + 8);
182+
183+
text("Approximate temperatures based on 8-bit pixel values", probeX - 42, probeY + 52); // Show approximation disclaimer
184+
185+
textSize(15);
186+
text(fileIndex, numbersX + 5, numbersY + 25); // Print frame number
187+
text(float(y) * 0.001, numbersX, numbersY + 74); // Print time stamp in seconds
188+
189+
if(celsiusFlag) { // Show 3 temps in Celsius
190+
fill(255, 128, 64);
191+
text(fixedPoint[4], numbersX, numbersY + 108);
192+
fill(255, 200, 64);
193+
text(fixedPoint[2], numbersX, numbersY + 128);
194+
fill(128, 128, 255);
195+
text(fixedPoint[0], numbersX, numbersY + 148);
196+
197+
} else { // or show them in Farenheit
198+
fill(255, 128, 64);
199+
text(fixedPoint[4] * 1.8 + 32.0, numbersX, numbersY + 108);
200+
fill(255, 200, 64);
201+
text(fixedPoint[2] * 1.8 + 32.0, numbersX, numbersY + 128);
202+
fill(128, 128, 255);
203+
text(fixedPoint[0] * 1.8 + 32.0, numbersX, numbersY + 148);
204+
}
205+
206+
noSmooth();
207+
stroke(0);
208+
fill(BGcolor + 24);
209+
for(x = 0; x < 8; ++x) // Draw 8 button rectangles
210+
rect(butnsX + x * 52, butnsY, 51, 24);
211+
for(x = 0; x < 50; ++x) { // Paint a mini colormap gradient within last button
212+
y = int(map(x, 0, 50, 0, 255));
213+
stroke(colorPal[4 * y + 2] & 0xFF, colorPal[4 * y + 1] & 0xFF, colorPal[4 * y + 0] & 0xFF);
214+
line(butnsX + 365 + x, butnsY + 1, butnsX + 365 + x, butnsY + 23);
215+
}
216+
smooth(); // Add text labels to buttons
217+
fill(255);
218+
textSize(15);
219+
text("|< << < > >> >|", butnsX + 70, butnsY + 17);
220+
if(celsiusFlag)
221+
text("C", butnsX + 20, butnsY + 18);
222+
else
223+
text("F", butnsX + 20, butnsY + 18);
224+
}
225+
226+
void enlarge8bitColor() { // Convert a small gray BMP array and plot an enlarged colormapped version
227+
int x, y;
228+
229+
noStroke();
230+
231+
for(y = 0; y < 24; ++y) { // Count all source pixels
232+
for(x = 0; x < 32; ++x) {
233+
int pixMid = b[54 + ((32 * y + x) + 0) * 3] & 0xFF;
234+
fill(colorPal[4 * pixMid + 2] & 0xFF, colorPal[4 * pixMid + 1] & 0xFF, colorPal[4 * pixMid + 0] & 0xFF); // Get color from table
235+
rect(offsetX + 8 * x, offsetY + 8 * (23 - y), 8, 8); // Draw a square pixel, bottom up
236+
}
237+
}
238+
}
239+
240+
void loadColorTable(int choiceNum, int offset) {
241+
int i, x;
242+
243+
switch(choiceNum) {
244+
case 1: // Load 8-bit BMP color table with computed ironbow curves
245+
for(x = 0; x < 256; ++x) {
246+
float fleX = (float)x / 255.0;
247+
248+
float fleG = 255.9 * (1.02 - (fleX - 0.72) * (fleX - 0.72) * 1.96);
249+
fleG = (fleG > 255.0) || (fleX > 0.75) ? 255.0 : fleG; // Truncate curve
250+
i = (int)fleG;
251+
colorPal[offset + x * 4 + 2] = byte(i & 0xFF); // Red vals
252+
253+
fleG = fleX * fleX * 255.9;
254+
i = (int)fleG;
255+
colorPal[offset + x * 4 + 1] = byte(i & 0xFF); // Grn vals
256+
257+
fleG = 255.9 * (14.0 * (fleX * fleX * fleX) - 20.0 * (fleX * fleX) + 7.0 * fleX);
258+
fleG = fleG < 0.0 ? 0.0 : fleG; // Truncate curve
259+
i = (int)fleG;
260+
colorPal[offset + x * 4 + 0] = byte(i & 0xFF); // Blu vals
261+
}
262+
break;
263+
case 2: // Compute quadratic "firebow" palette
264+
for(x = 0; x < 256; ++x) {
265+
float fleX = (float)x / 255.0;
266+
267+
float fleG = 255.9 * (1.00 - (fleX - 1.0) * (fleX - 1.0));
268+
i = (int)fleG;
269+
colorPal[offset + x * 4 + 2] = byte(i & 0xFF); // Red vals
270+
271+
fleG = fleX < 0.25 ? 0.0 : (fleX - 0.25) * 1.3333 * 255.9;
272+
i = (int)fleG;
273+
colorPal[offset + x * 4 + 1] = byte(i & 0xFF); // Grn vals
274+
275+
fleG = fleX < 0.5 ? 0.0 : (fleX - 0.5) * (fleX - 0.5) * 1023.9;
276+
i = (int)fleG;
277+
colorPal[offset + x * 4 + 0] = byte(i & 0xFF); // Blu vals
278+
}
279+
break;
280+
case 3: // Compute "alarm" palette
281+
for(x = 0; x < 256; ++x) {
282+
float fleX = (float)x / 255.0;
283+
284+
float fleG = 255.9 * (fleX < 0.875 ? 1.00 - (fleX * 1.1428) : 1.0);
285+
i = (int)fleG;
286+
colorPal[offset + x * 4 + 2] = byte(i & 0xFF); // Red vals
287+
288+
fleG = 255.9 * (fleX < 0.875 ? 1.00 - (fleX * 1.1428) : (fleX - 0.875) * 8.0);
289+
i = (int)fleG;
290+
colorPal[offset + x * 4 + 1] = byte(i & 0xFF); // Grn vals
291+
292+
fleG = 255.9 * (fleX < 0.875 ? 1.00 - (fleX * 1.1428) : 0.0);
293+
i = (int)fleG;
294+
colorPal[offset + x * 4 + 0] = byte(i & 0xFF); // Blu vals
295+
}
296+
break;
297+
case 4: // Grayscale, black hot
298+
for(x = 0; x < 256; ++x) {
299+
colorPal[offset + x * 4 + 2] = byte(255 - x & 0xFF); // Red vals
300+
colorPal[offset + x * 4 + 1] = byte(255 - x & 0xFF); // Grn vals
301+
colorPal[offset + x * 4 + 0] = byte(255 - x & 0xFF); // Blu vals
302+
}
303+
break;
304+
default: // Grayscale, white hot
305+
for(x = 0; x < 256; ++x) {
306+
colorPal[offset + x * 4 + 2] = byte(x & 0xFF); // Red vals
307+
colorPal[offset + x * 4 + 1] = byte(x & 0xFF); // Grn vals
308+
colorPal[offset + x * 4 + 0] = byte(x & 0xFF); // Blu vals
309+
}
310+
}
311+
}
312+
313+
// Rebuild a float from a fixed point decimal value encoded in 4 bytes
314+
float expandFloat(byte m1, byte m2, byte e1, byte e2) {
315+
int fracPart;
316+
float floatPart;
317+
318+
fracPart = ((e2 & 0xff) << 8) + (e1 & 0xff); // Reassemble 16-bit value
319+
floatPart = (float)fracPart / 49152.0; // Convert into fractional portion of float
320+
fracPart = ((m2 & 0xff) << 8) + (m1 & 0xff); // Reassemble 16-bit value
321+
return ((float)fracPart + floatPart) - 1000.0; // Complete reconstructing original float
322+
}
323+
324+
String[] listFileNames(String dir) { // Return the filenames from a directory as an array of Strings
325+
File file = new File(dir);
326+
327+
if (file.isDirectory()) {
328+
String names[] = file.list();
329+
return names;
330+
} else // It's not a directory
331+
return null;
332+
}

0 commit comments

Comments
 (0)