2019年6月27日

以imagej的macro實作GUI,進行重疊細胞的分割

上週五收到一個生技公司人員的來信,信中問到要作細胞的分割來計數,但是作分水嶺分割時出現問題。我寫imagej作細胞分割的文章其實寫了好幾篇,本來想說請他看看之前文章就好。不過因為給的圖片很漂亮,剛好議題又是我感興趣的,所以就試試看來解決了。

來看一下圖片。這是利用不同劑量的藥劑處理非癌化細胞後,再以螢光染劑進行染色。研究者的目的是要計算這有多少細胞。


由於有些細胞是重疊的,一般想法就是直接作分水嶺切割,然後再作analyze particles。不過這樣的作法會有大問題。


這是我們期待看到的,兩個細胞被分水嶺算法剛好切開。



但實際上會發生的是,單獨一個細胞往往會被切成好幾塊



或是兩個細胞被切成三塊



如何解決這樣的問題呢?
解決流程是這樣的
(1)先把單獨未重疊的單顆細胞抓出,並計數。
(2)只針對重疊的細胞作分水嶺切割,然後作計數。

現在問題來了,怎麼知道誰是單顆細胞,這裡會運用一個形態學的運算方式來幫助判斷。
以下兩張圖是先將圖片做threshold處理後,轉二值化影像,再用wand tool選取後,然後用Edit/Selection/Convex hull做出的新選取區。

convex hull是個重點!它就像是用條橡皮筋套住你的選取區,產生的新選取區就是convex hull。你看第一張圖片,是兩個細胞重疊,用convex圈起之後,會留下很多沒圈到的地方。第二張圖片是單獨的細胞,圈起來之後留下的空白很少。

所以啊,我們可以用convex hull和細胞的面積做運算,看看細胞在hull裡頭佔面積的多寡來推論那是不是單獨的細胞。








要計算這個東西,其實不用自己手動一個一個圈細胞。在measure中的測量值solidity就是這個啦。你可以勾選Analyze/Set Measurements/Shape descriptors,就可以呈現出這個數值了。





接下來的分析和製作Macro,我就交給影片說明了


影片中所使用的Macro 就是以下的文字啦

=============================================
Dialog.create("Preference");
Dialog.addNumber("solidity:", 0.88);
Dialog.addNumber("cell area min :", 2000);
Dialog.addCheckbox("Single cell fill color", true);
Dialog.addCheckbox("Watershed cell fill color", true);
Dialog.show();
solidityValue = Dialog.getNumber();
cellAreaMin = Dialog.getNumber();
singleCheck = Dialog.getCheckbox();
watershedCheck = Dialog.getCheckbox();

//關閉所有圖檔
run("Close All");
//製作結果表
Table.create("cellCounts");

//打開資料夾,取得檔案list
dir = getDirectory("Choose a Directory ");
list = getFileList(dir);
filecount = 1;
listFiles(dir);

function listFiles(dir) {

for (i=0; i<list.length; i++) {
if (endsWith(list[i], "/")){}

// listFiles(""+dir+list[i]);
else{
//print((filecount++) + ": " + dir + list[i]);
open(dir + list[i] );
bname=File.nameWithoutExtension;
cellCounts();
}
}
}

function cellCounts(){
//細胞有幾個
cellNum_single=0;     //一個單獨的細胞
cellNum_overlap=0;     //重疊的細胞團
  cellNum_watershed=0;  //分水嶺切割後,單獨的細胞

rename("original");

//製作空白的檔案
run("Duplicate...", "title=cell_single");
run("Select All");
setBackgroundColor(255, 255, 255);
run("Clear", "slice");
run("Duplicate...", "title=cell_overlap");
run("Duplicate...", "title=cell_watershed");

//分析前處理
selectWindow("original");
run("Duplicate...", "title=a");
run("8-bit");
setAutoThreshold("Default dark");
run("Convert to Mask");

//把所有細胞都抓出,設定measure參數
run("Set Measurements...", "area perimeter shape redirect=None decimal=3");
run("Analyze Particles...", "size="+cellAreaMin+"-Infinity circularity=0.10-1.00 display exclude clear include add");

//用Solidity推斷細胞是否有重疊
numROIs = roiManager("count");
for(i=0; i<numROIs;i++) {// loop through ROIs
solidity = getResult("Solidity", i);

if(solidity>solidityValue){//Solidity大於0.88者,應該是單顆細胞
cellNum_single =cellNum_single +1;
//塗色
if (singleCheck){
selectWindow("cell_single");
roiManager("Select", i);
setForegroundColor(255*random, 255*random,255*random); //隨機填入顏色
run("Fill");
}
}

else {//如果是小於0.88,可能是多個細胞重疊,進入迴圈作分水嶺切割
cellNum_overlap =cellNum_overlap +1;
selectWindow("cell_overlap");
roiManager("Select", i);
setForegroundColor(0,0,0); //填入黑色
run("Fill");

}
}

//到cell_overlap的圖片中,執行分水嶺算法
selectWindow("cell_overlap");
run("Make Binary");
run("Watershed");
run("Select All");
run("Analyze Particles...", "size="+cellAreaMin+"-Infinity circularity=0.10-1.00 display exclude clear include add");
numROIs = roiManager("count");
for(i=0; i<numROIs;i++) {// loop through ROIs
cellNum_watershed =cellNum_watershed +1;
if(watershedCheck){
selectWindow("cell_watershed");
roiManager("Select", i);
setForegroundColor(255*random, 255*random,255*random); //隨機填入顏色
run("Fill");
}
}

//輸出細胞的計數結果
rowsize = Table.size("cellCounts");
Table.set("filename", rowsize, bname,"cellCounts");
Table.set("cell_single",rowsize, cellNum_single,"cellCounts");
Table.set("cell_overlap",rowsize, cellNum_overlap,"cellCounts");
Table.set("cell_watershed",rowsize, cellNum_watershed,"cellCounts");
Table.update("cellCounts");

//關閉圖檔
selectWindow("cell_single");
saveAs("Jpeg", dir+"1/" + bname+"_1_single"+".Jpg");
selectWindow("cell_overlap");
saveAs("Jpeg", dir+"1/" + bname+"_2_overlap"+".Jpg");
selectWindow("cell_watershed");
saveAs("Jpeg", dir+"1/" + bname+"_3_watershed"+".Jpg");


close("cell_single");
close("cell_overlap");
close("cell_watershed");
close("a");
close("original");
close("Results");
close("ROI Manager");
}

