Correct 3D drift broken?

Latest version of Fiji, updated. Correct 3D drift doesn’t start, throws an error instead.

With Java 8 update sites enabled:
line 31, in <module> ImportError: No module named imagescience

Without Java 8 update sites enabled:
line 16, in <module> ImportError: No module named vecmath

Tried re-downloading Fiji and it still happens.

Hi @David

could you try to activate the ImageScience update site as explained here:

Cheers,
Robert

2 Likes

That solved it. I didn’t know I had to add that update site in order for the plugin to work.

The newest version of this script doesn’t have a dependency on imagescience any more. It’s probably just an oversight that this wasn’t uploaded together with the recent migration of imagescience to its own update site.

@ctrueden do you agree? :slight_smile:

Scripts are currently not copied by the imagej-maven-plugin. So yes, they tend to fall out of date.

Just now, I manually copied all scripts from plugins/Scripts to a fully up-to-date Java-8 installation, and analyzed the results. I found two scripts which had changed: Image/Hyperstacks/Temporal-Color_Code.ijm and Plugins/Registration/Correct_3D_drift.py. I uploaded both of them.

For reference, here are the diffs since last upload:

diff --git remote/plugins/Scripts/Plugins/Registration/Correct_3D_drift.py/var/folders/n2/5q09bjq11kv6xwpp0xlg4npc0000gq/T/diff-6926747504113206188.py-20160127145850 local/plugins/Scripts/Plugins/Registration/Correct_3D_drift.py/var/folders/n2/5q09bjq11kv6xwpp0xlg4npc0000gq/T/diff-4059216704426140794.py
index 43a1a52..a822e4a 100644
--- remote/plugins/Scripts/Plugins/Registration/Correct_3D_drift.py/var/folders/n2/5q09bjq11kv6xwpp0xlg4npc0000gq/T/diff-6926747504113206188.py-20160127145850
+++ local/plugins/Scripts/Plugins/Registration/Correct_3D_drift.py/var/folders/n2/5q09bjq11kv6xwpp0xlg4npc0000gq/T/diff-4059216704426140794.py
@@ -22,16 +22,42 @@ from ij.io import DirectoryChooser, FileSaver
 from ij.gui import GenericDialog, YesNoCancelDialog, Roi
 from mpicbg.imglib.image import ImagePlusAdapter
 from mpicbg.imglib.algorithm.fft import PhaseCorrelation
-from org.scijava.vecmath import Point3i
-from org.scijava.vecmath import Point3f
+from org.scijava.vecmath import Point3i  #from javax.vecmath import Point3i # java6
+from org.scijava.vecmath import Point3f  #from javax.vecmath import Point3f # java6
 from java.io import File, FilenameFilter
 from java.lang import Integer
-
 import math
-from imagescience.image import Image
-from imagescience.transform import Translate
 
-# imp stands for ij.ImagePlus instance
+# sub-pixel translation using imglib2
+from net.imagej.axis import Axes
+from net.imglib2.img.display.imagej import ImageJFunctions
+from net.imglib2.realtransform import RealViews, Translation3D
+from net.imglib2.view import Views
+from net.imglib2.img.imageplus import ImagePlusImgs
+from net.imglib2.converter import Converters
+from net.imglib2.converter.readwrite import RealFloatSamplerConverter
+from net.imglib2.interpolation.randomaccess import NLinearInterpolatorFactory
+
+def translate_single_stack_using_imglib2(imp, dx, dy, dz):
+  # wrap into a float imglib2 and translate
+  #   conversion into float is necessary due to "overflow of n-linear interpolation due to accuracy limits of unsigned bytes"
+  #   see: https://github.com/fiji/fiji/issues/136#issuecomment-173831951
+  img = ImagePlusImgs.from(imp.duplicate())
+  extended = Views.extendBorder(img)
+  converted = Converters.convert(extended, RealFloatSamplerConverter())
+  interpolant = Views.interpolate(converted, NLinearInterpolatorFactory())
+  transformed = RealViews.affine(interpolant, Translation3D(dx, dy, dz))
+  cropped = Views.interval(transformed, img)
+  # wrap back into bit depth of input image and return
+  bd = imp.getBitDepth()
+  if bd==8:
+    return(ImageJFunctions.wrapUnsignedByte(cropped,"imglib2"))
+  elif bd == 16:
+    return(ImageJFunctions.wrapUnsignedShort(cropped,"imglib2"))
+  elif bd == 32:
+    return(ImageJFunctions.wrapFloat(cropped,"imglib2"))
+  else:
+    return None
 
 def compute_stitch(imp1, imp2):
   """ Compute a Point3i that expressed the translation of imp2 relative to imp1."""
