jpg を dicom ファイルに変換して dcm4chee にインポート

jpg を python を使って dicom ファイルに変換するのは簡単なのですが、dcm4chee がインポートを簡単には許してくれません。いろいろと必要な条件があるようです。

SOP Class UID

SOP Class UID とは画像種別のコードのようで、今回は内視鏡一択なので変更する必要はありません。
以下のようにしました。


ds.SOPClassUID = '1.2.840.10008.5.1.4.1.1.77.1.1'

Study Instance UID

Study Instance UID とは、その検査の識別番号で以下のようにしました。


ds.StudyInstanceUID = '1.2.392.200059.10.2.107601092.1.1234'

この数字の前半部分の「1.2.392.200059.10.2.107601092」は、ISO・加盟機関・ベンダーコードなどでこの数字も固定されています。

ベンダーコードをこのまま使用するのは多分良くないことと思いますが、ここではそのまま使わせてもらいました。

後半部分は、実はよくわからないのですが、前半部分 + [ 1 ] + [ StudyID ] としました。

Series Instance UID

CT であればシリーズはいくつか存在するのですが、内視鏡ではシリーズは一つです。

以下のようにするようです。


ds.SeriesInstanceUID = '1.2.392.200059.10.2.107601092.2.1234.1'

つまり、固定部分である「1.2.392.200059.10.2.107601092」に [ 2 ] + [ StudyID ] + [ 1 ] とします。

シリーズのインスタンスは StudyID の前に 2 とする決まりなのかもしれません。
そして、最後の「1」がシリーズ番号です。これはおそらく「2」でも「3」でもいいのでしょう。

SOP Instance UID

SOP Instance UID は dicom ファイルの ID で一意でなければなりません。

以下のようにしました。


ds.SOPInstanceUID= '1.2.392.200059.10.2.107601092.5.1234.16041210393319.11'

ルールは、前半固定部分 + [ 5 ] + [ StudyID ] + [ 検査日 ] + [ 検査時刻 ] + [ InstanceNo ] です。

[ StudyID ] は本来一意ですが、それは内視鏡データベース内のことなので、CTや他の dicom システムと一緒にすると一意であるとは限りません。

そこで、検査日 + 検査時刻 を [ StudyID ] の後にくっつけています。これでも絶対に一意になるとは限りませんがおそらく大丈夫でしょう。

検査日は「160412」でこれは 2016年4月12日という意味です。
その後の「10393319」は10時39分33秒19という意味で、検査IDとこれを文字列として結合すると、この文字列が重なる可能性はほとんどないと思います。

[ InstanceNo ] は、autoincrement して1つずつアップします。

python プログラム

python プログラムの一部です。


"""  tag 書き込み  """
imagename = os.path.basename(eimg)
imagename_noext = os.path.splitext( imagename )[0]
self.StudyTime = imagename_noext.split('_')[1] + imagename_noext.split('_')[2] + imagename_noext.split('_')[3]
self.AcquisitionTime = imagename_noext.split('_')[2]
ds.StudyID = self.StudyID
ds.PatientID = self.PatientID
ds.PatientBirthDate = self.PatientBirthDate
ds.PatientName = self.PatientName
ds.PatientSex = self.PatientSex
ds.StudyDate = self.AcquisitionDate
ds.SeriesDate = self.AcquisitionDate
ds.AcquisitionDate = self.AcquisitionDate
ds.AcquisitionTime = self.AcquisitionTime
ds.Modality = 'ES'
"""  tag 書き込み --- その他の情報"""
ds.SOPClassUID = self.SOPClassUID
ds.StudyInstanceUID = self.bn + '1.' + self.StudyID
ds.SeriesInstanceUID = self.bn + '2.' + self.StudyID + '.1'
ds.SOPInstanceUID= self.bn + '5.' + self.StudyID + '.' + self.StudyTime + '.' + str(acn)
ds.InstanceNumber = str( acn )
""" 保存する """
dcmfile = '/var/www/html/testfolder/' + imagename_noext + '.dcm'
ds.save_as(dcmfile)
acn+= 1

元の MMR_PKG のファイル名規則で、「snap_180405_141524_94.jpg」というように年月・時刻がかなり正確に記録されているので、この文字列から「180405_141524_94」を抜き出してさらにアンダースコアを削除し「18040514152494」という文字列を作成し、それをSOP Instance UIDに加えています。

その他いろいろな患者情報をタグに書き込んで、新しい dicom ファイルとして保存します。

これを dcm4chee にインポートすれば、dcm4chee による内視鏡閲覧が可能になります。

すべての jpg を dicom ファイルに変換

