2018年12月24日

使用imagej的plugin TrackMate追蹤細胞

最近一個網友來信問用imagej追蹤細胞的問題,他的材料是一群T cell。在fiji的plugin中,有數個做tracking的plugin,設定最簡單的應該是MTrack 2,不過我一直沒有用這個東西做成功,而且MTrack2的網路資料也好少,很難找到參考文章。

所以後來我用一個設定很複雜的plugin,叫做TrackMate。雖然設定很複雜,但是還好有夠多的文章可以看怎麼做。例如這個 Getting started with TrackMate

做細胞或是物體追蹤的流程,大致上是這樣,要先偵測追蹤的物體在哪,然後把不同frame的同一物體辨識出來成為一個track。

其中還會遇到一些問題,比方說有些物體在某些frame會消失或是快速移動,想像一下拍攝車輛的影片,可能車輛會突然加速或被遮擋。或是某些物體會在途中突然變成兩個,例如細胞分裂的時候,從一個細胞變成兩個細胞。上述這些問題在TrackMate中,都有解決的方式,不過以下的教學影片就沒講到這個東西,反正還沒遇到。


在影片中會提到偵測物體的演算法,有幾個如下。不同的方法偵測的物體,各有所長,也些適合偵測大的,像是The Downsample LoG detector,而DoG detector就適合偵測小的。

  1. DoG detector:對於小尺寸(5 pixels)的追蹤很快
  2. LoG detector:5~20 pixels
  3. The Downsample LoG detector:大於20 pixels
詳細的處理就請看影片

小魚尾鰭血流觀察,印出尾鰭畫流向。

這幾年做小魚尾鰭血流觀察活動所建立的課程流程,我覺得還不錯。

  1. 讓學生拿手撈網在校園水池中自己撈小魚
  2. 小魚放在封口袋中保定
  3. 自製的倒立顯微鏡讓學生用iPad觀察與錄影紀錄血液流動
  4. 小魚放回水池後,再利用錄影放大與回放的功能,進一步看仔細血液流動

今年再多做一件事,讓學生把影片放大截圖印出來,然後對照影片,直接在印出的紙上標記血流的方向和位置。其實這是有點難度的喔,學生在這個活動中得要很用心才能找到目標的血管,並且透過回放檢視來看仔細這些細微構造,對於培養學生的專心與耐心是很有幫助的呢。










用3D列印模型學習心臟結構

在學習心臟構造的時候,學生有一個學習困難點就是看不懂圖。雖然課本上的圖畫得很詳細,但就是因為太詳細才會看不懂。

這張圖是以前我用inkscape畫的,已經懂心臟構造的人看這圖片當然看得懂,但是對於學習的新手而言有一定的理解困難之處,我認為其中一個原因是「立體結構變成平面」所造成。

克服這樣的困難點也不是沒辦法,我的話就會先從心臟的簡圖開始教起(一個愛心切成四個房間...),有興趣的可以看我給學生看的這個教學影片

heartDraw


認得心臟結構,然後呢?以往我可能就會帶學生示範解剖豬心或雞心
豬心解剖 http://youtu.be/O2ZZC3xuvn8 12:45
雞心解剖 http://youtu.be/Xuv_KQoql44 06:25
解剖熟豬心的切法 https://youtu.be/0qTGV5GSAdw 03:36

但是後來我總覺得在學習心臟結構之後,應該還可以有個什麼動手的活動,讓學生能知道心臟到底內部是怎樣的,畢竟實際內臟還是有點不是那麼容易懂。

這樣的想法在心裡頭留著,可是還是沒有具體想法,所幸在thingiverse這個蒐羅了許多3D模型檔案的網站上看到了這個 Anatomical Heart,作者說這是透過MRI實際掃瞄出的影像建出的模型,然後有經過清理和簡化的。我印了一組,沒想到花了超多時間。這個心臟共切三塊,每一塊就花了我六七個小時,所以一顆心臟印下來得花上兩天的時間。印了這個多,就知道花了數週的時間了。



