[Groovy][QuPath] 章8 QuPathスクリプト入門
既にWorkflowからQuPathスクリプトの雰囲気は分かったと思うが、ここからは自身でスクリプトを改変、新規作成していく。
"プログラミング言語としての変数/オブジェクト"と"QuPathのオブジェクト (アノテーションなどのROIの総称)"で表現が重複しているので、前者を"オブジェクト", 後者を"Object"と記載することにする。また、"Java用語のクラス"と"QuPathのObject分類のために使うクラス"でも表現が重複しているので、前者を"クラス"、後者を"class"と記載することにする。
QuPathスクリプトではGroovy言語で記載するが、QuPathのソースコード自体はJava言語で書かれている。Javaのクラスは、プログラムを実行するためのオブジェクトやそれに関連したメソッドをまとめた大枠と考えてもらうとよい。文字列や数字は、java.lang.String
, java.lang.Integer
のようなJavaクラスとして扱われている。
QuPathコマンドの出力の多くは、qupath.lib.images.ImageData
, qupath.lib.objects.classes.PathClass
のようなQuPath用に定義された特定のクラスのオブジェクトである。それらのクラスにどのような専用のメソッドが用意されているのかは調べながら進めていく。
Script Editor
まずは上部メニューのAutomate
> Show script editor
からScript Editorを立ち上げる。
QuPath Javadocs
Script Editor Help
> Show Javadocs
を押すと、QuPathのjavadocを開くことができる。
QuPath Javadocs
ここからQuPathコマンドの解説やQuPathで定義したJavaクラスの組み込みメソッドを確認することができる。
試しに、「自動化、バッチ処理」の章で登場したcreateAnnotationsFromPixelClassifier
を見てみる。
コマンド例では引数にはどのような型の値を指定すれば良いかが分かる。Parametersでは引数の簡単な解説、Returnsではコマンドの返り値が示されている。
PixelClassifierTools.CreateObjectOptions
の箇所がリンクになっていたので飛んでみると、次のような4つのオプションが使えるのが確認できた。実際には"DELETE_EXISTING"
のようにクオーテーションをつけて使用する。
コマンド例
それでは実際にQuPathコマンドを書いてみる。
QuPathスクリプトはコマンドを1行ずつ進めるのではなく、全実行内容を記載したスクリプトを1度処理することになる。Script Editor右下のRun
かCtr+R
でスクリプトを実行する。
Run
> Run selected code
かCtr+Shift+Rで選択した行だけの処理を進めることもできるが、選択領域だけで完結する内容でないとエラーが出る。
QuPath公式ページのCustom scriptsの項を基に幾つか紹介する。ただ実用的でないものも多いのでここでは画像データやProjectの情報を得るコマンドを紹介する程度に留めて、各章で関連するものを紹介することにする。
まずはQuPathスクリプトとしての実行例を示したうえで、各コマンドの解説を記す。深い理解は必要無いので解説は読み飛ばしても構わない。ただ各コマンドはどの型のオブジェクトに対して使えるのか、コマンドの出力はどんな型のオブジェクトなのか調べながら進める習慣をつければ、自身で自由なスクリプトを書くことができるようになる。
画像の情報を取得 ImageDataクラス
画像を開いた状態で以下を行う。
def imageData = getCurrentImageData()
print imageData
こちらでも同じ
def viewer = getCurrentViewer()
def imageData = viewer.getImageData()
print imageData
返り値はImageDataクラスのオブジェクト。画像の主な情報を含んでいる。
ImageDataクラスに対して行えるメソッドを確認してみる。
describe(getCurrentImageData())
ImageDataクラスの組み込みメソッド
INFO: qupath.lib.images.ImageData
Methods:
void addPropertyChangeListener(PropertyChangeListener)
ColorDeconvolutionStains getColorDeconvolutionStains()
PathObjectHierarchy getHierarchy()
Workflow getHistoryWorkflow()
ImageType getImageType()
String getLastSavedPath()
Map getProperties()
Object getProperty(String)
ImageServer getServer()
String getServerPath()
void hierarchyChanged(PathObjectHierarchyEvent)
boolean isBrightfield()
boolean isChanged()
boolean isFluorescence()
Object removeProperty(String)
void removePropertyChangeListener(PropertyChangeListener)
void setChanged(boolean)
void setColorDeconvolutionStains(ColorDeconvolutionStains)
void setImageType(ImageType)
void setLastSavedPath(String, boolean)
Object setProperty(String, Object)
String toString()
void updateServerMetadata(ImageServerMetadata)
void workflowUpdated(Workflow)
ImageDataクラスのオブジェクトに対して、get〇〇()
シリーズを使えば様々な情報が取り出せるのが分かる。
またset〇〇()
とあるように、画像の情報をスクリプトから登録することができる。
def imageData = getCurrentImageData()
imageData.setImageType(BRIGHTFIELD_H_DAB) \\ 他には次のものが選べる。 BRIGHTFIELD_H_E, BRIGHTFIELD_OTHER, FLUORESCENCE, OTHER, UNSET
画像のメタデータを取得
server情報
QuPathスクリプトではserverという情報をよく扱う。serverと言われてもイメージしにくいがjavadocを見ると、ピクセルやメタ情報のkey interfaceとのことである。
実際にデモデータでserver情報を見てみると、OpenslideImageServerを使っていることが分かる。
def imageData = getCurrentImageData()
def server = imageData.getServer()
print server
このようにしてもserver情報が得られる。
def server = getCurrentServer()
print server
serverオブジェクトに対して行えるメソッドを確認してみる。
describe(getCurrentServer())
OpenslideImageServerの組み込みメソッド
INFO: Result: qupath.lib.images.servers.openslide.OpenslideImageServer
Methods:
void close()
String dumpMetadata()
BufferedImage getAssociatedImage(String)
Object getAssociatedImage(String)
List getAssociatedImageList()
ServerBuilder getBuilder()
Object getCachedTile(TileRequest)
ImageChannel getChannel(int)
Object getDefaultThumbnail(int, int) throws IOException
double getDownsampleForResolution(int)
int getHeight()
Class getImageClass()
ImageServerMetadata getMetadata()
ImageServerMetadata getOriginalMetadata()
String getPath()
PixelCalibration getPixelCalibration()
PixelType getPixelType()
double[] getPreferredDownsamples()
String getServerType()
TileRequestManager getTileRequestManager()
Collection getURIs()
int getWidth()
boolean isEmptyRegion(RegionRequest)
boolean isRGB()
int nChannels()
int nResolutions()
int nTimepoints()
int nZSlices()
BufferedImage readRegion(RegionRequest) throws IOException
Object readRegion(RegionRequest) throws IOException
Object readRegion(double, int, int, int, int, int, int) throws IOException
Object readRegion(double, int, int, int, int) throws IOException
BufferedImage readTile(TileRequest) throws IOException
void setMetadata(ImageServerMetadata)
String toString()
getWidth()
やgetHeight()
は画像のピクセルサイズを調べるメソッドである。その他、解像度やデータ型、ファイルパスなど、画像を読み込む際に関連した情報がserverオブジェクトには含まれている。
serverオブジェクトからgetMetadata()
で得られる情報は代表的な情報をまとめたものが得られる。
後々登場するが、readRegion()
はserverオブジェクトから一部の領域を読み込む際に使用する。
projectに対する操作
QuPath上で現在開いている画像に対する処理だけでなく、projectに登録されている他の画像にアクセスする。
見た目には画像を開いていなくても、処理の中で画像を開いてその情報を取得することができる。
Project内の画像名を表示
\\ Projectの情報を変数projectに保存
def project = getProject()
\\ project変数から画像の一覧を作成し、1要素ずつの画像名を表示する。
for (entry in project.getImageList()) {
print entry.getImageName()
}
現在のprojectに登録されている画像名が表示される。
解説
- getProject()でQuPath project情報を取得
getProject機能の引数説明
出力はqupath.lib.projects.DefaultProject
getProject()の出力に対して行える組み込みmethod
describe(getProject())
INFO: Result: qupath.lib.projects.DefaultProject
Fields:
public static final java.lang.String qupath.lib.projects.DefaultProject.IMAGE_ID
Methods:
ProjectImageEntry addDuplicate(ProjectImageEntry, boolean) throws IOException
ProjectImageEntry addImage(ServerBuilder) throws IOException
Project createSubProject(String, Collection)
long getCreationTimestamp()
ProjectImageEntry getEntry(ImageData)
List getImageList()
boolean getMaskImageNames()
long getModificationTimestamp()
String getName()
Manager getObjectClassifiers()
Path getPath()
List getPathClasses()
Manager getPixelClassifiers()
URI getPreviousURI()
Manager getResources(String, Class, String)
Manager getScripts()
URI getURI()
String getVersion()
boolean isEmpty()
void removeAllImages(Collection, boolean)
void removeImage(ProjectImageEntry, boolean)
void setMaskImageNames(boolean)
boolean setPathClasses(Collection)
int size()
void syncChanges() throws IOException
String toString()
組み込みメソッドのうち幾つか試してみる。
// projectの名前
print getProject().getName()
// projectが空かどうか
print getProject().isEmpty()
// projectの登録数
print getProject().size()
// projectの画像リスト取得
print getProject().getImageList()
- 画像リストを作成し、for文で1要素ずつ取り出す
getProject().getImageList()
の出力は上記のようにリスト型。for文で1要素を取り出し、entryという変数に入れる。
- リストから取り出した要素から画像名を取得
getImageList()
の出力はリストだが、リストの要素は単純な文字列ではなく、qupath.lib.projects.DefaultProject$DefaultProjectImageEntry
というクラス。
DefaultProject$DefaultProjectImageEntryの組み込みメソッド
describe(qupath.lib.projects.DefaultProject$DefaultProjectImageEntry)
INFO: Result: qupath.lib.projects.DefaultProject$DefaultProjectImageEntry
Methods:
void clearMetadata()
boolean containsMetadata(String)
String getDescription()
Path getEntryPath()
String getID()
String getImageName()
Manager getImages()
Collection getMetadataKeys()
Map getMetadataMap()
String getMetadataSummaryString()
String getMetadataValue(String)
String getOriginalImageName()
ServerBuilder getServerBuilder()
String getSummary()
Object getThumbnail() throws IOException
BufferedImage getThumbnail() throws IOException
Collection getURIs() throws IOException
Collection getUris() throws IOException
boolean hasImageData()
String putMetadataValue(String, String)
PathObjectHierarchy readHierarchy() throws IOException
ImageData readImageData() throws IOException
String removeMetadataValue(String)
void saveImageData(ImageData) throws IOException
void setDescription(String)
void setImageName(String)
void setThumbnail(Object) throws IOException
void setThumbnail(BufferedImage) throws IOException
String toString()
boolean updateURIs(Map) throws IOException
boolean updateUris(Map) throws IOException
print getProject().getImageList()[0].getID()
print getProject().getImageList()[0].getImageName()
print getProject().getImageList()[0].getSummary()
最終的なgetImageName()
の出力は文字列
Project内の画像名とその画像データに含まれるアノテーションの数を表示
\\ Projectの情報を変数projectに保存
def project = getProject()
\\ project変数から画像の一覧を作成し、1要素ずつのentry変数へ渡す。
for (entry in project.getImageList()) {
\\ 画像データを読み込む
def imageData = entry.readImageData()
\\ ヒエラルキー情報を取得
def hierarchy = imageData.getHierarchy()
\\ ヒエラルキー情報からアノテーションの情報を取得 リスト型の戻り値
def annotations = hierarchy.getAnnotationObjects()
\\ 画像名とアノテーションリストのサイズを表示
print entry.getImageName() + '\t' + annotations.size()
}
解説
- QuPath project情報。
getProject()
--> qupath.lib.projects.DefaultProject
- 画像リストを作成し、for文で1要素ずつ取り出す
--> qupath.lib.projects.DefaultProject$DefaultProjectImageEntry
- 画像データを取得する
readImageData()
INFO: Result: qupath.lib.images.ImageData
Methods:
void addPropertyChangeListener(PropertyChangeListener)
ColorDeconvolutionStains getColorDeconvolutionStains()
PathObjectHierarchy getHierarchy()
Workflow getHistoryWorkflow()
ImageType getImageType()
String getLastSavedPath()
Map getProperties()
Object getProperty(String)
ImageServer getServer()
String getServerPath()
void hierarchyChanged(PathObjectHierarchyEvent)
boolean isBrightfield()
boolean isChanged()
boolean isFluorescence()
Object removeProperty(String)
void removePropertyChangeListener(PropertyChangeListener)
void setChanged(boolean)
void setColorDeconvolutionStains(ColorDeconvolutionStains)
void setImageType(ImageType)
void setLastSavedPath(String, boolean)
Object setProperty(String, Object)
String toString()
void updateServerMetadata(ImageServerMetadata)
void workflowUpdated(Workflow)
- 画像データからヒエラルキー情報を取得
getHierarchy()
ヒエラルキーはオブジェクト (アノテーションやCell, Detectionも含む)の階層情報などを含んだPathObjectHierarchyクラスを指す。階層だけでなく画像データのオブジェクトに関する情報はPathObjectHierarchyクラスのデータにまとめられていると想像するとよい。
またここにオブジェクトを追加して、画像データに反映させることもできる。
getHierarchy()の戻り値のclassと組み込みメソッド
Fields:
public static final java.util.Comparator qupath.lib.objects.hierarchy.PathObjectHierarchy.HIERARCHY_COMPARATOR
Methods:
void addListener(PathObjectHierarchyListener)
boolean addObject(PathObject)
boolean addObject(PathObject, boolean)
boolean addObjectBelowParent(PathObject, PathObject, boolean)
boolean addObjects(Collection)
void clearAll()
void fireHierarchyChangedEvent(Object)
void fireHierarchyChangedEvent(Object, PathObject)
void fireObjectClassificationsChangedEvent(Object, Collection)
void fireObjectMeasurementsChangedEvent(Object, Collection)
void fireObjectMeasurementsChangedEvent(Object, Collection, boolean)
void fireObjectsChangedEvent(Object, Collection)
void fireObjectsChangedEvent(Object, Collection, boolean)
Collection getAllObjects(boolean)
Collection getAnnotationObjects()
Collection getCellObjects()
Collection getDetectionObjects()
List getFlattenedObjectList(List)
Collection getObjects(Collection, Class)
Collection getObjectsForROI(Class, ROI)
Collection getObjectsForRegion(Class, ImageRegion, Collection)
Collection getPointObjects(Class)
PathObject getRootObject()
PathObjectSelectionModel getSelectionModel()
TMAGrid getTMAGrid()
Collection getTileObjects()
boolean hasObjectsForRegion(Class, ImageRegion)
boolean insertPathObject(PathObject, boolean)
boolean insertPathObjects(Collection)
boolean isEmpty()
int nObjects()
void removeListener(PathObjectHierarchyListener)
boolean removeObject(PathObject, boolean)
boolean removeObjectWithoutUpdate(PathObject, boolean)
void removeObjects(Collection, boolean)
void resolveHierarchy()
void setHierarchy(PathObjectHierarchy)
void setTMAGrid(TMAGrid)
String toString()
void updateObject(PathObject, boolean)
- ヒエラルキー情報からアノテーションの情報を取得
getAnnotationObjects()
オブジェクトのうちアノテーションオブジェクトを取得する。戻り値はアノテーションが1つであってもリスト型。
その要素はPathAnnotationObject
というクラス。今後もこのクラスの操作はよく登場する。
getAnnotationObjects()の戻り値の1要素のclassと組み込みメソッド
INFO: Result: qupath.lib.objects.PathAnnotationObject
Methods:
void addChildObject(PathObject)
void addChildObjects(Collection)
void clearChildObjects()
Collection getChildObjects(Collection)
Collection getChildObjects()
PathObject[] getChildObjectsAsArray()
double getClassProbability()
Set getClassifications()
Integer getColor()
Collection getDescendantObjects(Collection)
String getDescription()
String getDisplayedName()
UUID getID()
int getLevel()
MeasurementList getMeasurementList()
Map getMeasurements()
String getName()
PathObject getParent()
PathClass getPathClass()
ROI getROI()
boolean hasChildObjects()
boolean hasMeasurements()
boolean hasROI()
boolean isAnnotation()
boolean isCell()
boolean isDetection()
boolean isLocked()
boolean isRootObject()
boolean isTMACore()
boolean isTile()
int nChildObjects()
int nDescendants()
void readExternal(ObjectInput) throws IOException, ClassNotFoundException
void refreshID()
void removeChildObject(PathObject)
void removeChildObjects(Collection)
boolean resetPathClass()
void setClassifications(Collection)
void setColor(int, int, int)
void setColor(Integer)
void setDescription(String)
void setID(UUID) throws IllegalArgumentException
void setLocked(boolean)
void setName(String)
void setPathClass(PathClass, double)
void setPathClass(PathClass)
void setROI(ROI)
String toString()
void writeExternal(ObjectOutput) throws IOException
- 画像名とアノテーションリストのサイズを表示
上記の出力はリスト型である。size()
はリストの要素数を調べるメソッド。つまりアノテーションが幾つあったかを調べている。
readImageData()
は画像データを開いている分処理時間がかかるらしい。以下のようにすればヒエラルキー情報を画像データを開かずに取得でき高速化が図れる。
def project = getProject()
for (entry in project.getImageList()) {
def hierarchy = entry.readHierarchy()
def annotations = hierarchy.getAnnotationObjects()
print entry.getImageName() + '\t' + annotations.size()
}
readImageDataあり -> 2.38秒 | readImageDataなし -> 0.03秒 |
---|---|
![]() |
![]() |
このように最終的に欲しい情報に辿り着くのに複数通りのやり方がある。それに伴って、似たようなメソッドが沢山存在する。どのメソッドを使ってもよいが、高速化を目指すなら中間に重たい処理、重たい変数の保持を減らすことを心掛けるとよい。
Package
コマンドを検索する際に、コマンド名の前にそのコマンドが含まれているパッケージ名が記されている。
setImageType()
コマンドはqupath.lib.imagesパッケージに含まれている。
パッケージはimportコマンドで読み込んでから使用する。
QuPathではメインのパッケージはデフォルトで読み込まれているので、パッケージを意識せずに使用しているが、importしないと使用できないものもある。例えばImageJやOpenCVのコマンドをQuPathから使うには明示的にimportする必要がある。
ちなみにScript EditorのRun
からInclude default imports
のチェックを外すと、setImageType
はそんなコマンド見つかりませんとエラーが出る。
import qupath.lib.images.ImageData.*
のようにsetImageType()
が含まれるパッケージを明示的にimportしてやると、setImageType()
が使用できるようになる。
*
はqupath.lib.images内のコマンド全てをimportしている。setImageType()
はqupath.lib.imagesパッケージのImageDataクラスの組み込みmethodなので、import qupath.lib.images.ImageData
のようにしても使用できるようになる。
Discussion