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\n Seconds\n\n Degrees C" , numbersX + 5 , numbersY + 8 );
180+ else
181+ text (" Frame\n\n\n Seconds\n\n Degrees 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