Hi, with a range of -10 to 140 Celsius the scaling function would be:

```
def map_g_to_temp(g):
return ((g * 150) / 255.0) - 10
```

Which is about 0.588 C per pixel value. So, 3 C would not be a rounding error.

Stats are computed using a histogram:

```
memset(out->LBins, 0, out->LBinCount * sizeof(uint32_t));
int pixel_count = roi->w * roi->h;
float mult = (out->LBinCount - 1) / ((float) (COLOR_GRAYSCALE_MAX - COLOR_GRAYSCALE_MIN));
if ((!thresholds) || (!list_size(thresholds))) {
// Fast histogram code when no color thresholds list...
for (int y = roi->y, yy = roi->y + roi->h; y < yy; y++) {
uint8_t *row_ptr = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(ptr, y);
for (int x = roi->x, xx = roi->x + roi->w; x < xx; x++) {
int pixel = IMAGE_GET_GRAYSCALE_PIXEL_FAST(row_ptr, x);
((uint32_t *) out->LBins)[fast_floorf((pixel - COLOR_GRAYSCALE_MIN) * mult)]++;
}
}
} else {
// Reset pixel count.
pixel_count = 0;
for (list_lnk_t *it = iterator_start_from_head(thresholds); it; it = iterator_next(it)) {
color_thresholds_list_lnk_data_t lnk_data;
iterator_get(thresholds, it, &lnk_data);
for (int y = roi->y, yy = roi->y + roi->h; y < yy; y++) {
uint8_t *row_ptr = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(ptr, y);
for (int x = roi->x, xx = roi->x + roi->w; x < xx; x++) {
int pixel = IMAGE_GET_GRAYSCALE_PIXEL_FAST(row_ptr, x);
if (COLOR_THRESHOLD_GRAYSCALE(pixel, &lnk_data, invert)) {
((uint32_t *) out->LBins)[fast_floorf((pixel - COLOR_GRAYSCALE_MIN) * mult)]++;
pixel_count++;
}
}
}
}
}
float pixels = IM_DIV(1, ((float) pixel_count));
for (int i = 0, j = out->LBinCount; i < j; i++) {
out->LBins[i] = ((uint32_t *) out->LBins)[i] * pixels;
}
```

And then that histogram is used to compute the stats:

```
float mult = (COLOR_GRAYSCALE_MAX - COLOR_GRAYSCALE_MIN) / ((float) (ptr->LBinCount - 1));
float avg = 0;
float stdev = 0;
float median_count = 0;
float mode_count = 0;
bool min_flag = false;
for (int i = 0, j = ptr->LBinCount; i < j; i++) {
float value_f = (i * mult) + COLOR_GRAYSCALE_MIN;
int value = fast_floorf(value_f);
avg += value_f * ptr->LBins[i];
stdev += value_f * value_f * ptr->LBins[i];
if ((median_count < 0.25f) && (0.25f <= (median_count + ptr->LBins[i]))) {
out->LLQ = value;
}
if ((median_count < 0.5f) && (0.5f <= (median_count + ptr->LBins[i]))) {
out->LMedian = value;
}
if ((median_count < 0.75f) && (0.75f <= (median_count + ptr->LBins[i]))) {
out->LUQ = value;
}
if (ptr->LBins[i] > mode_count) {
mode_count = ptr->LBins[i];
out->LMode = value;
}
if ((ptr->LBins[i] > 0.0f) && (!min_flag)) {
min_flag = true;
out->LMin = value;
}
if (ptr->LBins[i] > 0.0f) {
out->LMax = value;
}
median_count += ptr->LBins[i];
}
out->LMean = fast_floorf(avg);
out->LSTDev = fast_floorf(fast_sqrtf(stdev - (avg * avg)));
break;
```

All I can think of is that the floor operations used above aren’t as accurate. These methods weren’t designed to be really incredibly accurate. Just fast.

Do you have the ability to edit the firmware? You can tweak them then to fix this. Otherwise, can you give me a target image in BMP file format and tell me what the expected output should be for get_stats()? I can then tweak the method until that’s correct.