DICOM(10)GDCM:サブフォルダのdcmファイルも一括処理するバッチファイルが完成

前の関連記事:DICOM(9)GDCM:バッチファイルで特定のファイル以外のファイルを削除する


DICOMにもGDCMにも関係のない記事が続きましたがようやくサブフォルダのdcmファイルも一括処理するバッチファイルが完成しました。DICOMファイルの情報を表示する機能も統合しました。

バッチファイルの作成方法:カウンタなしバージョン


まずはDICOM(6)Windowsで使える変換ツール:GDCM JPEG2000から変換可のとおりGDCMをインストールします。

パスの設定は「Do not add GDCM to the system PATH」以外を選択しておきます。

以下を「メモ帳」に入力します。

(2013.12.20追記。フォルダ名によっては意図せぬ動作をすることがわかったのでこのコードは削除しました。この記事の一番下にある修正版を使ってください。)

ファイル→名前をつけて保存。



ファイルの種類を「すべてのファイル」に変更。

ファイル名を「DICOM非圧縮変換.bat」にして保存します。

バッチファイルの使用方法


非圧縮にしたいdcmファイルが入ったフォルダをこのDICOM非圧縮変換.batにドラッグアンドドロップするとメニューが表示されます。

あとはそれにしたがって機能を選択するだけです。

フォルダではなくdcmファイルをドロップしたときはgdcminfoが起動してDICOM情報を表示してくれます。

フルパスに空白があるとエラーになる部分は修正済み


早速使ってみたらフルパスに空白があるとエラーがでました。さてどうしたものか、、、
gdcmconv -w %%i %%~dpiDec_%%~nxi を gdcmconv -w "%%i" "%%~dpiDec_%%~nxi"

del /q %%i を del /q "%%i"

以上の部分を変更したら直りました。

