9.1 ターゲットおよびリスク臓器のDVH情報のチェック

目的

ターゲットおよびリスク臓器のDVH情報(Dxx, Vxx, Dmean, Dmax, Dmin)を取得する。

必要な情報

与えられている引数

PlanSetupクラスのインスタンスplan

必要な情報へのアクセス方法

現在開いているプランの輪郭情報へアクセスするには、以下のようにします。

  1. 輪郭情報の格納された変数Structuresを取得する

    複数の輪郭情報はシーケンスに格納されており、指定されたインデックスの要素を取得するためにメンバ関数 ElementAt() またはforeach構文やLINQ構文を使用します。

    例1 : メンバ関数ElementAt()の引数に0を指定することで最初の輪郭情報を取得できます。

    1
    2
    var structure = plan.StructureSet.Structures.ElementAt(0);
    // planはPlanSetupクラスのインスタンス
    

    例2: foreach構文で目的の輪郭を探す場合

    1
    2
    3
    4
    5
    6
    7
    8
    9
    foreach (var structure in plan.StructureSet.Structures)
    {
        //IF構文で目的の輪郭であるかを判定
        if()
        {
            〜〜 処理を記入する 〜〜
        }
    
    }
    

    目的とする輪郭名をIF構文の判定基準とするときに、 変数structuresから輪郭名とDICOMタイプ(CTV等)を以下の方法で取得できます。

    1
    2
    3
    4
    5
    // 輪郭名の取得
    string structure_id = structure.Id;
    
    // DICOMタイプの取得
    string dicom_type = structure.DicomType;
    

    例3: LINQにより目的の輪郭を探す場合

    1
    2
    3
    4
    // 輪郭を取得
    var structures = plan.StructureSet.Structures;
    // DicomTypeがPTVのものだけ取り出す
    var ptvs = structures.Where(s => s.DicomType == "PTV");
    
  2. DVHデータ(Dxx, Vxx, Dmean, Dmax, Dmin)を取得する

    DVHデータを取得するには以下のメンバ関数を使用します。

    • DxxGy or Dxx%を取得する: GetDoseAtVolume()
    • VxxGyを取得する: GetVolumeAtDose()
    • Dmax, Dmin, Dmeanを取得する: GetDVHCumulativeData()

    それぞれの関数を使用したD95%, D1cc, V20Gy, 平均線量, 最大線量, 最小線量の取得方法の例は以下のとおりです。

    D95%

    1
    DoseValue d_95 = plan.GetDoseAtVolume(structure, 95.0, VolumePresentation.Relative, DoseValuePresentation.Absolute);
    

    D1cc

    1
    DoseValue d_1cc = plan.GetDoseAtVolume(structure, 1.0, VolumePresentation.Absolute, DoseValuePresentation.Absolute);
    

    V20Gy

    1
    2
    DoseValue dose_index = new DoseValue(2000.0, "cGy");
    double v_20 = plan.GetVolumeAtDose(structure, dose_index, VolumePresentation.Relative);
    

    平均線量(Dmean)

    1
    2
    double binWidth = 0.001; // DVHのビンサイズの設定
    double d_mean = plan.GetDVHCumulativeData(structure, DoseValuePresentation.Absolute, VolumePresentation.Relative, binWidth).MeanDose.Dose;
    

    最小線量(Dmin)

    1
    2
    double binWidth = 0.001; // DVHのビンサイズの設定
    double d_min = plan.GetDVHCumulativeData(structure, DoseValuePresentation.Absolute, VolumePresentation.Relative, binWidth).MinDose.Dose;
    

    最大線量(Dmax)

    1
    2
    double binWidth = 0.001; // DVHのビンサイズの設定
    double d_max = plan.GetDVHCumulativeData(structure, DoseValuePresentation.Absolute, VolumePresentation.Relative, binWidth).MaxDose.Dose;
    

メッセージボックスによる情報の表示

思ったように動作しない場合には(条件判定等)、メッセージボックスを使用して変数に代入されている値を確認しましょう。

メッセージボックスの使用方法は次のとおりです。

