目录
1,基础学习
脚本打印项目中所有图像
访问当前图像内容
访问图像元数据
访问像素
创建ROI
创建对象(使 ROI 可见)
多个ROI
Working with BufferedImage
使用 ImageJ
命令示例
2 脚本导出数据(重点)
3,脚本使用(老版本)
①统计不同类型的对象
②计数肿瘤分类
③计算百分比(重点)
④ 计算分类像素面积(重点)
4,阅读qupath概述
QuPath可以手动+脚本的方式进行运行。可以用于个批量处理数据和批量导出数据。
QuPath脚本是基于Groovy创建的。选择Groovy是因为Groovy具有很多新特性,同时又与QuPath本身所使用的Java编程语言非常匹配。熟悉Java语言的程序员应该很快就可以轻松地学习Groovy。
除了Groovy之外,还可以编写其他脚本语言。要切换到Javascript,只需打开脚本编辑器,然后选择Language(语言)→ Javascript 另外,还可以使用python、matlab或ImageJ宏语言。
在Groovy中,"//“被用作单行注释符号,用于注释掉一行代码。(下面部分习惯了R语言的#,自行修改)
def project = getProject()
for (entry in project.getImageList()) {
print entry.getImageName()
}
批量提取:依次打开每个图像;从层次结构中提取批注;打印每张图像的图像名称和注释计数
def project = getProject()
for (entry in project.getImageList()) {
def imageData = entry.readImageData() #打开项目
def hierarchy = imageData.getHierarchy() #提取层次
def annotations = hierarchy.getAnnotationObjects() #提取注释
print entry.getImageName() + '\t' + annotations.size() #输出名称+注释计数
}
#这个代码打开项目比较慢,因为他会打开一些不需要实际访问的像素
快速版:
def project = getProject()
for (entry in project.getImageList()) {
def hierarchy = entry.readHierarchy()
def annotations = hierarchy.getAnnotationObjects()
print entry.getImageName() + '\t' + annotations.size()
}
def imageData = getCurrentImageData()
print imageData
#或者
def viewer = getCurrentViewer()
def imageData = viewer.getImageData()
print imageData
def server = getCurrentServer()
print server
#查询图像的属性
def server = getCurrentServer()
print server.getWidth() + ' x ' + server.getHeight()
#metadata 原数据储存在一个对象中
def server = getCurrentServer()
print server.getMetadata()
#像素大小 PixelCalibrationObject
def server = getCurrentServer()
def cal = server.getMetadata().getPixelCalibration()
print cal
计算像素大小(微米)
def server = getCurrentServer()
def cal = server.getPixelCalibration() #获取像素大小
print cal.getPixelWidthMicrons() #像素宽度微米
print cal.getPixelHeightMicrons() #像素高度微米
print cal.getAveragedPixelSizeMicrons() #平均像素尺寸微米
#如果尺寸信息不可用,使用Double.NaN来输出结果
double myNaN = Double.NaN
// Two Java/Groovy-friendly ways to check values are 'usable'
print Double.isNaN(myNaN)
print Double.isFinite(myNaN)
包括服务器路径、缩减像素采样因子和边界框坐标(以全分辨率像素单位定义,原点位于图像的左上角)
import qupath.lib.regions.*
def server = getCurrentServer()
def path = server.getPath()
double downsample = 4.0
int x = 100
int y = 200
int width = 1000
int height = 2000
def request = RegionRequest.createInstance(path, downsample, x, y, width, height)
def img = server.readRegion(request)
print img
设置像素大小:
// Set pixel width and height to 0.5 microns
setPixelSizeMicrons(0.5, 0.5) #设置宽长的大小
有多种ROI: createEllipseROI
, createPolygonROI
, createLineROI
创建一个椭圆ROI
import qupath.lib.roi.ROIs
import qupath.lib.regions.ImagePlane
int z = 0
int t = 0
def plane = ImagePlane.getPlane(z, t)
def roi = ROIs.createRectangleROI(0, 0, 100, 100, plane)
print roi
import qupath.lib.objects.PathObjects #创建一个ROI项目
import qupath.lib.roi.ROIs #创建ROI
import qupath.lib.regions.ImagePlane ##传递参数
int z = 0
int t = 0
def plane = ImagePlane.getPlane(z, t) # 指定z-slice and timepoint,
def roi = ROIs.createEllipseROI(0, 0, 100, 100, plane) #创建矩形ROI
def annotation = PathObjects.createAnnotationObject(roi) ##创建的是一个注释项目
addObject(annotation)
##若要创建检测而不是注释(检测区域)
def detection = PathObjects.createDetectionObject(roi)
如何创建和合并两个 ROI
import qupath.lib.roi.ROIs
import qupath.lib.roi.RoiTools
import qupath.lib.objects.PathObjects
import qupath.lib.regions.ImagePlane
def plane = ImagePlane.getDefaultPlane()
def roi1 = ROIs.createRectangleROI(0, 0, 100, 100, plane)
def roi2 = ROIs.createEllipseROI(80, 0, 100, 100, plane)
def roi3 = RoiTools.combineROIs(roi1, roi2, RoiTools.CombineOp.ADD)
def annotation = PathObjects.createAnnotationObject(roi3)
addObject(annotation)
Once you have a BufferedImage, you are already in Java-land and don’t need QuPath-specific documentation for most things.Scripts like this one to create binary images can then help with one major change. Previously, you had to do some awkward gymnastics to convert a ROI
into a java.awt.Shape
object. That’s now easier:
用于从选定的 ROI 的 RGB 图像中提取一个区域,并在 ImageJ 中显示该区域以及新的二进制掩码。
import qupath.lib.regions.*
import ij.*
import java.awt.Color
import java.awt.image.BufferedImage
// Read RGB image & show in ImageJ (won't work for multichannel!)
def server = getCurrentServer() #当前工作
def roi = getSelectedROI() ##选择ROI区域
double downsample = 4.0
def request = RegionRequest.createInstance(server.getPath(), downsample, roi)
def img = server.readRegion(request)
new ImagePlus("Image", img).show()
// Create a binary mask & show in ImageJ #创建掩码
def shape = roi.getShape()
def imgMask = new BufferedImage(img.getWidth(), img.getHeight(), BufferedImage.TYPE_BYTE_GRAY)
def g2d = imgMask.createGraphics()
g2d.scale(1.0/request.getDownsample(), 1.0/request.getDownsample())
g2d.translate(-request.getX(), -request.getY())
g2d.setColor(Color.WHITE)
g2d.fill(shape)
g2d.dispose()
new ImagePlus("Mask", imgMask).show()
如果要在 QuPath 中应用 ImageJ 脚本,最好让 QuPath 负责转换。以下脚本与上述脚本类似,但适用于多通道图像并设置 ImageJ 属性。 它也不会直接创建掩码,而是转换 QuPath ROI,以便可以在 ImageJ 中执行进一步的处理(例如生成掩码)。
import qupath.lib.regions.*
import qupath.imagej.tools.IJTools
import qupath.imagej.gui.IJExtension
import ij.*
// Request an ImageJ instance - this will open the GUI if necessary
// This isn't essential, but makes it it possible to interact with any image that is shown
IJExtension.getImageJInstance()
// Read image & show in ImageJ
def server = getCurrentServer()
def roi = getSelectedROI()
double downsample = 4.0
def request = RegionRequest.createInstance(server.getPath(), downsample, roi)
def pathImage = IJTools.convertToImagePlus(server, request)
def imp = pathImage.getImage()
imp.show()
// Convert QuPath ROI to ImageJ Roi & add to open image
def roiIJ = IJTools.convertToIJRoi(roi, pathImage)
imp.setRoi(roiIJ)
import qupath.imagej.gui.IJExtension
import qupath.imagej.tools.IJTools
import qupath.lib.gui.scripting.QPEx
import qupath.lib.images.servers.ImageServer
import qupath.lib.io.GsonTools
import qupath.lib.objects.PathObjects
import qupath.lib.objects.classes.PathClassFactory
import qupath.lib.objects.classes.PathClassTools
import qupath.lib.regions.ImagePlane
import qupath.lib.regions.RegionRequest
import qupath.lib.roi.ROIs
import qupath.lib.roi.jts.ConverterJTS
import qupath.opencv.tools.OpenCVTools
用脚本进行结果输出Exporting measurements — QuPath 0.5.0 documentation
import qupath.lib.gui.tools.MeasurementExporter
import qupath.lib.objects.PathCellObject
// Get the list of all images in the current project
def project = getProject()
def imagesToExport = project.getImageList()
// Separate each measurement value in the output file with a tab ("\t")
def separator = "\t"
// Choose the columns that will be included in the export
// Note: if 'columnsToInclude' is empty, all columns will be included
def columnsToInclude = new String[]{"Name", "Class", "Nucleus: Area"}
// Choose the type of objects that the export will process
// Other possibilities include:
// 1. PathAnnotationObject
// 2. PathDetectionObject
// 3. PathRootObject
// Note: import statements should then be modified accordingly
def exportType = PathCellObject.class
// Choose your *full* output path
def outputPath = "M:/measurements.tsv"
def outputFile = new File(outputPath)
// Create the measurementExporter and start the export
def exporter = new MeasurementExporter()
.imageList(imagesToExport) // Images from which measurements will be exported
.separator(separator) // Character that separates values
.includeOnlyColumns(columnsToInclude) // Columns are case-sensitive
.exportType(exportType) // Type of objects to export
.filter(obj -> obj.getPathClass() == getPathClass("Tumor")) // Keep only objects with class 'Tumor'
.exportMeasurements(outputFile) // Start the export process
print "Done!"
编写自定义脚本 ·qupath/qupath 维基 (github.com)
Writing custom scripts · qupath/qupath Wiki (github.com)
detections = getDetectionObjects()
print("I have " + detections.size() + " detections") #计算检测对象
annotations = getAnnotationObjects()
print("I have " + annotations.size() + " annotations") #注释对象的数量
构建的一个应用程序是计算具有特定分类的检测次数。为此,我们首先需要获取对分类的引用,然后检查这是否与检测的分类匹配。
肿瘤计数脚本1(局限)
#以下脚本显示了一种执行此操作的方法,用于“肿瘤”分类
tumorClass = getPathClass("Tumor") #细胞分类器中的细胞
nTumor = 0
for (detection in getDetectionObjects()) { #检测ROI
pathClass = detection.getPathClass()
if (pathClass == tumorClass) ##肿瘤分类
nTumor++
}
print("Number of tumor detections: " + nTumor) ##计算肿瘤细胞
以上仅会计数严格等于Tumor分类的细胞,而不会统计Tumor分类下子类别的对象。这是因为它将只对准确分类为“肿瘤”的检测进行计数,而不会对衍生分类“肿瘤:阳性”或“肿瘤:阴性”进行计数。
classification 'Tumor' - but not the derived classifications 'Tumor: Positive' or 'Tumor: Negative'(也就是说对肿瘤进行再分类的就不适合上述代码统计)
肿瘤计数脚本2(完整)
想要实现子类的计数,可以修改代码为:通过使用每个分类中内置的isAncestorOf 方法,将执行检查以查看对象的分类是否等于或源自Tumor分类。
tumorClass = getPathClass("Tumor")
nTumor = 0
for (detection in getDetectionObjects()) {
pathClass = detection.getPathClass()
if (tumorClass.isAncestorOf(pathClass)) #isAncestorOftrue 执行检查以查看对象的分类是否等于肿瘤分类或派生自肿瘤分类
nTumor++
}
print("Number of tumor detections: " + nTumor)
通过使用每个分类中内置的方法,执行检查以查看对象的分类是否等于肿瘤分类或派生自肿瘤分类。如果其中任一情况,该方法将返回。isAncestorOf
true。
如果不使用派生分类,那么两个脚本会给出相同的结果。
通过对最后一个脚本的少量修改,还可以计算非肿瘤分类并确定比例或百分比
tumorClass = getPathClass("Tumor") #应该是训练的细胞分类器
nTumor = 0
nNonTumor = 0
for (detection in getDetectionObjects()) {
pathClass = detection.getPathClass()
if (tumorClass.isAncestorOf(pathClass)) #属于肿瘤分类(包括肿瘤大类)
nTumor++ ##计算肿瘤
else
nNonTumor++ #计算非肿瘤
}
print("Number of tumor detections: " + nTumor)
print("Number of non-tumor detections: " + nNonTumor)
percentageTumor = nTumor / (nTumor + nNonTumor) * 100 ##计算肿瘤分类比例
print("Percentage of tumor detections: " + percentageTumor)
统计每个类别的所有区域的面积,在计算TSR和TIL计算中很有作用。
tumorClass = getPathClass("Tumor")
nTumor = 0
nNonTumor = 0
areaTumor = 0
areaNonTumor = 0
for (detection in getDetectionObjects()) {
roi = detection.getROI() ##检测选定的ROI区域
pathClass = detection.getPathClass()
if (tumorClass.isAncestorOf(pathClass)) { ##计算肿瘤
nTumor++
areaTumor += roi.getArea() ##计算ROI中肿瘤面积
} else {
nNonTumor++ ##计算非肿瘤
areaNonTumor += roi.getArea() ##计算ROI中非肿瘤面积
}
}
print("Number of tumor detections: " + nTumor)
print("Number of non-tumor detections: " + nNonTumor)
percentageTumor = nTumor / (nTumor + nNonTumor) * 100
print("Percentage of tumor detections: " + percentageTumor)
percentageAreaTumor = areaTumor / (areaTumor + areaNonTumor) * 100 ##计算肿瘤上皮面积占比
print("Percentage of tumor area: " + percentageAreaTumor)
请注意,此脚本不会输出区域的绝对值。这是因为,默认情况下,面积值将以像素为单位给出。因此,百分比仍然有意义,但绝对值可能会产生误导,除非它们按像素大小缩放。(前面提到计算TSR时,像素面积对结果影响小)
TSR勾画学习-CSDN博客
QuPathGUI (QuPath 0.5.0) 介绍qupath部分函数
以下是 QuPath 中一般概念的概述:
您的图像可能(并且可能应该)组织在一个Project
项目中的每个图像都由
ProjectImageEntry
当您打开一个 时,您会在查看器中显示一个
ProjectImageEntry
ImageData
商店有一些东西,包括:
ImageData
(例如明场、荧光)
ImageType
任何必需的(如果是明场)
ColorDeconvolutionStains
一个 ,用于访问像素和元数据
ImageServer
A ,包含树状结构
PathObjectHierarchy
PathObjects
每个都包含一个和
PathObject
ROI
MeasurementList
当您在 QuPath 中分析图像时,您需要从 中获取 ,访问像素,并尝试表示图像包含在 .ImageData
ImageServer
PathObjectHierarchy
然后,查询对象层次结构以提取某种汇总度量。
参考:
1:自定义脚本 — QuPath 0.5.0 文档
2:QuPath script_qupath groovy脚本-CSDN博客
3:QuPath学习 ② H&E scripts_qupath 计算肿瘤面积-CSDN博客