第5章:Pythonで学ぶ組織解析実践

デジタル画像処理から機械学習まで

📖 読了時間: 30-35分 📊 難易度: Beginner 💻 コード例: 7個 📝 演習問題: 0問

5.1 組織画像解析の基礎

材料組織の定量的解析は、実験データからミクロ構造パラメータを抽出し、材料特性との相関を見出すために不可欠です。本章では、Pythonを用いた画像処理・粒界解析・機械学習による組織認識の実践的手法を学びます。

5.1.1 デジタル画像処理の基本

顕微鏡画像(SEM、光学顕微鏡)は、通常8ビット(256階調)または16ビット(65536階調)のグレースケールデータとして保存されます。画像解析の第一歩は、ノイズ除去とコントラスト強調による前処理です。

💡 実践のポイント

画像解析では、元画像を必ず保存してから処理を行いましょう。処理後の画像は情報が不可逆的に失われるため、パラメータを調整する際に元画像から再処理が必要になります。

Example 1: SEM画像の前処理

import numpy as np
import cv2
from scipy import ndimage
from skimage import filters, exposure
import matplotlib.pyplot as plt

# SEM画像の読み込みと前処理
def preprocess_sem_image(image_path):
    """
    SEM画像の前処理パイプライン

    Args:
        image_path: 画像ファイルのパス

    Returns:
        dict: 各処理段階の画像
    """
    # 画像読み込み(グレースケール)
    img = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE)

    # 1. ノイズ除去(Non-local Means Denoising)
    denoised = cv2.fastNlMeansDenoising(img, h=10)

    # 2. コントラスト強調(CLAHE)
    clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8,8))
    enhanced = clahe.apply(denoised)

    # 3. シャープニング(Unsharp masking)
    gaussian = cv2.GaussianBlur(enhanced, (5,5), 2)
    sharpened = cv2.addWeighted(enhanced, 1.5, gaussian, -0.5, 0)

    return {
        'original': img,
        'denoised': denoised,
        'enhanced': enhanced,
        'sharpened': sharpened
    }

# 使用例
results = preprocess_sem_image('steel_microstructure.tif')

# 結果の可視化
fig, axes = plt.subplots(2, 2, figsize=(10, 10))
axes = axes.flatten()

titles = ['Original', 'Denoised', 'Enhanced', 'Sharpened']
images = [results['original'], results['denoised'],
          results['enhanced'], results['sharpened']]

for ax, img, title in zip(axes, images, titles):
    ax.imshow(img, cmap='gray')
    ax.set_title(title)
    ax.axis('off')

plt.tight_layout()
plt.show()

# 出力: 4段階の前処理結果が表示される
# - Original: 元画像(ノイズあり)
# - Denoised: ノイズ除去後(滑らかに)
# - Enhanced: コントラスト強調後(明暗がくっきり)
# - Sharpened: シャープ化後(エッジが強調)

5.1.2 画像セグメンテーション

セグメンテーションは、画像を意味のある領域(粒、相、欠陥など)に分割する処理です。材料組織解析では、主に以下の手法が用いられます:

Example 2: Watershed法による粒界分割

import cv2
import numpy as np
from skimage import morphology, measure
from scipy import ndimage

def segment_grains_watershed(image):
    """
    Watershed法による結晶粒セグメンテーション

    Args:
        image: 前処理済みグレースケール画像

    Returns:
        labeled_grains: ラベル付き画像(各粒に固有ID)
    """
    # 1. 二値化(Otsu法)
    _, binary = cv2.threshold(image, 0, 255,
                               cv2.THRESH_BINARY + cv2.THRESH_OTSU)

    # 2. モルフォロジー処理でノイズ除去
    kernel = np.ones((3,3), np.uint8)
    opening = cv2.morphologyEx(binary, cv2.MORPH_OPEN,
                                kernel, iterations=2)

    # 3. 確実に背景とわかる領域を抽出
    sure_bg = cv2.dilate(opening, kernel, iterations=3)

    # 4. 確実に前景(粒内部)とわかる領域を抽出
    dist_transform = cv2.distanceTransform(opening,
                                            cv2.DIST_L2, 5)
    _, sure_fg = cv2.threshold(dist_transform,
                                0.5*dist_transform.max(),
                                255, 0)
    sure_fg = np.uint8(sure_fg)

    # 5. 不明領域(粒界候補)= 背景 - 前景
    unknown = cv2.subtract(sure_bg, sure_fg)

    # 6. 前景領域にマーカーラベルを付与
    _, markers = cv2.connectedComponents(sure_fg)
    markers = markers + 1  # 背景を0→1に
    markers[unknown == 255] = 0  # 不明領域を0に

    # 7. Watershed実行
    markers = cv2.watershed(cv2.cvtColor(image, cv2.COLOR_GRAY2BGR),
                            markers)

    # 8. 境界(-1)を除去してラベル画像を生成
    labeled_grains = markers.copy()
    labeled_grains[labeled_grains == -1] = 0

    return labeled_grains

# 使用例
preprocessed_img = results['enhanced']
grains = segment_grains_watershed(preprocessed_img)

print(f"検出された粒数: {grains.max()}")
# 出力: 検出された粒数: 347

5.2 粒界解析とステレオロジー

5.2.1 粒径分布の計算

セグメンテーション後のラベル画像から、各粒の形態パラメータ(面積、周囲長、アスペクト比など)を抽出できます。粒径分布は、材料特性(強度、延性)と密接に関連する重要な指標です。

Example 3: 粒径分布解析

from skimage import measure
import pandas as pd
import matplotlib.pyplot as plt

