回到首頁

影像處理決策

教學影片

流程

flowchart TD
    A[選擇合適通道]-->B[空間與強度校正]
    B-->C[降噪]
    C-->D[背景分離]
    D-->E[對比增強]    
    E-->F[邊緣偵測]
    F-->G[分割]
    G-->H[形態學處理]
    H-->I[特徵擷取]
    I-->J1[追蹤]    
    I-->J2[分類]    

前處理的目的是提升影像品質、減少干擾,使後續任務更準確、更穩定、更容易自動化。

類型 目的 常見方法
標準化 統一輸入格式、大小或色階 Resize、Normalize、Color Conversion
影像校準 幾何對齊、尺度統一、畸變矯正 Registration、Rescale
降噪 去除感測器雜訊或背景干擾 Gaussian、Median
背景分離 補償光照不均或不良背景 Rolling Ball、Subtract Background
對比增強 拉大前景與背景的強度差異 CLAHE、Histogram Equalization、Gamma
邊緣偵測 讓特徵變得更明顯 Edge enhancement、Top-hat、Morphology

流程說明

幾何轉換

1.彩色影像轉灰階

RGB 或多通道影像,需要先轉換為單一的灰階影像才能進行後續處理。


2.降噪

平均濾波器

中值濾波器

高斯模糊

實作

執行以下產生噪點影像的Macro(或手動產生影像),產生三種不同雜訊的圖片,再圈選區域進行降噪處理,然後使用直方圖觀察處理前後的差異。

Macro

newImage("高斯雜訊", "8-bit white", 512, 512, 1);
run("Add Noise");
newImage("更多高斯雜訊", "8-bit white", 512, 512, 1);
run("Add Specified Noise...", "standard=80");
run("Clown");
run("8-bit");
run("Salt and Pepper");
rename("椒鹽雜訊");

3.背景分離(前景/背景分割)

  1. 背景是拍攝的雜訊或你不需要的物件。
  2. 將背景分離得到前景的基本原理是整張圖片減去背景圖
  3. 減去背景的方法是Process > Image Calculator...,選擇原始影像與背景影像,運算方式選 Subtract
  4. 如何得到背景圖?有以下的幾種方式
    1. 拍攝一張沒有細胞或沒有染劑的影像
    2. 用一個stack的系列影像進行平均產生背景圖。假設物體分布隨機,將這些影像進行平均後,就會呈現背景光照分布。
    3. 用演算法直接算出背景影像,直接用 Process > Subtract Background...。原理我們在下方的產生實作影像後來說明。

實作-靜態建模背景

產生實作影像

請執行以下Macro,這會產生三張圖片,模擬的是本來有原細胞的影像,在一個不平均的光場(原光場)照明,並且伴隨著取樣雜訊,得到了待處理影像。你的目標就是從待處理影像還原得到原細胞這張圖的樣子。

width = 512;
height = 512;

centerX = width/2;
centerY = height/2;


newImage("原光場", "32-bit black", width, height, 1);
sigma = 200;
for (y = 0; y < height; y++) {
for (x = 0; x < width; x++) {
    dx = x - centerX;
    dy = y - centerY;
    value = exp(-(dx*dx + dy*dy)/(2*sigma*sigma));
    setPixel(x, y, value);
}
}
run("8-bit");


newImage("原細胞", "8-bit black", width, height, 1);
for (i = 0; i < 40; i++) {
    x = 20 + random()*480;
    y = 20 + random()*480;
    size = 8 + random()*10;
    setColor(255);
    makeOval(x, y, size, size);
    fill();
}

imageCalculator("Add create", "原光場", "原細胞");
run("Enhance Contrast...", "saturated=0.35 normalize");
run("Add Noise");
run("Gaussian Blur...", "sigma=0.5");
rename("待處理的影像");
run("Tile");

觀察影像

  1. 我們利用一些工具來觀察這些影像。請點選原光場,這是利用高斯函數產生的影像,模擬顯微鏡下產生的不均勻光場。先用直線工具拉一條由最左到最右的直線,然後選擇Analyze › Plot Profile。你看到的圖形就是高斯函數。
  2. Analyze › 3D Surface Plot分別觀察三張影像。

