Map recorded pixel greyscale to corrected/predicted incident intensity

I have taken a number of images using a camera assuming that it had a linear ratio of recorded greyscale to incident intensity. This was not actually the case, but I do now have a 2x256 array (.csv file) of recorded and corrected values to recalibrate the existing images. I would like to run through each pixel in each image and replace the pixel value with the one that neighbours it in the array. This will probably involve setCTable but I’m not sure how to use it.

I hope I’m not asking too much if somebody might even make a macro to do this, as I have no experience in this field.

Thanks for any help

Welcome to the forum, @joebron!

Here is a Groovy script that demonstrates how to use setCTable. It assigns randomized values to the currently active image.

#@ ImagePlus imp
table = new float[256]
for (i=0; i<table.length; i++) {
    table[i] = i*i + Math.random()
}
imp.getCalibration().setCTable(table, "quazworts")

Here is a short Groovy script that reads CSV files using the OpenCSV library included with Fiji:

#@ File csvFile
import com.opencsv.CSVReader
import java.io.FileReader
new CSVReader(new FileReader(csvFile)).withCloseable { r ->
  data = r.readAll() // data is returned as a List<String[]>
}
for (entry in data) println(entry)

Putting them together:

#@ ImagePlus imp
#@ File csvFile
#@ String units

import com.opencsv.CSVReader
import java.io.FileReader
import ij.IJ

// read CSV file
new CSVReader(new FileReader(csvFile)).withCloseable { r ->
  data = r.readAll()
}

// check that CSV file structure is as expected
if (data.size() != 2) {
	IJ.error("CSV file is not 2x256 (data length was ${data.size()})")
	return
}
indices = data[0]
values = data[1]
if (indices.length != 256 || values.length != 256) {
	IJ.error("CSV file is not 2x256 (lengths were ${indices.length} and ${values.length})")
	return
}

// initialize the table to identity function
table = new float[256]
for (i=0; i<table.length; i++) table[i] = i

// map old values to new values
for (i=0; i<indices.length; i++) {
	index = Integer.parseInt(indices[i].trim())
	value = Float.parseFloat(values[i].trim())
    table[index] = value
}

// assign the calibration table to the image
imp.getCalibration().setCTable(table, units)

This assumes your CSV file is laid out in two lines, the first line with 256 comma-separated index values, and the second with 256 comma-separated calibrated values. If your CSV file is laid out differently, feel free to post it here and we can tell you how to adjust the script to accommodate it. Also: the script above does an identity mapping from index value to same calibrated value (e.g. 137 stays at 137). If your CSV file’s index values do not cover all values from 0 to 255 inclusive, there will be gaps where that identity function is still in play—in other words, this script does not do any interpolation between your stated calibrated values.

2 Likes

Hi @joebron,

Besides the script that @ctrueden posted, if the calibration corresponds to fitting a particular type of curve to the camera response (e.g. linear), you can also try loading the CSV data into the Analyze > Calibrate... window, choosing the appropiate fitting function, and hitting ok. This will give you a calibrated version of the image (where underlying pixel values are still present). If you want to apply it, just convert it to 32-bits.

You can record the operation, and convert it into a simple macro to correct other images.

Nico

1 Like