def analyze_grain_size_distribution(labeled_grains, pixel_size_um):
    """
    粒径分布の統計解析

    Args:
        labeled_grains: Watershedで得たラベル画像
        pixel_size_um: 1ピクセルのサイズ [μm]

    Returns:
        pd.DataFrame: 粒ごとの統計データ
    """
    # 各粒の領域プロパティを抽出
    props = measure.regionprops(labeled_grains)

    grain_data = []
    for prop in props:
        # 等価円直径(面積から計算)
        area_um2 = prop.area * (pixel_size_um ** 2)
        diameter_um = 2 * np.sqrt(area_um2 / np.pi)

        grain_data.append({
            'grain_id': prop.label,
            'area_um2': area_um2,
            'diameter_um': diameter_um,
            'perimeter_um': prop.perimeter * pixel_size_um,
            'circularity': 4 * np.pi * prop.area / (prop.perimeter ** 2),
            'aspect_ratio': prop.major_axis_length / prop.minor_axis_length
        })

    df = pd.DataFrame(grain_data)

    # 統計量の計算
    print(f"平均粒径: {df['diameter_um'].mean():.2f} μm")
    print(f"標準偏差: {df['diameter_um'].std():.2f} μm")
    print(f"中央値: {df['diameter_um'].median():.2f} μm")

    # ヒストグラム描画
    plt.figure(figsize=(10, 6))
    plt.hist(df['diameter_um'], bins=30, edgecolor='black', alpha=0.7)
    plt.xlabel('Grain Diameter [μm]')
    plt.ylabel('Frequency')
    plt.title('Grain Size Distribution')
    plt.grid(True, alpha=0.3)
    plt.show()

    return df

# 使用例(ピクセルサイズ0.5 μm/pixel)
grain_stats = analyze_grain_size_distribution(grains, pixel_size_um=0.5)

# 出力例:
# 平均粒径: 12.34 μm
# 標準偏差: 4.56 μm
# 中央値: 11.20 μm

5.2.2 形態記述子の抽出

粒径に加えて、形状を定量化する記述子が重要です:

Example 4: 形態記述子の計算

from skimage.measure import regionprops

def calculate_morphology_descriptors(labeled_grains):
    """
    高度な形態記述子の計算

    Returns:
        pd.DataFrame: 各粒の形態特徴量
    """
    props = regionprops(labeled_grains)

    morphology_data = []
    for prop in props:
        # 基本形態量
        area = prop.area
        perimeter = prop.perimeter

        # 真円度
        circularity = 4 * np.pi * area / (perimeter ** 2)

        # 凸性(convexity)
        convexity = area / prop.convex_area

        # コンパクトネス
        compactness = perimeter ** 2 / area

        # 離心率(楕円フィッティング)
        eccentricity = prop.eccentricity

        # Solidity(充実度)
        solidity = prop.solidity

        morphology_data.append({
            'grain_id': prop.label,
            'circularity': circularity,
            'convexity': convexity,
            'compactness': compactness,
            'eccentricity': eccentricity,
            'solidity': solidity,
            'elongation': 1 - (prop.minor_axis_length /
                               prop.major_axis_length)
        })

    return pd.DataFrame(morphology_data)

# 使用例
morphology_df = calculate_morphology_descriptors(grains)

# 形状分類(真円度に基づく)
morphology_df['shape_class'] = pd.cut(
    morphology_df['circularity'],
    bins=[0, 0.6, 0.8, 1.0],
    labels=['Irregular', 'Elongated', 'Round']
)

print(morphology_df['shape_class'].value_counts())
# 出力:
# Round       189
# Elongated   104
# Irregular    54

5.3 EBSD(電子後方散乱回折)データ解析

EBSDは、結晶方位を空間的にマッピングする強力な手法です。Pythonでは、orixライブラリを用いてEBSDデータを解析できます。

5.3.1 方位マッピングと逆極点図

Example 5: EBSD方位マッピング

import numpy as np
from orix import io, plot
from orix.quaternion import Orientation
from orix.crystal_map import CrystalMap
import matplotlib.pyplot as plt

def analyze_ebsd_orientation(ebsd_file):
    """
    EBSDデータから方位マップと逆極点図を生成

    Args:
        ebsd_file: .ang または .ctf ファイル

    Returns:
        CrystalMap: 方位マップオブジェクト
    """
    # EBSDデータの読み込み
    xmap = io.load(ebsd_file)

    # 信頼性指標(CI)でフィルタリング
    xmap_filtered = xmap[xmap.ci > 0.1]

    # 逆極点図(IPF)カラーマップの生成
    ipf_key = plot.IPFColorKeyTSL(xmap_filtered.phases[0].point_group)
    ipf_colors = ipf_key.orientation2color(xmap_filtered.orientations)

    # 方位マップの可視化
    fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 5))

    # IPFマップ(Z方向)
    ax1.imshow(ipf_colors.reshape(xmap.shape + (3,)))
    ax1.set_title('IPF-Z Orientation Map')
    ax1.axis('off')

    # 信頼性指標(CI)マップ
    im = ax2.imshow(xmap.ci.reshape(xmap.shape), cmap='gray')
    ax2.set_title('Confidence Index Map')
    ax2.axis('off')
    plt.colorbar(im, ax=ax2, label='CI')

    plt.tight_layout()
    plt.show()

    # 方位統計
    print(f"測定点数: {xmap.size}")
    print(f"有効点(CI > 0.1): {xmap_filtered.size}")
    print(f"平均CI: {xmap.ci.mean():.3f}")

    return xmap_filtered

# 使用例
ebsd_map = analyze_ebsd_orientation('titanium_alloy.ang')
# 出力:
# 測定点数: 250000
# 有効点(CI > 0.1): 237845
# 平均CI: 0.842

5.3.2 粒界特性と結晶方位差

EBSDデータから、隣接粒間の方位差(ミスオリエンテーション)を計算し、粒界の種類(小傾角/大傾角、特殊粒界)を分類できます。

📘 理論の補足

ミスオリエンテーションは、隣接する2つの結晶粒の方位の差を表します。回転軸と回転角で記述され、粒界エネルギー・移動度・強度に直接影響します。

5.4 機械学習による組織分類

5.4.1 教師あり学習:Random Forestによる相分類

多相材料(フェライト・パーライト鋼など)では、各相を自動的に識別する分類モデルが有用です。教師データとして、専門家がラベル付けした画像領域を用います。

Example 6: Random Forestによる相分類

import numpy as np
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import train_test_split
from sklearn.metrics import classification_report, confusion_matrix
from skimage.feature import graycomatrix, graycoprops
import cv2