python ですべての jpg 画像を dicom ファイルに変換します。

その際、ディレクトリ構造も新しく作成します。

MMR_PKG ではファイル構造は以下のようになっています。


.
├── 21400272
│   └── 180405_135433_61
│       ├── MOVIE
│       ├── SCENE
│       ├── SNAP
│       │   ├── SnpInfo_180405_141353_34.xml
│       │   ├── SnpInfo_180405_141421_93.xml
│       │   ├── SnpInfo_180405_141437_35.xml
│       │   ├── pre_180405_141353_34.jpg
│       │   ├── pre_180405_141421_93.jpg
│       │   ├── pre_180405_141437_35.jpg
│       │   ├── snap_180405_141353_34.jpg
│       │   ├── snap_180405_141421_93.jpg
│       │   ├── snap_180405_141437_35.jpg
│       └── info_180405_135433_61.xml
├── 21400278
│   ├── 160412_101110_70
│   │   ├── MOVIE
│   │   ├── SCENE
│   │   ├── SNAP
│   │   │   ├── SnpInfo_160412_103259_93.xml
│   │   │   ├── SnpInfo_160412_103308_98.xml
│   │   │   ├── SnpInfo_160412_103312_75.xml
│   │   │   ├── pre_160412_103259_93.jpg
│   │   │   ├── pre_160412_103308_98.jpg
│   │   │   ├── pre_160412_103312_75.jpg
│   │   │   ├── snap_160412_103259_93.jpg
│   │   │   ├── snap_160412_103308_98.jpg
│   │   │   ├── snap_160412_103312_75.jpg
│   │   └── info_160412_101110_70.xml

つまり、「カルテ番号」- 「検査日時」- 「その他」のような構造です。

このような構造はバックアップがとても面倒です。
最初がカルテ番号なので、検査をすると新たに画像ファイルが入ってくるからです。

これを以下のようにします。


.
├── 2016
│   ├── 02
│   │   └── 20
│   │       └── 24874
│   │           ├── snap_160220_091152_81_2.dcm
│   │           ├── snap_160220_091203_56_2.dcm
│   │           ├── snap_160220_091314_44_2.dcm
│   │           ├── snap_160220_091713_62_2.dcm
│   │           ├── snap_160220_091721_33_2.dcm
│   │           ├── snap_160220_091728_94_2.dcm
│   │           └── snap_160220_091842_32_2.dcm
│   └── 04
│       └── 12
│           └── 25687
│               ├── snap_160412_103259_93.dcm
│               ├── snap_160412_103308_98.dcm
│               ├── snap_160412_103312_75.dcm
│               ├── snap_160412_103316_22.dcm
│               ├── snap_160412_103920_76.dcm
│               ├── snap_160412_103926_81.dcm
│               ├── snap_160412_103933_19.dcm
│               └── snap_160412_103941_93.dcm

つまり、検査施行日の「年」 – 「月 」 – 「日」 – 「検査ID」とします。

このようにすると、過去のディレクトリには新しいファイルは絶対に入ってきません。
バックアップをする場合には新しいものだけをバックアップすればいいのでとても便利なのです。

検査IDの小さい方から順番に処理していき、処理したものはデータベースに記録し、すべてを処理するまではプログラムを実行するようにすれば、すべての jpg を dicom ファイルに変換することができます。

コアな部分だけ提示すると、


    """ 実行条件を決定する """                
    def exec( self ):
        convID = 0
        cnt = self.emptyCheck()
        if cnt ==0 :
            convID = self.getExamMin()
        else :
            self.convMax = self.getConvMax()
            convID = self.getConvID()
        return convID
       
if __name__ == "__main__":
    dcm = ToDcm()
    convID = dcm.exec()
    print(convID)
    while convID is not None:
        convInfo = dcm.convInfo( convID )
        dcm.valset( convInfo )
        dcm.createDir( convInfo )
        imgarr=dcm.imgArr( convInfo )
        imgarr.sort()
        dcm.createDcm(imgarr)
        dcm.recStatus() 
        convID = dcm.exec()

ちなみに 129 個の jpg ファイルを dicom ファイルに変換するのに要する時間は 10 秒ほどでした。

変換した dicom ファイルは膨大な量なので、dcm4chee にインポートするのにはかなりの時間がかかりますが、これもそれほど難しくないはずです。

jpg を dicom ファイルにする必要はあるか

jpg ファイルでは 180 KB であったものが dicom にすると 922 KB まで 5 倍以上になります。

しかし画質はほとんど変わらないと思います。

dicom 化するメリットは、輝度を自由に変更できることと CT や他の画像と統一できることでしょうか。