上回书➡ 【QuPath】Multiplexed analysis之多重免疫荧光分析教程说到建立多通道分类器并识别细胞,那么结果该如何一次性导出呢?
这就不得不提到qupath的灵活性了~
我们可以在script editor输入自己的脚本,实现很多功能——
一般情况下,我们对一张图像进行注释分析以后,对应的数据会存储在:
既然如此,我们当然也可以通过脚本的方式实现批量输出啦~
🧠 「Groovy 脚本是 QuPath 实现自动化分析的核心工具。」
下面的脚本可以实现对项目内每一张图像的结果进行批量导出,只需要更改自己的目标文件夹即可~
// 配置输出目录(您的路径)
def outputDir = "您的路径"
def outputFolder = new File(outputDir)
outputFolder.mkdirs()
// 检查项目
def project = getProject()
if (!project) {
printError("没有打开的项目!")
return
}
// 获取图像列表
def images = project.getImageList()
if (images.isEmpty()) {
printError("项目中无图像!")
return
}
printHeader("开始导出 ${images.size()} 张图像 → ${outputDir}")
// 核心导出函数
images.eachWithIndex { entry, idx ->
try {
def imageName = entry.getImageName()
printProgress(idx+, images.size(), imageName)
// 读取图像数据(QuPath 0.5.1方式)
def imageData = entry.readImageData()
def hierarchy = imageData.getHierarchy()
// 获取检测对象(兼容细胞/注释对象)
def objects = hierarchy.getObjects(null, qupath.lib.objects.PathDetectionObject)
if (objects.isEmpty()) {
println " ⚠️ 无检测对象,跳过"
return
}
// 生成CSV文件
def csvFile = new File(outputDir, "${imageName}_measurements.csv")
exportMeasurementsToCSV(objects, csvFile)
} catch (Exception e) {
println " ❌ 处理失败: ${e.message}"
e.printStackTrace()
}
}
// 导出完成提示
printSuccess("导出完成!共处理 ${images.size()} 张图像")
showNotification("数据导出完成", "结果已保存至:\n${outputDir}")
// ========== 工具函数 ==========
void exportMeasurementsToCSV(objects, csvFile) {
// 获取所有测量指标(去重+排序)
def measurements = objects.collectMany { it.getMeasurementList().getMeasurementNames() }
.unique().sort()
csvFile.withWriter { writer ->
// 写入BOM头(解决Excel中文乱码)
writer.write('\uFEFF')
// 表头
writer.write("ObjectID,Class," + measurements.join(",") + "\n")
// 数据行
objects.eachWithIndex { obj, i ->
def ml = obj.getMeasurementList()
writer.write("${i+1},${obj.getPathClass()?.toString() ?: 'None'},")
writer.write(measurements.collect { ml.getMeasurementValue(it) ?: "" }.join(","))
writer.write("\n")
}
}
println " ✅ 已导出 → ${csvFile.name} (${objects.size()}个对象)"
}
// 进度显示
void printProgress(current, total, name) {
printf("[%2d/%2d] %-40s ", current, total, name.length() > ? name[0..37] + "..." : name)
}
// 信息格式化
void printHeader(msg) { println("="* + "\n" + msg + "\n" + "="*) }
void printError(msg) { System.err.println("❌ ERROR: " + msg) }
void printSuccess(msg) { println("\n" + "✓ " + msg + " " + "✓") }
// 弹窗通知
void showNotification(title, message) {
javafx.application.Platform.runLater {
new javafx.scene.control.Alert(
javafx.scene.control.Alert.AlertType.INFORMATION,
message
).with {
it.title = title
it.headerText = null
it.showAndWait()
}
}
}