2019年8月27日

用Javascript製作視野測定的程式

上週一個研究人員來詢問我十幾年前寫的一篇文章細節,他想知道《視野測定》的一些資料。詢問之後知道他是要做一些比較的實驗,不過用我當初寫的方式,做出來的結果我覺得會太粗糙。

於是週末的時候我就想應該可以寫個程式用電腦來進行測試。概念很簡單,就是讓螢幕先呈現一個小點,單眼靠近盯著它看,然後從旁邊慢慢移入一個色點,等到色點進入眼睛的視野時,就由測試者按鍵標定座標。重複上述步驟之後,再把這些色點的座標連起來就完成了。

不過要用什麼環境來做程式呢?一開始想到用python,不過應該找更容易操作的環境,後來就想到了用HTML5由Javascript來實現。

寫程式的過程其實是有趣的,因為沒寫過類似的程式,所以都要在網路上找程式寫法,找到答案之後,改寫成自己要的部份實作出來,成功了就很有成就感。可是要找答案,絕對不可能就把「視野測定」這樣的詞丟到論壇去找答案,我得先在腦中拆解問題,像是:
  • 用鍵盤移動螢幕圓點
  • Javascript的陣列操作方式
  • Javascript的資料呈現在html標籤內的方式
  • 資料儲存成檔案,和載入檔案的方式
最後就整理出了一個檔案,就像下圖這樣,可以設定每次測量的角度間隔,預設值是30度,所以總共會測試12個點。共會測試紅綠藍白四種顏色,然後紀錄的座標點都會顯示在螢幕上,最後可以按鍵作圖出來。
而考量實際做實驗的需求,有可能會要比較前次的實驗結果,所以我也做了存檔和載入檔案的功能。

唯獨要注意的是,如果是用這種手工《視野測定》的方法,紀錄的點就是視角,但這個看平面螢幕的紀錄方法,就單純是距離而已。

視野測定:檔案連結



使用方式則錄製成影片


2019年8月23日

到台中霧峰看看為猛禽搭的架

前幾天公共電視的節目《我們的島》播出了一個單元-為猛禽搭架

那是紀錄屏科大野保所團隊在台中霧峰五福社區進行的「邀黑翅鳶來捕鼠」的棲架推廣活動。記得在去年,就曾經在上下游看過這相關的活動,那是在高屏地區進行過的活動-邱靜慧/邀黑翅鳶幫農人抓老鼠!設置人工棲架DIY趕緊來


為什麼要幫猛禽搭棲架呢?無非就是食物鏈以及生物防治的概念。老鼠在田間常常會造成莫大的農損,除了破壞農作的根系,也可能挖洞破壞土堤影響田間保水。要一勞永逸除去鼠患,最直接的想法就是田間放老鼠藥來毒殺老鼠,但這樣卻反而讓吃老鼠的猛禽死亡。

不放老鼠藥來毒老鼠那怎麼辦呢?乾脆就讓猛禽來吃老鼠吧!其實猛禽吃老鼠本來就是再自然不過的事情了,只是現在現在棲地改變的情況下,會在田間吃老鼠的猛禽就沒有那麼多了。那麼如何吸引猛禽來田間呢?這集《我們的島》就是介紹這樣一個活動,搭了棲架之後,的確在後續觀察到黑翅鳶會前來在棲架上覓食、進食或交配。

看到這集的介紹,覺得實在好有趣,隔天就驅車前往看看棲架,幸運的話說不定還可以看到黑翅鳶?其實光看節目也不太確定到底棲架在哪,不過節目上有出現一個福德祠,我們就憑著這個印象前往了。正巧附近的便利商店外就有當地導覽圖,對照一下就找到了。


在節目中有提到,要能以猛禽做生物防治,該田間就是要符合不用藥的大前提,不然猛禽被吸引來了,不就被二次毒殺了嗎。




我四處張望算算這附近立的棲架只看到三根,不過看之前的新聞說有立了五根耶。










不過沒關係,在福德祠裡就有黑翅鳶的照片,拿起望遠鏡遠遠看這些照片,意思也是差不多啦。






想去這個點看看的,其實Google Map上搜尋「穀稻自然田」,就可以找到了喔。不過福德祠是在田間,開車是進不去的,然後附近的路是一輛車寬的小路,所以停在外頭慢慢散步過去也不錯。

2019年8月14日

用循環紙圈記憶分析血液循環途徑

上暑期輔導時,有幾個學生一直弄不懂幾個循環途徑的路線,像是肺循環、體循環、充氧血、減氧血等。其實循環路徑的口訣都是背過很多次的,那些「肺靜左房左室主動脈 大靜右房右室肺動脈」。只是說學生對於切分的分析有困難,於是我就弄了一個小紙圈來教他們。

先把這些詞在Excel上輸入,然後印出來剪下再黏貼。





檔案連結


實際使用的流程就如這個影片所示

2019年8月4日

關於《用漱口水取得的大片口腔皮膜組織》想起的科學素養

關於這篇《用漱口水取得的大片口腔皮膜組織》,寫完之後突然想到和素養有很大關係耶。