@@ -51,20 +77,19 @@ def extract_frame(imp, frame, channel):
   stack = imp.getStack() # multi-time point virtual stack
   vs = ImageStack(imp.width, imp.height, None)
   for s in range(1, imp.getNSlices()+1):
-    i = imp.getStackIndex(channel, s, frame)  
+    i = imp.getStackIndex(channel, s, frame)
     vs.addSlice(str(s), stack.getProcessor(i))
   return vs
 
-
 def extract_frame_process_roi(imp, frame, channel, process, roi):
-  # extract frame and channel 
+  # extract frame and channel
   imp_frame = ImagePlus("", extract_frame(imp, frame, channel)).duplicate()
   # check for roi and crop
   if roi != None:
     #print roi.getBounds()
     imp_frame.setRoi(roi)
     IJ.run(imp_frame, "Crop", "")
-  # process  
+  # process
   if process:
     IJ.run(imp_frame, "Mean 3D...", "x=1 y=1 z=0");
     IJ.run(imp_frame, "Find Edges", "stack");
@@ -85,10 +110,9 @@ def subtract_Point3f(p1, p2):
   p3.z = p1.z - p2.z
   return p3
 
-
 def shift_between_rois(roi2, roi1):
-  """ computes the relative xy shift between two rois 
-  """ 
+  """ computes the relative xy shift between two rois
+  """
   dr = Point3f(0,0,0)
   dr.x = roi2.getBounds().x - roi1.getBounds().x
   dr.y = roi2.getBounds().y - roi1.getBounds().y
@@ -100,46 +124,44 @@ def shift_roi(imp, roi, dr):
   if the shift would cause the roi to be outside the imp,
   it only shifts as much as possible maintaining the width and height
   of the input roi
-  """ 
+  """
   if roi == None:
     return roi
   else:
     r = roi.getBounds()
-    #print r, dr.x, dr.y, imp.width, imp.height
     # init x,y coordinates of new shifted roi
     sx = 0
     sy = 0
     # x shift
     if (r.x + dr.x) < 0:
       sx = 0
-    elif (r.x + dr.x + r.width) > imp.width: 
+    elif (r.x + dr.x + r.width) > imp.width:
       sx = int(imp.width-r.width)
     else:
       sx = r.x + int(dr.x)
     # y shift
     if (r.y + dr.y) < 0:
       sy = 0
-    elif (r.y + dr.y + r.height) > imp.height: 
+    elif (r.y + dr.y + r.height) > imp.height:
       sy = int(imp.height-r.height)
     else:
       sy = r.y + int(dr.y)
     # return shifted roi
     shifted_roi = Roi(sx, sy, r.width, r.height)
-    #print shifted_roi
-    return shifted_roi   
-  
+    return shifted_roi
+
 def compute_and_update_frame_translations_dt(imp, channel, dt, process, shifts = None):
   """ imp contains a hyper virtual stack, and we want to compute
   the X,Y,Z translation between every t and t+dt time points in it
-  using the given preferred channel. 
-  if shifts were already determined at other (lower) dt 
+  using the given preferred channel.
+  if shifts were already determined at other (lower) dt
   they will be used and updated.
   """
   nt = imp.getNFrames()
   # get roi (could be None)
   roi = imp.getRoi()
   if roi:
-    print "ROI is at",roi.getBounds()
+    print "ROI is at", roi.getBounds()
   # init shifts
   if shifts == None:
     shifts = []
