Manual subpixel registration

Hi, I’m looking for a way to manually register channels from a multi-channel acquisition with subpixel accuracy (like moving a given channel in any direction by a defined fraction of a pixel and visually see the result in order to manually align several channels). Do you know of something that would do that?

Hi Christophe!

What kind of information are you going to use to manually assess the alignment? Do you think it can be used to extract the required displacements to automatically (e.g. multichannel point sources) ?

Hi Nico, I usually know how the cellular structures in the different channels should align like axons across the image, some clusters should fall in “holes” in another channel, etc. Channels labels can be very different so autocorrelation or similar strategies cannot usually be used. This shift comes from random drift between channels acquisition so it has to be determined each time. in that way it is different from chromatic aberrations due to optical factors (objective, filter cubes) that I correct upstream (using affine transform matrixes determined on a filed of sub-resolution multicolor beads) and are the same for every acquisition.

Oh, I see. Is it only 2D or do you need also Z?

2D is fine - 3D would be excellent but would be much more difficult to visually assess within a simple interface

I’ll get you something useful (and interactive) in a couple of minutes :smiley:

Ready! :robot:
Here’s a macro tool to perform the 2D manual alignment:

Just install it, select your image and press F5. You’ll ge a new image. With the new tool in the toolbar selected, drag the channels as you please. You can move between channels by scrolling with the mouse wheel. If you press ctrl while dragging, the movement is larger. You can zoom in (and out), and the displacement will be be proportionally smaller, so you can get more precision the closer you get.
You can also right click in the tool to get some help, and to configure the displacement multipliers.
The new image is always generated from the original one, so each channel gets transformed only once. You can save the new image when you’re satisfied with the result.

I’ll try to post a video to show it in action.

/* Macro tool to move channels in multichannel images with sub-pixel resolution
 * Nicolas De Francesco - March 2020
 */

var net_dx;
var net_dy;
var title;
var corrected="";
var temp;
var small = 30;
var big = 90;


macro "Setup [F5]" {
	title = getTitle();
	corrected = "corrected-"+title;
	temp = "temp-"+title;
	
	getDimensions(width, height, channels, slices, frames);
	
	net_dx=newArray(channels);
	net_dy=newArray(channels);

	if(!isOpen(corrected)) run("Duplicate...", "title=&corrected duplicate");
	moveChannel(1, 0, 0);
	}

macro "Move channels Tool - Cf0f L18f8L818f Cfb8 O11ee O22cc" {
   	shift=1;
	ctrl=2; 
	rightButton=4;
	alt=8;
	leftButton=16;
	insideROI = 32;
	getCursorLoc(x, y, z, flags);
	
	if(flags&leftButton!=0 && corrected==getTitle()){ 
		x2=x;
		y2=y;
		Stack.getPosition(channel, slice, frame);
		
		while (flags & leftButton != 0){
			getCursorLoc(x, y, z, flags);
			if (x!=x2 || y!=y2){
				dx = x - x2;
				dy = y - y2;
				mult = small;
				if(flags & ctrl == 0) mult = big;
				moveChannel(channel, dx/mult, dy/mult);
				wait(50);
				x2=x;
				y2=y;
				}
		 	}
		}
	}

macro "Move channels Tool Options" {
	Dialog.create("Move Channels Options");
	Dialog.addMessage(	"Usage:\n \n"+
				      	"Select the image to correct, and press F5 to create\n"+
				      	"a working copy (or to reset it). Select the move channel tool.\n \n"+
				      	"Select a channel to move in the copy, and click & drag.\n"+		
						"Pressing Ctrl amplifies the mouse movement.\n"+
						"The zoom state of the image also changes the amount moved.\n \n"+
						"You can save the new image at any point.\n \n" );
						
	Dialog.addNumber("Small step multiplier:", small);
	Dialog.addNumber("Big step multiplier:", big);
	Dialog.show();
	small = Dialog.getNumber();
	big = Dialog.getNumber();
	}

function moveChannel(channel, ndx, ndy){
	setBatchMode(true);
	selectWindow(title);
	run("Duplicate...", "title=&temp duplicate channels=&channel");
	net_dx[channel-1] += ndx;
	net_dy[channel-1] += ndy;
	run("Translate...", "x="+net_dx[channel-1]+" y="+net_dy[channel-1]+" interpolation=Bicubic slice");
	run("Copy");
	close();
	selectWindow(corrected);
	Stack.setChannel(channel);
	run("Paste");
	run("Select None");
	setBatchMode(false);
	}

Let me know if it works for you,

Cheers!
Nico

2 Likes

Hi @christlet,

Bigdataviewer as a nice feature that lets you transform images relative to one another (subpixel of course). Press the T hotkey to start transforming, Esc cancels. The same controls one would usually use to navigate (in 3d) are used to transform one channel relative to the others. So you can’t do an arbitrary affine, but rather a similarity transform. See the quick screencast below.

The limitation right now is that exporting the transformed image is not built-in, but would not be so difficult to implement if you find you like interacting in this way.

John

P.S. Wow @NicoDF, quick work! :smiley:

4 Likes

Great, thanks a lot!

Two small modifications that I will try to implement:

  • ctrl clashes with ctrl-click on Mac (OSX alternative to right-click) so better replaced by shift (that one is trivial)
  • would be great to also have option to move from 1 small (big) step with keyboard arrows (+shift) for more control