素養是什麼?素養指的是「個人為了適應現今生活及未來挑戰,所應具備的知識、能力與態度」,那麼我就藉由上篇來談談科學素養吧。(是說這主題也跳太多了吧。)

當你在生活中看到了現象,發想了問題《漱口後出現白膜,那是什麼?》你會怎麼找答案。

上網查幾乎是大多數人的答案,但是查到的答案真的可信嗎?對我來說,我沒有看到足夠有公信力的網站告訴我那是什麼,最多都只有看到分享這樣經驗而已,所以實質上並沒有解答我這樣的問題,上網查了之後,頂多是讓我知道....原來我不孤單,喔不,是原來這並非單一個案,意思就是這不是偶發的事件。

接下來我針對這個問題想了很多答案,這些就是假說,像是「漱口水引起的皮膜脫落」、「漱口水乾掉產生的」、「漱口水和唾液混合產生的」、「口腔裡細菌形成的生物膜」。

這些假說都可以藉由實驗來證實,前一篇雖然只有說我實際用顯微鏡觀察那白膜,但其實我有針對幾個假說都有實際進行實驗觀察。

而在進行用顯微鏡觀察的實驗,我應用了過去學習的知識和能力,像是如何用顯微鏡觀察,用染色讓觀察物變得明顯。最後我也用了我的知識去判斷那些東西是細胞。

因為我有了這些證據,因此我可以去驗證我的假說,所以我就可以產生一個論證:「我主張某牌漱口水會引起口腔皮膜細胞脫落,因為我用顯微鏡觀察過那些白膜,證實它們的確是口腔皮膜細胞」

不過這樣的證據是否充分呢?主張夠不夠強呢?其實是不夠的。

我後來也用其他的實驗方式來驗證,像是使用稀釋後的漱口水,或是其他品牌的漱口水,結果發現用了稀釋後的漱口水後,脫落的皮膜組織出現的量就少很多,而用了其他品牌的,則完全沒有出現脫落皮膜組織。


寫了這些,我們再來看看素養是什麼:個人為了適應現今生活及未來挑戰,所應具備的知識、能力與態度。

其實所謂的挑戰不一定和想像中的恐怖挑戰有關係,它也可能會是一些簡單到不行的生活小問題。從廚房料理的問題,到使用漱口水的問題,也有可能會是實驗室裡的科學問題。面臨這些大大小小的問題時,我們期待無論自己或是學生都會有足夠的知識能和態度能以合適合理的方式來面對和處理。

用漱口水取得的大片口腔皮膜組織

最近因為看了牙醫,用了一個某家四個字的漱口水,可是沒想到使用過後的隔天一早,卻發現嘴裡出現奇怪的白色東西,是有點像是白膜的東西。
搞不清楚是什麼東西,要知道答案看起來就是先上網查查看,結果發現不是只有我有這樣的經驗,但還是不確定那是什麼東西啊。
心中有幾個答案浮出來,可能是口腔皮膜,也可能是漱口水在嘴裡乾掉留下的東西,也有可能是漱口水和唾液產生了化學反應形成的。
有這些可能的答案,當然就要驗證一下囉,如何知道是不是口腔皮膜呢?那就用顯微鏡看看吧,如果真是口腔皮膜,那麼根據細胞學說,應該可以看到細胞才是。
那我就先蒐集一下這些白膜囉,找個燒杯裝水,把這些嘴裡留下的白膜放進去。在水裡這樣載浮載沉的東西真的是一片一片的膜耶,我本來還以為是膠結成一團的東西哩。



再來就是先染色囉,找了魚藥的甲基藍來染看看。染下去,就真相大白了,唉啊啊,這一大片東西都是口腔皮膜細胞啊,原來用了這四個字的漱口水後,會有很大量的口腔皮膜組織出現啊,後面猜測的幾個答案也不用特別去做了啊。



後來我就在杯裡找了一片大片的膜在玻片上拼起來了,居然還蠻大片的,背景一小格是1平方公分,你看它有多大啊。

國中生對口腔皮膜細胞或組織都應該熟悉,那是顯微鏡觀察時,唯一一個從自己身上取下的細胞。一般觀察時是用牙籤刮一刮抹在玻片上,通常看到頂多就是小小一片,或是散開的細胞。很少有機會能夠看到這樣小小的細胞構成一大片組織,而且這些是有很多層的細胞呢。話說後來這片有皮膜的玻片被我蔭乾保留下來了,看來是可以留下來當傳家之寶了。

用Imagej製作灰階影像加彩色線條的視覺效果


前幾天看到網路上流傳了一個在灰階影像上加上彩色線條之後,看起來就好像是彩色圖片的視覺效果,我看了覺得很有趣,很想自己來動手玩一玩。於是就用imagej玩看看,順便寫了一個code script放在文章底下,有興趣的可以複製貼上玩玩看。

可以做什麼效果呢?可以針對個別區域製作橫紋效果。下面組圖是用同樣粗細的線寬,但是不同的線距


也可以做出斜紋效果



或是直紋效果



這些線寬都不一樣,但是線寬和線距比例都是1:3



同樣的code改一改,也可以做出網紋的效果