1
2
3
4
5
6
7
int ans = 1 + 1;

// MessageBoxで表示できるのが文字列のためToStringメソッドを使用して変換を行う
String str_ans = ans.ToString ();

//Showメソッドを使用して表示する
MessageBox.Show(str_ans);
ShowはMessageBoxクラス(System.Windows.Forms名前空間)のメソッドです。

実装

ターゲットおよびリスク臓器の線量パラメータの自動的に取得して表示する仕組みを作ります。 それぞれの取得する輪郭名と線量パラメータの組み合わせは以下のとおりです。

ターゲット

  • CTV: D98%
  • PTV: D95%, Dmean, HI

リスク臓器

  • Lung: V20Gy
  • Brainstem(またはSpinalCord): D1cc
1
2
3
4
5
6
////////////////////////////////////////////////////////////////////////////////
// Check DVH parameters
//


 ~~以下にコドを記述~~

foreachを使用した方法の実装例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
////////////////////////////////////////////////////////////////////////////////
// Check DVH parameters
//

// Bin width of DVH curves
double binWidth = 0.001;

oText += " ----- Target volumes -------------------------- \n";

foreach (var structure in plan.StructureSet.Structures)
{
    // CTV 
    if (structure.Id.IndexOf("CTV") >= 0 && structure.DicomType == "CTV")
    {
        // D98%
        DoseValue d_98 = plan.GetDoseAtVolume(structure, 98.0, VolumePresentation.Relative, DoseValuePresentation.Absolute);
        oText += "(" + structure.Id + ") D98%:" + string.Format("{0:f2}", d_98.Dose) + " " + plan.TotalDose.UnitAsString + "\n";
    }

    // PTV
    if (structure.Id.IndexOf("PTV") >= 0 && structure.DicomType == "PTV")
    {

        // D95%
        DoseValue d_95 = plan.GetDoseAtVolume(structure, 95.0, VolumePresentation.Relative, DoseValuePresentation.Absolute);
        oText += "(" + structure.Id + ") D95%:" + string.Format("{0:f2}", d_95.Dose) + " " + plan.TotalDose.UnitAsString + "\n";

        // Dmean
        double d_mean = plan.GetDVHCumulativeData(structure, DoseValuePresentation.Absolute, VolumePresentation.Relative, binWidth).MeanDose.Dose;
        oText += "(" + structure.Id + ") Dmean:" + string.Format("{0:f2}", d_mean) + " " + plan.TotalDose.UnitAsString + "\n";

        // HI 
        // Dmax/Dmin
        double maxDose = plan.GetDVHCumulativeData(structure, DoseValuePresentation.Absolute, VolumePresentation.Relative, binWidth).MaxDose.Dose;
        double minDose = plan.GetDVHCumulativeData(structure, DoseValuePresentation.Absolute, VolumePresentation.Relative, binWidth).MinDose.Dose;
        double homogeneityIndex = maxDose/minDose;
        oText += "(" + structure.Id + ") Homogeneity Index:" + string.Format("{0:f2}", homogeneityIndex) + "\n";

        // HI 
        // (D2% - D98%) / D50% 
        DoseValue d_2 = plan.GetDoseAtVolume(structure, 2.0, VolumePresentation.Relative, DoseValuePresentation.Absolute);
        DoseValue d_98 = plan.GetDoseAtVolume(structure, 98.0, VolumePresentation.Relative, DoseValuePresentation.Absolute);
        DoseValue d_50 = plan.GetDoseAtVolume(structure, 50.0, VolumePresentation.Relative, DoseValuePresentation.Absolute);
        double homogeneityIndex_ICRU83 = (d_2.Dose - d_98.Dose)/d_50.Dose;
        oText += "(" + structure.Id + ") Homogeneity Index(ICRU 83):" + string.Format("{0:f2}", homogeneityIndex_ICRU83) + "\n";

    }
}

oText += " ----- OARs -------------------------- \n";

