Picking up on a closed topic that I had the need to accomplish as well. I have a Zenmuse XTR camera that I have used to image several sites. I am able to stitch them using odm, but as you all know these result in pixel values of 0 - 256 and not in the temperature data that we would like as the result.
I also tried agisoft mata, and pix4d mapper and neither program worke for this.
Rooting around on some discussions I found an R package called ‘Thermimage’ that could see the data - and convert it to a raw format - with mention of followup analysis using imagej. Here is the code that loops through all of my images to get them converted to raw files - where the images are stored in a directory called Thermal, and I export them to a directory called Thermal/raw.
library(Thermimage)
listimagesn <- dir('Thermal', pattern= '*.jpg', full.names = F) # just filenames
listimages <- dir('Thermal', pattern= '*.jpg', full.names = T) #full path names for read
dir.create('Thermal/raw')
for(i in 1:length(listimages)){
img <-readflirJPG(listimages[i], exiftoolpath="installed")
cams<-flirsettings(listimages[i], exiftoolpath="installed", camvals="")
ObjectEmissivity<- cams$Info$Emissivity # Image Saved Emissivity - should be ~0.95 or 0.96
dateOriginal<-cams$Dates$DateTimeOriginal # Original date/time extracted from file
dateModif<- cams$Dates$FileModificationDateTime # Modification date/time extracted from file
PlanckR1<- cams$Info$PlanckR1 # Planck R1 constant for camera
PlanckB<- cams$Info$PlanckB # Planck B constant for camera
PlanckF<- cams$Info$PlanckF # Planck F constant for camera
PlanckO<- cams$Info$PlanckO # Planck O constant for camera
PlanckR2<- cams$Info$PlanckR2 # Planck R2 constant for camera
ATA1<- cams$Info$AtmosphericTransAlpha1 # Atmospheric Transmittance Alpha 1
ATA2<- cams$Info$AtmosphericTransAlpha2 # Atmospheric Transmittance Alpha 2
ATB1<- cams$Info$AtmosphericTransBeta1 # Atmospheric Transmittance Beta 1
ATB2<- cams$Info$AtmosphericTransBeta2 # Atmospheric Transmittance Beta 2
ATX<- cams$Info$AtmosphericTransX # Atmospheric Transmittance X
OD<- cams$Info$ObjectDistance # object distance in metres
FD<- cams$Info$FocusDistance # focus distance in metres
ReflT<- cams$Info$ReflectedApparentTemperature # Reflected apparent temperature
AtmosT<- cams$Info$AtmosphericTemperature # Atmospheric temperature
IRWinT<- cams$Info$IRWindowTemperature # IR Window Temperature
IRWinTran<- cams$Info$IRWindowTransmission # IR Window transparency
RH<- cams$Info$RelativeHumidity # Relative Humidity
h<- cams$Info$RawThermalImageHeight # sensor height (i.e. image height)
w<- cams$Info$RawThermalImageWidth # sensor width (i.e. image width)
#photoparams[[i]] <- c(ObjectEmissivity, OD, ReflT, AtmosT, IRWinT, IRWinTran, RH, PlanckR1, PlanckB, PlanckF, PlanckO, PlanckR2, ATA1, ATA2, ATB1, ATB2, ATX)
temperature<-raw2temp(img, ObjectEmissivity, OD, ReflT, AtmosT, IRWinT, IRWinTran, RH, PlanckR1, PlanckB, PlanckF, PlanckO, PlanckR2, ATA1, ATA2, ATB1, ATB2, ATX)
writeFlirBin(as.vector(t(temperature)), templookup=NULL, w=w, h=h, I="", rootname=paste0('Thermal/raw/',listimagesn[i]))
}
After this I created an imagej macro using the following code.
library(stringr)
library(rlist)
rawd <- 'Thermal/raw'
filez <- dir(rawd, full.names = T)
filezn <- dir(rawd, full.names = F)
thisd <- getwd()
newfilez <- str_replace(filezn, '.raw','.tif')
tifd <- 'Thermal/tifs'
dir.create(tifd)
pt1 <- paste0('run("Raw...", "open=',thisd,'/')
pt2 <- paste0(' image=[32-bit Real] width=640 height=512 little-endian use");
saveAs("Tiff", "',thisd,'/',tifd,'/')
cmdlist <- list()
for(i in 1:length(filez)){
cmdlist[[i]] <- paste0(pt1, filez[i],pt2, newfilez[i],'");\nclose();')
}
cmdlist <- list.rbind(cmdlist)
#cmdlist <- rbind(cmdlist,'close();')
write.table(cmdlist, 'cmdlist.txt', row.names = F)
This creates a file that needs a little cleanup to remove "run and replace with run
remove the backslashes, and remove the quote after close();
The macro process for each file ends up looking like this:
run("Raw...", "open=/home/knussear/SynUAS/14_Argentina_Drones/Flights/Black_Site/Flight_3_Thermal_Noon/Thermal/raw/DJI_0001.jpg_W640_H512_F1_I.raw image=[32-bit Real] width=640 height=512 little-endian use");
saveAs("Tiff", "/home/knussear/SynUAS/14_Argentina_Drones/Flights/Black_Site/Flight_3_Thermal_Noon/Thermal/tifs/DJI_0001.jpg_W640_H512_F1_I.tif");
close();
Following this I now have a directory full of tif files with the values in degrees C. I now need to re-insert the exif data to get the files to process. To do so I use exiftool in r to loop through each image, get the exif data from the jpg and insert it into the tif file. It does spit some error for tags it doesn’t like or need, but the result is a tif image file that stitches into a perfect orthophoto and results in values in degrees C for my study area. Here is the code for that segment.
library(exiftoolr)
thisd <- getwd()
tifd <- '/Thermal/tifs'
filez1 <- dir(paste0(thisd,'/','Thermal'), pattern = '*.jpg$', full.names = T, include.dirs = T)
filez2 <- dir(paste0(thisd,tifd), pattern = '*.tif$', full.names = T)
exifs1 <- list()
for(i in 1:length(filez1)){
# exifs1[[i]] <- exif_read(filez1[i])
thiscmd <- paste0('exiftool -args -G1 --filename --directory ',filez1[i],' > ',i,'out.args') # reads jpg data and exports to an args file
system(thiscmd)
thiscmd2 <- paste0("exiftool -@ ",i,"out.args -sep ', ' ",filez2[i]) #puts args into tif
system(thiscmd2)
}
I hope this helps someone. It took me a while to get it up and running, but I am now able to actually visualize and analyze my thermal camera data.