def extract_texture_features(image, window_size=15):
    """
    テクスチャ特徴量の抽出(GLCMベース)

    Args:
        image: グレースケール画像
        window_size: 特徴抽出ウィンドウサイズ

    Returns:
        feature_vector: 特徴量ベクトル
    """
    # GLCM(灰色共起行列)から特徴量を抽出
    glcm = graycomatrix(image, distances=[1],
                         angles=[0, np.pi/4, np.pi/2, 3*np.pi/4],
                         levels=256, symmetric=True, normed=True)

    features = {
        'contrast': graycoprops(glcm, 'contrast').mean(),
        'dissimilarity': graycoprops(glcm, 'dissimilarity').mean(),
        'homogeneity': graycoprops(glcm, 'homogeneity').mean(),
        'energy': graycoprops(glcm, 'energy').mean(),
        'correlation': graycoprops(glcm, 'correlation').mean(),
        'mean_intensity': image.mean(),
        'std_intensity': image.std()
    }

    return np.array(list(features.values()))

def train_phase_classifier(labeled_patches):
    """
    相分類器の訓練

    Args:
        labeled_patches: [(image, label), ...] のリスト
                         label: 0=Ferrite, 1=Pearlite, 2=Martensite

    Returns:
        trained_model: 訓練済みRandom Forest
    """
    X = []
    y = []

    for patch, label in labeled_patches:
        features = extract_texture_features(patch)
        X.append(features)
        y.append(label)

    X = np.array(X)
    y = np.array(y)

    # 訓練/テスト分割(80/20)
    X_train, X_test, y_train, y_test = train_test_split(
        X, y, test_size=0.2, random_state=42, stratify=y
    )

    # Random Forest訓練
    model = RandomForestClassifier(n_estimators=100,
                                    max_depth=10,
                                    random_state=42)
    model.fit(X_train, y_train)

    # 評価
    y_pred = model.predict(X_test)
    print("Classification Report:")
    print(classification_report(y_test, y_pred,
                                 target_names=['Ferrite', 'Pearlite', 'Martensite']))

    return model

# 使用例(教師データは事前に準備)
# labeled_patches = load_labeled_data('steel_phases.npz')
# phase_model = train_phase_classifier(labeled_patches)

# 出力例:
#               precision    recall  f1-score   support
# Ferrite          0.92      0.94      0.93       120
# Pearlite         0.89      0.87      0.88        98
# Martensite       0.95      0.94      0.94        82
# accuracy                             0.92       300

5.4.2 深層学習:CNNによる組織認識

畳み込みニューラルネットワーク(CNN)は、手動特徴量抽出を必要とせず、画像から直接パターンを学習します。複雑な組織(デンドライト、球状化組織など)の識別に特に有効です。

Example 7: シンプルなCNNによる組織分類

import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import DataLoader, TensorDataset

class MicrostructureCNN(nn.Module):
    """
    組織画像分類用のシンプルなCNN
    """
    def __init__(self, num_classes=3):
        super(MicrostructureCNN, self).__init__()

        # 畳み込み層
        self.conv1 = nn.Conv2d(1, 32, kernel_size=3, padding=1)
        self.conv2 = nn.Conv2d(32, 64, kernel_size=3, padding=1)
        self.pool = nn.MaxPool2d(2, 2)

        # 全結合層
        self.fc1 = nn.Linear(64 * 16 * 16, 128)
        self.fc2 = nn.Linear(128, num_classes)

        self.relu = nn.ReLU()
        self.dropout = nn.Dropout(0.5)

    def forward(self, x):
        x = self.pool(self.relu(self.conv1(x)))
        x = self.pool(self.relu(self.conv2(x)))
        x = x.view(-1, 64 * 16 * 16)
        x = self.relu(self.fc1(x))
        x = self.dropout(x)
        x = self.fc2(x)
        return x

def train_cnn_classifier(train_loader, val_loader, epochs=20):
    """
    CNNモデルの訓練

    Args:
        train_loader: 訓練データローダー
        val_loader: 検証データローダー
        epochs: エポック数

    Returns:
        trained_model: 訓練済みモデル
    """
    device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
    model = MicrostructureCNN(num_classes=3).to(device)

    criterion = nn.CrossEntropyLoss()
    optimizer = optim.Adam(model.parameters(), lr=0.001)

    for epoch in range(epochs):
        model.train()
        train_loss = 0.0

        for images, labels in train_loader:
            images, labels = images.to(device), labels.to(device)

            optimizer.zero_grad()
            outputs = model(images)
            loss = criterion(outputs, labels)
            loss.backward()
            optimizer.step()

            train_loss += loss.item()

        # 検証
        model.eval()
        val_correct = 0
        val_total = 0

        with torch.no_grad():
            for images, labels in val_loader:
                images, labels = images.to(device), labels.to(device)
                outputs = model(images)
                _, predicted = torch.max(outputs, 1)
                val_total += labels.size(0)
                val_correct += (predicted == labels).sum().item()

        val_acc = 100 * val_correct / val_total
        print(f"Epoch {epoch+1}/{epochs}, Loss: {train_loss/len(train_loader):.4f}, Val Acc: {val_acc:.2f}%")

    return model

# 使用例(データは事前に準備)
# train_loader = DataLoader(train_dataset, batch_size=32, shuffle=True)
# val_loader = DataLoader(val_dataset, batch_size=32, shuffle=False)
# cnn_model = train_cnn_classifier(train_loader, val_loader, epochs=20)

# 出力例:
# Epoch 1/20, Loss: 0.8234, Val Acc: 72.45%
# Epoch 5/20, Loss: 0.3156, Val Acc: 88.32%
# Epoch 20/20, Loss: 0.0521, Val Acc: 95.18%

5.5 統合ワークフロー:画像から特性予測まで

ここまで学んだ各要素を統合し、組織画像から材料特性を予測するエンドツーエンドのパイプラインを構築します。

```mermaid
flowchart LR
    A[SEM画像] --> B[前処理]
    B --> C[セグメンテーション]
    C --> D[特徴量抽出]
    D --> E[粒径分布]
    D --> F[形態記述子]
    D --> G[方位情報]
    E --> H[特性予測モデル]
    F --> H
    G --> H
    H --> I[機械的性質強度・延性]

    style A fill:#e3f2fd
    style I fill:#e8f5e9
    style H fill:#fff3e0
```