以下就是教學影片了,前兩部是從原理製作開始講,如果要直接使用code來做的人,就從第三部來看就可以了。





以下就是imagej的macro囉
===========================
lineW = 2;
space = 8;
angle = 0;


run("Select None");
rename("color");
run("Duplicate...", "title=grey");

selectWindow("grey");
run("8-bit");
run("RGB Color");
run("Set Measurements...", "area bounding redirect=None decimal=3");
roiManager("Select", 0);
run("Rotate...", "angle="+ angle +"");
roiManager("Add");
run("To Bounding Box");
run("Measure");

BX = getResult("BX", nResults-1);
BY = getResult("BY", nResults-1);
Width = getResult("Width", nResults-1);
Height = getResult("Height", nResults-1);

lineN = floor(Height/(lineW+space));
print(lineN);
for (i = 0; i < lineN; i++) {
//建立選取區
run("Specify...", "width="+ Width +" height="+ lineW +" x="+ BX +" y="+ BY+(space+lineW)*i +"");
//run("Rotate...", "angle="+ angle +"");
roiManager("Add");
//交集運算
roiManager("Select", newArray(1,roiManager("count")-1));
roiManager("AND");
//產生要繪圖的選取區ROI
roiManager("Add");
roiManager("deselect")
roiManager("Select", roiManager("count")-2);
roiManager("Delete");

//roiManager("Select", roiManager("count")-1);
//roiManager("Delete");
}

a1 = newArray(roiManager("count")-2);
for (i=0; i<a1.length; i++)
  a1[i] = i+2;
roiManager("Select", a1);
roiManager("Combine");
roiManager("Add");

a1 = newArray(roiManager("count")-2);
for (i=0; i<a1.length; i++)
  a1[i] = i+1;
roiManager("Select", a1);
roiManager("Delete");


roiManager("Select",roiManager("count")-1);
run("Rotate...", "angle="+ -angle +"");
roiManager("Add");

//====複製原圖,貼到黑白圖====
selectWindow("color");
roiManager("Select", roiManager("count")-1);
run("Copy");

selectWindow("grey");
roiManager("Select", roiManager("count")-1);
run("Paste");
//=========================

roiManager("Select", newArray(roiManager("count")-1,roiManager("count")-2));
roiManager("Delete");

selectWindow("color");
run("Select None");

selectWindow("grey");
run("Select None");
rename(lineW+":"+space);

text = "w"+lineW+":space"+space;
setFont("SansSerif", 14, " antialiased");
makeText(text, 20, getHeight()-20);
run("Add Selection...", "stroke=yellow fill=#660000ff new");
run("Select None");

2019年8月1日

用Imagej做顯微追焦

顯微追焦是什麼?其實我也沒想過要怎麼去命名這種影片。我用以下的影片來說明好了,它就是把在顯微影片下,本來四處跑來跑去的生物,用影像處理讓它固定在同一個地方,甚至身體方向也固定下來。這跟攝影的追焦有點像啦,所以就叫顯微追焦囉。
處理成這樣的影片有什麼好處呢?我實作的結果,發現這樣更能夠更仔細看到這些生物的細節,或是運動方式。如果是原先到處跑的影片,你又要追它的位置,又要看清楚,那就實在太累了。



處理的流程是先標記生物的位置,然後用程式碼位移每個影格,讓標記的位置都放在同一個地方,然後你就可以裁切獲得追焦後的影像。
這系列的教學影片共有四集,會用到兩種簡單的程式碼,分別是「用點標記」和「用線標記」的兩種。程式我就貼在文章最底下
要使用這些程式,就只要在Imagej中開啟File/New/Text Window
把以下的程式按照需求貼在那window就好了(要確認功能表上的Language,看一下勾選IJM喔)。











=====用點標記的======
origin_x= getWidth/2;
origin_y= getHeight/2;
for (i = 0; i <  nResults ; i++) {
slice_x = getResult("X", i);
slice_y = getResult("Y", i);

translate_x= origin_x-slice_x;
translate_y= origin_y-slice_y;

setSlice(getResult("Slice", i));
run("Translate...", "x="+translate_x+" y="+translate_y+" interpolation=None slice");
}

====用線標記的=========
origin_x= getWidth/2;
origin_y= getHeight/2;
for (i = 0; i <  nResults ; i++) {
BX =  getResult("BX", i);
BY =  getResult("BY", i);
W =  getResult("Width", i);
H =  getResult("Height", i);
A = getResult("Angle", i);

if(A>-180 && A<=-90){
X=BX+W;
Y=BY;
}
if(A>-90 && A<=0){
X=BX;
Y=BY;
}
if(A>0 && A<=90){
X=BX;
Y=BY+H;
}
if(A>90 && A<=180){
X=BX+W;
Y=BY+H;
}


translate_x= origin_x-X;
translate_y= origin_y-Y;
rotate_A = A + 90;
setSlice(getResult("Slice", i));
run("Translate...", "x="+translate_x+" y="+translate_y+" interpolation=None slice");
run("Rotate... ", "angle="+ rotate_A +" grid=1 interpolation=Bilinear");
}
==============