2019年6月21日

以Onshape製作有卡榫的雷切盒子

像下圖這種有螺絲卡榫T slot的壓克力盒子,在網路上可以找到一些製作程式,例如inkscape的外掛,Google相關的關鍵字可以找到一些。

不過用2D的軟體製作時,遇到一些要微調或組合調整的時機時,有時候會難以想像,所以若是能以3D建模軟體製作,應該能夠省事一些。

目前線上3D軟體很多,這篇介紹的Onshape只是其中之一,功能強大值得學習。只要用瀏覽器就可以執行,不需要另外安裝更新軟體,檔案也都是雲端儲存。目前有教育版可以使用,只要簡單註冊程序即可。

製作的成品如下



組合出來的樣子會是這樣





軟體可以產生供雷切機使用的dxf檔案


可以製作爆炸圖,看各個零件的關係





如何入門這套軟體?Onshape的Learning Center有一系列的教學課程,我推薦跟著其中的Learning Pathways學習,可以順利跨過入門階段,能夠恣意利用軟體製作想做的模型。

以下這個播放清單是我製作雷切盒子的教學影片,如果你有興趣以Onshape製作類似的模型,也可以參考這個影片來學習製作。影片總長1小時30分。


2019年6月12日

關於寄生

因為昨天可能吃到了很多海獸胃線蟲,結果晚上做夢就夢到了很多蟲,最特別的是夢到我抓到了一隻巨大熊蟲﹑足足三公分大啊。要知道如果用肉眼看熊蟲,充其量就是個小點點而已,我可是夢到了一隻巨無霸啊。


既然講到寄生,就來寫本書的紀錄,「繪圖解說-寄生蟲的世界」,是去年出版的,作者是長谷川英男,日本的寄生蟲學者。

書裡一單元提到何謂寄生,作者提了幾個有趣的知識,生物的交互關係其實是會在不同情況下改變的。

就以小丑魚和海葵兩者來說,小丑魚會住在海葵的觸手之間,因為其他魚類會被小丑魚觸手的刺絲胞傷害,所以不會靠近海葵。但是小丑魚體表會分泌不會刺激刺絲胞的黏液,所以牠就可以安然無事躲在觸手之間。就海葵的角度來說,小丑魚算是片利共生的傢伙,因為自己不會得到好處。

但是當小丑魚會來驅趕其他吃海葵觸手的魚類,或是清除海葵觸手之間的垃圾時,這時關係又變成了互利共生。然而小丑魚有時也會吃掉海葵的觸手,那麼這關係卻又偏向寄生了。

再以鮣魚這種會藉由吸盤吸附在大型魚類身上來移動的魚類來說,這種關係對於大魚來說沒好處也沒壞處,算是片利共生的例子。其實身上黏著一條條魚,行動也很麻煩吧。然而鮣魚有時候又會吃掉在大魚體表的寄生型甲殼類,這樣的關係又會成了互利共生。

再來是螞蟻和蚜蟲這課本常提的互利共生例子,其實蚜蟲如果沒有辦法發揮功能的話,螞蟻也會吃掉牙蟲的。那這樣又成了捕食關係。

而牛和其體內的微生物,那些原生動物、細菌們之間的關係也很複雜,牛吃下的草料是藉由微生物的作用才能消化纖維素讓牛獲得養分,如此的關係成了互利共生。但是微生物在牛反芻後就會被胃液殺死消化,成了牛隻的營養,啊這樣的關係又成了什麼。





書中p.10還有一篇講到有寄生蟲的動物群。其實說寄生「蟲」也不好,因為有些根本不是蟲。

在這圖片中列出了動物的類群,類群上頭的符號黑圈圈或白圈圈代表了此類群有寄生種,即使是脊椎動物也有寄生的物種喔。像是八目鰻、盲鰻會吸附在其他魚體身上吸血或是侵入魚體吃牠們的內臟(就這點看來是捕食囉)。

