This code will allow the MLX90640 to be used with OpenCV. OpenCV is installed on a Raspberry Pi 3. I used gadgetoidās fbuf code as a foundation with the assistance of StackOverflow.
#include <stdint.h>
#include <iostream>
#include <cstring>
#include <fstream>
#include <chrono>
#include <thread>
#include <math.h>
#include "headers/MLX90640_API.h"
#include "opencv2/opencv.hpp"
using namespace cv;
using namespace std;
#define MLX_I2C_ADDR 0x33
// Valid frame rates are 1, 2, 4, 8, 16, 32 and 64
// The i2c baudrate is set to 1mhz to support these
#define FPS 8
#define FRAME_TIME_MICROS (1000000/FPS)
// Despite the framerate being ostensibly FPS hz
// The frame is often not ready in time
// This offset is added to the FRAME_TIME_MICROS
// to account for this.
#define OFFSET_MICROS 850
int main(){
static uint16_t eeMLX90640[832];
float emissivity = 1;
uint16_t frame[834];
static float image[768];
static float mlx90640To[768];
float eTa;
static uint16_t data[768*sizeof(float)];
auto frame_time = std::chrono::microseconds(FRAME_TIME_MICROS + OFFSET_MICROS);
MLX90640_SetDeviceMode(MLX_I2C_ADDR, 0);
MLX90640_SetSubPageRepeat(MLX_I2C_ADDR, 0);
switch(FPS){
case 1:
MLX90640_SetRefreshRate(MLX_I2C_ADDR, 0b001);
break;
case 2:
MLX90640_SetRefreshRate(MLX_I2C_ADDR, 0b010);
break;
case 4:
MLX90640_SetRefreshRate(MLX_I2C_ADDR, 0b011);
break;
case 8:
MLX90640_SetRefreshRate(MLX_I2C_ADDR, 0b100);
break;
case 16:
MLX90640_SetRefreshRate(MLX_I2C_ADDR, 0b101);
break;
case 32:
MLX90640_SetRefreshRate(MLX_I2C_ADDR, 0b110);
break;
case 64:
MLX90640_SetRefreshRate(MLX_I2C_ADDR, 0b111);
break;
default:
printf("Unsupported framerate: %d", FPS);
return 1;
}
MLX90640_SetChessMode(MLX_I2C_ADDR);
paramsMLX90640 mlx90640;
MLX90640_DumpEE(MLX_I2C_ADDR, eeMLX90640);
MLX90640_ExtractParameters(eeMLX90640, &mlx90640);
while (1){
auto start = std::chrono::system_clock::now();
MLX90640_GetFrameData(MLX_I2C_ADDR, frame);
MLX90640_InterpolateOutliers(frame, eeMLX90640);
eTa = MLX90640_GetTa(frame, &mlx90640);
MLX90640_CalculateTo(frame, &mlx90640, emissivity, eTa, mlx90640To);
Mat IR_mat (32,24, CV_32FC1, data);
for(int y = 0; y < 24; y++){
for(int x = 0; x < 32; x++){
float val = mlx90640To[32 * (23-y) + x];
IR_mat.at<float>(x,y) = val;
}
}
// Normalize the mat
Mat normal_mat;
normalize(IR_mat, normal_mat, 0,1.0, NORM_MINMAX, CV_32FC1);
// Convert Mat to CV_U8 to use applyColorMap
double minVal, maxVal;
minMaxLoc(normal_mat, &minVal, &maxVal);
Mat u8_mat;
normal_mat.convertTo(u8_mat, CV_8U, 255.0/(maxVal - minVal), -minVal);
// Resize mat
Mat size_mat;
resize(u8_mat, size_mat, Size(240,320), INTER_CUBIC);
// Apply false color
Mat falsecolor_mat;
applyColorMap(size_mat, falsecolor_mat, COLORMAP_JET);
// Display stream in window
namedWindow( "IR Camera Window");
imshow ("IR Camera Window", falsecolor_mat);
waitKey(1);
auto end = std::chrono::system_clock::now();
auto elapsed = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
std::this_thread::sleep_for(std::chrono::microseconds(frame_time - elapsed));
}
return 0;
}