@@ -150,7 +172,7 @@ def compute_and_update_frame_translations_dt(imp, channel, dt, process, shifts =
   for t in range(dt, nt+dt, dt):
     if t > nt-1: # together with above range till nt+dt this ensures that the last data points are not missed out
       t = nt-1 # nt-1 is the last shift (0-based)
-    IJ.log("      between frames "+str(t-dt+1)+" and "+str(t+1))      
+    IJ.log("      between frames "+str(t-dt+1)+" and "+str(t+1))
     # get (cropped and processed) image at t-dt
     roi1 = shift_roi(imp, roi, shifts[t-dt])
     imp1 = extract_frame_process_roi(imp, t+1-dt, channel, process, roi1)
@@ -158,8 +180,8 @@ def compute_and_update_frame_translations_dt(imp, channel, dt, process, shifts =
     roi2 = shift_roi(imp, roi, shifts[t])
     imp2 = extract_frame_process_roi(imp, t+1, channel, process, roi2)
     if roi:
-      print "ROI at frame",t-dt+1,"is",roi1.getBounds()   
-      print "ROI at frame",t+1,"is",roi2.getBounds()   
+      print "ROI at frame",t-dt+1,"is",roi1.getBounds()
+      print "ROI at frame",t+1,"is",roi2.getBounds()
     # compute shift
     local_new_shift = compute_stitch(imp2, imp1)
     if roi: # total shift is shift of rois plus measured drift
@@ -167,29 +189,28 @@ def compute_and_update_frame_translations_dt(imp, channel, dt, process, shifts =
       local_new_shift = add_Point3f(local_new_shift, shift_between_rois(roi2, roi1))
     # determine the shift that we knew alrady
     local_shift = subtract_Point3f(shifts[t],shifts[t-dt])
-    # compute difference between new and old measurement (which come from different dt)   
+    # compute difference between new and old measurement (which come from different dt)
     add_shift = subtract_Point3f(local_new_shift,local_shift)
     print "++ old shift between %s and %s: dx=%s, dy=%s, dz=%s" % (int(t-dt+1),int(t+1),local_shift.x,local_shift.y,local_shift.z)
     print "++ add shift between %s and %s: dx=%s, dy=%s, dz=%s" % (int(t-dt+1),int(t+1),add_shift.x,add_shift.y,add_shift.z)
     # update shifts from t-dt to the end (assuming that the measured local shift will presist till the end)
     for i,tt in enumerate(range(t-dt,nt)):
       # for i>dt below expression basically is a linear drift predicition for the frames at tt>t
-      # this is only important for predicting the best shift of the ROIs 
-      # as the drifts will be corrected by the next measurements
+      # this is only important for predicting the best shift of the ROI
+      # the drifts for i>dt will be corrected by the next measurements
       shifts[tt].x += 1.0*i/dt * add_shift.x
       shifts[tt].y += 1.0*i/dt * add_shift.y
       shifts[tt].z += 1.0*i/dt * add_shift.z
       print "updated shift till frame",tt+1,"is",shifts[tt].x,shifts[tt].y,shifts[tt].z
     IJ.showProgress(1.0*t/(nt+1))
-  
+
   IJ.showProgress(1)
   return shifts
 
-
 def convert_shifts_to_integer(shifts):
   int_shifts = []
-  for shift in shifts: 
-    int_shifts.append(Point3i(int(round(shift.x)),int(round(shift.y)),int(round(shift.z)))) 
+  for shift in shifts:
+    int_shifts.append(Point3i(int(round(shift.x)),int(round(shift.y)),int(round(shift.z))))
   return int_shifts
 
 def compute_min_max(shifts):
@@ -208,7 +229,7 @@ def compute_min_max(shifts):
     minz = min(minz, shift.z)
     maxx = max(maxx, shift.x)
     maxy = max(maxy, shift.y)
-    maxz = max(maxz, shift.z)  
+    maxz = max(maxz, shift.z)
   return minx, miny, minz, maxx, maxy, maxz
 
 def zero_pad(num, digits):
@@ -259,16 +280,16 @@ def register_hyperstack(imp, channel, shifts, target_folder, virtual):
   stack = imp.getStack()
 
   if virtual is False:
-  	registeredstack = ImageStack(width, height, imp.getProcessor().getColorModel())
+    registeredstack = ImageStack(width, height, imp.getProcessor().getColorModel())
   names = []
-  
+
   for frame in range(1, imp.getNFrames()+1):
- 
+
     shift = shifts[frame-1]
-    
+
     print "frame",frame,"correcting drift",-shift.x-minx,-shift.y-miny,-shift.z-minz
     IJ.log("    frame "+str(frame)+" correcting drift "+str(-shift.x-minx)+","+str(-shift.y-miny)+","+str(-shift.z-minz))
-    
+
     fr = "t" + zero_pad(frame, len(str(imp.getNFrames())))
     # Pad with empty slices before reaching the first slice
     for s in range(shift.z):
@@ -285,8 +306,7 @@ def register_hyperstack(imp, channel, shifts, target_folder, virtual):
         else:
           empty = imp.getProcessor().createProcessor(width, height)
           registeredstack.addSlice(str(name), empty)
-    
-    
+
     # Add all proper slices
     stack = imp.getStack()
     for s in range(1, imp.getNSlices()+1):
@@ -348,8 +368,6 @@ def register_hyperstack(imp, channel, shifts, target_folder, virtual):
     return registeredstack_imp
   return CompositeImage(registeredstack_imp, mode)
 
-
-
 def register_hyperstack_subpixel(imp, channel, shifts, target_folder, virtual):
   """ Takes the imp, determines the x,y,z drift for each pair of time points, using the preferred given channel,
   and outputs as a hyperstack.
@@ -372,76 +390,68 @@ def register_hyperstack_subpixel(imp, channel, shifts, target_folder, virtual):
   slices = int(maxz - minz + imp.getNSlices())
 
   print "New dimensions:", width, height, slices
-  
+
   # prepare stack for final results
   stack = imp.getStack()
-  if virtual is True: 
+  if virtual is True:
     names = []
   else:
     registeredstack = ImageStack(width, height, imp.getProcessor().getColorModel())
-  
+
   # prepare empty slice for padding
   empty = imp.getProcessor().createProcessor(width, height)
 
   IJ.showProgress(0)
-  
+
+  # get raw data as stack
+  stack = imp.getStack()
+
+  # loop across frames
   for frame in range(1, imp.getNFrames()+1):
-      
+
     IJ.showProgress(frame / float(imp.getNFrames()+1))
     fr = "t" + zero_pad(frame, len(str(imp.getNFrames()))) # for saving files in a virtual stack
-    
-    # init
-    shift = shifts[frame-1]
-    registeredstackthisframe = ImageStack(width, height, imp.getProcessor().getColorModel())
 
+    # get and report current shift
+    shift = shifts[frame-1]
     print "frame",frame,"correcting drift",-shift.x-minx,-shift.y-miny,-shift.z-minz
     IJ.log("    frame "+str(frame)+" correcting drift "+str(round(-shift.x-minx,2))+","+str(round(-shift.y-miny,2))+","+str(round(-shift.z-minz,2)))
-        
-    # Add all slices of this frame
-    stack = imp.getStack()
-    for s in range(1, imp.getNSlices()+1):
-      for ch in range(1, imp.getNChannels()+1):
-         ip = stack.getProcessor(imp.getStackIndex(ch, s, frame))
-         ip2 = ip.createProcessor(width, height) # potentially larger
-         ip2.insert(ip, 0, 0)
-         registeredstackthisframe.addSlice("", ip2)
 
-    # Pad the end (in z) of this frame
-    for s in range(imp.getNSlices(), slices):
-      for ch in range(1, imp.getNChannels()+1):
-         registeredstackthisframe.addSlice("", empty)
-
-    # Set correct dimensions as below Translate() function needs this
-    # ..it is important *not* to set the calibration as Translate() works with units if present
-    registeredstackthisframe_imp = ImagePlus("registered time points", registeredstackthisframe)
-    registeredstackthisframe_imp.setProperty("Info", imp.getProperty("Info"))
-    registeredstackthisframe_imp.setDimensions(imp.getNChannels(), slices, 1)
-    registeredstackthisframe_imp.setOpenAsHyperStack(True)
-    
-    # Translate and set dimensions to translated result
-    translator = Translate()
-    output = translator.run(Image.wrap(registeredstackthisframe_imp),shift.x,shift.y,shift.z,Translate.LINEAR)
-    imp_translated = output.imageplus()
-    imp_translated.setProperty("Info", imp.getProperty("Info"))
-    imp_translated.setDimensions(imp.getNChannels(), slices, 1)
-    imp_translated.setOpenAsHyperStack(True)
-    
-    # Add the translated frame to the final time-series
-    stack = imp_translated.getStack()
-    for s in range(1, imp_translated.getNSlices()+1):
-      ss = "_z" + zero_pad(s, len(str(slices)))
-      for ch in range(1, imp_translated.getNChannels()+1):
-         ip = stack.getProcessor(imp_translated.getStackIndex(ch, s, 1))
-         if virtual is True:
-           name = fr + ss + "_c" + zero_pad(ch, len(str(imp.getNChannels()))) +".tif"
-           names.append(name)
-           currentslice = ImagePlus("", ip)
-           currentslice.setCalibration(imp.getCalibration().copy())
-           currentslice.setProperty("Info", imp.getProperty("Info"));
-           FileSaver(currentslice).saveAsTiff(target_folder + "/" + name)
-         else:
-           registeredstack.addSlice("", ip)
-  
+    # loop across channels
+    for ch in range(1, imp.getNChannels()+1):
+
+      tmpstack = ImageStack(width, height, imp.getProcessor().getColorModel())
+
+      # get all slices of this channel and frame
+      for s in range(1, imp.getNSlices()+1):
+        ip = stack.getProcessor(imp.getStackIndex(ch, s, frame))
+        ip2 = ip.createProcessor(width, height) # potentially larger
+        ip2.insert(ip, 0, 0)
+        tmpstack.addSlice("", ip2)
+
+      # Pad the end (in z) of this channel and frame
+      for s in range(imp.getNSlices(), slices):
+        tmpstack.addSlice("", empty)
+
+      # subpixel translation
+      imp_tmpstack = ImagePlus("", tmpstack)
+      imp_translated = translate_single_stack_using_imglib2(imp_tmpstack, shift.x, shift.y, shift.z)
+
+      # Add translated frame to final time-series
+      translated_stack = imp_translated.getStack()
+      for s in range(1, translated_stack.getSize()+1):
+        ss = "_z" + zero_pad(s, len(str(slices)))
+        ip = translated_stack.getProcessor(s).duplicate() # duplicate is important as otherwise it will only be a reference that can change its content
+        if virtual is True:
+          name = fr + ss + "_c" + zero_pad(ch, len(str(imp.getNChannels()))) +".tif"
+          names.append(name)
+          currentslice = ImagePlus("", ip)
+          currentslice.setCalibration(imp.getCalibration().copy())
+          currentslice.setProperty("Info", imp.getProperty("Info"));
+          FileSaver(currentslice).saveAsTiff(target_folder + "/" + name)
+        else:
+          registeredstack.addSlice("", ip)
+
   IJ.showProgress(1)
 
   if virtual is True:
@@ -461,7 +471,7 @@ def register_hyperstack_subpixel(imp, channel, shifts, target_folder, virtual):
     registeredstack_imp.setOpenAsHyperStack(True)
     if 1 == registeredstack_imp.getNChannels():
       return registeredstack_imp
-     
+
   #IJ.log("\nHyperstack dimensions: time frames:" + str(registeredstack_imp.getNFrames()) + ", slices: " + str(registeredstack_imp.getNSlices()) + ", channels: " + str(registeredstack_imp.getNChannels()))
 
   # Else, as composite
@@ -472,7 +482,6 @@ def register_hyperstack_subpixel(imp, channel, shifts, target_folder, virtual):
     return registeredstack_imp
   return CompositeImage(registeredstack_imp, mode)
 
-
 class Filter(FilenameFilter):
   def accept(self, folder, name):
     return not File(folder.getAbsolutePath() + "/" + name).isHidden()
@@ -509,13 +518,12 @@ def getOptions(imp):
   dt = gd.getNextNumber()
   return channel, virtual, multi_time_scale, subpixel, process
 
-
 # Need function to get colors for each channel. Loop channels extracting color model and then apply to registered
 
 def run():
 
   IJ.log("Correct_3D_Drift")
-  
+
   imp = IJ.getImage()
   if imp is None:
     return
@@ -536,7 +544,7 @@ def run():
     print "multi_time_scale="+str(multi_time_scale)
     print "virtual="+str(virtual)
     print "process="+str(process)
-    
+
   if virtual is True:
     dc = DirectoryChooser("Choose target folder to save image sequence")
     target_folder = dc.getDirectory()
@@ -545,14 +553,14 @@ def run():
     if not validate(target_folder):
       return
   else:
-    target_folder = None 
+    target_folder = None
 
   # compute shifts
   IJ.log("  computing drifts..."); print("\nCOMPUTING SHIFTS:")
 
-  IJ.log("     at frame shifts of 1"); 
+  IJ.log("    at frame shifts of 1");
   dt = 1; shifts = compute_and_update_frame_translations_dt(imp, channel, dt, process)
-  
+
   # multi-time-scale computation
   if multi_time_scale is True:
     dt_max = imp.getNFrames()-1
@@ -562,16 +570,16 @@ def run():
     dts = [3,9,27,81,243,729,dt_max]
     for dt in dts:
       if dt < dt_max:
-        IJ.log("     at frame shifts of "+str(dt)) 
+        IJ.log("    at frame shifts of "+str(dt))
         shifts = compute_and_update_frame_translations_dt(imp, channel, dt, process, shifts)
-      else: 
-        IJ.log("     at frame shifts of "+str(dt_max));
+      else:
+        IJ.log("    at frame shifts of "+str(dt_max));
         shifts = compute_and_update_frame_translations_dt(imp, channel, dt_max, process, shifts)
         break
 
   # invert measured shifts to make them the correction
   shifts = invert_shifts(shifts)
-  
+
   # apply shifts
   IJ.log("  applying shifts..."); print("\nAPPLYING SHIFTS:")
   if subpixel:
@@ -579,8 +587,7 @@ def run():
   else:
     shifts = convert_shifts_to_integer(shifts)
     registered_imp = register_hyperstack(imp, channel, shifts, target_folder, virtual)
-  
-  
+
   if virtual is True:
     if 1 == imp.getNChannels():
       ip=imp.getProcessor()
@@ -588,15 +595,15 @@ def run():
       ip2.setColorModel(ip.getCurrentColorModel())
       registered_imp.show()
     else:
-    	registered_imp.copyLuts(imp)
-    	registered_imp.show()
+      registered_imp.copyLuts(imp)
+      registered_imp.show()
   else:
     if 1 ==imp.getNChannels():
-    	registered_imp.show()
+      registered_imp.show()
     else:
-    	registered_imp.copyLuts(imp)
-    	registered_imp.show()
-  
+      registered_imp.copyLuts(imp)
+      registered_imp.show()
+
   registered_imp.show()
 
 run()
diff --git remote/plugins/Scripts/Image/Hyperstacks/Temporal-Color_Code.ijm/var/folders/n2/5q09bjq11kv6xwpp0xlg4npc0000gq/T/diff-2172924583044439740.ijm-20130708101215 local/plugins/Scripts/Image/Hyperstacks/Temporal-Color_Code.ijm/var/folders/n2/5q09bjq11kv6xwpp0xlg4npc0000gq/T/diff-7494276464570120425.ijm
index 044f4a1..0627270 100644
--- remote/plugins/Scripts/Image/Hyperstacks/Temporal-Color_Code.ijm/var/folders/n2/5q09bjq11kv6xwpp0xlg4npc0000gq/T/diff-2172924583044439740.ijm-20130708101215
+++ local/plugins/Scripts/Image/Hyperstacks/Temporal-Color_Code.ijm/var/folders/n2/5q09bjq11kv6xwpp0xlg4npc0000gq/T/diff-7494276464570120425.ijm
@@ -34,6 +34,7 @@ var Glut = "Fire";	//default LUT
 var Gstartf = 1;
 var Gendf = 10;
 var GFrameColorScaleCheck = 1;
+var GbatchMode = 0;
 
 macro "Time-Lapse Color Coder" {
 	Stack.getDimensions(ww, hh, channels, slices, frames);
@@ -128,7 +129,9 @@ macro "Time-Lapse Color Coder" {
 	close();
 
 	selectImage(resultImageID);
-	setBatchMode("exit and display");
+	
+	if (GbatchMode == 0)
+		setBatchMode("exit and display");
 
 	if (GFrameColorScaleCheck)
 		CreateScale(Glut, Gstartf, Gendf);
@@ -179,11 +182,13 @@ function showDialog() {
 	Dialog.addNumber("start frame", Gstartf);
 	Dialog.addNumber("end frame", Gendf);
 	Dialog.addCheckbox("Create Time Color Scale Bar", GFrameColorScaleCheck);
+	Dialog.addCheckbox("Batch mode? (no image output)", GbatchMode);
 	Dialog.show();
  	Glut = Dialog.getChoice();
 	Gstartf = Dialog.getNumber();
 	Gendf = Dialog.getNumber();
 	GFrameColorScaleCheck = Dialog.getCheckbox();
+	GbatchMode = Dialog.getCheckbox();
 }
 
 function CreateScale(lutstr, beginf, endf){
@@ -212,4 +217,4 @@ function leftPad(n, width) {
     while (lengthOf(s) < width)
         s = "0" + s;
     return s;
-}
\ No newline at end of file
+}
2 Likes

I can confirm that after the uploads ctrueden made, Correct 3D drift now works without the ImageScience update site enabled.

2 Likes