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