“Some speed up” - it now processes quicker than it acquires, meaning the buffer never fills. Running the processing at the same time as acquisition takes 60.9s for 100k images, or if I acquire the images and then process, it takes 60.9s to acquire and 9.5s to process.
I’ve realised it would be useful for me to display an image every few seconds so I can see that I still have a bead held in the optical trap. I’ve implemented this using a RAM datastore, and scripted it to automatically save the datastore at the end of the acquisition. This hasn’t impacted the time it takes to run a 100k image acquisition.
If you wish to include my script with the others in the github repo or on micro-manager.org, please do. Thanks again for all the help!
Edit: I realised I forgot to calculate timings. I’ve used the fact that I am taking tagged images to record a time for the first image, and the last tagged image and store the average time per frame across this interval. It’s not perfect but it’s a good start.
/**
* Using sequence acquisition and real-time processing to track the location of a particle
*/
// You need to manually create a folder, then write the path here
folderPath = "E:/Will/beads/2020_11_09_2um/";
// Find and enter bead location in px
// { X , Y };
Centre = new int[]{ 960, 470 };
int n = 10001; // number of frames
Size = new int[]{ 64, 64}; // Image size in px - choosing more than 64 rows will slow acquisition
// Import processors for finding CofM
import ij.process.ShortStatistics;
import ij.process.ImageStatistics;
import ij.process.ShortProcessor;
// Define two functions for saving the data
byte[] doubleToByteArray( double[] i, int len )
{ // Converts double array to byte array. Needs to be given the length of the array
ByteArrayOutputStream bos = new ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream(bos);
for (int idx = 0; idx < len; idx++){
dos.writeDouble(i[idx]);
}
dos.flush();
return bos.toByteArray();
}
void writeDoubleArrayToFile(double[] array, int len, String path)
{ // Writes double array to file as byte array. Needs to be given the length of the array
file = new File(path);
fos = new FileOutputStream(file);
if (!file.exists()){
file.createNewFile();
}
bytesArray = doubleToByteArray(array, len);
fos.write(bytesArray);
fos.flush();
fos.close();
}
// Allocate some variables
double XSum, YSum, PSum;
XCentres = new double[n];
YCentres = new double[n];
Times = new double[n];
int i = 0;
int iSave = 0;
int saveInterval = 10000; // How often to save an image (in frames)
double firstTime = 0, lastTime = 0;
java.awt.Rectangle ROI = new Rectangle(Centre[0]-Size[0]/2, Centre[1]-Size[1]/2, Size[0], Size[1]);
// Check saveInterval is sensible compared to n
if (saveInterval >= n){
print("number of frames, n = " + n + " saveInterval = " + saveInterval);
throw new Exception("saveInterval must be less than number of frames otherwise timing calculation doesn't work");
}
// Set the ROI and check it has been set
mm.setROI(ROI);
int width = mmc.getImageWidth();
int height = mmc.getImageHeight();
print("w = " + width + " h = " + height);
// Set exposure
mmc.setExposure(0.2);
// Prepare an image processor
ip = new ShortProcessor(width, height);
// Create a Datastore for the images to be stored in, in RAM.
store = mm.data().createRAMDatastore();
saveMode = org.micromanager.data.Datastore.SaveMode.valueOf("MULTIPAGE_TIFF");
// Create a display to show images as they are acquired.
mm.displays().createDisplay(store);
// Set up a Coords.CoordsBuilder for applying coordinates to tagged images.
builder = mm.data().getCoordsBuilder().z(0).channel(0).stagePosition(0);
// Start acquiring and process frames as they arrive
mmc.stopSequenceAcquisition(); // Stop previous acquisition in case it is running
startTime = System.nanoTime();
mmc.startSequenceAcquisition(n, 0.0, true);
print("T = " + (System.nanoTime() - startTime)/1e6 + "ms");
img = 0;
while (mmc.getRemainingImageCount() > 0 || mmc.isSequenceRunning(mmc.getCameraDevice())) {
if (mmc.getRemainingImageCount() > 0)
{
pixels = new short[64*64];
// Get the next frame and calculate centre of mass
if (i%saveInterval == 0)
{ // Every [saveInterval] frames, get the metadata and display the image
img = mmc.popNextTaggedImage();
image = mm.data().convertTaggedImage(img,
builder.time(iSave).build(), null);
store.putImage(image);
pixels = image.getRawPixels();
iSave++;
if (i == 0) {firstTime = img.tags.getDouble("ElapsedTime-ms");}
} else
{ // The rest of the time, just get the pixel data
pixels = mmc.popNextImage();
}
ip.setPixels(pixels);
ss = new ShortStatistics(ip, ij.process.ImageStatistics.CENTER_OF_MASS, null);
XCentres[i] = ss.xCenterOfMass;
YCentres[i] = ss.yCenterOfMass;
if (i%saveInterval == 0) // Every [saveInterval] frames, print this frame's info
{
print("i " + i + " X = " + XCentres[i] + " Y = " + YCentres[i]);
print("Remaining: " + mmc.getRemainingImageCount());
print("Acquired: " + (i + mmc.getRemainingImageCount()));
print("T = " + (System.nanoTime() - startTime)/1e6 + "ms");
}
i++;
}
}
lastTime = img.tags.getDouble("ElapsedTime-ms");
if (iSave > 1) {
frameTime = (lastTime - firstTime)/((iSave-1)*saveInterval);
for (int i = 0; i < n; i++){
Times[i] = i * frameTime;}
}
print("Completed at " + (System.nanoTime() - startTime)/1e9 + "s");
mmc.stopSequenceAcquisition();
writeDoubleArrayToFile(XCentres, n, folderPath + "XCentres.dat");
writeDoubleArrayToFile(YCentres, n, folderPath + "YCentres.dat");
writeDoubleArrayToFile(Times, n, folderPath + "Times.dat");
store.save(saveMode, folderPath+"images_and_metadata");