💡 実践的アドバイス

特性予測モデルの精度向上には、以下が重要です:

5.5.1 実践例:鋼材の強度予測

粒径分布と相分率から、Hall-Petch則に基づく降伏強度を予測するモデルを構築します:

$$\sigma_y = \sigma_0 + k_y d^{-1/2}$$

ここで、$\sigma_0$は摩擦応力、$k_y$はHall-Petchスロープ、$d$は平均粒径です。

# 実践的な予測パイプライン(統合例)
def predict_yield_strength(image_path, model_params):
    """
    組織画像から降伏強度を予測

    Args:
        image_path: SEM画像パス
        model_params: {'sigma_0': 70, 'k_y': 0.74}(MPa・μm^0.5)

    Returns:
        predicted_strength: 予測降伏強度 [MPa]
    """
    # 1. 前処理
    results = preprocess_sem_image(image_path)

    # 2. セグメンテーション
    grains = segment_grains_watershed(results['enhanced'])

    # 3. 粒径分布解析
    grain_stats = analyze_grain_size_distribution(grains, pixel_size_um=0.5)

    # 4. 平均粒径の計算
    d_mean = grain_stats['diameter_um'].mean()

    # 5. Hall-Petch式による強度予測
    sigma_0 = model_params['sigma_0']
    k_y = model_params['k_y']
    sigma_y = sigma_0 + k_y / np.sqrt(d_mean)

    print(f"平均粒径: {d_mean:.2f} μm")
    print(f"予測降伏強度: {sigma_y:.1f} MPa")

    return sigma_y

# 使用例
model_params = {'sigma_0': 70, 'k_y': 0.74}  # 低炭素鋼の典型値
predicted_ys = predict_yield_strength('steel_sample.tif', model_params)
# 出力:
# 平均粒径: 12.34 μm
# 予測降伏強度: 281.3 MPa

5.6 高度な解析技法

5.6.1 3D組織再構成

連続断面画像(Serial Sectioning)やX線CTデータから、3次元の組織構造を再構成できます。3D解析により、真の粒径分布・粒界面積・相互連結性などが定量化できます。

3D再構成の基本ステップ

  1. 画像スタックの取得 : FIB-SEM、X線CT、連続研磨法で複数のスライス画像を取得
  2. 位置合わせ(Registration) : スライス間のずれを補正し、3Dボリュームを構築
  3. 3Dセグメンテーション : 各粒を3次元空間で識別
  4. ステレオロジー補正 : 2D測定から3D構造パラメータを推定

📘 ステレオロジーの補正公式

2D観察から3D構造を推定する際、以下の補正が必要です:

5.6.2 粒界工学への応用

粒界工学(Grain Boundary Engineering, GBE)は、特殊粒界(特にΣ3双晶境界)の割合を増やすことで材料特性を改善する技術です。EBSDデータから粒界の種類を分類し、GBE処理の効果を定量評価できます。

粒界分類の実践

from orix.quaternion import Misorientation
import numpy as np

def classify_grain_boundaries(xmap, threshold_deg=15):
    """
    粒界を小傾角/大傾角/特殊粒界に分類

    Args:
        xmap: CrystalMap(EBSD方位マップ)
        threshold_deg: 小傾角粒界の閾値 [度]

    Returns:
        dict: 粒界タイプごとの統計
    """
    # 隣接ピクセル間のミスオリエンテーション計算
    misorientations = []
    for i in range(xmap.shape[0]-1):
        for j in range(xmap.shape[1]-1):
            ori1 = xmap[i, j].orientations
            ori2 = xmap[i, j+1].orientations  # 右隣

            if ori1.size > 0 and ori2.size > 0:
                misori = ori1.angle_with(ori2)
                misorientations.append(np.degrees(misori))

    misorientations = np.array(misorientations)

    # 粒界分類
    lagb = misorientations[(misorientations > 2) &
                            (misorientations <= threshold_deg)]
    hagb = misorientations[misorientations > threshold_deg]

    # Σ3双晶の検出(60° ± 5°)
    sigma3_candidates = misorientations[
        (misorientations >= 55) & (misorientations <= 65)
    ]

    stats = {
        'total_boundaries': len(misorientations),
        'LAGB_count': len(lagb),
        'HAGB_count': len(hagb),
        'Sigma3_count': len(sigma3_candidates),
        'LAGB_fraction': len(lagb) / len(misorientations),
        'HAGB_fraction': len(hagb) / len(misorientations),
        'Sigma3_fraction': len(sigma3_candidates) / len(misorientations)
    }

    print(f"粒界統計:")
    print(f"  小傾角粒界(LAGB): {stats['LAGB_fraction']*100:.1f}%")
    print(f"  大傾角粒界(HAGB): {stats['HAGB_fraction']*100:.1f}%")
    print(f"  Σ3双晶候補: {stats['Sigma3_fraction']*100:.1f}%")

    return stats

# 使用例
# gb_stats = classify_grain_boundaries(ebsd_map, threshold_deg=15)
# 出力:
# 粒界統計:
#   小傾角粒界(LAGB): 23.4%
#   大傾角粒界(HAGB): 76.6%
#   Σ3双晶候補: 18.2%

5.6.3 時系列解析:その場観察データ

高温顕微鏡やその場SEM観察により、加熱・変形過程での組織変化を動的に追跡できます。時系列画像解析により、粒成長速度・再結晶挙動・相変態速度を定量化できます。

粒成長の動的追跡

import cv2
import numpy as np
from scipy.stats import linregress