1 Like

That one is trivial indeed:

if(flags & shift == 0) mult = big;

This one, AFAIK, not so much for macros (If anyone can prove me wrong, please, do! I’d love to be wrong on this one). There is no (easy?) way to set up a key listener for macros. The only way that I’ve gone around this is by setting individual macros for each keystroke (a limited subset). If you have a numeric keypad, this extra bit might work (othewise, you’ll have to move to using F keys):

macro "move up a notch [n8]" {
	checkAndMove(0, -1);
	}

macro "move down a notch [n2]" {
	checkAndMove(0, 1 );
	}

macro "move left a notch [n4]" {
	checkAndMove(-1, 0);
	}

macro "move right a notch [n6]" {
	checkAndMove(1, 0);
	}
	
function checkAndMove(mx, my) {
	if (corrected==getTitle()) { 
		Stack.getPosition(channel, slice, frame);
		mult = small;
		if (isKeyDown("shift") mult = big;
		moveChannel(channel, mx/mult, my/mult);
		}
	}

Unfortunately, using isKeyDown("shift") does not behave as I would expect (a bug maybe?): if you keep it pressed, the next time it’s called, it will report the key is down. So, you’ll have to release the key and press again to have a new (single) big step.

In any case, I’d think that working with a close-up zoom should give you plenty of precision to do it by dragging. Remember that, the closer you are, the smaller the dragged distance (it’s measured in image pixels). So you get extremely small steps at high magnifications

Anyway, Here’s the whole thing:

/* Macro tool to move channels in multichannel images with sub-pixel resolution
 * Nicolas De Francesco - March 2020
 */

var net_dx;
var net_dy;
var title;
var corrected="";
var temp;
var small = 30;
var big = 90;

macro "Setup [F5]" {
	title = getTitle();
	corrected = "corrected-"+title;
	temp = "temp-"+title;
	
	getDimensions(width, height, channels, slices, frames);
	
	net_dx=newArray(channels);
	net_dy=newArray(channels);

	if(!isOpen(corrected)) run("Duplicate...", "title=&corrected duplicate");
	for (i = 0; i < channels; i++) {
		moveChannel(channels-i, 0, 0);
		}
	}

macro "Move channels Tool - Cf0f L18f8L818f Cfb8 O11ee O22cc" {
   	shift=1;
	ctrl=2; 
	rightButton=4;
	alt=8;
	leftButton=16;
	insideROI = 32;
	getCursorLoc(x, y, z, flags);
	
	if(flags&leftButton!=0 && corrected==getTitle()){ 
		x2=x;
		y2=y;
		Stack.getPosition(channel, slice, frame);
		
		while (flags & leftButton != 0){
			getCursorLoc(x, y, z, flags);
			if (x!=x2 || y!=y2){
				dx = x - x2;
				dy = y - y2;
				mult = small;
				if(flags & shift == 0) mult = big;
				moveChannel(channel, dx/mult, dy/mult);
				wait(50);
				x2=x;
				y2=y;
				}
		 	}
		}
	}

macro "Move channels Tool Options" {
	Dialog.create("Move Channels Options");
	Dialog.addMessage(	"Usage:\n \n"+
				      	"Select the image to correct, and press F5 to create\n"+
				      	"a working copy (or to reset it). Select the move channel tool.\n \n"+
				      	"Select a channel to move in the copy, and click & drag.\n"+		
						"Pressing Shift amplifies the mouse movement.\n"+
						"The zoom state of the image also changes the amount moved.\n \n"+
						"You can save the new image at any point.\n \n" );
						
	Dialog.addNumber("Small step multiplier:", small);
	Dialog.addNumber("Big step multiplier:", big);
	Dialog.show();
	small = Dialog.getNumber();
	big = Dialog.getNumber();
	}

macro "move up a notch [F2]" {
	checkAndMove(0, -1);
	}

macro "move down a notch [F3]" {
	checkAndMove(0, 1 );
	}

macro "move left a notch [F8]" {
	checkAndMove(-1, 0);
	}

macro "move right a notch [F9]" {
	checkAndMove(1, 0);
	}
	
function checkAndMove(mx, my) {
	if (corrected==getTitle()) { 
		Stack.getPosition(channel, slice, frame);
		mult = small;
		if (isKeyDown("shift") mult = big;
		moveChannel(channel, mx/mult, my/mult);
		}
	}

function moveChannel(channel, ndx, ndy){
	setBatchMode(true);
	selectWindow(title);
	run("Duplicate...", "title=&temp duplicate channels=&channel");
	net_dx[channel-1] += ndx;
	net_dy[channel-1] += ndy;
	run("Translate...", "x="+net_dx[channel-1]+" y="+net_dy[channel-1]+" interpolation=Bicubic slice");
	run("Copy");
	close();
	selectWindow(corrected);
	Stack.setChannel(channel);
	run("Paste");
	run("Select None");
	setBatchMode(false);
	}

Cheers!
Nico

Thanks, John @bogovicj!

I had originally thought of BigWarp for this case, but I have only used it with two images, so no multichannel. I didn’t realize that BDB could be an option (with full fledged similarity transforms). The ease of input/output is indeed something that has to be overcome to make it easily accesible. Also, TrakEM2 could be used, although setting it up would also be cumbersome.

Cheers!
Nico