foreach (var structure in plan.StructureSet.Structures)
{
    // Change uppercase letters to lowercase
    String str_lowercase = structure.Id.ToLower();

    // Lung
    // V20Gy
    if (str_lowercase.IndexOf("lung") >= 0)
    {
        DoseValue dose_index = new DoseValue(2000.0, "cGy");
        double v_20 = plan.GetVolumeAtDose(structure, dose_index, VolumePresentation.Relative);
        if(v_20 < 30.0)
        {
            oText += "(" + structure.Id + ") V20Gy:" + string.Format("{0:f2}", v_20) + " %" + "\n";
        }
        else
        {
            oText += "WARNING!! (" + structure.Id + ") V20Gy:" + string.Format("{0:f2}", v_20) + " % (QUANTEC: < 30.0)" + "\n";
        }
    }

    // Brainstem
    // D1cc
    if (str_lowercase.IndexOf("brain") >= 0 && str_lowercase.IndexOf("stem") >= 0)
    {
        DoseValue d_1cc = plan.GetDoseAtVolume(structure, 1.0, VolumePresentation.Absolute, DoseValuePresentation.Absolute);
        if(d_1cc.Dose < 59.0)
        {
            oText += "(" + structure.Id + ") D1cc:" + string.Format("{0:f2}", d_1cc.Dose) + " " + plan.TotalDose.UnitAsString + "\n";
        }
        else
        {
            oText += "WARNING!! (" + structure.Id + ") D1cc:" + string.Format("{0:f2}", d_1cc.Dose) + " " + plan.TotalDose.UnitAsString + " (QUANTEC: < 59.0)\n";
        }
    }
} 