那麼這些心臟怎麼在教學上使用呢?我是讓學生拿著電線或迴紋針,讓他們從靜脈穿入心臟後,看看線可以穿到心房然後到心室,再從動脈出來。

不過一開始要教學,當然還是要個別化教學囉




學生就拿著iPad播放我的影片,然後一邊看一邊學。他們要準備貼紙寫上左心房、左心室...等,然後先認識心臟的腔室後,貼上貼紙,再用線穿來穿去,找到動脈、靜脈...。附帶一提的是,線材建議用的是多芯線,這種才夠軟,可以在腔室裡轉來轉去。








學生看完影片,插完心臟也不是沒事,他們得再用iPad錄製一段解說影片才行。目的很簡單,看是一回事,但是要能講才是真的學會。




他們的影片再一起用同一帳號上傳到youtube
這就是成果的播放清單了





2018年11月27日

用imagej的z project分析小魚尾鰭血液流動影片,...

數週前第一次用imagej的Z project的standard deviation模式做影片疊圖,覺得在處理影片上能夠看到很特別的資訊。(那次的經驗是處理小p的影片,請見此文章

這個standard deviation的特色是讓影片變動的部份變得明顯,而不變的部份則不明顯。

最近讓學生拍小魚尾鰭的血液流動影片時,突然想起可以用imagej來分析看看,也許可以血管抓出來?不過這次分析的影片是用一年前拍攝的孔雀魚尾鰭血液流動,可見微血管


流程就如上次做《用Imagej疊圖看水波干涉的腹線(antinodal line)》那樣,到最後一步選的是standard deviation。

影片長度很長,代表影格也很多,不過並不是每個影格都適合拿來做處理,因為我需要的穩定拍攝的畫面,也就是魚鰭不動,純粹只有血液流動的畫面。

以下分別是取出100張、500張、1000張、2000張影格疊出的畫面。從100張影格疊出的畫面,的確很容易就看到近乎平行的數條血管,而且也能夠看出橫向的微血管。隨著疊加的影格越多,我發現血管之外的好幾個圓圈圈也越來越明顯,意味著在長時間的變化下(約1分鐘),那個圓圈圈是會變動的!


100 frames

500 frames

1000 frames

2000 frames


欸?引發我的好奇心,那到底是什麼?回去影片仔細觀察,原來是魚鰭上的色素細胞。在觀察血液的時候,根本就沒有留意到這個構造是會改變形狀的。當我再仔細觀察影片,才發現它們可真的會變大變小的,而且有件事讓我疑惑,本來我以為所有的色素細胞改變會是一致的,不過仔細去看才發現是有的會變大,有的會變小,看來有些事情可以拿來研究研究了。

2018年11月21日

用OpenSCAD設計聽診器的耳塞頭

也不知道是什麼神秘力量,是學生的耳朵裡長牙齒嗎?聽診器插入耳朵的部分居然會爆裂?
一時也沒想到去哪買這個頭?我想是可以去買耳機的耳機套來替換啦,不過短期之間就先用3D印表機來印一個吧。


使用OpenSCAD來寫程式生成模型吧



OpenSCAD的Code如下,放入OpenSCAD之後再存出stl檔就可以拿去印了。用pla印出的東西好不好塞入耳呢?坦白說印出來的東西硬硬的,短暫使用算是可以,但是耳朵會有點不舒服,也許改用軟料列印會好一點。



difference(){
    translate([0,0,0.5])
    rotate_extrude(covexity= 10,$fn=100){
        translate([1,0.5])
        minkowski(){
        polygon(points=[[0,0], [2,0],[4,2],[2,11],[0,11]]);
        circle(r=1);   
        }
    }
    cylinder($fn = 100,h=14,r1=2.5,r2=2.5);
}

2018年11月17日

imagej分離色頻後選取區域的方法

附圖是一個放射醫學的研究生問的問題,想要從這張圖裡把綠色的弧形區域選取出(第二張圖那樣的區域)

這樣的問題分成幾個部份:
1.怎麼圈出綠色的框框?
2.圈出綠色框之後,中間的空洞怎麼填滿?




第一個問題就用threshold就行,不過像這類黑白圖上再套疊彩色的圖,我發現把圖片轉成Lab stake的效果會比較好。(請看影片示範)

而第二個問題,會用到幾個二值化影像處理的指令,像是Dilate膨脹、Erode侵蝕、Fill Hole填洞或是Despeckle去雜點。(一樣請看影片示範)

最後得到的二值化影像(如上面第二張圖,就可以用make selection去轉成選取區)


2018年11月5日

用imagej加手機前鏡頭偵測脈搏






有很多手機app都可以利用手機的前後鏡頭來偵測脈搏,只要手指蓋住鏡頭,App可以自動偵測出脈搏。其原理是計算每幀影格的像素強度變化,可能做些濾波和數值處理。

其實好幾年前就想自己寫個程式來偵測看看,但是當時對於電腦視覺根本不熟,現在想想其實imagej就可以做處理了。

讀入影像
1.使用fiji,先在Help/Update..,更新imagej,然後在Imagej Updater的視窗左下角選擇Manager Update sites,勾選FFMPEG,然後Apply Changes。這會加入一個匯入mp4影像的外掛。安裝後重新啟動fjij



2.開啟fiji,在File/Import/Movie(ffmpeg),把拍攝的影片匯入成stack。
3.由於影片的前後不一定是需要的部份,比方說還沒拍到手指,或是手指已經離開。因此需要剪接stack。不過imagej對於stack內的slices刪除不是很好用(或者是說我還沒找到好方法),所以建議是用複製影格的方式來做,比方說第10影格到第90影格才是需要的部份,那就在Image/Duplicate...把要的影格複製出來成一個stack。

繪製像素強度變化圖(即脈搏圖)
1.利用方框選取工具,然後選擇Image/Stack/Plot z-axis profile,即可以畫出如下圖的脈搏圖




如果不嫌費工夫,也可以把stack在分離出不同色頻來分析,例如這個是分析R Channel的強度變化,雜訊會少很多。方法有點複雜
1.利用Image/Type/RGB Stack,可以把原始的stack分離出不同的色頻,然後再用Image/Stack/Plot z-axis profile來繪製。




同樣也可以分離出HSB,這個是亮度B( brightness )的圖



以下是處理過程的教學影片

2018年10月30日

用Imagej疊圖看水波干涉的腹線(antinodal line)

前幾天看到小p拿出他的兵器-做水波干涉實驗的兵器,錄製了一段影片。突然我想到我在這篇《imagej將時間資料視覺化》寫到imagej可以將時間資料濃縮,可以從中看出特別的時間資訊。

一時手癢就來把小p拍攝的影片用imagej疊圖分析看看。

1.先用youtube-dl把影片用mp4格式下載回來。
2.用fiji(特製的imagej)的file/import/Movie(FFMPEG)將mp4檔讀成stacks
3.用Image/Stacks/Z Project製作疊圖

以下分別是用Min intensity和standard deviation製作的疊圖






Min intensity是把影像中每個隨著時間變化的像素,用最小的數值(相當於最暗的)去疊出來。

而standard deviation的圖我是第一次用這疊,發現還蠻有趣的。疊出來的影像中,黑色的部份代表標準差很小,意思就是數值變動不大,所以在影片中沒什麼變化的點就會呈現黑色。而動得越厲害的部份,就會越彩色。所以左方的水波產生器顏色就很彩色。

把影片做成疊圖來分析,在看水波干涉時產生的腹線(antinodal line),如中央腹線、第一腹線、第二腹線等可是特別明顯呢。

2018年10月28日

使用imagej的Macro切割細胞與細胞追蹤



來自網友的這個提問,其實目的是要做細胞追蹤,要處理的是細胞分裂的影片。希望能將A細胞在每個影格都能定位出來,網友也提供了判斷邏輯。將第一影格的A細胞和第二影格的兩個細胞都做交集處理,看看和第二影格的哪個細胞交集的比較多,就可以找到第二影格的A細胞在哪。

由於之前沒有寫過類似的Macro,所以也看了幾次說明文件,還參考了其他人寫的Macro,總算是寫出來了。要處理這個問題之前,其實得先把細胞切割好,還好網友已經先切好了給我處理。



影片中用的Macro我貼在本文最後,使用前提是要用wand tool先點選一個細胞,加入ROI Manager之後,再去執行Macro。

雖然網友已經提供給我切割好的細胞輪廓,不過我也還是手癢想處理看看這個影片,想說有沒有辦法可以把細胞切割好,不過我試了好幾個方法都切不好。最後只好用半自動的方法來做。

方法是先手動在細胞分界處劃線,存成ROI,再用這個ROI去切割開細胞。具體的作法就看影片囉



*********************************


setBatchMode(true);
setAutoThreshold("Default");
width = getWidth;
height = getHeight;

n = getSliceNumber();



for (i=n; i<=nSlices-1; i++) {
//for (i=n; i<=n; i++) {
print("==========");
print("i=",i);
roiMan_n = roiManager("count");
print("roiMan_n=",roiMan_n);

run("Select None");
setSlice(i+1);

run("Analyze Particles...", "display add slice");
roiMan_n_new = roiManager("count");
print("roiMan_n_new=",roiMan_n_new);

//計算下一張Slice增加了多少個ROI
roi_add = roiMan_n_new - roiMan_n ;
print("roi_add=",roi_add);

roiManager("Show None");

AND_Big_area= 0; //預設取交集後的面積為零
AND_Big_area_n = 0 ;//預設取交集後的面積,最大的是第零個

for (j=1; j<=roi_add; j++) {
roiManager("Select", newArray(roiMan_n-1,roiMan_n-1+j));
roiManager("AND");
getStatistics(area);
print("j=",j,",area=",area);

//沒交集的面積會回報成整張圖片的面積,所以將回報面積設為0
if(area == width * height){
area =0;
}

if(area>AND_Big_area){
AND_Big_area = area;
AND_Big_area_n = j;
}
}
print("Big=",AND_Big_area_n);
print("AND_Big_area=",AND_Big_area);

//select biggest area and add the new ROI in the next slice
roiManager("Select", roiMan_n -1 + AND_Big_area_n);
roiManager("Add");

//delete all new ROI in the next slice
for (j=1; j<=roi_add; j++) {
roiManager("Select", roiMan_n-1+1);
roiManager("Delete");
}

}

resetThreshold();
setBatchMode(false);

使用imagej將影像中的維管束群組化

最近一個網友來信問到的問題,是和影像群組化有關的。這方向的問題也是很多提問的人會問到的,不如在影片中詳細解說一下,有此疑惑的也可以藉此練習看看。


就以下面這圖為例,大方塊和小方塊在做Analyze Particles時,會被視作是不同的兩個Particle,但是如果我們的需求是要把這兩個圈在同一個ROI,那應該怎麼做?



什麼時候會有這種情況呢?來看看這個例子,這是竹子維管束的圖片,右圖是進行threshold之後,維管束分離的樣子,而在分析的時候,我們需要將那些分離的小碎片視作是一個完整的Particle。


以下四部影片就是在介紹這些步驟

第一部影片:imagej 將分散的小區塊用二值化影像處理流程做群組1 二值化處理的功能說明
是進行二值化處理的教學流程,介紹erode、dilate等程序的作用

第二部影片:imagej 將分散的小區塊用二值化影像處理流程做群組2 一步一步說明
利用上述的二值化程序一步一步說明,如何將影像中分散的小顆粒在分析時處理成一個完整的部份。


第三部影片:imagej 將分散的小區塊用二值化影像處理流程做群組3 用程式碼加快處理流程
將第二部的步驟化處理程序寫成Imagej的Macro程式碼。程式碼的檔案我放在雲端硬碟,文字我也貼在本文的最後。



第四部影片:imagej用Macro將影像的ROI都各自存圖
將分析後的ROI各自存成圖,然後再組成一個Montage
結果會如這張圖

程式碼比較簡單一些,我直接貼在這邊
***********************
path = "/home/shawn-pc/桌面/crop/";
//path="c:/users/user/Desktop/crop/";
fileTitle=getTitle; 

for (i=0; i<roiManager("count"); ++i) {
    roiManager("Select", i);
    roiname = call("ij.plugin.frame.RoiManager.getName", i);
    run("Duplicate...", "title=crop");
    saveAs("Jpeg", path + roiname +".jpeg");
    close();
    //Next round!
    selectWindow(fileTitle);
}

***********************


第三部影片提到的程式碼,則是這樣
*************************
//input = "C:/Users/user/Desktop/roi/";
input = "/home/shawn-pc/桌面/imagej竹子維管束/roi/"

particle_size = "70-140";
particle_size_connect= "150-500";
particle_size_horseshoe = "30-200";
particle_size_fragment = "10-100";


particle_cir = "0.6-1.0";
particle_cir_01 = "0.0-1.0";


analy_string = "size="+ particle_size +" circularity="+ particle_cir +" show=Masks clear include summarize add";
analy_string_connect = "size="+ particle_size_connect +" circularity= " + particle_cir_01 +" show=Masks clear include summarize add";
analy_string_horseshoe = "size="+ particle_size_horseshoe +" circularity= " + particle_cir_01 +" show=Masks clear include summarize add";
analy_string_fragment = "size="+ particle_size_fragment +" circularity= " + particle_cir_01 +" show=Masks clear include summarize add";

function single(m){
run("Duplicate...", "title="+m);
run("Analyze Particles...", analy_string);
if(Table.size != 0){
//if ((Table.get("Count",Table.size-1)) != 0) { 
roiManager("Save", input + m +".zip");

selectWindow("0");
n=roiManager("count");
for(i=0;i<n;i++){
roiManager("Select", i);
setForegroundColor(255, 255, 255);
run("Fill", "slice");
}
}
n=roiManager("count");
for(i=0;i<n;i++){
roiManager("Select", 0);
roiManager("delete");
}
run("Select None");
roiManager("show none");
close("Mask of "+m);
close("Mask of 0");
close("Mask of Mask of 0");
close(m);

}

function connect(m){
run("Analyze Particles...", analy_string_connect);
run("Watershed");
single(m);
}

//=========================================


name=getTitle; 
run("Duplicate...", "title=0");
setAutoThreshold("Default");
run("Convert to Mask");

single(1);

connect(2);

//抓馬蹄形,單獨的
run("Analyze Particles...", "size=70-140 circularity=0.00-1.00 show=Masks clear include summarize add");
run("Close-");
single(3);

//抓馬蹄形,連在一起的
run("Analyze Particles...", analy_string_horseshoe);
run("Close-");
connect(4);

//抓碎片
run("Analyze Particles...", analy_string_horseshoe);
run("Close-");
single(5);

//抓更碎片
run("Analyze Particles...", analy_string_fragment);
run("Close-");
run("Dilate");
run("Fill Holes");
run("Watershed");
run("Erode");
single(6);


//抓更更碎片
run("Analyze Particles...", analy_string_fragment);
run("Dilate");
run("Fill Holes");
run("Erode");
single(7);




list = getFileList(input);
for (i = 0; i < list.length; i++){
roiManager("Open", input+list[i]);

}


selectWindow(name);
roiManager("show none");

roiManager("Show All without labels");


*************************

2018年10月12日

用在生態系的AR App介紹: WWF Free Rivers

昨天看到Apple網站上對新版本的iPad的廣告,特別強調了AR擴增實境的介紹。特別點進去看,看到了介紹幾款APP,然後我就試用了這個WWF Free Rivers。

這是WWF世界自然基金會出品的,簡單地說這個APP是個用AR方式帶你看一個模型裡發生的故事,本身互動性我覺得還好,沒有什麼特別的互動功能,但是能夠你讓你用上帝的視角去東看西看。

APP一開始先讓你設定模型的位置,你需要一張桌子(或是地板),然後你就決定模型要擺放的方向。這APP有中文介面,也有中文介紹,畫面就如同下面這樣,是個以河流為主的模型。不過陸域的設定其實是綜合的,看那裏面的動物,同時有熊、獅子、老虎、鱷魚還有白鱀豚...。大概是包含了非洲草原、熱帶雨林、三角洲等等之類的。



可以看看河流和周圍動物的之間的關係,或是人類怎麼利用河流,然後故事會進展到看到起雲降雨(這個場景用AR來看還蠻有意思的),接著就是蓋起水壩,看看水壩上游和下游有什麼不同,然後就是拆掉水壩再看看有什麼變化。蓋水壩是為了能源需求,所以場景中也加入了可以用風力發電來提供能源。












除了上述的場景外,也提供另外一個體驗,它像是你面向一個大展版,展板前方還有一些模型這樣。







我覺得這個APP在生態和環境的課程中應該可以使用看看,不過就不適合老師絮絮叨叨地介紹,應該是提供一個觀察提問的指引單,讓學生透過模型去看那些現象。

2018年10月8日

用QGIS取出海岸線的方法

最近一個網友來信問了問題,他想跟著做這篇文章的東西。
QGIS作透明的立體等高線地形模型

但是有個問題,取等高線的時候沒辦法直接取出等高線為0的線,其實也就是海岸線。

取不出那條海岸線的原因其實是因為圖檔中數值為0的地方為海,並不是海岸線。意思就是如果我們光看圖檔裡的數值,其實海洋的區域就是滿滿的0,而海岸線其實是一堆零和非零的數字交界的邊緣。

所以要取出海岸線就要換個想法來做,首先把所有有高度的地方(非零)都變成1,也就是做成二值化影像,然後將影像向量化,就可以取出海岸線的那條線了。

做法我也錄成影片啦


2018年9月27日

細胞熱縮片吊飾製作



去年參加工作坊做了微生物的雷射吊飾之後,當時就想了應該也可以放在課程中讓學生玩玩。只是製作流程和素材,就不會是使用雷射切割機和壓克力,畢竟是耗時又有成本,熱縮片應該是不錯的選擇。不過想著想著,材料也買了,但是就是放在那裡沒動作了。




前幾週看到永欽老師在臉書上貼文,就是讓學生以熱縮片製作細胞示意圖的課程,剛好我材料和課程也都剛好,所以就讓學生來玩玩吧。




這個課程適合在講完細胞構造,或是用顯微鏡觀察完動植物細胞構造之後。




製作熱縮片吊飾需要的工具如下,手機吊繩(賣熱縮片的網路賣家有些也有賣)、打孔機、剪成約5cm x 5cm的熱縮片(半透明,單面磨過方便上色),剪刀。另外還有熱風槍和玻璃杯是用來加熱用的。



繪圖工具有黑色油性筆和彩色色鉛筆



學生繪畫的素材包含課本的幾種細胞圖片(皮膜、肌肉、神經、紅血球)、動植物細胞模式圖、粒線體、葉綠體、顯微觀察的草履蟲、保衛細胞...等。先以鉛筆打底稿,再用油性筆畫輪廓,最後用色鉛筆上色。



用打孔機在圖片邊緣打一個孔,然後用剪刀把細胞輪廓剪下,但要注意打孔的位置要留多一些邊,避免縮小時邊緣會太窄。



將熱縮片放入玻璃杯中(我是使用燒杯),再用熱風槍加熱,只要幾秒就可以縮完成了,但是剛縮好的熱縮片會有點捲,所以要立刻倒出用物體壓平。完成後再用手機吊繩串在上面,讓學可以掛在鉛筆袋的拉鍊上。



以下就是學生的成果展示。紅血球



神經細胞



葉綠體和粒線體



紅血球


口腔皮膜細胞



肌肉細胞



精子



某某細胞



團藻



幾種細胞和胞器大集合



神經細胞