最終更新: a few seconds agoRemove Netlify-related code from main (grafted, HEAD)

CIRCUS RS 概要

CIRCUS RS は一言でいうと「DICOM 画像ビューア&画像配信サーバ、ただし CAD 研究のためのアノテーション機能がついている」。

  • TypeScript。
  • 画像を配信する サーバ側コード (src/server)、画像を表示する ブラウザ側コード (src/client)、それらの 共用コード (src/common)の構成。
  • アノテーション(注記)というのは画像上に矢印や丸などでマークをつけること。
  • トップレベルアプリである CIRCUS CS/DB の両方で使う基盤技術であり、これ単体では画像を表示するなどの基本的機能しか有さない。

最低限のサーバの起動のさせかた

  • Node.js, NPM, Git を準備してリポジトリをクローンする。

  • npm install または npm ci する。

  • 画像置き場となるディレクトリを準備する。以下では例として D:/dicom-data/ とするが適宜読み替えを。

  • Dropbox にあるサンプル画像データをコピーして画像置き場に配置する。 D:/dicom-data/1.3.46.670589.33.1.63654370002337807300004.5603469123163469769/???.dcm のようになる。この数字の羅列は Series Instance UID と呼ばれる DICOM 画像のグローバルにユニークな識別子。

  • プロジェクトの設定を行う。プロジェクトの config ディレクトリの下に local.js というファイルを作り、以下のような設定を書き込む。default.jsをコピーして始めると確実。この設定は default.js の内容を上書きするので適宜そちらの設定を参照。

    module.exports = {
      dicomFileRepository: {
        options: {
          dataDir: 'D:/dicom-data'
        }
      }
    };
    
  • npm start する。

テスト

npm test でエラーが出ないことを確認。

把握のためのドキュメントなど

作成中のユーザドキュメント

ユーザドキュメント(つまり CIRCUS RS を使って何かの具体的なアプリを開発する開発者向け)は circus-rs リポジトリの /docs にある(ただし作成途中かつ英語)。 Jekyll (Ruby 製の static site generator) を使えば綺麗な HTML をビルドしてそこで読むこともできるが、とりあえず GitHub 上で閲覧する場合は以下から。

オンラインデモ兼開発サーバ

npm run devserver すると webpack-dev-server が起動し、 CIRCUS RS の使い方を一通りデモ/確認できるようになっている。 (デフォルトではポート 8080)

demo

「正しいシリーズインスタンス UID」「正しいサーバ(API エンドポイント)のアドレス」「ImageSource の種類」「デモの種類」を指定して "Run" する。

とりあえず最初に試すデモの種類としては、最低限の画像表示を行い注記などの派生的機能は使わない "Default demo" の実行を推奨。

コンポーネント別の説明

サーバ側 (src/server)

基本的には Koa で書かれており単体で動作するウェブサーバ。ただし単体のウェブサーバとして動作させると CORS などの制限を突破するのが面倒なので、そのウェブサーバ自体を現時点では CIRCUS API に「マウント」して使っている。

単体のトークンベースの認証機能を有しているが、上記の事情があるため実際には使われていない。

クライアント側 (src/client)

使い方はドキュメントに書かれている通りなので省略。とりあえず ImageSourceViewStateCompositionViewer の 4 つがとても基本的なクラスであり、これらが何をやっているのかを理解できれば全体像は把握可能。

  • Viewer: ブラウザ上の div 要素にマウントするコンポーネントそのもの。Composition に接続しそれを表示する。それぞれの Viewer は ViewState を保持しており、画像を表示する際にそれを参照する。
  • ImageSource: 「何を表示するか」。サーバからの画像の受信などを行って非同期的にビットマップを出力する。基本的には 1 つの DICOM シリーズと、そのビットマップへの表現方法を表しているクラス。
  • ViewState: 「どのように表示するか」。ImageSource の表示条件(コントラスト、角度など)を決める。
  • Composition: ImageSource と Annotation をまとめたもの。
  • Annotatoin: 矢印などの画像注記。

ほとんどの処理は canvas ベースなので、特にフレームワーク(jQuery や React など)は使っていない。

ビルドシステムは webpack + ts-loader。

共用コード (src/common)

  • 主に座標計算系のライブラリが入っている。
  • 行列演算ライブラリとして当初は gl-matrix というライブラリを使っていたが人気が微妙だったので three.js に途中で変更。 …しようとしてそれ関連のバグが多少残っている。

Series へのアクセスに関する構成メモ 2018.08.27

type SeriesAccessor = {
    load: (imageNum: number) => Promise</* unparsed */ArrayBuffer>;
    images: string;
};

type DicomRepository = { getSeries: (seriesUid: string) => Promise<SeriesAccessor> };

export interface ParsedDicomData {
  metadata: DicomMetadata;
  pixelData?: ArrayBuffer; // parsed
  /* など */
}

type PixelData = ArrayBuffer; // parsed

// image = a file in dicom series.

// Injected to ctx by middleware
declare class SeriesImageStore { // previously SeriesSliceStore
    constructor(images: string, load /* from seriesAccessor */);
    private loadedImages: Map<number, ParsedDicomData>;
    load: (multiRange: string, priority: number) => Promise<void>;
    isLoaded: (multiRange: string) => boolean;
    getImage: (multiRange: string) => ParsedDicomData[];
};

declare function loadMetadata(store: SeriesImageStore): Promise<Metadata>;
declare function loadVolume(store: SeriesImageStore, targetImages: string): Promise<PixelData>;

declare function createImageStoreProvider(
    repository: DicomRepository,
    cache: LRU.Cache<string, SeriesImageStore>
): ImageStoreProvider;

// Injected to helper
type ImageStoreProvider = (seriesUID: string) => Promise<SeriesImageStore>;