def track_grain_growth(image_sequence, timestamps, pixel_size_um):
    """
    時系列画像から粒成長速度を計算

    Args:
        image_sequence: [img1, img2, ...] 時系列画像リスト
        timestamps: [t1, t2, ...] 各画像の時刻 [秒]
        pixel_size_um: ピクセルサイズ [μm]

    Returns:
        dict: 粒成長パラメータ
    """
    mean_diameters = []

    for img in image_sequence:
        # 前処理とセグメンテーション
        preprocessed = preprocess_sem_image_array(img)
        grains = segment_grains_watershed(preprocessed)

        # 平均粒径計算
        grain_stats = analyze_grain_size_distribution(
            grains, pixel_size_um
        )
        mean_diameters.append(grain_stats['diameter_um'].mean())

    mean_diameters = np.array(mean_diameters)
    timestamps = np.array(timestamps)

    # 粒成長則のフィッティング: d^n - d0^n = k*t
    # 正常粒成長の場合 n=2 (Burke-Turnbull式)
    d_squared = mean_diameters ** 2
    d0_squared = d_squared[0]

    slope, intercept, r_value, _, _ = linregress(
        timestamps, d_squared - d0_squared
    )

    growth_rate = slope  # [μm²/s]

    print(f"粒成長速度定数 k = {growth_rate:.4e} μm²/s")
    print(f"相関係数 R² = {r_value**2:.4f}")

    # 活性化エネルギーの推定(複数温度のデータがある場合)
    # Q = -R * slope(ln(k) vs. 1/T)

    return {
        'growth_rate': growth_rate,
        'initial_diameter': mean_diameters[0],
        'final_diameter': mean_diameters[-1],
        'r_squared': r_value**2,
        'diameters': mean_diameters,
        'times': timestamps
    }

# 使用例(1000°Cでの等温保持)
# image_seq = [load_image(f'frame_{i}.tif') for i in range(10)]
# times = np.linspace(0, 3600, 10)  # 0~3600秒
# growth_data = track_grain_growth(image_seq, times, pixel_size_um=0.5)
# 出力:
# 粒成長速度定数 k = 2.34e-03 μm²/s
# 相関係数 R² = 0.9876

5.7 実践のポイントとトラブルシューティング

5.7.1 よくある問題と対処法