for /r  %%i in (*.dcm) do ( を for /r  "%%i" in (*.dcm) do (にするとエラーになりました。

どうして?、なんか統一性がないようでバッチファイルはなかなか気難しいですね。

上記の部分はさかのぼって全部修正しました。

処理ファイル数をカウントする機能をつける


修正ついでに進行状況を修正する機能を考えました。

動作が遅くなるだけなのですがなんか終わる目処があったほうが落ち着くので、、、

ドロップしたフォルダにあるDec_以外で始まるファイル名のdcmファイルをすべて数える方法
@echo off
pushd %1
setlocal enabledelayedexpansion
    SET Count1=0
    for /r  %%i in (*.dcm) do (
      SET STR=%%~nxi
      if not "!STR:~0,4!"=="Dec_" (
        SET /a Count1=Count1+1
      )
    )
echo Dec_以外で始まるファイル名のdcmファイルは%Count1%個です。
echo.
pause
endlocal
ドロップしたフォルダにあるDec_で始まるファイル名のdcmファイルをすべて数える方法
@echo off
pushd %1
    SET Count2=0
    for /r  %%i in (Dec_*.dcm) do SET /a Count2=Count2+1
echo Dec_で始まるファイル名のdcmファイルは%Count2%個です。
echo.
pause
endlocal

バッチファイル:カウンタつきバージョン


GDCMのインストール方法はカウンタなしバージョンと同じです。

処理中のファイル数を表示する機能を追加しています。

ファイルカウントは1つのfor文で処理しています。

Dec_ファイルを再度変換しないように条件判定も追加しました。

Count1は変換前のdcmファイル数、Count2は変換後のファイル数、Count3は処理済のファイル数です。

(2013.12.20追記。フォルダ名によっては意図せぬ動作をすることがわかったのでこのコードは削除します。この記事の一番下にある修正版を使ってください。)

バッチファイルでも結構いろいろなことができましたね。かなりの学習時間をくいましたけど。

ドロップしたファイルのあるフォルダ名を得る方法


ちょっとてこずったのでメモ。
@echo off
for  %%A in ("%CD%") do set CURRENT_DIR_NAME=%%~nxA
echo このファイルのあるフォルダ名は「%CURRENT_DIR_NAME%」です。
echo.
pause
フォルダをドロップしてもそのフォルダのあるファイル名を表示してくれます。

フルパスにある文字によってはgdcmconvはエラーがでることが判明


さっそく出来上がったバッチファイルでOsirixのサイトにあるサンプルデータを変換してみました。(zioTerm2009(3)OsirixのサイトにあるDICOMサンプルデータをインポートする

ところが変換後のデータをチェックしてみると変換されていないものがあります。

原因を追究してみると以下のようなフォルダ名が原因とわかりました。

ちなみにフルパスに日本語があってもだめなようです。

IRM ceフ〉eフ|rale, neuro-craフOe (元名IRM cérébrale, neuro-crâne)
Extreフ[iteフ《 infeフ〉ieures Pied_cheville_UHR (Adulte) (元名Extrémités inférieures Pied_cheville_UHR (Adulte))
TeフUe Dental (Adulte) (元名Tête Dental (Adulte))
CT2 teフUe, face, sinus (元名CT2 tête, face, sinus)

バッチコマンドは問題なく動きますがgdcmconvとかgdcminfoはこれらのフォルダ名がフルパスにあるとファイルを読み込めません。

変換されていないのに変換元のdcmファイルを削除されると困ります。

そこでdcmファイルの変換がうまくいったかどうかのチェック機能をつけました。

修正版:変換成否判定つき


(2013.12.22追記。このコードも削除しました。この記事の一番下にあるコードを使ってください。)

バッチファイルの変数は自然数だけしか扱えないのですね。

set /a Count3=Count3-Count4でマイナスになる場合でもCount3は0になりました。

検索したらバッチファイルで小数の四則演算をするには? • C言語交流フォーラム ~ mixC++ ~で力技で小数を扱う方法が編み出されていました。とっても大変そうですが、、、

さらにエラーログを吐き出す機能を追加


実際に使ってみて44178個のファイルを変換してそのうち出た3個の失敗ファイルを探すのに苦労したのでエラーログを吐く機能を追加しました。

変換失敗したファイルのフルパスをバッチファイルのあるフォルダにErrorLog.txtとして記録します。
@echo off
setlocal enabledelayedexpansion
echo %~a1|find "d">NUL
if %ERRORLEVEL% EQU 0 (
  pushd %1
  set Count1=0
  set Count2=0
  set Count3=0
  for /r  %%i in (*.dcm) do (
    set /a Count3=Count3+1
    cls
    echo dcmファイルをカウントしています。!Count3!個目。
    set STR=%%~nxi
    if "!STR:~0,4!"=="Dec_" (
      set /a Count2=Count2+1
    ) else (
      set /a Count1=Count1+1
    )
  )
  goto menu
) else (
  echo ファイル変換をする場合はdcmファイルを含んだフォルダをドロップしてください。
  echo.
  if "%~x1"==".dcm" (
    echo 以下はドロップされたdcmファイルの情報です。
    echo.
    gdcminfo %1
  ) else (
    echo dcmファイルの情報を表示するにはdcmファイルをドロップしてください。
  )
  echo.
  pause
  exit
)
:menu
cls
echo ************ dcmファイル非圧縮変換バッチ ************
echo.
echo [1] : !Count1!個のdcmファイルをすべて非圧縮形式に変換する。
echo.
echo [2] : 変換元ファイルを削除する。!Count1!個あります。
echo        (ファイル名がDec_から始まるファイルのみを残してそれ以外を削除します。)
echo.
echo [3] : 変換後ファイルを削除する。!Count2!個あります。
echo        (ファイル名がDec_から始まるファイルを削除します。)
echo.
echo.[4] : 終了する。
echo.
echo ****************************************************
echo.
set /p NUM="機能を数字で選択してEnterキーを押してください。"
set Count3=0
set Count4=0
if "%NUM%"=="1" (
  cls
  echo サブフォルダ内を含む!Count1!個のdcmファイルをすべて非圧縮形式に変換します。
  echo.
  echo はい(y^)/いいえ(n^)を選択後Enterキーを押してください。
  echo.
  set /p KEY="変換後に変換元ファイルを削除しますか?(y/n)"
  cls
  if "!KEY!" == "y" (
    for /r  %%i in (*.dcm) do (
      set STR=%%~nxi
      if not "!STR:~0,4!"=="Dec_" (
        set /a Count3=Count3+1
        echo %%iを変換中(!Count3!/!Count1!^)
        if not !Count4!==0 ( echo 変換失敗(!Count4!/!Count3!^) )
        gdcmconv -w "%%i" "%%~dpiDec_%%~nxi"
        if exist "%%~dpiDec_%%~nxi" (
          del /q "%%i"
        ) else (
          set /a Count4=Count4+1
          echo %%i>>"%~dp0ErrorLog.txt"
        )
        cls
      )
    )
    set /a Count3=Count3-Count4
    echo !Count3!個のファイル変換を成功しました。
    if not !Count4!==0 (
      echo !Count4!個は変換失敗しました。
      echo 失敗の原因としては不正なパス名などが考えられます。
    )
    echo 変換に成功した!Count3!個の変換元ファイルは削除しました。
  )
  if "!KEY!" == "n" (
    for /r  %%i in (*.dcm) do (
      set STR=%%~nxi
      if not "!STR:~0,4!"=="Dec_" (
        set /a Count3=Count3+1
        echo %%iを変換中(!Count3!/!Count1!^)
        if not !Count4!==0 ( echo 変換失敗(!Count4!/!Count3!^) )
        gdcmconv -w "%%i" "%%~dpiDec_%%~nxi"
        if not exist "%%~dpiDec_%%~nxi" ( 
          set /a Count4=Count4+1
          echo %%i>>"%~dp0ErrorLog.txt"
        )
        cls
      )
    )
    set /a Count3=Count3-Count4
    echo !Count3!個のファイル変換を成功しました。
    if not !Count4!==0 (
      echo !Count4!個は変換失敗しました。
      echo 失敗の原因としてはGDCMで読み込めないパス名などが考えられます。
      echo バッチファイルと同じフォルダのErrorLog.txtに失敗ファイル一覧があります。
    )
    echo 変換元ファイルは残っています。
  )
  echo.
  pause
  exit
)
if "%NUM%"=="2" (
  cls
  echo はい(y^)/いいえ(n^)を選択後Enterキーを押してください。
  echo.
  set /p KEY="本当に!Count1!個の変換元ファイルを削除しますか?(y/n)"
  cls
  if "!KEY!" == "y" (
    for /r  %%i in (*.dcm) do (
      set STR=%%~nxi
      if not "!STR:~0,4!"=="Dec_" (
        set /a Count3=Count3+1
        echo %%iを削除中(!Count3!/!Count1!^)
       del /q "%%i"
        cls
      )
    )
    echo ファイル名がDec_から始まるファイルのみを残してそれ以外のファイル!Count3!個を削除しました。
    echo.
    pause
    exit
  ) else (
    goto menu
  )
)
if "%NUM%"=="3" (
  cls
  echo はい(y^)/いいえ(n^)を選択後Enterキーを押してください。
  echo.
  set /p KEY="本当に!Count2!個の変換後ファイルを削除しますか?(y/n)"
  cls
  if "!KEY!" == "y" (
    for /r  %%i in (Dec_*.dcm) do (
      set /a Count3=Count3+1
      echo %%iを削除中(!Count3!/!Count2!^)
     del /q "%%i"
      cls
    )
    echo ファイル名がDec_から始まるファイル!Count3!個を削除しました。
    echo.
    pause
    exit
  ) else (
    goto menu
  )
)
if "%NUM%"=="4" (
  exit
)
echo.
echo 1から4までの数値を入力してください。
echo.
pause
goto menu
endlocal

重複部分をまとめた版


ちょこちょこ修正するときに複数個所修正するのが面倒になってきたので重複部分をまとめました。

@echo off
setlocal enabledelayedexpansion
echo %~a1|find "d">NUL
if %ERRORLEVEL% EQU 0 (
  pushd %1
  set Count1=0
  set Count2=0
  set Count3=0
  for /r  %%i in (*.dcm) do (
    set /a Count3=Count3+1
    cls
    echo dcmファイルをカウントしています。!Count3!個目。
    set STR=%%~nxi
    if "!STR:~0,4!"=="Dec_" (
      set /a Count2=Count2+1
    ) else (
      set /a Count1=Count1+1
    )
  )
  goto menu
) else (
  echo ファイル変換をする場合はdcmファイルを含んだフォルダをドロップしてください。
  echo.
  if "%~x1"==".dcm" (
    echo 以下はドロップされたdcmファイルの情報です。
    echo.
    gdcminfo %1
  ) else (
    echo dcmファイルの情報を表示するにはdcmファイルをドロップしてください。
  )
  echo.
  pause
  exit
)
:menu
cls
echo ************ dcmファイル非圧縮変換バッチ ************
echo.
if !Count1!==0 (
  echo 変換元となるdcmファイルはありません。
) else (
  echo [1] : !Count1!個のdcmファイルをすべて非圧縮形式に変換する。
  echo.
  echo [2] : 変換元ファイルを削除する。!Count1!個あります。
  echo        (ファイル名がDec_から始まるファイルのみを残してそれ以外を削除します。)
)
echo.
if !Count2!==0 ( 
  echo 変換後のファイルはありません。
) else (
  echo [3] : 変換後ファイルを削除する。!Count2!個あります。
  echo        (ファイル名がDec_から始まるファイルを削除します。)
)
echo.
echo.[4] : 終了する。
echo.
echo ****************************************************
echo.
set /p NUM="機能を数字で選択してEnterキーを押してください。"
set Count3=0
set Count4=0
cls
if "%NUM%"=="1" (
  echo サブフォルダ内を含む!Count1!個のdcmファイルをすべて非圧縮形式に変換します。
  echo.
  echo はい(y^)/いいえ(n^)を選択後Enterキーを押してください。
  echo.
  set /p KEY="変換後に変換元ファイルを削除しますか?(y/n)"
  cls
  if not "!KEY!" == "y" ( 
    if not "!KEY!" == "n" (
      echo はい(y^)/いいえ(n^)を選択後Enterキーを押してください。
      echo.
      pause
      goto menu
    )
  )
  for /r  %%i in (*.dcm) do (
    set STR=%%~nxi
    if not "!STR:~0,4!"=="Dec_" (
      set /a Count3=Count3+1
      echo %%iを変換中(!Count3!/!Count1!^)
      if not !Count4!==0 ( echo 変換失敗(!Count4!/!Count3!^) )
      gdcmconv -w "%%i" "%%~dpiDec_%%~nxi"
      if not exist "%%~dpiDec_%%~nxi" ( 
        set /a Count4=Count4+1
        echo %%i>>"%~dp0ErrorLog.txt"
      ) else (
        if "!KEY!" == "y" ( del /q "%%i" )
      )
      cls
      )
    )
  set /a Count3=Count3-Count4
  echo !Count3!個のファイル変換を成功しました。
  if not !Count4!==0 (
    echo !Count4!個は変換失敗しました。
    echo 失敗の原因としてはGDCMで読み込めないパス名などが考えられます。
    echo バッチファイルと同じフォルダのErrorLog.txtに失敗ファイル一覧があります。
  )
  if "!KEY!" == "y" (
    echo 変換に成功した!Count3!個の変換元ファイルは削除しました。
  ) else (
    echo !Count1!個の変換元ファイルは残っています。
  )
  echo.
  pause
  exit
)
if "%NUM%"=="2" (
  echo はい(y^)/いいえ(n^)を選択後Enterキーを押してください。
  echo.
  set /p KEY="本当に!Count1!個の変換元ファイルを削除しますか?(y/n)"
  cls
  if "!KEY!" == "y" (
    for /r  %%i in (*.dcm) do (
      set STR=%%~nxi
      if not "!STR:~0,4!"=="Dec_" (
        set /a Count3=Count3+1
        echo %%iを削除中(!Count3!/!Count1!^)
       del /q "%%i"
        cls
      )
    )
    echo ファイル名がDec_から始まるファイルのみを残してそれ以外のファイル!Count3!個を削除しました。
    echo.
    pause
    exit
  ) else (
    goto menu
  )
)
if "%NUM%"=="3" (
  echo はい(y^)/いいえ(n^)を選択後Enterキーを押してください。
  echo.
  set /p KEY="本当に!Count2!個の変換後ファイルを削除しますか?(y/n)"
  cls
  if "!KEY!" == "y" (
    for /r  %%i in (Dec_*.dcm) do (
      set /a Count3=Count3+1
      echo %%iを削除中(!Count3!/!Count2!^)
     del /q "%%i"
      cls
    )
    echo ファイル名がDec_から始まるファイル!Count3!個を削除しました。
    echo.
    pause
    exit
  ) else (
    goto menu
  )
)
if "%NUM%"=="4" (
  exit
)
echo.
echo 1から4までの数値を入力してください。
echo.
pause
goto menu
endlocal

参考にしたサイト


バッチファイルでフォルダ内のファイル数をカウントしたい | Windows系OSのQ&A【OKWave】
やっぱりforでカウントするしかないのですかね。

ト、キョウソウ: Windowsで特定フォルダ以下の全ファイル数をカウント。
試してはいないですがこんな方法もあるみたいです。

バッチファイルでUnixのbasenameコマンド相当の結果を取得する - 今日つかったスニペット
バッチファイルでファイルのフルパスからフォルダ名を得る方法

バッチファイルで小数の四則演算をするには? • C言語交流フォーラム ~ mixC++ ~
バッチファイルで小数を無理やり扱う方法

次の関連記事:DICOM(11)OsirixのDICOMサンプルデータをWindowsで使う

PR

0 件のコメント:

コメントを投稿