# 側流道內的微流井 Microwell in side channel
以下將會介紹怎麼圈選出 side channel 的 microwell,如下圖:

圈選成功的話會長這樣:

再放大一些:

## 分析思路
### 目標
以 side channel 為單位,圈選出該 side channel 內的 12 個 microwell 再移動到下一個 side channel,這樣得到的數據才比較有規律,方便之後計算各個 side channel 的平均,或是依據距離中間主流道分組計算平均。
### 作法
1. 用巢狀迴圈算出每個 side channel 中的最左上的 microwell
2. 在上面的迴圈內再加入新的巢狀迴圈,算出 12 個 microwell 的位置
:::info
- 以上共四層迴圈
- 每算出一個就加到 ImageJ 的 ROI manager 中
:::
## 執行流程
提示使用者要先選好四個角落
``` java=
waitForUser("Tips", "Please select 4 points. TL, BL, TR, BR.");
```
---
通常顯微鏡拍出來的影像會自動帶入一個scale,但通常都是不對的,所以為了方便後續單位統一,這邊都移除,用像素(pixel)就好
```java=+
// remove scale
run("Set Scale...", "distance=0 known=0 unit=pixel");
```
---
使用`selectionType()`來判斷前面是否有成功選取四個角落
`-1`表示沒有選取
`10`表示為point selection
最後再用`getSelectionCoordinates(xPoints,yPoints)`來取得四個角落的座標,它們的x,y會依序存進`xPoints`和`yPoints`這兩個Array中
```java=+
s = selectionType();
// get coordinates
if( s == -1 ) {
exit("There was no selection.");
} else if( s != 10 ) {
exit("The selection wasn't a point selection.");
} else {
getSelectionCoordinates(xPoints,yPoints);
}
```
---
接下來,要判斷四個點的相對關係,看哪個是左上、左下、右上、右下
這邊我使用的判斷方式是計算出四點的重心之後,觀察它跟重心座標的相對關係
要計算出Array中的數值的平均值,要使用`Array.getStatistics`
:::warning
:warning: 由於排列上有可能會是歪的,因此不適合直接比較四點座標來判斷相對位置
:::
```java=+
// define tl, bl, tr, br
Array.getStatistics(xPoints, xmin, xmax, xmean, xstdDev);
Array.getStatistics(yPoints, ymin, ymax, ymean, ystdDev);
for (i=0; i<4; i++) {
if (xPoints[i] < xmean && yPoints[i] < ymean) {
tl_x = xPoints[i]; // top left of circle
tl_y = yPoints[i]; // top left of circle
}
else if (xPoints[i] < xmean && yPoints[i] > ymean) {
bl_x = xPoints[i]; // bottom left
bl_y = yPoints[i];
}
else if (xPoints[i] > xmean && yPoints[i] < ymean) {
tr_x = xPoints[i]; // top right
tr_y = yPoints[i];
}
else {
br_x = xPoints[i]; // bottom right
br_y = yPoints[i];
}
}
```
---
確認四個角落的座標後,把座標輸出,並將輸出的座標儲存在第`48`~`55`行中,下次分析就可以將這段取消註解,並上面所有程式碼註解掉,即可再現分析結果
(每次選取位置都可能會有誤差)
```java=+
print("tl_x = " + tl_x);
print("tl_y = " + tl_y);
print("bl_x = " + bl_x);
print("bl_y = " + bl_y);
print("tr_x = " + tr_x);
print("tr_y = " + tr_y);
print("br_x = " + br_x);
print("br_y = " + br_y);
/*
//parameters:
tl_x = 550
tl_y = 710
bl_x = 353.6667
bl_y = 1797.5
tr_x = 14826.5
tr_y = 284.5
br_x = 14627.75
br_y = 1361.25
*/
```
---
依據待分析的影像設定參數,其中`r`指的是待分析的圓圈的半徑,我習慣設定的略小於實際microwell的大小,以確保不會圈選到microwell以外的區域而產生誤差。
不同焦距、顯微鏡放大倍率等都會影響到`r`的理想值,所以分析時需確認一下半徑`r`是否需要調整
```java=+
r = 70;
column = 32;
row = 2;
```
---
計算microwell的間距
:::warning
:warning: 儘管理論上microwell array應該是一個平行四邊形,但是實際上會有一些誤差,因此這邊我在四條邊界都有計算間距,之後就可以用內差的方式算出所有的microwell位置
:::
```java=+
v_dx_l = (bl_x - tl_x)/(row -1); // vertical left
v_dy_l = (bl_y - tl_y)/(row -1);
v_dx_r = (br_x - tr_x)/(row -1); // vertical right
v_dy_r = (br_y - tr_y)/(row -1);
h_dx_t = (tr_x - tl_x)/(column -1); // horizontal top
h_dy_t = (tr_y - tl_y)/(column -1);
h_dx_b = (br_x - bl_x)/(column -1); // horizontal bottom
h_dy_b = (br_y - bl_y)/(column -1);
```
---
畫出左上 (第一個) 圓圈,並記錄其相關數值
```java=+
// create the circles
makeOval(tl_x - r, tl_y - r, r*2, r*2);
getSelectionBounds(x, y, w, h);
setSelectionLocation(x, y);
```
---
開始以巢狀迴圈設定所有位置
```java=+
for (i=0; i<row; i++) {
j = 0;
if (i != 0) {
roiManager("select", 0); // select the first rectangle
}
// select the first rectangle, than move down
getSelectionBounds(x, y, w, h);
setSelectionLocation(x + i*v_dx_l, y + i*v_dy_l);
roiManager('Add');
//print(i);
//waitForUser("Tips", "i=");
//----------------------------------------------------------------
tl_x = 551;
tl_y = 711;
bl_x = 568;
bl_y = 1287;
tr_x = 666;
tr_y = 706;
br_x = 684;
br_y = 1284;
sv_dx_l = (bl_x - tl_x)/(6 -1);
sv_dy_l = (bl_y - tl_y)/(6 -1);
sh_dx_t = (tr_x - tl_x)/(2 -1); // horizontal top
sh_dy_t = (tr_y - tl_y)/(2 -1);
sh_dx_b = (br_x - bl_x)/(2 -1); // horizontal bottom
sh_dy_b = (br_y - bl_y)/(2 -1);
for (m=0; m<6;m++) {
n = 0;
if (m != 0) {
roiManager("select", i*2*6*column);
}
getSelectionBounds(x, y, w, h);
setSelectionLocation(x + m*sv_dx_l, y + m*sv_dy_l);
if (m != 0) {
roiManager('Add');
//print(i,m);
//waitForUser("Tips", "i, m="+ i + m);
}
sh_dx = ((6 - m)*sh_dx_t + m*sh_dx_b)/6;
sh_dy = ((6 - m)*sh_dy_t + m*sh_dy_b)/6;
for (n=1; n<2; n++) {
if (n == 1) {
getSelectionBounds(x, y, w, h);
}
setSelectionLocation(x + n*sh_dx, y + n*sh_dy); // move right
roiManager('Add');
//print(i,m,n);
//waitForUser("Tips", "i, m, n=");
}
}
//--------------------------------------------------------------
roiManager("select", i*2*6*column)
h_dx = ((row - i)*h_dx_t + i*h_dx_b)/row;
h_dy = ((row - i)*h_dy_t + i*h_dy_b)/row;
for (j=1; j<column; j++) {
if (j !=1) {
roiManager("select", i*2*6*column);
getSelectionBounds(x, y, w, h);
}
if (j == 1) {
getSelectionBounds(x, y, w, h);
}
setSelectionLocation(x + j*h_dx, y + j*h_dy); // move right
roiManager('Add');
for (m=0; m<6;m++) {
n = 0;
if (m != 0) {
roiManager("select", i*2*6*column + j*2*6);
}
getSelectionBounds(x, y, w, h);
setSelectionLocation(x + m*sv_dx_l, y + m*sv_dy_l);
if (m != 0) {
roiManager('Add');
}
sh_dx = ((6 - m)*sh_dx_t + m*sh_dx_b)/6;
sh_dy = ((6 - m)*sh_dy_t + m*sh_dy_b)/6;
for (n=1; n<2; n++) {
if (n == 1) {
getSelectionBounds(x, y, w, h);
}
setSelectionLocation(x + n*sh_dx, y + n*sh_dy); // move right
roiManager('Add');
}
}
}
}
```