QuPath script

QuPath腳本是Groovy

QuPath腳本是基於Groovy創建的。選擇Groovy是因爲Groovy具有很多新特性,同時又與QuPath本身所使用的Java編程語言非常匹配。熟悉Java語言的程序員應該很快就可以輕鬆地學習Groovy。

除了Groovy之外,還可以編寫其他腳本語言。要切換到Javascript,只需打開腳本編輯器,然後選擇Language(語言)→ Javascript

另外,還可以使用python、matlab或ImageJ宏語言

腳本編輯器示例

接下來介紹如何使用QuPath的腳本編輯器創建一些簡單的腳本。

要進行後續操作,應該打開圖像並創建幾個標註,並對某些單元進行檢測和分類。

統計對象

打開腳本編輯器(Automate → Show script editor),輸入以下代碼,然後選擇Run → Run。

n = nObjects()
print("I have " + n + " objects in total")

這裏不過多解釋Groovy語言的內容,如果想要了解可以自行google,我們將重點介紹QuPath中腳本的細節,nObjects() 函數是QuPath提供的內置函數,返回當前圖像中對象的個數。另外,腳本編輯器具有自動完成的功能,通過ctrl+space快捷鍵即可調用(不過我的電腦上好像沒有效果)。

統計不同類型的對象

以下代碼可以分別計算檢測對象和標註對象的數量:

detections = getDetectionObjects()
print("I have " + detections.size() + " detections")
annotations = getAnnotationObjects()
print("I have " + annotations.size() + " annotations")

循環

我們可以使用循環來遍歷特定類型的對象,例如以下腳本遍歷所有的標註對象並打印該對象。瞭解更多for循環的信息,可以查看Groovy的文檔

for (annotation in getAnnotationObjects()) {
    print(annotation)
}

或者,可以打印標註的某些特定屬性。以下代碼僅僅打印ROI,而不是對象本身:

for (annotation in getAnnotationObjects()) {
    roi = annotation.getROI()
    print(roi)
}

而以下代碼可以打印標註的分類:

for (annotation in getAnnotationObjects()) {
    pathClass = annotation.getPathClass()
    print(pathClass)
}

類別計數

根據以上的代碼,我們可以編寫一段代碼來實現特定分類的標註的數量統計,首先獲取某一類型對象,然後遍歷每一個標註對象,將標註對象的類型與特定分類類型進行對比,看代碼:

tumorClass = getPathClass("Tumor")
nTumor = 0
for (detection in getDetectionObjects()) {
    pathClass = detection.getPathClass()
    if (pathClass == tumorClass)
      nTumor++
}
print("Number of tumor detections: " + nTumor)

不過在實際情況中,上面的代碼可能存在一些問題。這是因爲它僅會計數嚴格等於Tumor分類的細胞,而不會統計Tumor分類下子類別的對象。想要實現子類的計數,可以修改代碼爲:

tumorClass = getPathClass("Tumor")
nTumor = 0
for (detection in getDetectionObjects()) {
    pathClass = detection.getPathClass()
    if (tumorClass.isAncestorOf(pathClass))
      nTumor++
}
print("Number of tumor detections: " + nTumor)

通過使用每個分類中內置的isAncestorOf 方法,將執行檢查以查看對象的分類是否等於或源自Tumor分類。

計算百分比

對上面代碼稍作修改,我們就可以實現對非腫瘤區域計數並計算百分比,看代碼:

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)

面積統計

在某些情況下,我們可能需要統計每個類別的所有區域的面積和,以下代碼實現了腫瘤區域的面積統計:

tumorClass = getPathClass("Tumor")
nTumor = 0
nNonTumor = 0
areaTumor = 0
areaNonTumor = 0
for (detection in getDetectionObjects()) {
    roi = detection.getROI()
    pathClass = detection.getPathClass()
    if (tumorClass.isAncestorOf(pathClass)) {
      nTumor++
      areaTumor += roi.getArea()
    } else {
      nNonTumor++
      areaNonTumor += roi.getArea()
    }
}
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)

注意,以上代碼統計出來的面積是以像素爲單位的,而不是實際的物理面積。

附加技術點

如果希望瞭解QuPath腳本的設計和工作原理可以繼續閱讀以下內容,相反,如果這是希望在QuPath中簡單使用一下腳本,就可以不必向下閱讀了。

默認方法和導入

以上內容中,使用了幾種特定於QuPath的方法,這些方法在普通的Groovy代碼中是不可用的,但是我們在使用的時候,並不需要在任何地方聲明或導入它們。

這使得在QuPath中編寫腳本有點像使用自定義的宏語言,而不是簡單地使用Groovy。這樣做的目的是試圖幫助不熟悉腳本的用戶以最小的學習成本來解決問題。

但是,這些默認方法從何而來,QuPath如何知道它們的存在?

QP和QPEx中的靜態方法

這些方法是在以下這兩個Java類中實現的:

可以閱讀這兩個代碼文件,更進一步地瞭解這些方法。

QP.java 包含不依賴用戶界面的方法,而QPEx.java 包含qp.java中的所有代碼,還包含一些依賴界面的方法。在使用過程中可直接使用qpex.java中的方法。

導入靜態方法

使用腳本編輯器運行腳本的時候,QuPath會自動導入qp和qpex中的方法。

但是,在發現默認的導入與編寫腳本有某種衝突的時候,我們可以禁用自動導入的功能,在 Run → Include default bindings 設置,禁用後,我們需要手動導入QP或QPEx:

import qupath.lib.scripting.QP
import qupath.lib.scripting.QPEx

參考文檔

https://github.com/qupath/qupath/wiki/Writing-custom-scripts

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章