LINQ構文を使用した方法の実装例

  • LINQ(Language INtegrated Query)とは
    .Net Framework 3.5 / C# 3.0以降でサポートされた新しいデータアクセスのための構文です。 .Net FrameworkはWindows OSで動作するアプリケーション開発のフレームワークで、 システム開発を楽に行えるようにプログラム等のひな形を提供しています。

  • LINQの書き方 クエリ構文とメソッド構文

    1
    2
    3
    4
    5
    6
    7
    var list = new List<int> { 1, 2, 3, 4, 5, 6 };
    
    // クエリ構文
    var query1 = from x in list where x > 3 select x * 3;
    
    // メソッド構文
    var query2 = list.Where(x => x > 3).Select(x => x * 3);
    

    メソッド構文の引数はラムダ式になっています。 ラムダ式は匿名関数の一つである匿名メソッドを簡潔に記述する方法です。
    (わざわざ関数名を定義しないで、その関数の引数と処理や戻り値を匿名の関数で定義する)

    例: 上記プログラムのラムダ式の部分をラムダ式導入以前の方法で書き換える場合の比較

    1
    2
    3
    4
    5
    // C# 2.0以前の書き方
    delegate(int x){ return x * 3;};
    
    // ラムダ式を使って書き直すと..簡潔に表現可能
    x => x * 3;
    

  • foreachを使用した方法の実装例をLINQで書き換えると..

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
        if(plan.StructureSet.Structures.Any(s => s.Id.IndexOf("CTV") >= 0 && s.DicomType == "CTV"))
        {
            var structures = plan.StructureSet.Structures.Where(s => s.Id.IndexOf("CTV") >= 0 && s.DicomType == "CTV");
    
            foreach(var structure in structures)
            {
                DoseValue d_98 = plan.GetDoseAtVolume(structure, 98.0, VolumePresentation.Relative, DoseValuePresentation.Absolute);
    
                oText += "(" + structure.Id + ") D98%:" + string.Format("{0:f2}", d_98.Dose) + " " + plan.TotalDose.UnitAsString + "\n";
            }
    
        }
    
    
        if(plan.StructureSet.Structures.Any(s => s.Id.IndexOf("PTV") >= 0 && s.DicomType == "PTV"))
        {
    
            var structures = plan.StructureSet.Structures.Where(s => s.Id.IndexOf("PTV") >= 0 && s.DicomType == "PTV");
    
            foreach(var structure in structures)
            {
                // D95%
                DoseValue d_95 = plan.GetDoseAtVolume(structure, 95.0, VolumePresentation.Relative, DoseValuePresentation.Absolute);
                oText += "(" + structure.Id + ") D95%:" + string.Format("{0:f2}", d_95.Dose) + " " + plan.TotalDose.UnitAsString + "\n";
    
                // Dmean
                double d_mean = plan.GetDVHCumulativeData(structure, DoseValuePresentation.Absolute, VolumePresentation.Relative, binWidth).MeanDose.Dose;
                oText += "(" + structure.Id + ") Dmean:" + string.Format("{0:f2}", d_mean) + " " + plan.TotalDose.UnitAsString + "\n";
    
                // HI 
                // Dmax/Dmin
                double maxDose = plan.GetDVHCumulativeData(structure, DoseValuePresentation.Absolute, VolumePresentation.Relative, binWidth).MaxDose.Dose;
                double minDose = plan.GetDVHCumulativeData(structure, DoseValuePresentation.Absolute, VolumePresentation.Relative, binWidth).MinDose.Dose;
                double homogeneityIndex = maxDose/minDose;
                oText += "(" + structure.Id + ") Homogeneity Index:" + string.Format("{0:f2}", homogeneityIndex) + "\n";
    
                // HI 
                // (D2% - D98%) / D50% 
                DoseValue d_2 = plan.GetDoseAtVolume(structure, 2.0, VolumePresentation.Relative, DoseValuePresentation.Absolute);
                DoseValue d_98 = plan.GetDoseAtVolume(structure, 98.0, VolumePresentation.Relative, DoseValuePresentation.Absolute);
                DoseValue d_50 = plan.GetDoseAtVolume(structure, 50.0, VolumePresentation.Relative, DoseValuePresentation.Absolute);
                double homogeneityIndex_ICRU83 = (d_2.Dose - d_98.Dose)/d_50.Dose;
                oText += "(" + structure.Id + ") Homogeneity Index(ICRU 83):" + string.Format("{0:f2}", homogeneityIndex_ICRU83) + "\n";
            } 
        }
    
        // Lung
        // V20Gy
        if (plan.StructureSet.Structures.Any(s => s.Id.ToLower().IndexOf("lung") >= 0))
        {
            var structures = plan.StructureSet.Structures.Where(s => s.Id.ToLower().IndexOf("lung") >= 0);
    
            foreach(var structure in structures)
            {
                DoseValue dose_index = new DoseValue(2000.0, "cGy");
                double v_20 = plan.GetVolumeAtDose(structure, dose_index, VolumePresentation.Relative);
                if(v_20 < 30.0)
                {
                    oText += "(" + structure.Id + ") V20Gy:" + string.Format("{0:f2}", v_20) + " %" + "\n";
                }
                else
                {
                    oText += "WARNING!! (" + structure.Id + ") V20Gy:" + string.Format("{0:f2}", v_20) + " % (QUANTEC: < 30.0)" + "\n";
                }
            }
        }
    
        // Brainstem
        // D1cc
        if (plan.StructureSet.Structures.Any(s => s.Id.ToLower().IndexOf("brain") >= 0 && s.Id.ToLower().IndexOf("stem") >= 0))
        {
    
            var structures = plan.StructureSet.Structures.Where(s => s.Id.ToLower().IndexOf("brain") >= 0 && s.Id.ToLower().IndexOf("stem") >= 0);
    
            foreach(var structure in structures)
            {
                DoseValue d_1cc = plan.GetDoseAtVolume(structure, 1.0, VolumePresentation.AbsoluteCm3, DoseValuePresentation.Absolute);
                if(d_1cc.Dose < 59.0)
                {
                    oText += "(" + structure.Id + ") D1cc:" + string.Format("{0:f2}", d_1cc.Dose) + " " + plan.TotalDose.UnitAsString + "\n";
                }
                else
                {
                    oText += "WARNING!! (" + structure.Id + ") D1cc:" + string.Format("{0:f2}", d_1cc.Dose) + " " + plan.TotalDose.UnitAsString + " (QUANTEC: < 59.0)\n";
                }
            }
        }