有種會住在海參腸子裡的潛魚會吃掉海參的生殖腺,讓海參無法生殖

有部分種類的鮟鱇魚,其雄魚會和雌魚的身體融合,寄生在雌魚身上。

杜鵑鳥有產卵在其他鳥類巢中的社會性寄生行為。

圖中的演化樹是參考 佐藤 矩行  発生と進化 (シリーズ進化学 (4)) 単行本 – 2004/6/8

2019年6月11日

生物遊戲之蝙蝠捉飛蛾

這個活動最早是在一次去新竹自然谷參加攀樹活動時親身玩過的。而最近又在這本《全世界孩子都想上的自然探險課:來自「森林學校」的遊戲教育與成長指南》書裡又看到這個活動。

想起前次段考時,考了一題蝙蝠捉蛾與演化有關係的活動,於是就在下課前十分鐘,讓學生玩玩這個遊戲。

材料很簡單,就一個眼罩和一個空曠的場地。不過我沒有眼罩,所以就用口罩代替了。
一個學生當蝙蝠,戴上眼罩。另一位學生當蛾,可在場地裡自由移動。其他學生擔任樹,必須圍住場地。

活動的原則是蝙蝠發出超音波覓食,當超音波碰到飛蛾時,飛蛾會反射超音波,蝙蝠就會知道前面有飛蛾,可以前去覓食。而蝙蝠也會發射超音波來偵測樹木在哪裡,藉此避開樹木。

不過我們模擬活動是沒有辦法發出超音波的,所以就由蝙蝠小朋友自己選自己要發出什麼聲音,有人選「啊」,有人選「逼」還有人選「亞美蝶」(選這麼長的字很慘,因為要整場一直亞美蝶亞美蝶的叫」

擔任飛蛾的小朋友也是選一個聲音,當蝙蝠朝著你叫的時候,你就要發出那個聲音。其實應該要跟著蝙蝠一樣的聲音才對,不過我就讓他們自己選。想不到要發什麼聲音,那就發「蛾蛾蛾」

當樹的小朋友也很忙,因為當蝙蝠朝著你叫的時候,你要反射回去啊。如果想不到要發什麼聲音,那就發「樹樹樹」。

活動在樹圍起來的場地中進行,飛蛾可以自由飛行,但是不能離開樹的範圍。而樹要保護好蝙蝠,不能讓蝙蝠誤闖到界線外,可能會有危險。蝙蝠必須在時間內快速用「超音波」找到飛蛾抓到牠。

活動就這樣,規則很簡單,但是會玩得很瘋狂。試試看,很有趣的!

生物遊戲之猜猜我是誰

最近在課程中進行一個活動,就名為「猜猜我是誰」吧!

進行方式是這樣的
準備一疊牌,是從病毒到哺乳類,每一種學過的生物。學生群中一人當自願者,到台前抽一張生物牌,而這張牌只有老師和此學生可以看,能夠知道那是什麼生物。而其他人是看不到的。

接下來,其他學生舉手問yes/no的問題,台前學生只能回答yes或no。例如「是生物嗎?」「是內溫動物嗎?」諸如此類的題目。台下學生必須用最少的問題來答出那張牌的生物是什麼,最後答對的人就可以拿到此題的加分。當然用的問題越多,加的分數就越少。

對於提問的學生,這是練習分類的一種好方法,要如何用精確的方式快速分類出生物。而對於回答的學生,他自己要很清楚這些問題的對錯。而老師的角色就是檢查學生是否有回答錯,適時給予協助。

此活動過程,可以視學生程度讓學生拿筆記或課本來提問,或是讓學生空手提問也是一種挑戰。

在幾次活動中,也有些有趣的插曲,例如有些學生在前面縮範圍的題目都是靜靜不說話,但是當答案已經呼之欲出時,就會跑出來撿尾刀,殺個措手不及啊。

我沒有要吃海獸胃線蟲謝謝

大學時有一門課叫做寄生蟲學,修課前學長諄諄教誨,上課前要吃飽,不然上完課就會不敢吃東西。

果然,那門課上完後,我再也沒有吃過白帶魚,也沒有用手直接摸蝸牛,更別說是碰觸鳥大便。不吃白帶魚是因為那時要觀察海獸胃線蟲時,只要去買一條白帶魚幾乎都可以看得到,在魚體內長什麼樣子,Google一下就看得到。

過了幾十年後的今天中午,覓食時想說吃個口味重的,揀了一塊白帶魚來吃,就在快要吃光光的時候,眼睛仔細看了筷下的肉,滿嘴的飯和魚肉一時不知道該吞下去還是吐出來。

雖然知道這些蟲煮熟了也是蛋白質來源,但是看到一群在魚肉上還是抖了兩下。抖了第二下是因為都已經吃到這樣了才發現,那之前吃下的那些哩?我想吃口味重的,但是沒有想要吃口味這麼重的線蟲動物門啊!

後記:臉書上一提,結果好多人都吃過,因為有些人以為是血管就一樣吃下去了。