週刊 DICOMデコーダーを創る (3) シークエンス解読


## SQ(シークエンス)の構造
DICOM規格日本語訳のPS3.5 p39-を読み解いていきます
図のように，SQのVRが来ると，その次の2byteは予約済み(普通は0x0000がはいっている)で，関係が無いので飛ばします．
続く4byteにデータ長が入っています．
データ長は2種類あり，
<img width="60%" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1465632/d6c8ae8c-c547-8bbb-c694-a5224dd3ab1a.png" alt="image.png">

|種類 | データ長のバイト列 | 概要|
|:-:|:-:|:-|
|明示的長さ| 0x00 01 13 50 (例)  | このように値が入っている場合はデータ長を示す  |
|未定義長さ| 0xFF FF FF FF  | データ長が明示されていない．<br>この場合は，項目を区切るタグを目印によむ |

このような未定議長さの場合には，区切りタグを基に読む必要があります．

| タグ | 意味 |概要|
|:-:|:-:|:-|
| FFFE, E0000  | 項目の開始  |項目の開始に必要なタグ|
| FFFE, E00D  | 項目の区切り  |項目の長さが明示されている場合は省略される|
| FFFE, E0DD  | SQの終わり  |シークエンスの長さが明示されている場合は省略される|

今回のファイルで最初にSQが登場するのは，アドレス0x21C(540)からです．
この部分を図示すると次の通りとなります．

<img width="80%" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1465632/ac259c9d-e066-80dc-3e42-21ccb41eb3e3.png" alt="image.png">

## 実装
####コーディング
```DicomData.swift
    func analyzeData(){
        currentPosition = 128 + 4
        
        // シークエンス用
        var hierarchy = 0
        
        while currentPosition < dicomData.count {
            
            let position = currentPosition
            
            let group = readUInt16()
            let element = readUInt16()
        
            // SQ用の特殊タグをチェックする
            if (group == 0xFFFE && element == 0xE000){
                // 項目開始
                
                let length = readUInt32()
                if length == 0xFFFFFFFF{
                    
                    printLog(hierarchy: hierarchy, position: position, group: group, element: element, vr: "", length: length)
                    
                    hierarchy += 1
                    continue
                }else{
                    // TODO: 明示長さの場合は，指定の部位まで読んだら階層を下げる必要がある
                    printLog(hierarchy: hierarchy, position: position, group: group, element: element, vr: "", length: length)
                    currentPosition += Int(length)
                    continue
                }
            }else if (group == 0xFFFE && element == 0xE00D){
                // 項目終わり
                
                let length = readUInt32() // 0xFFFFFFFF
                printLog(hierarchy: hierarchy, position: position, group: group, element: element, vr: "", length: length)
                
                hierarchy -= 1
                continue
            }else if (group == 0xFFFE && element == 0xE0DD){
                // シークエンスの終わり
                
                let length = readUInt32() // 0xFFFFFFFF
                printLog(hierarchy: hierarchy, position: position, group: group, element: element, vr: "", length: length)
                hierarchy -= 1
                continue
            }
            
            let vr = readChar(length: 2)
            
            if ["OB", "OW", "OF", "UT"].contains(vr){
                // VRの続きの2byteは意味をなさないので飛ばす
                currentPosition += 2
                let length = readUInt32()
        
                if length == 0xFFFFFFFF{
                    printLog(hierarchy: hierarchy, position: position, group: group, element: element, vr: vr, length: length)
                    hierarchy += 1
                }else{
                    printLog(hierarchy: hierarchy, position: position, group: group, element: element, vr: vr, length: length)
                    currentPosition += Int(length)
                }
                
                
            }else if vr == "SQ"{
                // SQでは，データ長さが0xFFFFFFFFの場合に注意
                currentPosition += 2
                let length = readUInt32()
                if length == 0xFFFFFFFF{
                    // 未定義長さ
                    printLog(hierarchy: hierarchy, position: position, group: group, element: element, vr: vr, length: length)
                    hierarchy += 1
                }else{
                    // 明示長さ
                    currentPosition += Int(length)
                    printLog(hierarchy: hierarchy, position: position, group: group, element: element, vr: vr, length: length)
                }
                
                
            }else{
                let length = readUInt16()
                
                print("\(String(repeating: " ", count: hierarchy))0x\(String(position, radix: 16).uppercased())(\(position)), tag: (\(String(format: "%04x", group)), \(String(format: "%04x", element))), VR: \(vr), Length: \(UInt32( length))")
                
                currentPosition += Int( length)
                
            }   
        }
    }
    
    (省略 readUInt16, readUInt32, readCharは変更なし)
    
    
    func printLog(hierarchy: Int, position: Int, group: UInt16, element:UInt16, vr:String, length:UInt32){
        if length == 0xFFFFFFFF{
            print("\(String(repeating: " ", count: hierarchy))0x\(String(position, radix: 16).uppercased())(\(position)), tag: (\(String(format: "%04x", group)), \(String(format: "%04x", element))), VR: \(vr), Length:")
        }else{
            print("\(String(repeating: " ", count: hierarchy))0x\(String(position, radix: 16).uppercased())(\(position)), tag: (\(String(format: "%04x", group)), \(String(format: "%04x", element))), VR: \(vr), Length: \(length)")
        }
    }
```