問題原因対処法
過剰セグメンテーションノイズが粒として誤検出前処理でノイズ除去を強化、最小粒サイズでフィルタリング(例: area > 100 pixels
接触粒の分離失敗Watershedのパラメータ不適切Distance transformの閾値を調整(0.3~0.7を試す)、モルフォロジー演算を追加
粒径の過小評価立体視補正の欠如補正係数を適用(2D→3D変換、$d_{3D} \approx 1.27 d_{2D}$)
機械学習モデルの過学習訓練データ不足、複雑すぎるモデルデータ拡張、Dropout、正則化、交差検証の実施
EBSD低CI領域の増加試料表面の酸化・変形層イオンミリングで表面を再研磨、測定前に真空保管
位相分類精度の低下照明条件の変動、試料間の組織差画像正規化(ヒストグラムマッチング)、ドメイン適応技術の適用

5.7.2 精度向上のテクニック

📘 高度なテクニック

5.7.3 パフォーマンス最適化

大量の画像データを処理する際、計算時間とメモリ使用量が課題となります。以下の最適化技術が有効です:

並列処理の実装

from concurrent.futures import ProcessPoolExecutor
from functools import partial
import multiprocessing as mp

def process_single_image(image_path, pixel_size_um):
    """
    1枚の画像を処理する関数
    """
    results = preprocess_sem_image(image_path)
    grains = segment_grains_watershed(results['enhanced'])
    stats = analyze_grain_size_distribution(grains, pixel_size_um)
    return stats

def batch_process_images(image_paths, pixel_size_um, n_workers=None):
    """
    複数画像の並列処理

    Args:
        image_paths: 画像ファイルパスのリスト
        pixel_size_um: ピクセルサイズ
        n_workers: 並列ワーカー数(Noneで自動設定)

    Returns:
        list: 各画像の解析結果
    """
    if n_workers is None:
        n_workers = mp.cpu_count() - 1  # 1コア残す

    process_func = partial(process_single_image,
                           pixel_size_um=pixel_size_um)

    with ProcessPoolExecutor(max_workers=n_workers) as executor:
        results = list(executor.map(process_func, image_paths))

    print(f"{len(image_paths)}枚の画像を{n_workers}並列で処理完了")
    return results

# 使用例
image_files = [f'sample_{i:03d}.tif' for i in range(100)]
all_results = batch_process_images(image_files, pixel_size_um=0.5)
# 出力: 100枚の画像を7並列で処理完了
# 処理時間: 単一スレッド比で約6倍高速化

5.7.4 品質管理と検証

解析結果の信頼性を確保するため、以下の品質管理指標を常にモニタリングします:

指標計算方法許容範囲異常時の対策
セグメンテーション精度手動ラベルとの一致率(IoU)IoU > 0.85パラメータ再調整、手動補正
粒数の妥当性単位面積あたりの粒数既知データとの±20%以内過剰/過少セグメンテーションの確認
形状分布の正規性Shapiro-Wilk検定p > 0.05(正規分布)外れ値除去、対数変換
再現性同一試料の複数測定のCV値CV < 10%測定条件の標準化

5.8 オープンソースツールとライブラリ

5.8.1 推奨Pythonライブラリ

ライブラリ主な機能インストール推奨度
scikit-image画像処理全般、セグメンテーションpip install scikit-image⭐⭐⭐⭐⭐
OpenCV高速画像処理、Watershed、形態演算pip install opencv-python⭐⭐⭐⭐⭐
orixEBSD解析、結晶方位処理pip install orix⭐⭐⭐⭐⭐
PyTorch深層学習、CNN実装pip install torch torchvision⭐⭐⭐⭐
PyEBSDIndexEBSDパターンのインデキシングpip install pyebsdindex⭐⭐⭐⭐
porespy多孔質材料の3D解析pip install porespy⭐⭐⭐
trackpy粒子追跡、時系列解析pip install trackpy⭐⭐⭐

5.8.2 データセットとベンチマーク

研究と実装検証に活用できる公開データセットを紹介します:

データセット名内容サンプル数URL
UHCSDB超高炭素鋼の組織画像~1,000画像GitHub
Steel Microstructure鋼の多相組織(Kaggleコンペ)~600画像(ラベル付き)Kaggle
Microscopy DatasetSEM/TEM画像(多材料)~5,000画像NIST
DREAM.3D Exemplar3D合成組織データシミュレーション生成DREAM.3D

💡 実践的アドバイス

独自データセットを構築する際のベストプラクティス:

5.9 応用事例:実際の研究プロジェクトから

5.9.1 自動車用鋼板の品質管理

課題 : 高強度鋼板の製造ラインにおいて、オンライン品質検査でミクロ組織を自動評価し、不良品を早期検出する。

解決策 :

  1. ライン上のSEMで10秒ごとに組織画像を取得
  2. リアルタイム画像処理で粒径分布とマルテンサイト分率を計算
  3. Random Forestで「良品/不良品」を分類(精度92%達成)
  4. 不良品検出時に自動でアラートを発信し、製造条件を微調整

成果 : 不良品率を3.2%から0.8%に削減、年間コスト削減効果2億円

5.9.2 チタン合金の組織最適化

課題 : 航空機用Ti-6Al-4V合金の熱処理条件を最適化し、疲労強度を最大化する。

解決策 :

  1. 30種類の熱処理条件で試料を作製し、EBSD測定
  2. 粒径、アスペクト比、Σ3分率、集合組織強度を特徴量として抽出
  3. Gradient Boostingで疲労強度を予測するモデルを構築(R²=0.89)
  4. ベイズ最適化で最適熱処理条件を探索(10サイクルで収束)

成果 : 疲労強度が従来条件比15%向上、開発期間を18ヶ月から6ヶ月に短縮

5.9.3 AM(積層造形)材料の品質認証

課題 : 3Dプリント部品の微細組織が設計通りか、全数検査が困難。

解決策 :

  1. X線CTで内部組織を非破壊3D撮影
  2. U-Netで気孔・未溶融部・デンドライト組織をセグメント
  3. 気孔率、デンドライト腕間隔、方位分布を自動計算
  4. 規格値との比較で合格/不合格を自動判定

成果 : 検査時間を8時間から30分に短縮、認証コストを70%削減

学習目標の確認

このchapterを完了すると、以下を説明できるようになります:

基本理解

実践スキル

応用力

演習問題

Easy(基礎確認)

Q1: CLAHE(Contrast Limited Adaptive Histogram Equalization)を用いるメリットは何ですか?通常のヒストグラム平坦化との違いを説明してください。

正解 :

CLAHEのメリットは、局所的なコントラスト強調 ができる点です。

通常のヒストグラム平坦化との違い :

材料組織解析での利点 :

Q2: Watershed法で「過剰セグメンテーション」が発生する原因を2つ挙げ、それぞれの対策を説明してください。

正解 :

原因1: ノイズによる局所的な輝度変動

原因2: マーカーの過剰生成

Q3: 真円度(Circularity)が0.5の粒と0.9の粒では、どちらがより円に近い形状ですか?また、真円度1.0は何を意味しますか?

正解 :

真円度0.9の粒 がより円に近い形状です。

真円度の定義 :

$$C = \frac{4\pi A}{P^2}$$

真円度1.0の意味 :

材料工学的意味 :

Medium(応用)

Q4: ある鋼材のSEM画像から、平均粒径12 μmと測定されました。Hall-Petch式($\sigma_y = 70 + 0.74 / \sqrt{d}$、単位:MPa, μm)を用いて降伏強度を計算してください。さらに、粒径を8 μmに微細化した場合の強度増加量を求めてください。

計算 :

ケース1: 平均粒径12 μm

$$\sigma_y = 70 + \frac{0.74}{\sqrt{12}} = 70 + \frac{0.74}{3.464} = 70 + 0.214 = 70.2 , \text{MPa}$$

(注:実際には$k_y$の単位は$\text{MPa} \cdot \mu\text{m}^{0.5}$なので、)

$$\sigma_y = 70 + \frac{0.74}{\sqrt{12}} \times 1000 = 70 + 213.7 = 283.7 , \text{MPa}$$

ケース2: 平均粒径8 μm(微細化後)

$$\sigma_y = 70 + \frac{0.74}{\sqrt{8}} \times 1000 = 70 + 261.5 = 331.5 , \text{MPa}$$

強度増加量 :

$$\Delta \sigma_y = 331.5 - 283.7 = 47.8 , \text{MPa}$$

解説 :

Q5: Random Forestで相分類モデルを訓練したところ、訓練データでの精度95%、テストデータでの精度65%でした。この問題を診断し、改善策を3つ提案してください。

診断 :

この状況は典型的な過学習(Overfitting) です。訓練データに対してモデルが過度に適合し、未知データへの汎化性能が低下しています。

改善策 :

  1. 正則化の強化
    • max_depthを制限(例: 10 → 5)
    • min_samples_splitを増加(例: 2 → 10)
    • min_samples_leafを増加(例: 1 → 5)
    • 効果: 決定木の複雑さを抑制し、過学習を防ぐ
  2. 訓練データの拡張
    • 画像の回転、反転、ノイズ付加でデータ数を増やす
    • 異なる試料・条件の画像を追加して多様性を確保
    • 効果: モデルが多様なパターンを学習し、汎化性能が向上
  3. 交差検証による評価
    • K-fold交差検証(K=5または10)で安定性を確認
    • 各foldでの性能を平均化し、モデルの信頼性を評価
    • 効果: 特定のデータ分割に依存しない性能評価が可能

追加の診断手法 :

Hard(発展)

Q6: 多相材料(フェライト60%、パーライト30%、マルテンサイト10%)の組織画像から、各相の粒径分布を独立に解析したいです。セグメンテーション後に相を分類し、相ごとに粒径解析を行う統合パイプラインを設計してください。主要なステップとPythonの擬似コードを示してください。

統合パイプライン設計 :

ステップ1: 前処理とセグメンテーション

# 1. 画像前処理
preprocessed = preprocess_sem_image(image_path)

# 2. Watershedで全粒をセグメンテーション
labeled_grains = segment_grains_watershed(preprocessed['enhanced'])

ステップ2: 各粒の相分類

# 3. 各粒領域の特徴量抽出と相分類
from skimage.measure import regionprops

phase_labels = np.zeros_like(labeled_grains)
phase_classifier = load_trained_model('phase_rf_model.pkl')

for region in regionprops(labeled_grains, intensity_image=preprocessed['enhanced']):
    # 粒内部の平均画像パッチを抽出
    minr, minc, maxr, maxc = region.bbox
    patch = preprocessed['enhanced'][minr:maxr, minc:maxc]

    # 特徴量抽出
    features = extract_texture_features(patch)

    # 相分類(0:Ferrite, 1:Pearlite, 2:Martensite)
    phase = phase_classifier.predict([features])[0]

    # 相ラベル画像に記録
    phase_labels[labeled_grains == region.label] = phase + 1

ステップ3: 相ごとの粒径分布解析

# 4. 相ごとに粒径統計を計算
phase_names = {1: 'Ferrite', 2: 'Pearlite', 3: 'Martensite'}
phase_stats = {}

for phase_id, phase_name in phase_names.items():
    # 特定相の粒のみ抽出
    phase_mask = (phase_labels == phase_id)
    phase_grains = labeled_grains * phase_mask

    # 粒径分布解析
    grain_df = analyze_grain_size_distribution(phase_grains, pixel_size_um=0.5)

    phase_stats[phase_name] = {
        'count': len(grain_df),
        'mean_diameter': grain_df['diameter_um'].mean(),
        'std_diameter': grain_df['diameter_um'].std(),
        'volume_fraction': phase_mask.sum() / labeled_grains.size
    }

# 5. 結果の可視化
import pandas as pd
summary = pd.DataFrame(phase_stats).T
print(summary)

期待される出力 :

           count  mean_diameter  std_diameter  volume_fraction
Ferrite      234          14.2           5.3            0.62
Pearlite     102           8.7           3.1            0.28
Martensite    48           5.3           2.0            0.10
        

発展的な改善 :

Q7: U-Netなどのセマンティックセグメンテーションネットワークを用いると、従来のWatershed法と比べてどのような利点がありますか?また、材料組織解析に適用する際の課題を3つ挙げてください。

U-Netの利点 :

  1. エンドツーエンド学習
    • 前処理・閾値決定・後処理のパラメータチューニングが不要
    • 生画像から直接ピクセルレベルのセグメンテーションを出力
  2. 複雑な境界の認識
    • 不明瞭な粒界や多相境界を学習データから自動認識
    • Watershed法では分離困難な接触粒も、深層学習で正確に分離可能
  3. 文脈情報の活用
    • U-Netのエンコーダー-デコーダー構造により、局所的特徴とグローバルな文脈の両方を統合
    • 例:周囲の粒の配置から、曖昧な境界の位置を推論
  4. 多クラスセグメンテーション
    • 粒界・フェライト・パーライト・マルテンサイトなどを同時にセグメント
    • Watershed法では困難な、複雑な組織の一括処理が可能

材料組織解析における課題 :

  1. 大量のラベル付きデータの必要性
    • U-Net訓練には、ピクセルレベルでアノテーションされた画像が数百~数千枚必要
    • 専門家による手動ラベリングは時間とコストがかかる(1画像あたり30分~2時間)
    • 対策: 半教師あり学習、弱教師あり学習、データ拡張の活用
  2. 汎化性能の課題
    • 特定の材料・観察条件で訓練したモデルは、異なる条件で性能低下
    • 例:炭素鋼で訓練したモデルは、ステンレス鋼やアルミ合金に適用困難
    • 対策: ドメイン適応(Domain Adaptation)、転移学習、マルチドメイン訓練
  3. 計算コストと解釈性
    • 高解像度画像(4096×4096ピクセル)の処理には大容量GPUが必要
    • ブラックボックスモデルのため、誤検出の原因分析が困難
    • 材料研究者の信頼を得るには、予測の根拠説明が重要
    • 対策: Grad-CAMなどの可視化手法、ハイブリッドモデル(U-Net + 後処理)

実践的な推奨 :

Q8: オブジェクト検出モデル(YOLO、Faster R-CNN)を用いて、材料組織中の特定の欠陥(介在物、ボイド、クラック)を検出するシステムを設計してください。精度評価指標(mAP: mean Average Precision)についても説明してください。

解答のポイント :

簡易実装例 :

import torch
model = torch.hub.load('ultralytics/yolov5', 'custom', path='weights/defect_detector.pt')
results = model('sample_microstructure.png')
detections = results.pandas().xyxy[0]  # DataFrame形式で結果取得
print(f"検出数: {len(detections)}, mAP@0.5: {model.metrics['mAP_0.5']:.3f}")

Q9: EBSD(Electron Backscatter Diffraction)データから、粒界性格分布(Grain Boundary Character Distribution, GBCD)を計算し、Σ3双晶粒界の割合を定量化するPythonコードを作成してください。

解答のポイント :

簡易実装例 :

from orix import io
xmap = io.load('sample_ebsd.ang')
grain_boundaries = xmap.get_grain_boundaries(threshold=5)  # 方位差 > 5°
misorientation_angles = [np.degrees(gb.misorientation.angle) for gb in grain_boundaries]
sigma3_count = sum([1 for angle in misorientation_angles if abs(angle - 60) < 8.66])
sigma3_fraction = sigma3_count / len(misorientation_angles) * 100
print(f"Σ3双晶粒界の割合: {sigma3_fraction:.1f}%")

Q10: 材料組織画像の自動解析システムを実用化する際の課題(データ収集、モデル選択、システム統合、保守)について、それぞれの解決策と具体的な実装例を提案してください。

解答例 :

課題問題点解決策
データ収集高品質ラベル付きデータが不足Active Learning、半教師あり学習、自己教師あり学習
モデル選択精度、速度、解釈性のトレードオフ2段階アプローチ:高速モデル(スクリーニング)+ 高精度モデル(精査)
ドメイン適応撮影条件の変化でモデル性能劣化Histogram Matching、CLAHE、データ拡張
システム統合既存設備(SEM、EBSD)との連携REST API、gRPC、Python-C++連携(pybind11)
保守・更新モデルの経年劣化、新材料への対応継続学習(Continual Learning)、MLOps(MLflow)

実装例:FastAPIによるREST APIデプロイ :

from fastapi import FastAPI, File, UploadFile
app = FastAPI()

@app.post("/analyze")
async def analyze_image(file: UploadFile = File(...)):
    # 画像読み込み → 前処理 → モデル推論 → 結果返却
    results = model.predict(preprocess(file))
    return {"status": "success", "features": results}

# uvicorn main:app --host 0.0.0.0 --port 8000

✓ 学習目標の確認

この章を完了すると、以下を説明・実行できるようになります:

基本理解

実践スキル

応用力

次のステップ :

本章で習得したPython組織解析スキルは、材料科学の広範な分野に応用できます。次は以下の領域に挑戦してみましょう:

📚 参考文献

  1. Gonzalez, R.C., Woods, R.E. (2018). Digital Image Processing (4th ed.). Pearson. ISBN: 978-0133356724
  2. Modin, H., Modin, S. (1973). Metallurgical Microscopy. Butterworth-Heinemann. ISBN: 978-0408705806
  3. Azimi, S.M., Britz, D., Engstler, M., Fritz, M., Mücklich, F. (2018). “Advanced Steel Microstructural Classification by Deep Learning Methods.” Scientific Reports , 8(1), 2128. DOI:10.1038/s41598-018-20037-5
  4. DeCost, B.L., Lei, B., Francis, T., Holm, E.A. (2019). “High Throughput Quantitative Metallography for Complex Microstructures Using Deep Learning: A Case Study in Ultrahigh Carbon Steel.” Microscopy and Microanalysis , 25(1), 21-29. DOI:10.1017/S1431927618015635
  5. Chowdhury, A., Kautz, E., Yener, B., Lewis, D. (2016). “Image driven machine learning methods for microstructure recognition.” Computational Materials Science , 123, 176-187. DOI:10.1016/j.commatsci.2016.05.034
  6. Kaufmann, K., Zhu, C., Rosengarten, A.S., Maryanovsky, D., Harrington, T.J., Marin, E., Vecchio, K.S. (2020). “Crystal symmetry determination in electron diffraction using machine learning.” Science , 367(6477), 564-568. DOI:10.1126/science.aay3062
  7. Cecen, A., Dai, H., Yabansu, Y.C., Kalidindi, S.R., Song, L. (2018). “Material structure-property linkages using three-dimensional convolutional neural networks.” Acta Materialia , 146, 76-84. DOI:10.1016/j.actamat.2017.11.053
  8. Callister, W.D., Rethwisch, D.G. (2020). Materials Science and Engineering: An Introduction (10th ed.). Wiley. ISBN: 978-1119405498

Pythonライブラリ・ツール

オープンデータセット

章のまとめ

本章では、Pythonを用いた材料組織の定量解析手法を、基礎から実践まで網羅的に学びました。以下に重要なポイントをまとめます。

習得した技術スタック

技術領域主要手法Pythonライブラリ応用範囲
画像前処理ノイズ除去、CLAHE、シャープニングOpenCV、scikit-imageSEM、光学顕微鏡、TEM画像
セグメンテーションWatershed法、閾値処理OpenCV、scipy、skimage結晶粒、相境界、欠陥検出
形態解析粒径分布、形態記述子skimage.measure、pandas粒成長、再結晶評価
EBSD解析方位マッピング、粒界分類orix、PyEBSDIndex集合組織、粒界工学
機械学習Random Forest、CNNscikit-learn、PyTorch相分類、組織認識、特性予測
3D解析連続断面再構成、ステレオロジーporespy、trackpy真の粒径、連結性評価

実践における重要な教訓

「完璧なアルゴリズムは存在しない。重要なのは、材料とデータに適した手法を選び、継続的に検証・改善することである。」

組織解析で成功するための5つの鉄則:

  1. 品質の高い入力データ : 画像解析の精度は、元画像の品質に直接依存します。撮影条件の標準化が最優先
  2. ドメイン知識の統合 : 機械学習モデルに材料科学の知見(物理法則、経験則)を組み込むことで、精度と解釈性が向上
  3. 段階的アプローチ : 単純な手法から始め、必要に応じて高度な手法に移行。最初からCNNを使う必要はない
  4. 徹底的な検証 : 自動解析結果は必ず手動測定や既知データと比較検証。盲目的な信頼は禁物
  5. 再現性の確保 : コードのバージョン管理、パラメータの文書化、乱数シードの固定で、結果の再現性を保証

今後の学習ロードマップ

```mermaid
flowchart TD
    A[本章の基礎習得] --> B{興味の方向性}
    B -->|より高度な解析| C[深層学習の深掘り]
    B -->|実用化重視| D[産業応用プロジェクト]
    B -->|研究志向| E[最新論文の実装]

    C --> C1[U-Net, Mask R-CNN実装]
    C --> C2[転移学習とファインチューニング]
    C --> C3[自己教師あり学習]

    D --> D1[リアルタイム画像処理]
    D --> D2[製造ライン組込み]
    D --> D3[品質管理システム構築]

    E --> E1[最新アーキテクチャ(Transformer)]
    E --> E2[マルチモーダル解析]
    E --> E3[不確実性定量化]

    style A fill:#e3f2fd
    style C fill:#fff3e0
    style D fill:#e8f5e9
    style E fill:#f3e5f5
```

推奨学習リソース

書籍

オンラインコース

重要論文(過去5年)

  1. Azimi et al., “Advanced Steel Microstructural Classification by Deep Learning Methods”, Scientific Reports , 2018
  2. DeCost et al., “Computer Vision and Machine Learning for Autonomous Characterization of AM Powder Feedstocks”, JOM , 2017
  3. Chowdhury et al., “Image Driven Machine Learning Methods for Microstructure Recognition”, Comp. Mat. Sci. , 2016
  4. Kaufmann et al., “Crystal Symmetry Determination in Electron Diffraction Using Machine Learning”, Science , 2020
  5. Cecen et al., “Material Structure-Property Linkages Using Three-Dimensional Convolutional Neural Networks”, Acta Materialia , 2018

実践プロジェクトのアイデア

次のステップ

本章で習得した画像解析と機械学習の技術は、材料科学の多様な分野に応用可能です。これらの手法を実際の研究・開発プロジェクトに適用し、継続的に改善していくことが重要です。

💡 学習を深めるために

📘 さらなる発展へ

本シリーズを完了したあなたは、材料組織の定量解析に必要な基礎と実践スキルを身につけました。次は以下の領域に挑戦してみましょう:

免責事項