分離前景與背景

  1. 觀察直方圖會發現這個影像有隨機雜訊,所以先用 Process > Filters > Gaussian Blur...去雜訊
  2. 以下展示兩種分離背景的方式,所以我們將去雜訊處理後的影像再複製一份,選擇Image > Duplicate...
  3. 由於已經有原始的光場影像,所以可以直接減去這張影像。使用Process > Image Calculator...,選擇原始影像與背景影像,運算方式選 Subtract
  4. 選擇剛剛複製後的另外一個去雜訊後影像,執行Process > Subtract Background...,設定 rolling ball 半徑。Subtract Background...的演算法原理是有一顆特定半徑的球在平面下方滾動,它所接觸的區域就是背景。滾完影像之後,就可以得到一張背景圖。因為要用一顆球在背景下方滾動,所以不可以讓球滾進前進的高峰底部,所以通常會設定球的半徑至少是前景目標半徑的三倍左右。你可以點選Create Background,觀察這種演算法算出的背景圖是不是接近原光場
  5. 選項中的Sliding parabolic filter (滑動拋物線濾波器)會將「滾動球」的概念替換為一個具有相同曲率的滑動拋物面。這個拋物面在影像數據的下方滑動,其頂部的軌跡被用來估計背景,可以處理更大的像素值範圍
    • 拋物面比球體在處理大範圍的影像像素值(例如,16 位元或 32 位元影像,像素值遠大於典型物體大小)
    • 複雜的背景模式
    • 前景物體的形狀或強度不完全符合滾動球的假設時

實作-動態建模背景

產生實作影像

執行以下Macro,這會產生一個stack,有一群細胞流動,背景有一些方塊。你的目的是將前景的細胞分離出來

// 參數
stackSize = 20;
width = 512;
height = 512;
numCells = 30;
numBoxes = 5;
boxSize = 30;

setBatchMode(true);

// 儲存背景固定位置
fixedX = newArray(numBoxes);
fixedY = newArray(numBoxes);
for (i = 0; i < numBoxes; i++) {
    fixedX[i] = random() * (width - boxSize);
    fixedY[i] = random() * (height - boxSize);
}

// 每個細胞的屬性:位置、半徑、速度
cellX = newArray(numCells);
cellY = newArray(numCells);
cellR = newArray(numCells);
cellDX = newArray(numCells);
cellDY = newArray(numCells);

// 初始位置與屬性
for (i = 0; i < numCells; i++) {
    cellX[i] = random() * (width - 40) + 20;
    cellY[i] = random() * (height - 40) + 20;
    cellR[i] = 8 + random() * 4; // 半徑 8~12
    cellDX[i] = random()*12 - 6;  // 速度 -6 ~ 6
    cellDY[i] = random()*12 - 6;
}

// 建立空 stack
run("New...", "name=細胞流 type=8-bit width="+width+" height="+height+" slices=1 fill=Black");
selectWindow("細胞流");


// 每張 slice
for (s = 0; s < stackSize; s++) {
    newImage("Temp", "8-bit black", width, height, 1);

    // 畫背景固定方塊
    setColor(80);
    for (i = 0; i < numBoxes; i++) {
        makeRectangle(fixedX[i], fixedY[i], boxSize, boxSize);
        run("Fill");
    }

    // 畫細胞
    setColor(200);
    for (i = 0; i < numCells; i++) {
        // 畫圓形細胞
        r = cellR[i];
        makeOval(cellX[i] - r, cellY[i] - r, 2*r, 2*r);
        run("Fill");

        // 更新位置(下一張用)
        cellX[i] += cellDX[i];
        cellY[i] += cellDY[i];

        // 邊界反彈(避免跑出去)
        if (cellX[i] < r || cellX[i] > width - r) cellDX[i] *= -1;
        if (cellY[i] < r || cellY[i] > height - r) cellDY[i] *= -1;
    }

    run("Select None");

    // 貼到主 stack
    run("Copy");
    selectWindow("細胞流");
    run("Add Slice");
    run("Paste");

    // 關閉暫時影像
    selectWindow("Temp");
    close();
}

selectWindow("細胞流");
setSlice(1);
run("Delete Slice");
resetMinAndMax();
setBatchMode(false);

  1. 從stack產生背景圖片

    1. 選擇細胞流的stack,執行Image › Stacks › Z Project...
    2. Projection type,有幾種選擇,你可以試試看Average Instensity或是Max Instensity,然後產生背景圖。
  2. 將stack的細胞前景與背景分離

    1. 使用Process > Image Calculator...,選擇stack與背景影像,運算方式選 Subtract

4.對比度增強/亮度均勻化