#### 解読の流れ

1. タグ(Group, Element)をよむ
1. 特殊なタグの場合は個別処理
    1. FFFE, E000 : 項目の始まりなので，階層を上げる
    1. FFFE, E00D : 項目の終わりなので，階層を下げる
    1. FFFE, E0DD : シークエンスの終わりなので，階層を下げる
1. 上記の特殊タグの場合は続くVRは無いので，`continue`でループの最初に戻る
1. VRを読む
    1. "OB", "OW", "OF", "UT"の場合は画像データが入っていたりする．<br>いまはまだ読まないので，後日実装
    1. SQの場合はシークエンスの始まり．<br>長さは明示されている場合もあるし未定義の場合もある<br>現時点では明示されている場合は，その長さ分すっ飛ばして解読を続けている
    1. 他のVRは2byteの長さを読む

#### 実行結果

```
dicom file loaded
0x84(132), tag: (0002, 0000), VR: UL, Length: 4
0x90(144), tag: (0002, 0001), VR: OB, Length: 2
0x9E(158), tag: (0002, 0002), VR: UI, Length: 28
0xC2(194), tag: (0002, 0003), VR: UI, Length: 46
0xF8(248), tag: (0002, 0010), VR: UI, Length: 22
0x116(278), tag: (0002, 0012), VR: UI, Length: 8
0x126(294), tag: (0008, 0008), VR: CS, Length: 38
0x154(340), tag: (0008, 0016), VR: UI, Length: 28
0x178(376), tag: (0008, 0018), VR: UI, Length: 46
0x1AE(430), tag: (0008, 0020), VR: DA, Length: 8
0x1BE(446), tag: (0008, 0030), VR: TM, Length: 6
0x1CC(460), tag: (0008, 0050), VR: SH, Length: 0
0x1D4(468), tag: (0008, 0060), VR: CS, Length: 2
0x1DE(478), tag: (0008, 0070), VR: LO, Length: 0
0x1E6(486), tag: (0008, 0080), VR: LO, Length: 0
0x1EE(494), tag: (0008, 0081), VR: ST, Length: 0
0x1F6(502), tag: (0008, 0090), VR: PN, Length: 0
0x1FE(510), tag: (0008, 1030), VR: LO, Length: 0
0x206(518), tag: (0008, 1050), VR: PN, Length: 0
0x20E(526), tag: (0008, 2110), VR: CS, Length: 2
0x218(536), tag: (0008, 2112), VR: SQ, Length:
 0x224(548), tag: (fffe, e000), VR: , Length:
  0x22C(556), tag: (0008, 1150), VR: UI, Length: 28
  0x250(592), tag: (0008, 1155), VR: UI, Length: 46
  0x286(646), tag: (fffe, e00d), VR: , Length: 0
 0x28E(654), tag: (fffe, e0dd), VR: , Length: 0
0x296(662), tag: (0009, 0010), VR: LO, Length: 14
0x2AC(684), tag: (0009, 1002), VR: OB, Length: 904
0x640(1600), tag: (0009, 1003), VR: OB, Length: 528
0x85C(2140), tag: (0009, 1005), VR: OB, Length: 24
0x880(2176), tag: (0010, 0010), VR: PN, Length: 12
0x894(2196), tag: (0010, 0020), VR: LO, Length: 8
0x8A4(2212), tag: (0010, 0030), VR: DA, Length: 8
0x8B4(2228), tag: (0010, 0040), VR: CS, Length: 2
0x8BE(2238), tag: (0018, 0060), VR: DS, Length: 0
0x8C6(2246), tag: (0018, 1063), VR: DS, Length: 2
0x8D0(2256), tag: (0018, 1152), VR: IS, Length: 0
0x8D8(2264), tag: (0018, 1155), VR: CS, Length: 2
0x8E2(2274), tag: (0018, 1500), VR: CS, Length: 0
0x8EA(2282), tag: (0018, 1510), VR: DS, Length: 4
0x8F6(2294), tag: (0018, 1511), VR: DS, Length: 2
0x900(2304), tag: (0019, 0010), VR: LO, Length: 16
0x918(2328), tag: (0019, 1030), VR: UL, Length: 4
0x924(2340), tag: (0020, 000d), VR: UI, Length: 54
0x962(2402), tag: (0020, 000e), VR: UI, Length: 54
0x9A0(2464), tag: (0020, 0010), VR: SH, Length: 0
0x9A8(2472), tag: (0020, 0011), VR: IS, Length: 2
0x9B2(2482), tag: (0020, 0013), VR: IS, Length: 0
0x9BA(2490), tag: (0020, 0020), VR: CS, Length: 0
0x9C2(2498), tag: (0021, 0010), VR: LO, Length: 16
0x9DA(2522), tag: (0021, 1013), VR: IS, Length: 2
0x9E4(2532), tag: (0028, 0002), VR: US, Length: 2
0x9EE(2542), tag: (0028, 0004), VR: CS, Length: 12
0xA02(2562), tag: (0028, 0008), VR: IS, Length: 2
0xA0C(2572), tag: (0028, 0009), VR: AT, Length: 4
0xA18(2584), tag: (0028, 0010), VR: US, Length: 2
0xA22(2594), tag: (0028, 0011), VR: US, Length: 2
0xA2C(2604), tag: (0028, 0100), VR: US, Length: 2
0xA36(2614), tag: (0028, 0101), VR: US, Length: 2
0xA40(2624), tag: (0028, 0102), VR: US, Length: 2
0xA4A(2634), tag: (0028, 0103), VR: US, Length: 2
0xA54(2644), tag: (0028, 1040), VR: CS, Length: 4
0xA60(2656), tag: (0028, 1090), VR: CS, Length: 4
0xA6C(2668), tag: (0028, 6040), VR: US, Length: 6
0xA7A(2682), tag: (0028, 6100), VR: SQ, Length:
 0xA86(2694), tag: (fffe, e000), VR: , Length:
  0xA8E(2702), tag: (0028, 6101), VR: CS, Length: 4
  0xA9A(2714), tag: (0028, 6110), VR: US, Length: 2
  0xAA4(2724), tag: (fffe, e00d), VR: , Length: 0
 0xAAC(2732), tag: (fffe, e0dd), VR: , Length: 0
0xAB4(2740), tag: (0029, 0010), VR: LO, Length: 16
0xACC(2764), tag: (0029, 1000), VR: SQ, Length:
 0xAD8(2776), tag: (fffe, e000), VR: , Length:
  0xAE0(2784), tag: (0029, 0010), VR: LO, Length: 16
  0xAF8(2808), tag: (0029, 1001), VR: US, Length: 4
  0xB04(2820), tag: (0029, 1002), VR: US, Length: 50
  0xB3E(2878), tag: (0029, 1003), VR: FL, Length: 4
  0xB4A(2890), tag: (fffe, e00d), VR: , Length: 0
 0xB52(2898), tag: (fffe, e0dd), VR: , Length: 0
0xB5A(2906), tag: (5000, 0005), VR: US, Length: 2
0xB64(2916), tag: (5000, 0010), VR: US, Length: 2
0xB6E(2926), tag: (5000, 0020), VR: CS, Length: 4
0xB7A(2938), tag: (5000, 0030), VR: SH, Length: 10
0xB8C(2956), tag: (5000, 0103), VR: US, Length: 2
0xB96(2966), tag: (5000, 0104), VR: US, Length: 0
0xB9E(2974), tag: (5000, 0105), VR: US, Length: 0
0xBA6(2982), tag: (5000, 0106), VR: US, Length: 0
0xBAE(2990), tag: (5000, 0110), VR: US, Length: 4
0xBBA(3002), tag: (5000, 0112), VR: US, Length: 2
0xBC4(3012), tag: (5000, 0114), VR: US, Length: 2
0xBCE(3022), tag: (5000, 3000), VR: OW, Length: 7680
0x29DA(10714), tag: (7fe0, 0010), VR: OB, Length:
 0x29E6(10726), tag: (fffe, e000), VR: , Length: 384
 0x2B6E(11118), tag: (fffe, e000), VR: , Length: 17912
 0x716E(29038), tag: (fffe, e000), VR: , Length: 18680
 0xBA6E(47726), tag: (fffe, e000), VR: , Length: 18644
 0x1034A(66378), tag: (fffe, e000), VR: , Length: 17820
 0x148EE(84206), tag: (fffe, e000), VR: , Length: 16848
 0x18AC6(101062), tag: (fffe, e000), VR: , Length: 16308
 0x1CA82(117378), tag: (fffe, e000), VR: , Length: 16020
 0x2091E(133406), tag: (fffe, e000), VR: , Length: 16088
 0x247FE(149502), tag: (fffe, e000), VR: , Length: 16280
 0x2879E(165790), tag: (fffe, e000), VR: , Length: 16408
 0x2C7BE(182206), tag: (fffe, e000), VR: , Length: 16568
 0x3087E(198782), tag: (fffe, e000), VR: , Length: 16556
 0x34932(215346), tag: (fffe, e000), VR: , Length: 16604
 0x38A16(231958), tag: (fffe, e000), VR: , Length: 16536
 0x3CAB6(248502), tag: (fffe, e000), VR: , Length: 16628
 0x40BB2(265138), tag: (fffe, e000), VR: , Length: 16428
 0x44BE6(281574), tag: (fffe, e000), VR: , Length: 16328
 0x48BB6(297910), tag: (fffe, e000), VR: , Length: 16456
 0x4CC06(314374), tag: (fffe, e000), VR: , Length: 16468
 0x50C62(330850), tag: (fffe, e000), VR: , Length: 16528
 0x54CFA(347386), tag: (fffe, e000), VR: , Length: 16576
 0x58DC2(363970), tag: (fffe, e000), VR: , Length: 16648
 0x5CED2(380626), tag: (fffe, e000), VR: , Length: 16684
 0x61006(397318), tag: (fffe, e000), VR: , Length: 16788
 0x651A2(414114), tag: (fffe, e000), VR: , Length: 16852
 0x6937E(430974), tag: (fffe, e000), VR: , Length: 16960
 0x6D5C6(447942), tag: (fffe, e000), VR: , Length: 17164
 0x718DA(465114), tag: (fffe, e000), VR: , Length: 17376
 0x75CC2(482498), tag: (fffe, e000), VR: , Length: 17428
 0x7A0DE(499934), tag: (fffe, e000), VR: , Length: 17392
 0x7E4D6(517334), tag: (fffe, e000), VR: , Length: 17240
 0x82836(534582), tag: (fffe, e000), VR: , Length: 17216
 0x86B7E(551806), tag: (fffe, e000), VR: , Length: 17272
 0x8AEFE(569086), tag: (fffe, e000), VR: , Length: 17416
 0x8F30E(586510), tag: (fffe, e000), VR: , Length: 17480
 0x9375E(603998), tag: (fffe, e000), VR: , Length: 17568
 0x97C06(621574), tag: (fffe, e000), VR: , Length: 17572
 0x9C0B2(639154), tag: (fffe, e000), VR: , Length: 17676
 0xA05C6(656838), tag: (fffe, e000), VR: , Length: 17612
 0xA4A9A(674458), tag: (fffe, e000), VR: , Length: 17760
 0xA9002(692226), tag: (fffe, e000), VR: , Length: 17716
 0xAD53E(709950), tag: (fffe, e000), VR: , Length: 17800
 0xB1ACE(727758), tag: (fffe, e000), VR: , Length: 17952
 0xB60F6(745718), tag: (fffe, e000), VR: , Length: 17916
 0xBA6FA(763642), tag: (fffe, e000), VR: , Length: 17984
 0xBED42(781634), tag: (fffe, e000), VR: , Length: 18120
 0xC3412(799762), tag: (fffe, e000), VR: , Length: 18112
 0xC7ADA(817882), tag: (fffe, e000), VR: , Length: 18008
 0xCC13A(835898), tag: (fffe, e000), VR: , Length: 17984
 0xD0782(853890), tag: (fffe, e000), VR: , Length: 17908
 0xD4D7E(871806), tag: (fffe, e000), VR: , Length: 17960
 0xD93AE(889774), tag: (fffe, e000), VR: , Length: 18112
 0xDDA76(907894), tag: (fffe, e000), VR: , Length: 18256
 0xE21CE(926158), tag: (fffe, e000), VR: , Length: 18240
 0xE6916(944406), tag: (fffe, e000), VR: , Length: 18168
 0xEB016(962582), tag: (fffe, e000), VR: , Length: 18092
 0xEF6CA(980682), tag: (fffe, e000), VR: , Length: 18052
 0xF3D56(998742), tag: (fffe, e000), VR: , Length: 18008
 0xF83B6(1016758), tag: (fffe, e000), VR: , Length: 18048
 0xFCA3E(1034814), tag: (fffe, e000), VR: , Length: 17976
 0x10107E(1052798), tag: (fffe, e000), VR: , Length: 18056
 0x10570E(1070862), tag: (fffe, e000), VR: , Length: 18040
 0x109D8E(1088910), tag: (fffe, e000), VR: , Length: 18068
 0x10E42A(1106986), tag: (fffe, e000), VR: , Length: 17984
 0x112A72(1124978), tag: (fffe, e000), VR: , Length: 18012
 0x1170D6(1142998), tag: (fffe, e000), VR: , Length: 18024
 0x11B746(1161030), tag: (fffe, e000), VR: , Length: 18048
 0x11FDCE(1179086), tag: (fffe, e000), VR: , Length: 17940
 0x1243EA(1197034), tag: (fffe, e000), VR: , Length: 17984
 0x128A32(1215026), tag: (fffe, e000), VR: , Length: 17952
 0x12D05A(1232986), tag: (fffe, e000), VR: , Length: 18048
 0x1316E2(1251042), tag: (fffe, e000), VR: , Length: 17996
 0x135D36(1269046), tag: (fffe, e000), VR: , Length: 18040
 0x13A3B6(1287094), tag: (fffe, e000), VR: , Length: 18048
 0x13EA3E(1305150), tag: (fffe, e000), VR: , Length: 17936
 0x143056(1323094), tag: (fffe, e000), VR: , Length: 18020
 0x1476C2(1341122), tag: (fffe, e000), VR: , Length: 18112
 0x14BD8A(1359242), tag: (fffe, e000), VR: , Length: 18200
 0x1504AA(1377450), tag: (fffe, e000), VR: , Length: 18128
 0x154B82(1395586), tag: (fffe, e000), VR: , Length: 18164
 0x15927E(1413758), tag: (fffe, e000), VR: , Length: 18192
 0x15D996(1431958), tag: (fffe, e000), VR: , Length: 18108
 0x16205A(1450074), tag: (fffe, e000), VR: , Length: 18088
 0x16670A(1468170), tag: (fffe, e000), VR: , Length: 18116
 0x16ADD6(1486294), tag: (fffe, e000), VR: , Length: 18032
 0x16F44E(1504334), tag: (fffe, e000), VR: , Length: 17976
 0x173A8E(1522318), tag: (fffe, e000), VR: , Length: 18096
 0x178146(1540422), tag: (fffe, e000), VR: , Length: 18016
 0x17C7AE(1558446), tag: (fffe, e000), VR: , Length: 18032
 0x180E26(1576486), tag: (fffe, e000), VR: , Length: 17996
 0x18547A(1594490), tag: (fffe, e000), VR: , Length: 17964
 0x189AAE(1612462), tag: (fffe, e000), VR: , Length: 17992
 0x18E0FE(1630462), tag: (fffe, e000), VR: , Length: 17996
 0x192752(1648466), tag: (fffe, e000), VR: , Length: 17952
 0x196D7A(1666426), tag: (fffe, e000), VR: , Length: 17972
 0x19B3B6(1684406), tag: (fffe, e000), VR: , Length: 17976
 0x19F9F6(1702390), tag: (fffe, e0dd), VR: , Length: 0
```

このように，DICOMファイルの終端までエラーなく読むことができました．
`0x29DA(10714), tag: (7fe0, 0010)`に格納されている情報が画像データとなっています．

## 次回
次回以降，各tagに入っているデータをチェックしていきましょう
なお，今回のコードでは，SQや項目タグの長さが明示されている場合にはうまく読み取れないかもしれません．
次回以降も，初回に記載したDICOMデータファイルで解読を進めます．