JavaScript is a high-level, dynamic, untyped programming language, supporting multiple paradigms including object-oriented, imperative and functional programming styles. Although there are similarities between JavaScript and Java, including language name and syntax, the two are distinct languages and differ greatly in their design.
关于 JavaScript 引擎的注释
ImageJ 通过 Java 的内置JavaScript支持 JavaScript。Java 8 的 Java 版本包含 Mozilla 的Rhino JavaScript engine。这两个引擎在很大程度上(但不完全)兼容,这意味着为旧版本 ImageJ(使用 Rhino)编写的一些旧脚本与当前 ImageJ 版本(使用 Nashorn)一起运行时可能无法正常运行。
ImageJ 的 JavaScript 教程
语言基础知识
导入类
要从 JavaScript 中引用 Java 类,您需要导入它们。
You can specify imports in JavaScript as follows:
importClass(Packages.java.io.File)
其中java.io.File是要导入的类。
对于最常见的东西,例如IJ、RoiManager 或 GenericDialog 需要以下几行:
importClass(Packages.ij.IJ);
importClass(Packages.ij.plugin.frame.RoiManager);
importClass(Packages.ij.gui.GenericDialog);
使用类的全名
有时使用类的全名比使用导入更容易。例如。您可以使用 Packages.ij.gui.GenericDialog 访问 GenericDialog 类一次。
对于某些包,有内置的快捷方式。您可以使用 java.io.File而不是 Packages.java.io.File,因为 java 是 Packages.java 的快捷方式(Packages.java、Packages.javax、Packages.com、Packages.edu、Packages.javafx 和 Packages.org 都有快捷方式)。
变量
有两种方法:
- 直接:变量是全局可见的(危险!会导致严重的错误。)
- 使用
var声明:变量为本地变量,仅在最里面的代码块中可见。
局部变量也更快,因为它们可以是efficiently optimized for access。示例:
importClass(Packages.ij.IJ);
// global variable 'imp'
imp = IJ.getImage();
// local variable 'i', visible within the loop only:
for (var i = 0; i < 10; i++ ) {
IJ.log("i is " + i);
}
以合理的方式混合全局变量和局部变量:
importClass(Packages.ij.IJ);
// global variables 'base_url' and 'names'
// and local variable 'imp', the latter visible within the loop only:
base_url = "https://imagej.net/ij/images/";
names = ["blobs.gif", "boats.gif", "bridge.gif"];
for (var i = 0; i< names.length; i++) {
var imp = IJ.openImage( base_url + names[i] );
// process image
// ...
imp.show();
}
// ERROR: variable 'imp' is not visible outside the loop
// IJ.log("The last image opened was: " + imp);
###储备
JavaScript 数据库和原生 java 数据库都有。
JavaScript 备份
创建 JavaScript 数据库的方法有很多种。以下是一些:
importClass(Packages.ij.IJ);
// One dimensional:
var names = ["blobs.gif", "boats.gif", "bridge.gif"];
IJ.log("We have " + names.length + " names.");
// Two dimensional:
var coords = [[10, 20, 30], // X coords
[15, 25, 35]]; // Y coords
IJ.log( "x0, y0 = " + coords[0][0] + ", " + coords[1][0] );
IJ.log( "All X coords: " + coords[0] );
// Uneven dimensions:
var coords = [[10, 20, 30], // X coords
[15, 25, 35, 45, 55, 75]]; // Y coords
IJ.log( "Number of X coords: " + coords[0].length );
IJ.log( "Number of Y coords: " + coords[1].length );
JavaScript 中的备份非常灵活:
importClass(Packages.ij.IJ);
// Empty arrays:
var names = new Array();
IJ.log("First name is: " + names[0]); // --> prints "undefined", i.e. null.
// Creating array entries at arbitrary index positions:
names[0] = "Table";
names[5] = "Window";
IJ.log("Number of names: " + names.length); // --> prints 6 ! All other entries are "undefined", null.
// Using the array as a dictionary:
names["one"] = 1;
IJ.log("Number of names: " + names.length); // --> still prints 6! But now the array has a map in it as well.
IJ.log("The 'one' is " + names["one"]); // --> prints 1
// Array entries can contain anything, including other arrays!
names[3] = new Array();
names[3][0] = "Ok";
names[3][1] = "Good";
names[3][2] = ["Arrays", "are", "very", "flexible"]; // another array!
IJ.log(names);
原生 Java 备份
本机java请求可以直接传递给java函数和方法。例如,要提供请求请求:
width = 512
height = 512
pixels = java.lang.reflect.Array.newInstance(java.lang.Byte.class, width * height);
print(pixels);
print(pixels.length);
但要操作此类数据库,只需将它们视为 JavaScript 数据库即可。请注意,现在您仅限于数字索引,并且仅限于数据库大小!
width = 512
height = 512
pixels = java.lang.reflect.Array.newInstance(java.lang.Byte.class, width * height);
print(pixels[10]);
java.util.Arrays.fill(pixels, new java.lang.Byte(0));
print(pixels[10]);
// Subtract 25 to each pixel:
for (var i = 0; i < pixels.length; i++) {
pixels[i] -= 25;
}
print(pixels[10]);
借助 Java 8 的 nashorn JavaScript 引擎,可以更轻松地创建本机 Java 数据库。您可以创建一个可以重用的构造函数:
var ByteArray = Java.type("byte[]");
width = 512
height = 512
var pixels = new ByteArray(width*height);
print(pixels.length);
print(pixels[10]);
// Subtract 25 to each pixel:
for (var i = 0; i < pixels.length; i++) {
pixels[i] -= 25;
}
print(pixels[10]);
var pixels2 = new ByteArray(10);
print(pixels2.length);
如上所述,请注意因操作带符号的byte[]备份而产生的所有问题,其值应首先设为无符号、修改,然后再签名回备份中。
函数
简单的例子:
importClass(Packages.ij.IJ);
function invertImage(imp) {
var ip = imp.getProcessor();
ip.invert();
}
// Obtain the current image:
var imp = IJ.getImage();
// Invoke our function
invertImage(imp);
// Update screen:
imp.updateAndDraw();
调用函数时使用的参数数量是灵活的。调用函数时使用的所有参数都收集在名为 arguments 的变量中。
例如:
importClass(Packages.ij.IJ);
function createImage(width, height) {
IJ.log( "Number of arguments: " + arguments.length);
// Default image type:
var type = "8-bit";
// Check if an extra argument for the image type was provided:
if (arguments.length > 2) {
// Check that the extra arg makes any sense:
if ("RGB" == arguments[2]) {
type = "RGB";
} else {
IJ.log("Don't know how to use " + arguments[2]);
return null;
}
}
var imp = IJ.createImage("New image", type, width, height, 1);
return imp;
}
var imp = createImage(400, 400, "RGB");
imp.show();
对于复杂的示例,请参见示例脚本Multithreaded_Image_Processing_in_Javascript.js,除了附件之外,它还说明了如何将函数作为参数传递给其他函数,以及如何使用可变数量的参数调用它们。
函数作为对象
可以在函数体上通过其自身的this自引用指针动态创建任意数量的变量。
要在 JavaScript 中创建对象,首先声明一个存储对象数据的函数:
// Use uppercase, by convention
function Data(image, annotation) {
this.image = image;
this.annotation = annotation;
}
然后创建它:
importClass(Packages.ij.IJ);
function Data(image, annotation) {
this.image = image;
this.annotation = annotation;
}
var data = new Data(IJ.getImage(), "The current image");
IJ.log("data contains: " + data.image + "\n" + "with annotation: " + data.annotation);
要添加方法来操作新的数据对象,请创建一个对象作为参数的函数:
importClass(Packages.ij.IJ);
importClass(Packages.ij.gui.GenericDialog);
function Data(image, annotation) {
this.image = image;
this.annotation = annotation;
}
var data = new Data(IJ.getImage(), "The current image");
IJ.log("data contains: " + data.image + "\n" + "with annotation: " + data.annotation);
function annotate(data) {
var gd = new GenericDialog("Annotate");
gd.addStringField("New annotation:", data.annotation, data.annotation.length);
gd.showDialog();
if (gd.wasCanceled()) return;
// assign new annotation:
var newAnnotation = gd.getNextString();
IJ.log("Changing the annotation from \"" + data.annotation + "\" to \"" + newAnnotation + "\"");
data.annotation = newAnnotation;
}
// Invoke on the existing Data object:
annotate(data);
由于 JavaScript 使用基于父类的对象,我们可以将 annotate 转换为对象方法。通过将函数添加到对象的父类中,该对象的每个实例都将拥有该方法。
importClass(Packages.ij.IJ);
importClass(Packages.ij.gui.GenericDialog);
function Data(image, annotation) {
this.image = image;
this.annotation = annotation;
}
var data = new Data(IJ.getImage(), "The current image");
IJ.log("data contains: " + data.image + "\n" + "with annotation: " + data.annotation);
Data.prototype.annotate = function () {
var gd = new GenericDialog("Annotate");
gd.addStringField("New annotation:", this.annotation, this.annotation.length);
gd.showDialog();
if (gd.wasCanceled()) return;
// assign new annotation:
this.annotation = gd.getNextString();
}
// Invoke on the existing Data object:
data.annotate();
###创建导入命名空间
在 Java 8 中,新的 JavaScript 引擎 nashorn 引入了新的 JavaImporter。 JavaScript with 语句可以使用 JavaImporter 的实例限制。 这个插入导入范围为 with 语句的大逻辑内部的代码。
下一个代码片段展示了如何使用JavaImporter编写的注释示例:
function Data(image, annotation) {
this.image = image;
this.annotation = annotation;
}
function annotate(data) {
var importer = new JavaImporter(Packages.ij.gui.GenericDialog, Packages.ij.IJ);
with (importer) {
var gd = new GenericDialog("Annotate");
gd.addStringField("New annotation:", data.annotation, data.annotation.length);
gd.showDialog();
if (gd.wasCanceled()) return;
// assign new annotation:
var newAnnotation = gd.getNextString();
IJ.log("Changing the annotation from \"" + data.annotation + "\" to \"" + newAnnotation + "\"");
data.annotation = newAnnotation;
}
}
var importerIJ = new JavaImporter(Packages.ij.IJ);
with (importerIJ) {
var data = new Data(IJ.getImage(), "The current image");
IJ.log("data contains: " + data.image + "\n" + "with annotation: " + data.annotation);
// Invoke on the existing Data object:
annotate(data);
}
###检查对象的字段和方法
因此,您返回一个函数的对象,但您不知道它是什么。
要在解释器中打印所属类别:
ob = ...
ob.getClass();
或到日志窗口:
ob = ...
IJ.log(ob.getClass());
要打印它具有的方法列表及其返回类型和参数类型:
ob = ...
m = ob.getClass().getMethods();
for (var i=0; i<m.length; i++) IJ.log(m[i]);
数学
所有可用的数学函数:
Math.abs(a) // the absolute value of a
Math.acos(a) // arc cosine of a
Math.asin(a) // arc sine of a
Math.atan(a) // arc tangent of a
Math.atan2(a,b) // arc tangent of a/b
Math.ceil(a) // integer closest to a and not less than a
Math.cos(a) // cosine of a
Math.exp(a) // exponent of a
Math.floor(a) // integer closest to and not greater than a
Math.log(a) // log of a base e
Math.max(a,b) // the maximum of a and b
Math.min(a,b) // the minimum of a and b
Math.pow(a,b) // a to the power b
Math.random() // pseudorandom number in the range 0 to 1
Math.round(a) // integer closest to a
Math.sin(a) // sine of a
Math.sqrt(a) // square root of a
Math.tan(a) // tangent of a
内置常量:
Math.E
Math.PI
简单的例子:
var root = Math.sqrt(12);
IJ.log("The root of 12 is " + root);
函数式编程
假设您想使用另一幅图像中像素的平方值来创建一个新图像。
首先,我们得到一张source图片(比如当前活动的图片):
var source = Packages.ij.IJ.getImage(); // the current image (an ImagePlus)
通常,您将循环处理所有像素并将其平方的结果应用到其他图像:
// Return a new ImageProcessor containing the square of each pixel value in ImageProcessor ip
function square(ip) {
var ip2 = ip.duplicate().convertToFloat();
var pix = ip.getPixels();
for (var i = 0; i < pix.length; i++) {
pix[i] = Math.pow(pix[i], 2);
}
return ip2;
}
var ip2 = square( source.getProcessor() );
// Show the result:
new Packages.ij.ImagePlus("square of " + source.title, ip2).show();
但想象一下,现在您想要获得带有平方根而不是平方或对数的图像。那不一样吗?
我们必须编写名为 sqrt 和 pow3 的类似函数。等等。
相反,我们应该停下来想一想:有一个共同的模式。我们要做的就是对图像中的每个像素应用一个函数,把结果设置到图像中的另一个宽度相同的像素中。在函数式编程中,这种模式称为map操作。由于JavaScript允许我们将函数作为参数传递,因此我们可以定义自己的map函数:
function map(fn, ip) {
var ip2 = ip.duplicate().convertToFloat();
var pix = ip2.getPixels();
for (var i = 0; i < pix.length; i++) {
pix[i] = fn(pix[i]);
}
return ip2;
}
现在,配备了 map 函数,可以应用我们想要的任何数学攻击:
var ip_sqrt = map( Math.sqrt, source.getProcessor() );
var ip_log = map( Math.log, source.getProcessor() );
...
但是!等等我们没有通过任何额外的论点。我们如何为pow建立一个泛型函数,以便我们可以应用 2 或 3 的幂等?
我们可以像这样重写我们的 map 函数:
function map(fn, ip) {
var ip2 = ip.duplicate().convertToFloat();
var pix = ip2.getPixels();
for (var i = 0; i < pix.length; i++) {
pix[i] = fn(pix[i], arguments[2]);
}
return ip2;
}
…其中 arguments[2] 是超出任何声明的参数的参数(如果有)。这是可行的,因为在 javascript 中,函数可以接受可变数量的参数:
var ip2 = map( Math.pow, source.getProcessor(), 2 );
var ip3 = map( Math.pow, source.getProcessor(), 3 );
我们的第 map 函数的个版本有点变态:在标准函数式编程技术中,给出的参数函数将验证每个列表的索引i处的每个元素;即该函数将接收与我们提供给map的列表相同的多个参数。
有什么大不了的呢?我们抽象产生了一个常见的模式——循环,而且,我们还降低了程序的复杂性。因此,例如,现在对map函数应用优化将改进all我们代码中使用的位置!
(添加调试消息也是如此,什么都不是。任何你想要的)。
例如,由于每个像素的处理是独立于其他像素的,因此我们可以进行任务处理!
function map(fn, ip) {
var ip2 = ip.duplicate().convertToFloat();
var pix = ip2.getPixels();
var n_threads = java.lang.Runtime.getRuntime().availableProcessors();
var threads = new Array();
var ai = new java.util.concurrent.atomic.AtomicInteger(0);
var width = ip.getWidth();
var height = ip.getHeight();
var arg = arguments[2];
for (var t = 0; t < n_threads; t++) {
threads[t] = new java.lang.Thread( function() {
// Process one line at a time:
for (var line = ai.getAndIncrement(); line < height; line = ai.getAndIncrement()) {
var offset = line * width;
for (var i = 0; i < width; i++) {
// invoke function on each pixel, with the optional extra argument
pix[offset + i] = fn(pix[offset + i], arg);
}
}
});
threads[t].start();
}
// Wait until all threads finish:
for (var t = 0; t < n_threads; t++) {
threads[t].join();
}
return ip2;
}
我们将像以前一样调用现在的多线程 map 函数:
var ip_sqrt = map( Math.sqrt, source.getProcessor() );
var ip_log = map( Math.log, source.getProcessor() );
var ip2 = map( Math.pow, source.getProcessor(), 2 );
var ip3 = map( Math.pow, source.getProcessor(), 3 );
上图:注意,完成化像Math.sqrt这样的简单函数可能会导致执行速度慢,因为多线程前置以及从多个线程访问同一个像素阵列时的极端竞争用。也许您想要有两个版本的映射:简单版本和完成版本,然后用于复杂、繁重的功能。
为了进一步简化,我们可以创建一个 show 函数来避免进一步的重复:
function mapAndShow(imp, fn) {
var ip1 = imp.getProcessor();
// Map the function to each pixel, into a new ImageProcessor:
var ip2 = map(fn, ip1, arguments[2]); // pass any extra argument as well
// Fix LUT range for best visualization:
ip2.findMinAndMax();
// Open image in a new window:
new Packages.ij.ImagePlus(fn.name + " of " + imp.title, ip2).show();
}
with (new JavaImporter(Packages.ij.IJ)) {
mapAndShow( IJ.getImage(), Math.sqrt);
mapAndShow( IJ.getImage(), Math.pow, 3);
}
以上所有示例旨在让您了解 JavaScript 和 ImageJ 的用途。如果您有兴趣将数学函数评估图像,那么最好使用 ImageJ 内部命令,如菜单 Plugins › JavaScript 中启动的:
with (new JavaImporter(Packages.ij.IJ, Packages.ij.ImagePlus)) {
var imp = IJ.getImage();
var imp2 = new ImagePlus("copy of " + imp.title, imp.getProcessor().duplicate().convertToFloat());
// Set each pixel to the square of its value:
IJ.run( imp2, "Square", "");
imp2.show();
}
使用其他 .js 文件作为库
当代码被重用时,编程工具是最有用的。因此,许多剪辑师将他们的代码分割为也可以从脚本中调用其他函数。
为了避免一直复制粘贴(这总是会导致代码过时且难以维护),一个好主意是将此类通用函数存储在单独的 .js 文件中,并让 JavaScript 解释器了解它们。您可以使用IJ.getDirectory("plugins")以独立于系统的方式获取脚本的位置:
load(Packages.ij.IJ.getDirectory("plugins") + "JavaScript" + java.io.File.separator + "my-library-of-useful-functions.js");
// now you can use the functions defined in above .js file
为了保持全局范围干净,建议仅使用库中的函数。由于 JavaScript 具有函数作用域,在函数内声明的所有变量在全局作用域中不可见,因此无法覆盖正在使用的脚本中的变量。
最佳实践是在每个库中创建一个与该库同名的对象。
// This is the code of the library SimpleObject.js
var SimpleObject = function (x, y, val) {
this.x = x;
this.y = y;
this.val = val;
}
// Each new object that is created from SimpleObject owns this function
SimpleObject.prototype.print = function () {
with (new JavaImporter(Packages.ij.IJ)) {
IJ.log("x: " + this.x);
IJ.log("y: " + this.y);
IJ.log("val: " + this.val);
}
}
以下代码演示了如何使用该库(如果它在 Process › Math 中保存为SimpleObject.js)。
load(Packages.ij.IJ.getDirectory("plugins") + "JavaScript" + java.io.File.separator + "SimpleObject.js");
var obj = new SimpleObject(10, 10, 100);
obj.print();
ImageJ 交互
打开并创建 ImageJ 图像
imp = new Opener().openImage("/path/to/image.jpg");
// do some processing
// ...
imp.show();
或者更简单地说:
imp = IJ.openImage("/path/to/image.jpg");
imp.show();
还有URL(在这种情况下,我们直接调用show(),而不是将返回的图像指针保存到任何变量中):
IJ.openImage("https://imagej.net/ij/images/blobs.gif").show();
从头开始创建图像,包括LUT:
// From scratch:
width = 512
height = 512
pixels = new java.lang.reflect.Array.newInstance(java.lang.Byte.TYPE, width * height);
// process the pixels
// ...
// Create LUT:
channel = new java.lang.reflect.Array.newInstance(java.lang.Byte.TYPE, 256);
for (i=0; i<channel.length; i++)
channel[i] = Integer(i).byteValue();
cm = new LUT(channel, channel, channel);
// Create the image as 8-bit with the LUT we just created:
imp = new ImagePlus("the title", new ByteProcessor(width, height, pixels, cm));
imp.show();
使用默认灰度LUT更方便地创建图像:
imp = ImagePlus("the title", new ByteProcessor(512, 512));
pixels = imp.getProcessor().getPixels();
// do some processing ...
// ...
imp.show();
使用 GenericDialog
此示例演示如何使用 GenericDialog 获取 2 个打开的图像和一个前台。
// get 2 images and a checkbox
var createWindow = true; //default checkbox value
var imp = null; // default chosen images
var data = getTwoImages( createWindow ); // call the function
if (data) {
// there was data returned
imp = [data[0], data[1]];
createWindow = data[2];
// show the choices in the log window
IJ.log(imp[0]); // first image
IJ.log(imp[1]); // second image
IJ.log(createWindow); // the status of the checkbox
// the rest of your code goes here
}
function getTwoImages( createWindow ) {
var wList = WindowManager.getIDList();
if (null == wList || wList.length<2){ //there are less than 2 images or no images
IJ.showMessage("Error", "There must be at least two windows open");
return;
}
//get all the image titles so they can be shown in the dialog
var titles = new Array();
for (var i=0, k=0; i<wList.length; i++) {
var limp = WindowManager.getImage(wList[i]);
if (limp)
titles[k++] = limp.getTitle();
}
//construct the dialog
gd = new GenericDialog("Options");
gd.addMessage("Binary Reconstruction v 2");
gd.addChoice("mask i1:", titles, titles[0]);
gd.addChoice("seed i2:", titles, titles[1]);
gd.addCheckbox("Create New Window", createWindow);
gd.showDialog(); //show it
if (gd.wasCanceled())
return;
var i1Index = gd.getNextChoiceIndex();
var i2Index = gd.getNextChoiceIndex();
var create_window = gd.getNextBoolean();
return [WindowManager.getImage(wList[i1Index]),
WindowManager.getImage(wList[i2Index]),
create_window];
}
运行 ImageJ 命令
启动命令:
// in a separate thread:
IJ.doCommand("FFT");
// instead, waiting until it finishes:
IJ.run("FFT");
要在特定图像上运行命令:
// Obtain an image to work on:
var imp = IJ.getImage();
// Call the command to add noise to the current image
// which is here the one provided as argument:
IJ.run(imp, "Add Noise", "");
// Subtract 25 to each pixel value:
IJ.run(imp, "Subtract...", "value=25");
在上面,您可以通过打开插件 - 宏记录器,然后手动运行命令来查看要添加到命令中的参数。宏字符串将打印在记录器窗口上。
###检查对象中的java方法和字段
打印ImagePlus类的静态字段和方法:
s = "";
for (a in ImagePlus) { s += " " + a; }
…打印INTEGRATED_DENSITYAREA_FRACTION等。
打印ImagePlus实例的字段和方法(即已经存在的图像):
// get the current image
imp = IJ.getImage();
// print fields and methods
s = ""; for (a in imp) { s += " " + a; }
…它打印所有方法名称,例如 getStatistics isHyperStack 等以及宽度和高度等字段(因为它有公共“get”方法,例如 getWidth() 和 getHeight() 。)
为 ImageJ 创建脚本
将 JavaScript 脚本保存在文本文件中:
1.延长线.js
2.名称中标注下划线_:my_first_script.js
…然后将其放入 ImageJ 的插件文件夹或子文件夹中。
启动时,脚本将出现在相应的菜单中。
如果您在 ImageJ 启动后添加脚本,只需调用“帮助 - 更新菜单”,它就会被选中。
您可以继续修改并保存脚本文件。每次从菜单中读取它时,都会从文件系统运行中读取它。
接口和匿名类
要创建 ImageListener 声明而不实现此类 java 接口的新类,只需使用将映射到其所有方法的函数(只要它们具有相同的签名,在本例中就是这样):
ImagePlus.addImageListener( function (imp, name) {
if (name == "imageOpened") {
IJ.log("Opened image: " + imp);
} else if (name == "imageClosed") {
IJ.log("Closed image: " + imp);
} else if (name == "imageUpdated") {
IJ.log("Updated image: " + imp);
}
});
或者,可以创建一个内部声明了函数的对象,然后将其分配给从接口动态创建的匿名类:
body = {
run: function () {
IJ.log("Running!");
}
}
// Runnable is an interface
runnable = new Runnable(body);
new Thread(runnable).start();
简化:
new Thread( function () { IJ.log("Running!"); } ).start();
上面的代码做了什么:寻找一个可以采用不带参数的方法(由函数表示)的接口,并实例化一个实现此类接口的匿名类,并将函数映射到其方法。
另请参阅example plugin,了解用javascript编写的ImageJ。
JavaScript 中的多线程图像处理
下面示例演示如何创建一个名为 multithreader 的通用函数,该函数接受另一个函数作为参数并执行多次。作为一个非常简单的示例,将 printer 函数传递给 multithreader,并在不重复内容的情况下打印数字列表,当然也不保留顺序。
multithreader可分割计算机提供所需的CPU内核。
永远记住:只有完全独立的任务才能有效地玩具化!
多线程的一个好的策略包括仔细考虑毛发化的任务:块可以小到什么程度?对于图像来说,块可以是一个像素或一条线,但通常这些都太小而无法克服毛发化的开销。
下面的示例很简单,但multithreader框架函数允许输入可变数量的参数,如完整的插件Multithreaded Image Processing in JavaScript中如下所示。该脚本展示了以多线程方式生成具有随机像素值的图像,以及如何选择要处理的块的效果合理的。
// Import all classes that are used more than once:
importClass(Packages.ij.IJ);
importClass(Packages.java.lang.Thread);
function multithreader(fun, start, end) {
var threads = java.lang.reflect.Array.newInstance(Thread.class, java.lang.Runtime.getRuntime().availableProcessors());
var ai = new java.util.concurrent.atomic.AtomicInteger(start);
// Prepare arguments: all other arguments passed to this function
// beyond the mandatory arguments fun, start and end:
var args = new Array();
var b = 0;
IJ.log("Multithreading function \"" + fun.name + "\" with arguments:\n argument 0 is index from " + start + " to " + end);
for (var a = 3; a < arguments.length; a++) {
args[b] = arguments[a];
IJ.log(" argument " + (b+1) + " is " + args[b]);
b++;
}
var body = {
run: function() {
for (var i = ai.getAndIncrement(); i <= end; i = ai.getAndIncrement()) {
// Execute the function given as argument,
// passing to it all optional arguments:
fun(i, args);
Thread.sleep(100); // NOT NEEDED, just to pretend we are doing something!
}
}
}
// start all threads
for (var i = 0; i < threads.length; i++) {
threads[i] = new Thread(new java.lang.Runnable(body)); // automatically as Runnable
threads[i].start();
}
// wait until all threads finish
for (var i = 0; i < threads.length; i++) {
threads[i].join();
}
}
// The actual desired effect: the printer
function printer(i) {
IJ.log("i is " + i);
}
// Execute:
multithreader(printer, 0, 10);
请在这里查看完整文件:Multithreaded_Image_Processing_in_Javascript.js
链接
- Mozilla Rhino 网页上的 Tutorial (Java 6)。
- Scripting Java with JavaScript(Java 6)。
- Performance tips(Java 6)。
- Oracle Nashorn (Java 8) 上的Tutorial。