當影像中細胞與背景的對比過低(灰階值接近),整體偏灰或邊界模糊,將導致無法清楚分辨細胞輪廓。這時可以使用對比度增強的方法,使影像中目標物的邊界更清晰,提升後續分割或量測的效果。

適用情境:

ImageJ 操作:

功能 操作路徑 說明
亮度/對比調整 Image > Adjust > Brightness/Contrast... 手動調整亮度與對比,適合預覽最佳視覺效果。
對比度自動增強 Process > Enhance Contrast... 自動調整直方圖分布,提升對比。可勾選 Normalize 或 Equalize Histogram。
Gamma 調整 Process > Math > Gamma... 調整影像的 Gamma 值,讓暗部或亮部更明顯。適用於全灰階影像。
區域對比增強(CLAHE) Process > Enhance Local Contrast (CLAHE) 局部自適應對比增強,適合處理光照不均、背景不平坦的影像。

調整亮度對比

產生實作影像

請執行以下macro,這會產生一張影像,細胞的像素強度和背景十分接近。

newImage("Test", "8-bit black", 512, 512, 1);
setColor(50);   // 背景值
run("Select All");
run("Fill");

setColor(52);   // 細胞值
makeOval(200, 200, 100, 100);
run("Fill");
run("Select None");

執行Image > Adjust > Brightness/Contrast...,調整各種參數按下apply之後,會改變像素的強度值

對比度增強

執行 Process > Enhance Contrast... 是調整影像對比度的常用方法。

Saturated Pixels

這個參數設定了在自動拉伸對比度時,允許多少百分比的像素被「飽和」(即變成純黑0或純白255)。預設值(如0.35%)會忽略最亮和最暗的一小部分像素,避免極端值過度影響整體對比度,讓結果更貼近視覺感受。

選項分析與適用情境

Enhance Contrast 對話框中,主要有 NormalizeEqualize Histogram 兩個勾選框。不同的組合適用於不同的分析需求。

選項組合 行為 改變數據? 主要用途
不勾選 僅改變顯示 (LUT),線性視覺增強 否 (除非按 Apply) 安全的初步觀察,保留原始數據
勾選 Normalize 線性拉伸灰階值至全範圍 增強整體對比,為分割做準備
勾選 Equalize Histogram 非線性重排灰階值,使直方圖平坦化 強化局部細節與紋理,不適用於強度定量

情境一:不勾選任何選項 (預設)

情境二:勾選 Normalize

情境三:勾選 Equalize Histogram

Gamma

執行 Gamma... 時,會對影像中每個像素 p 套用以下公式:

\[ f(p) = \left( \frac{p}{255} \right)^\gamma \times 255 \]

CLAHE

對傳統 Histogram Equalization(HE,直方圖均衡) 的改進

方法 原理簡述
HE 將整張影像的直方圖拉平,提升整體對比,但容易產生過度對比或強化雜訊。
AHE 把影像切成小區塊(局部),各自做 HE,改善區域對比,但更容易過強或放大雜訊。
CLAHE 在 AHE 基礎上加入 對比限制,抑制雜訊放大,效果更穩定自然。

運作流程:

  1. 將影像切成小區塊(tiles),例如每個 tile 是 8×8、16×16 pixels。
  2. 每個 tile 各自進行直方圖均衡,將灰階分布平均化,提高區域內對比。
  3. 加入對比限制(Clip Limit),限制直方圖中的最大頻率,防止某些灰階值過度增強,抑制雜訊。
  4. 將不同區塊間進行雙線性插值,避免不同區塊之間出現突兀的邊界,讓整體視覺自然平滑。

適用情況

顯微鏡下光照不均、對比不足、目標邊界不明。

產生實作影像

請執行以下macro,這會產生一個亮度不均勻的影像,然後針對不同的blocksize執行Process › Enhance Local Contrast (CLAHE)

setBatchMode(true);

// 建立影像「原光場」
newImage("原光場", "8-bit black", 256, 256, 1);
for (y = 0; y < 256; y++) {
  for (x = 0; x < 256; x++) {
    v = 0.6*(x + y)/2 + 40*sin(x/30.0)*cos(y/45.0);
    if (v < 0) v = 0;
    if (v > 200) v = 200;
    setPixel(x, y, v);
  }
}
run("Gaussian Blur...", "sigma=30");


