检测到开型代码语句的递归_ImageJ开发教程——递归函数

7d94861da2a7db29c0ccf1f27071198a.png

之前的一篇文章介绍了利用for循环进行批量处理和储存

Treasure琛:ImageJ开发教程——循环和存储​zhuanlan.zhihu.com
39bb30e86825214373702d9f6f1adaf2.png

for循环可以解决平时80%以上的问题,但对于某些特殊情况,利用for循环就难以解决了。

例如,当我们想对一个文件夹中的所有图片进行某些操作时,通常会使用循环:

path = getDirectory("Choose Source Directory ");
list = getFileList(path);
for(i = 0; i < list.length; i++) {
        open(list[i]);
        ...//your operations

这里通过for循环,将指定文件夹中的图片逐一打开,然后进行一样的处理。但如果这个文件夹里不仅仅有图片,还有其他文件夹,而且这些文件夹中还有图片呢?

这时利用for循环就会报错,因为open()不能直接打开文件夹。我们当然可以加一个条件结构if,来判断每个文件是图片还是文件夹,但我们依旧无法对文件夹中的文件夹进行操作。

这就要用到递归(Recursion)的思想了。递归,看起来很高大上,但其实我们早就接触过这一概念,举个例子:

从前有座山,山里有座庙,庙里有个和尚,和尚在讲故事,这个故事是:

从前有座山,山里有座庙,庙里有个和尚,和尚在讲故事,这个故事是:

从前有座山...

用表情包就更好说明了:

d1edd28cad3b64aa18346c92fc5efb4f.png

把这里“大哥”换成函数,“抽”换成调用,其实就表达出了递归的核心思想

递归函数可以自己调用自己。

递归函数本质就是一个封装了的函数,参考这篇文章:

Treasure琛:ImageJ开发教程——函数封装​zhuanlan.zhihu.com
83bbeb1103575bbbfc123cf339210a7b.png

举一个最简单的例子,利用递归Macro算阶乘(Factorial)

function f(n) { 
	if (n<=1) 
	{
	return 1;
	}
	return n*f(n-1);
}

从这里可以看出递归的两个要素:

1、找到函数自身的关系式。这个例子中,即f(n)=n*f(n-1);

2、递归的终止条件。这个例子中,当n<=1的时候,递归终止。

具体过程可以分解为如下的过程:

f(6)
=> 6 * f(5)
=> 6 * (5 * f(4))
=> 6 * (5 * (4 * f(3)))
=> 6 * (5 * (4 * (3 * f(2))))
=> 6 * (5 * (4 * (3 * (2 * f(1)))))
=> 6 * (5 * (4 * (3 * (2 * 1))))
=> 720 

回到文章一开始提到的问题:如果一个文件夹中不仅有图片,而且有文件夹,且该文件夹中还有图片及文件夹...怎样一键处理其中的所有图片?

这篇文章会介绍一个Macro,利用递归的思想解决这一问题。

先上代码:

08900426cc314cc99a48524a85c6214c.png

结果如下,一键将文件夹中的所有图片(包括子文件夹):

950378ac13cec379b325bf77499f270d.png

一定注意!!!如果不在saveAs()中加入"_after",这个代码就 可以直接覆盖原始数据。

不可恢复!!!三思而后行。


代码解析

代码可以分为三大部分:

一、主体部分(3-5行)

path = getDirectory("Choose Source Directory ");
setBatchMode(true);//Prevent images from showing up to speed up
process_all(path);

首先获得getDirectory函数,获得文件路径path。

setBatchMode(true)相当于批处理模式,不会显示处理过程,从而加快处理过程。

process_all()函数是后面定义的递归函数,需要将path路径输入这个函数。

二、递归函数定义(7-17行)

function process_all(path) {//Recursion function
list = getFileList(path);
for (i=0; i<list.length; i++) {
    if (endsWith(list[i], "/")){//It's a folder.
    process_all(path+list[i]);}//Recurse to enter the subfolder
    else {//It's an image.
    dir = path + list[i];
    print(dir);//Show processed images
    processimage(dir);} 
    }
}

这里定义了一个process_all(path)函数,需要一个路径path的输入。

1、利用getFileList(path)这个函数,需要获得这个路径下所有的文件列表。

2、然后进入for循环,遍历这个文件列表

3、通过if/else条件语句,判断这个文件是不是文件夹,如果是文件夹,会以"/"为结尾,endsWith()函数会返回TRUE。

接下来就是递归的思想,如果这个文件是一个文件夹,则需要进入这个文件夹,再进行一次同样的判断和处理,所以就可以直接调用自己。

这里就找到了递归的第一个要素:找到函数自身的关系式(这个关系式不一定是数学关系式)

但这时的输入函数的路径就有变化了,path输入需要加上list[i],表示文件夹的路径:process_all(path+list[i])

4、else语句在这里充当了递归的第二个要素:递归的终止条件。

如果这个文件不是一个文件夹,则应该是一张图片(当然还可以再加一个判断,其结尾是否是".tif"),可以然后直接调用processimage()函数。

三、图像处理函数的定义(18-25行)

function processimage(path) {//Process function
open(path);
title = getTitle();
run("8-bit");
run("Invert");
saveAs("Tiff", path + "_after");//Prevent image overwrite
close(title);
}

这里定义了一个processimage(path)函数,需要一个路径path的输入。

这里实现的是将图片转为8-bit、反转并保持到各自文件夹,可以根据自己的需求进行更改

但一定注意saveAs(),如果直接是saveAs("Tiff", path),保存文件会直接将原始图片覆盖掉,而且是不可逆的,三思而后行!!!


这篇文章的代码可以在GitHub下载到:

GitHub-ImageJ Tutorial​github.com
436de378fdbc49ab446a6915b27b1e6c.png

如果对于ImageJ使用有什么问题可以私信我,或者给我发邮件:zhaoyc9@163.com

更多教程可以关注我的专栏:

ImageJ实用教程​zhuanlan.zhihu.com
841e4ff9cfd6951be0bbf096acea1e8f.png
ImageJ开发教程​zhuanlan.zhihu.com
5cbb35eaea4f1aed1572753653542a00.png

希望对大家有帮助~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值