Hi all,
Does anyone have any information or specifications for the "MicroImage" (?) .fc file format used by CalcImage for image palettes?
I can replicate the files successfully, but I'm struggling to create a custom.fc file based on the viridis (and associated) colour palette.
In particular, do we know what the (e.g.) two 128 values represent in the example below? They're both in the 0-255 range.
The large value at the end seems to be the decimal representation of the colour.
e.g.
Item= 128 128 3472512
Thanks!
I think these codes will help.
Here's the code to convert a Surfer .CLR file to a Probe Image/CalcImage .FC (false color) file:
Sub ImageConvertCLRtoFC(tFileCLR As String, tFileFC As String)
' Convert the passed CLR file to a FC file with the same name
ierror = False
On Error GoTo ImageConvertCLRtoFCError
Dim astring As String
Dim RGBColor As Long
Dim rval As Long, gval As Long, bval As Long
Dim n As Integer
Dim Percent As Single
' Check for proper extensions
If UCase$(MiscGetFileNameExtensionOnly$(tFileCLR$)) <> ".CLR" Then GoTo ImageConvertCLRtoFCBadLUTExtension
If UCase$(MiscGetFileNameExtensionOnly$(tFileFC$)) <> ".FC" Then GoTo ImageConvertCLRtoFCBadFCExtension
If Dir$(tFileCLR$) = vbNullString Then GoTo ImageConvertCLRtoFCNotFound
Open tFileCLR$ For Input As #Temp1FileNumber%
Open tFileFC$ For Output As #Temp2FileNumber%
astring$ = "False color description for CalcImage"
Print #Temp2FileNumber%, astring$
astring$ = "BEGIN Items"
Print #Temp2FileNumber%, astring$
astring$ = " Interpolate = 1" ' perform color interpolation
Print #Temp2FileNumber%, astring$
' Read column labels in CLR file "ColorMap 1 1"
Line Input #Temp1FileNumber%, astring$
If astring$ <> "ColorMap 1 1" Then GoTo ImageConvertCLRtoFCNotCLR
' Loop through all values in .CLR file
Do Until EOF(Temp1FileNumber%)
Input #Temp1FileNumber%, Percent!, rval&, gval&, bval&
' Convert percent to color index
n% = Percent! / 100# * 255#
If n% < 0# Then n% = 0
If n% > 255# Then n% = 255
' Convert RGB to RGB color
Call BMPRGB(RGBColor&, rval&, gval&, bval&)
If ierror Then Exit Sub
astring$ = " Item=" & Format$(n%) & " " & Format$(n%) & " " & Format$(RGBColor&)
Print #Temp2FileNumber%, astring$
Loop
astring$ = "END Items"
Print #Temp2FileNumber%, astring$
Close #Temp1FileNumber%
Close #Temp2FileNumber%
Exit Sub
' Errors
ImageConvertCLRtoFCError:
MsgBox Error$, vbOKOnly + vbCritical, "ImageConvertCLRtoFC"
Close #Temp1FileNumber%
Close #Temp2FileNumber%
ierror = True
Exit Sub
ImageConvertCLRtoFCBadLUTExtension:
msg$ = "File " & tFileCLR$ & " does not have the proper .CLR extension."
MsgBox msg$, vbOKOnly + vbExclamation, "ImageConvertCLRtoFC"
ierror = True
Exit Sub
ImageConvertCLRtoFCBadFCExtension:
msg$ = "File " & tFileFC$ & " does not have the proper .FC extension."
MsgBox msg$, vbOKOnly + vbExclamation, "ImageConvertCLRtoFC"
ierror = True
Exit Sub
ImageConvertCLRtoFCNotCLR:
msg$ = "File " & tFileCLR$ & " does not have the proper first line"
MsgBox msg$, vbOKOnly + vbExclamation, "ImageConvertCLRtoFC"
ierror = True
Exit Sub
ImageConvertCLRtoFCNotFound:
msg$ = "File " & tFileCLR$ & " was not found."
MsgBox msg$, vbOKOnly + vbExclamation, "ImageConvertCLRtoFC"
ierror = True
Exit Sub
End Sub
And here is how to convert a JEOL .LUT file to a ProbeImage/CalcImage .FC file:
Sub ImageConvertLUTtoFC(tFileLUT As String, tFileFC As String)
' Convert the passed LUT file to a FC file with the same name
ierror = False
On Error GoTo ImageConvertLUTtoFCError
Dim i As Integer
Dim astring As String
Dim RGBColor As Long
Dim n As Long, rval As Long, gval As Long, bval As Long
' Check for proper extensions
If UCase$(MiscGetFileNameExtensionOnly$(tFileLUT$)) <> ".LUT" Then GoTo ImageConvertLUTtoFCBadLUTExtension
If UCase$(MiscGetFileNameExtensionOnly$(tFileFC$)) <> ".FC" Then GoTo ImageConvertLUTtoFCBadFCExtension
If Dir$(tFileLUT$) = vbNullString Then GoTo ImageConvertLUTtoFCLUTNotFound
Open tFileLUT$ For Input As #Temp1FileNumber%
Open tFileFC$ For Output As #Temp2FileNumber%
astring$ = "False color description for CalcImage"
Print #Temp2FileNumber%, astring$
astring$ = "BEGIN Items"
Print #Temp2FileNumber%, astring$
astring$ = " Interpolate = 0" ' all 256 values are specified
Print #Temp2FileNumber%, astring$
' Read column labels
Line Input #Temp1FileNumber%, astring$
' Loop through all values
For i% = 0 To 255
Input #Temp1FileNumber%, n&, rval&, gval&, bval&
' Convert RGB to RGB color
Call BMPRGB(RGBColor&, rval&, gval&, bval&)
If ierror Then Exit Sub
astring$ = " Item=" & Format$(i%) & " " & Format$(i%) & " " & Format$(RGBColor&)
Print #Temp2FileNumber%, astring$
Next i%
astring$ = "END Items"
Print #Temp2FileNumber%, astring$
Close #Temp1FileNumber%
Close #Temp2FileNumber%
Exit Sub
' Errors
ImageConvertLUTtoFCError:
MsgBox Error$, vbOKOnly + vbCritical, "ImageConvertLUTtoFC"
Close #Temp1FileNumber%
Close #Temp2FileNumber%
ierror = True
Exit Sub
ImageConvertLUTtoFCBadLUTExtension:
msg$ = "File " & tFileLUT$ & " does not have the proper .LUT extension."
MsgBox msg$, vbOKOnly + vbExclamation, "ImageConvertLUTtoFC"
ierror = True
Exit Sub
ImageConvertLUTtoFCBadFCExtension:
msg$ = "File " & tFileFC$ & " does not have the proper .FC extension."
MsgBox msg$, vbOKOnly + vbExclamation, "ImageConvertLUTtoFC"
ierror = True
Exit Sub
ImageConvertLUTtoFCLUTNotFound:
msg$ = "File " & tFileLUT$ & " was not found."
MsgBox msg$, vbOKOnly + vbExclamation, "ImageConvertLUTtoFC"
ierror = True
Exit Sub
End Sub
The first two numbers are the color index from 0 to 255 (I don't recall why there's two numbers off hand) and the third number is the RGB color value.
Please post your viridis file. This can be utilized in CalcImage by naming it to CUSTOM.FC.
Also check this topic:
https://smf.probesoftware.com/index.php?topic=1003.0
Thanks for the code!
I don't suppose you could share the BMPRGB function? It looks like both the scripts above are using this function, passing an RGBColor$ variable (by reference?) alongside the read-in (R,G,B) values and presumably the function is altering the RGBColor$ value accordingly, which is then pasted in to the .FC file. Problem is, I'm still not sure what format the colo(u)r value is in. I originally though it was decimal, but there are several values which aren't decimal. It certainly isn't hexadecimal, nor is it (R,G,B).
Here's my latest attempt at the Plasma colour scheme, with how it should look plotted up underneath the CalcImage interpretation of it. Getting there, but not quite right yet!
(https://smf.probesoftware.com/gallery/796_24_04_24_8_13_22.jpeg)
Very nice.
The BMPRGB (and UnBMPRGB) functions are here:
Sub BMPRGB(RGBColor As Long, redvalue As Long, greenvalue As Long, bluevalue As Long)
' Convert RGB to 24 bit color
ierror = False
On Error GoTo BMPRGBError
RGBColor& = RGB(CInt(redvalue&), CInt(greenvalue&), CInt(bluevalue&))
Exit Sub
' Errors
BMPRGBError:
MsgBox Error$, vbOKOnly + vbCritical, "BMPRGB"
ierror = True
Exit Sub
End Sub
Sub BMPUnRGB(RGBColor As Long, redvalue As Long, greenvalue As Long, bluevalue As Long)
' Convert 24 bit color to RGB
ierror = False
On Error GoTo BMPUnRGBError
' RGBcolor = Format(Hex(redvalue&) & Hex(greenvalue&) & Hex(bluevalue&), "000000")
redvalue& = (RGBColor& And &HFF&)
greenvalue& = (RGBColor& And &HFF00&) \ 256 ' this is correct appraently
bluevalue& = (RGBColor& And &HFF0000) \ 65536 ' this is correct apparently
Exit Sub
' Errors
BMPUnRGBError:
MsgBox Error$, vbOK + vbCritical, "BMPUnRGB"
ierror = True
Exit Sub
End Sub
These functions just convert RGB values to 24 bit color values (and back again).
Well, ain't that the weirdest...
I ended up replicating the RGB call in VB to try and figure out where I was going wrong and it turns out VB switches the offset for the R and B channels, hence why my colours were all mixed up (i.e. the blue channel is multiplied by 65536, rather than the red channel). Simply swapped this around and everything is sorted! They are decimal values though, so I wasn't going completely mad.
This is the new LUTs in action:
Gradient .grd file:
(https://smf.probesoftware.com/gallery/796_24_04_24_10_48_20.png)
PfE demo data:
(https://smf.probesoftware.com/gallery/796_24_04_24_10_48_55.jpeg)
and I've attached the .FC files for CalcImage to this post. To use them, just copy them to your PfE directory e.g. C:/ProgramData/Probe Software/Probe for EPMA/ and rename your LUT of choice to CUSTOM.FC and then re/start CalcImage, open your project/grd and go to Image Processing > Change Current Image Palettes and select Custom.
Quote from: JonF on April 24, 2024, 10:55:39 AM
Well, ain't that the weirdest...
I ended up replicating the RGB call in VB to try and figure out where I was going wrong and it turns out VB switches the offset for the R and B channels, hence why my colours were all mixed up (i.e. the blue channel is multiplied by 65536, rather than the red channel). Simply swapped this around and everything is sorted! They are decimal values though, so I wasn't going completely mad.
...
and I've attached the .FC files for CalcImage to this post. To use them, just copy them to your PfE directory e.g. C:/ProgramData/Probe Software/Probe for EPMA/ and rename your LUT of choice to CUSTOM.FC and then re/start CalcImage, open your project/grd and go to Image Processing > Change Current Image Palettes and select Custom.
Ha! I think I ran into the same issue which is why in my code above I have the comment "this is correct apparently" (with the typo now fixed!)
' RGBcolor = Format(Hex(redvalue&) & Hex(greenvalue&) & Hex(bluevalue&), "000000")
redvalue& = (RGBColor& And &HFF&)
greenvalue& = (RGBColor& And &HFF00&) \ 256 ' this is correct appraently
bluevalue& = (RGBColor& And &HFF0000) \ 65536 ' this is correct apparently
Quote from: Probeman on April 24, 2024, 11:45:37 AM
Ha! I think I ran into the same issue which is why in my code above I have the comment "this is correct apparently" (with the typo now fixed!)
I saw your comment and had a chuckle once I'd figured out what was going awry!
For completeness, I've also attached the CUSTOM_cividis.FC file to this post
Hi JonF
What did you code in? Is the code available?
Hi Ben,
Quote from: Ben Buse on May 19, 2025, 11:44:22 PMWhat did you code in?
I coded it up in R using Visual Studio Code.
Quote from: Ben Buse on May 19, 2025, 11:44:22 PMIs the code available?
Sure:
require(viridis)
require(Rmpfr)
viridiscols <- viridis(256,alpha = 1, begin = 0, end = 1, direction = 1)
viridisdecs = array(dim=c(256))
for (i in 1:length(viridiscols)) {
tmp_vir <- col2rgb(viridiscols[i])
print(tmp_vir)
viridisdecs[i] <- (tmp_vir[1]) + (tmp_vir[2]*256) + (tmp_vir[3]*65536)
print(viridisdecs[i])
}
cat(" False color description for MicroImage",file="CUSTOM.FC",sep="\n")
cat(" BEGIN Items",file="CUSTOM.FC",append=TRUE,sep="\n")
cat(" Interpolate=0",file="CUSTOM.FC",append=TRUE,sep="\n")
for (i in 1:256) {
cat(paste0(" Item= ", i-1, " ", i-1, " ", viridisdecs[i]),file="CUSTOM.FC",append=TRUE,sep="\n")
}
for (i in 10:99) {
cat(paste0(" Item= ", i-1, " ", i-1, " ", viridisdecs[i]),file="CUSTOM.FC",append=TRUE,sep="\n")
}
for (i in 100:176) {
cat(paste0(" Item= ", i-1, " ", i-1, " ", viridisdecs[i]),file="CUSTOM.FC",append=TRUE,sep="\n")
}
for (i in 177:256) {
cat(paste0(" Item= ", i-1, " ", i-1, " ", viridisdecs[i]),file="CUSTOM.FC",append=TRUE,sep="\n")
}
cat(" END Items",file="CUSTOM.FC",append=TRUE)It was a while ago now and my memory isn't great at the best of times, but I've just run that through again and can create a CUSTOM.FC file that is saved to the work directory (type getwd() in to the R prompt to get the current work directory). There was a lot of trying to figure out why it wasn't working at first (see above!), but the above code should do it.
The code will probably also work for the Brewer colour palettes, but I've not checked them.
Great. Thanks. And just looking viridis has a series of color palettes https://cran.r-project.org/web/packages/viridis/vignettes/intro-to-viridis.html (https://cran.r-project.org/web/packages/viridis/vignettes/intro-to-viridis.html) which could be easily used https://search.r-project.org/CRAN/refmans/viridisLite/html/viridis.html (https://search.r-project.org/CRAN/refmans/viridisLite/html/viridis.html)
Yep, I created the .FC files in the above posts for viridis, cividis, plasma and magma. You can select which colour palette you'd like to create using the above code by changing where it says "viridis" to e.g. "magma" or "cividis" (or turbo or mako, which I didn't create). You can invert the colour scale using the "direction" option, and change the start/end points of the scale.
e.g.
viridiscols <- viridis(256,alpha = 1, begin = 0, end = 1, direction = 1)
to
viridiscols <- plasma(256,alpha = 1, begin = 0, end = 1, direction = 1)
You could also load in the RColorBrewer package and do something similar with the Brewer colour scales.
I've updated and tidied up the .FC colour palette code above so that it can now use palettes from either the Viridis set (e.g. viridis, magma, plasma etc, see here: CRAN viridis site (https://cran.r-project.org/web/packages/viridis/vignettes/intro-to-viridis.html)) or the Brewer set (e.g. spectral, blues, see: ColorBrewer site (https://colorbrewer2.org/)) to create the .FC file.
You can even make your own palette by specifying start and end point colours in the colorRampPalette function and interpolate between them: you want a colour scale ranging from turquoise to mauve? Go ahead!
I've also included the option to invert the colour scale for the Brewer colours, this bit: col1 <- rev(brewer.pal(9,"Blues"))...as the original white = low, dark blue = high doesn't make sense to me.
Here's the code in full:
require(viridis)
require(Rmpfr)
require(ggplot2)
require(RColorBrewer)
#Select which range and palette you would like to use e.g.
##Viridis Palettes
#For "magma" from the viridis set:
colshex <- magma(256,alpha = 1, begin = 0, end = 1, direction = 1)
### OR
##RColorBrewer Palettes
#or for "Blues" from the RColorBrewer palettes:
col1 <- brewer.pal(9,"Blues")
#NB you can invert the colour palettes to make more sense e.g. red = high vs blue = high
col1 <- rev(brewer.pal(9,"Blues"))
#Interpolate 256 colours along the selected palette
col1_pal <- colorRampPalette(col1)(256)
#And create a list of the hexadecimal colour values
colshex <- col1_pal
#Create array to fill with colour values converted to decimal
coldecs = array(dim=c(256))
#Convert hexadecimals to RGB values
for (i in 1:length(colshex)) {
tmp_vir <- col2rgb(colshex[i])
print(tmp_vir)
coldecs[i] <- (tmp_vir[1]) + (tmp_vir[2]*256) + (tmp_vir[3]*65536)
print(coldecs[i])
}
cat(" False color description for MicroImage",file="CUSTOM.FC",sep="\n")
cat(" BEGIN Items",file="CUSTOM.FC",append=TRUE,sep="\n")
cat(" Interpolate=0",file="CUSTOM.FC",append=TRUE,sep="\n")
for (i in 1:256) {
cat(paste0(" Item= ", i-1, " ", i-1, " ", coldecs[i]),file="CUSTOM.FC",append=TRUE,sep="\n")
}
cat(" END Items",file="CUSTOM.FC",append=TRUE)
Nice, thanks for posting
You've inspired me to look at the python matplotlib color tables, they have a range of colour tables termed colour maps available https://matplotlib.org/stable/users/explain/colors/colormaps.html (https://matplotlib.org/stable/users/explain/colors/colormaps.html) and it's easy to make your own
So here's the code to make .fc color palette
Create own colour table, where list colors it interpolates between
cmap=matplotlib.colors.LinearSegmentedColormap.from_list(name='testmap',colors=['red','blue'],N=255)
Specify one of preexisting colour tables
cmap = matplotlib.colormaps['gist_earth']
import matplotlib
#create own
#cmap=matplotlib.colors.LinearSegmentedColormap.from_list(name='testmap',colors=['red','blue'],N=255)
#read existing colour tables
#cmap = matplotlib.colormaps('Spectral')
cmap = matplotlib.colormaps['gist_earth']
#list all color maps avaliable
#list(matplotlib.colormaps)
#colourtable is 0 to 1, so applying it to range of interest
norm = matplotlib.colors.Normalize(vmin=0, vmax=255)
import pandas as pd
import tkinter
import tkinter.filedialog
savelocation = tkinter.filedialog.asksaveasfilename(title="select save location")
f = open(savelocation,"a")
f.write(" False color description for MicroImage"+"\n")
f.write(" BEGIN Items"+"\n")
f.write(" Interpolate=0"+"\n")
for x in range(0,255):
#cmap() returns rgba; [] specifies r or g or b or a; norm(x) is 0 to 255 given as 0 to 1 for color map
r = cmap(norm(x))[0]
g = cmap(norm(x))[1]
b = cmap(norm(x))[2]
#rgb are in range 0 to 1
rgbdecimal = int(b*255) * 65536 + int(g*255) * 256 + int(r*255)
f.write(" Item= "+str(x)+" "+str(x)+" "+str(rgbdecimal)+"\n")
f.write(" END Items")
f.close()
Having seen the other thread about the new image export option and the request for the Surfer rainbow and rainbow2 colour scales as .FC format, you can amend the R code above to define the interpolated points from those spectra like so:
#OR interpolate colours between known colours
rainbow2<-c("#000000","#0000ff", "#00ffff", "#009900","#ffff00","#ff0000","#ffffff")
rainbow<-c("#9966ff","#0000ff", "#00ff00", "#ffff00","#ff6600","#ff0000")
col1_pal <- colorRampPalette(rainbow,interpolate="linear")(256)