// 建立影像「原細胞」
newImage("原細胞", "8-bit black", 256, 256, 1);
setColor(255);
gridSize = 5;
spacingX = 256 / gridSize;
spacingY = 256 / gridSize;
radius = 5;
for (i = 0; i < gridSize; i++) {
  for (j = 0; j < gridSize; j++) {
    x = i * spacingX + spacingX / 2;
    y = j * spacingY + spacingY / 2;
    makeOval(x - radius, y - radius, 2 * radius, 2 * radius);
    fill();
  }
}


// 使用影像計算器合成影像
imageCalculator("Add create", "原光場", "原細胞");


// 接著對合成影像做後續處理
selectWindow("Result of 原光場");
run("Enhance Contrast...", "saturated=0.35 normalize");
run("Add Noise");
run("Gaussian Blur...", "sigma=0.5");
rename("待處理的影像");

run("Select None");
resetMinAndMax();

// 解除批次模式,強制刷新並顯示所有視窗
setBatchMode(false);

selectImage("待處理的影像");
run("Duplicate...", "title=127");
selectImage("127");
run("Enhance Local Contrast (CLAHE)", "blocksize=127 histogram=256 maximum=3 mask=*None*");


selectImage("待處理的影像");
run("Duplicate...", "title=63");
selectImage("63");
run("Enhance Local Contrast (CLAHE)", "blocksize=63 histogram=256 maximum=3 mask=*None*");
run("Tile");

selectImage("待處理的影像");
run("Duplicate...", "title=31");
selectImage("31");
run("Enhance Local Contrast (CLAHE)", "blocksize=31 histogram=256 maximum=3 mask=*None*");
run("Tile");

selectImage("待處理的影像");
run("Duplicate...", "title=15");
selectImage("15");
run("Enhance Local Contrast (CLAHE)", "blocksize=15 histogram=256 maximum=3 mask=*None*");
run("Tile");


5.邊緣偵測

ImageJ 內建了一些邊緣檢測的方式,也可以用 Process > Filters > Convolve... 自訂卷積核進行各種邊緣偵測。

內建方法

自訂卷積核加上Macro

邊緣強度的計算方式

邊緣強度(Gradient Magnitude)是透過兩個方向的導數(梯度)來估算的:

Scharr計算為例

我們可以直接用Macro來直接產生邊緣偵測後的圖。


// Scharr Filter - Full Edge Magnitude Macro
// 適用於灰階圖像

// Step 0: 檢查影像並轉為 32-bit
run("Duplicate...", "title=Original");
run("32-bit");

// Step 1: Apply Scharr X
run("Duplicate...", "title=Gx");
selectWindow("Gx");
run("Convolve...", "text1='3 0 -3\n10 0 -10\n3 0 -3'");

// Step 2: Apply Scharr Y
selectWindow("Original");
run("Duplicate...", "title=Gy");
selectWindow("Gy");
run("Convolve...", "text1='3 10 3\n0 0 0\n-3 -10 -3'");

// Step 3: Gx^2
selectWindow("Gx");
run("Square");
rename("Gx2");

// Step 4: Gy^2
selectWindow("Gy");
run("Square");
rename("Gy2");

// Step 5: Add Gx^2 + Gy^2
imageCalculator("Add create", "Gx2", "Gy2");
rename("G2");

// Step 6: Square root → Gradient Magnitude
run("Square Root");
rename("Scharr Gradient Magnitude");

// Step 7: Optional - enhance visibility
run("Enhance Contrast", "saturated=0.35");

把上述Macro,改成Sobel的Kernel

X 方向的 kernel

1 0 -1
2 0 -2
1 0 -1

Y 方向

1 2 1
0 0 0
-1 -2 -1

Macro實作

觀察 Find EdgesUnsharp MaskVarianceLaplaceBilateral FilterTop Hat等濾波器進行前處理的效果。

run("Blobs (25K)");
run("Invert LUT");

run("Duplicate...", "title=sobel");
run("Find Edges");

selectImage("blobs.gif");
run("Duplicate...", "title=UnsharpMask-10-0.6");
run("Unsharp Mask...", "radius=10 mask=0.6");

selectImage("blobs.gif");
run("Duplicate...", "title=Variance0");
run("Variance...", "radius=0");

selectImage("blobs.gif");
run("Laplace (3D)");
rename("Laplace");

selectImage("blobs.gif");
run("Bilateral Filter", "spatial=10 range=50");
rename("Bilateral");

selectImage("blobs.gif");
run("Duplicate...", "title=topHat50");
run("Top Hat...", "radius=50");

run("Tile");