DCMTK 라이브러리를 VC++ 2012 프로젝트에 설정하기



dcmtk-3.6.1_20121102.tar.gz


개요

VC++ 프로젝트에서 사용할 수 있는 DICOM 라이브러리 중에서 널리 알려진 2개의 라이브러리(DCMTK, GDCM) 중에서 DCMTK를 이용하여 화면에 DICOM영상을 출력하고자 한다. DCMTK 3.6.1 부터는 설정(Configure)할 때 하나의 DLL 파일로 빌드되도록 설정할 수 있으므로 여기서 적용하려고 한다.


DCMTK Download

http://dicom.offis.de/dcmtk.php.en

 - 압축을 해제하면 dcmtk-3.6.1 폴더 안에 CMakeLists.txt 파일이 포함되어 있는 것을 확인할 수 있다.

 - CMakeLists.txt 파일은 CMake 툴을 이용하여 VC++ 2012 프로젝트 파일을 생성할 때 참조되는 프로젝트 구성정보를 포함하고 있다


CMake를 이용하여 다운로드한 DCMTK 소스를 VC++ 2012 프로젝트로 설정하기

소스코드를 다운로드하여 Windows 라이브러리(*.lib) 로 컴파일하여 프로젝트에 사용해야 하므로 CMake 라는 프로그램을 이용하여 VC++ 2012 프로젝트 파일을 생성하고, VC++2012 에서 그 프로젝트 파일을 열고  컴파일하여 lib 파일을 생성해야 한다. 이렇게 생성된 Static Library는 다른 VC 프로젝트에서 사용될 수 있다.

1. CMake 다운로드 및 설치

  - 위의 링크를 클릭하여 최신버전을 다운로드하고 압축을 해제한 후 실행파일을 더블클릭하면 쉽게 설치할 수 있다.

2. CMake 실행 및 DCMTK 프로젝트 정보 설정

  - Where is the source code : DCMTK의 폴더 중에서 CMakeLists.txt 파일을 포함한 폴더를 확인하여 그 중 최상위에 있는 폴더를 선택한다

  - Where to build the binaries: 프로젝트 파일을 생성할 디렉토리, 위의 디렉토리 아래에 projects 등의 이름으로 지정해주면 된다

3. Configure

  - CMake화면 왼쪽 하단에 있는 'Configure' 버튼을 클릭한다.

  - 컴파일러를 선택하는 콤보박스에서 Visual Studio 11 를 선택한다 (참고로, VC++2012 의 컴파일러 버전은 11 이다)

  - 위의 절차를 마치면 CMake는 몇분에 걸쳐 긴 설정작업을 한다.

  - 빨강색으로 선택된 리스트가 나타나면 화면 중앙 상부에 있는 [Advanced] 체크박스를 선택한다

  - 화면중앙에 BUILD 항목 아래에 BUILD_SINGLE_SHARED_LIBRARY 체크하면 모든 모듈이 합쳐져서 하나의 DLL 파일과 하나의 LIB 파일을 생성할 수 있다  체크후에 다시 'Configure' 버튼을 눌러주면 적색바탕이 헤제된다 

4. Generate

  - 'Configure' 버튼 오른쪽에 있는 'Generate' 버튼을 누르면 CMake는 CMakeLists.txt와 소스코드를 이용하여 VC++2012 프로젝트 파일을 생성해낸다

5. 생성된 프로젝트 파일의 확인

  - 2번 항목에서 지정한 폴더에 VC++2012 프로젝트 파일이 생성될 것이다. 예를 들어 projects 라는 폴더를 지정했다면 그 폴더 안을 확인한다

  - 중요: 위에서 지정한 projects 폴더 안에는 VC++ 2012 용 헤더파일도 생성되는데, 다른 프로젝트에서 DCMTK 라이브러리를 사용할 때는 projects 폴더 안에 생성된 헤더파일을 소스코드에 포함해야 한다는 것을 주의해야 한다. 그렇지않고 DCMTK 원본 소스에 포함된 헤더파일을 사용하는 경우에는 컴파일할 때 오류가 발생하며 그 오류의 원인을 추측하기가 매우 어렵게 된다. 특히 config/include/osconfig.h 헤더파일을 소스코드에 include 할 때는 반드시 이 점을 지켜야 한다.

osconfig.h


생성된 프로젝트 파일을 VC++ 2012에서 열고 컴파일하기

 - CMake 가 생성해준 프로젝트 파일(DCMTK.sln)을 VC++ 2012에서 연다

 - 프로젝트 위에서 마우스 우측을 눌러서 Configuration Properties > General > Character Set : Use Unicode Character Set 으로 설정

 - VC++ 2012에 열린 프로젝트 중에서 ALL_BUILD 프로젝트를 빌드( Build > Build Solution)한다 (약 20분정도 지나면 빌드가 완성된다)

 - 이어서 프로젝트 중에 INSTALL 위에서 마우스 우측을 눌러 Project Only Build INSTALL을 선택하면 위에서 빌드된 dcmtk.lib, dcmtk.dll 그리고         include 폴더에 헤더파일이 저장되어 최종적으로 C:/Program Files/DCMTK 안에 저장된다

 - 위의 DCMTK 프로젝트가 Release 모드에서 /MT 로 설정하고 라이브러리를 빌드했다면 이 라이브러리를 사용하는 프로젝트가 Debug 모드일 경우에 Multi-Threaded debug(/MTd) 으로 설정하여 테스트하고 Release일 경우에는Multi-Threaded(/MT)으로 설정해야 오류가 발생하지 않았다


VC++ 2012 에서 빌드한 결과물 확인하기

 - 위에서 지정한 projects /bin 폴더 안에 Debug 혹은 Release 폴더가 설정에 따라 생성되고 그 안에 dcmtk.dll 파일이 생성된다.

 - 위에서 지정한 projects / lib 폴더 안에는 dcmtk.lib 파일이 생성된다.

 - dcmtk 라이브러리를 사용하는 프로젝트에서는 dcmtk.dll 파일과 dcmtk.lib을 동시에 사용하도록 설정해야 한다.

  


dcmtk_dll.zip

dcmtk.lib



위에서 생성한 lib 파일을 다른 프로젝트에서 사용하기

  - 프로젝트를 생성하고 프로젝트 위에서 마우스 우측을 눌러 Properties항목을 선택하여 VC++ Directories 에서 다음 2가지 정보를 등록하면 된다

  - Include Directories: DCMTK 가 포함하고 있는 헤더파일이 있는 폴더를 등록한다

  - Lib Directories : 위에서 생성한 dcmtk.lib 파일을 포함한 폴더(lib) 를 등록한다

  - DLL 파일을 사용할 경우에는 DLL 파일을 사용하고자 하는 프로젝트의 Debug/Release 안에 복사해주면 된다


소스코드에서 설정할 것들

필요한 DCMTK 헤더파일을 포함한다 (#include "dcmtk/dcmimgle/dcmimage.h" 등 )

필요한 라이브러리를 링크한다 (#pragma comment(lib, "dcmtk.lib") )


test.dcm 파일을 로드하고 DCM 파일에서 환자의 이름을 추출하여 콘솔창에 출력하는 예 (http://support.dcmtk.org/docs-dcmrt/mod_dcmdata.html)

dcmtk.dll 파일은 현재 프로젝트의 Debug 혹은 Release 폴더에 두면 된다

#include "dcmtk/config/osconfig.h

#include "dcmtk/dcmdata/dctk.h"

#include <iostream>


#pragma comment(lib, "dcmtk.lib")


using namespace std;


int main()

{

DcmFileFormat fileformat;

OFCondition status = fileformat.loadFile("test.dcm");

if (status.good())

{

 OFString patientName;

 if (fileformat.getDataset()->findAndGetOFString(DCM_PatientName, patientName).good())

 {

cout << "Patient's Name: " << patientName << endl;

 } 

 else

 {

cerr << "Error: cannot access Patient's Name!" << endl;

 }

else

{

 cerr << "Error: cannot read DICOM file (" << status.text() << ")" << endl;

}

  return 0;

}


DICOM 파일에 태그를 삽입하는 예

char uid[100]; DcmFileFormat fileformat; DcmDataset *dataset = fileformat.getDataset();

dataset->putAndInsertString(DCM_SOPClassUID, UID_SecondaryCaptureImageStorage); dataset->putAndInsertString(DCM_SOPInstanceUID, dcmGenerateUniqueIdentifier(uid, SITE_INSTANCE_UID_ROOT)); dataset->putAndInsertString(DCM_PatientName, "Doe^John"); /* ... */ dataset->putAndInsertUint8Array(DCM_PixelData, pixelData, pixelLength);


//변경된 태그를 실제파일에 반영함. 이때 기존동일파일이 있으면 둘다 파일포맷이 손상되므로 주의요. 반드시 새로운 파일에 저장 OFCondition status = fileformat.saveFile("test.dcm", EXS_LittleEndianExplicit); if (status.bad()) cerr << "Error: cannot write DICOM file (" << status.text() << ")" << endl;


DICOMDIR을 생성하고 파일을 추가하는 예

DicomDirInterface dicomdir;
OFCondition status = dicomdir.createNewDicomDir();
if (status.good())
{
  while ( /* there are files */ )
    dicomdir.addDicomFile( /* current filename */ );
  status = dicomdir.writeDicomDir();
  if (status.bad())
    cerr << "Error: cannot write DICOMDIR (" << status.text() << ")" << endl;
} else
  cerr << "Error: cannot create DICOMDIR (" << status.text() << ")" << endl;


File open

// File open
OPENFILENAME ofn;
TCHAR lpstrFile[MAX_PATH] = L"";
memset(&ofn, 0, sizeof(ofn));
ofn.lStructSize = sizeof(ofn);
ofn.hwndOwner   = hWnd;
ofn.lpstrFile   = lpstrFile;
ofn.nMaxFile    = MAX_PATH;
 
if (GetOpenFileName(&ofn) != 0)
{
 USES_CONVERSION;
 char* filename = W2A(ofn.lpstrFile);
 
 if(ptrDicomImage) delete ptrDicomImage;
 
 E_DecompressionColorSpaceConversion opt_decompCSconversion = EDC_photometricInterpretation;
 // register JPEG decompression codecs
 DJDecoderRegistration::registerCodecs(opt_decompCSconversion);
 
 ptrDicomImage = new DicomImage(filename);
 
 if(!ptrDicomImage)
 {
  // Fail to open file
 }
 
 if(ptrDicomImage->getStatus() == EIS_Normal)
 {
  // Fail to open file
 }
 
 InvalidateRect(hWnd, NULL, FALSE);
}


Display

int width  = (int)ptrDicomImage->getWidth();
int height = (int)ptrDicomImage->getHeight();
void *data = NULL; 
 
ptrDicomImage->setMinMaxWindow();
if(ptrDicomImage->createWindowsDIB(data, width*height) && data)
{ 
 BITMAPINFO bi; 
 bi.bmiHeader.biSize        = sizeof(bi); 
 bi.bmiHeader.biWidth       = width; 
 bi.bmiHeader.biHeight      = -height; 
 bi.bmiHeader.biPlanes      = 1; 
 bi.bmiHeader.biBitCount    = 24; 
 bi.bmiHeader.biCompression = BI_RGB; 
 bi.bmiHeader.biSizeImage   = 0;
 
 HBITMAP hbmp = CreateDIBitmap(hdc, &bi.bmiHeader,
    CBM_INIT, data, &bi, DIB_RGB_COLORS); 
 
 if(hbmp)
 { 
  HDC memDC = CreateCompatibleDC(hdc); 
  SelectObject(memDC, hbmp); 
 
  SetStretchBltMode(hdc, HALFTONE);
  StretchBlt(hdc, 0, 0, 
   width, height,
   memDC, 0, 0, width, height, SRCCOPY);
 
  DeleteDC(memDC); 
  DeleteObject(hbmp);
 } 
}
delete data;


Posted by cwisky

댓글을 달아 주세요

  1. 마력의코드 2014.01.09 11:11

    좋은 정보 감사합니다. ^^

  2. 우유좋아 2014.04.01 18:06

    안녕하세요. 저는 대학생 4학년 졸업과제를 준비하고 있는 의공학과 학생입니다. DICOM에 대해 공부하고 있는데 DCMTK 라이브러리를 이용해서 DICOM파일을 MFC에서 불러올려고 하는데 위의 내용을 따라 실행해보았습니다. CMAKE를 이용하여 configure하는 부분, '화면중앙에 BUILD 항목 아래에 BUILD_SINGLE_SHARED_LIBRARY 체크하면 모든 모듈이 합쳐져서 하나의 DLL 파일과 하나의 LIB 파일을 생성할 수 있다 체크후에 다시 'Configure' 버튼을 눌러주면 적색바탕이 헤제된다'에서 이 부분을 찾지 못했습니다. 이 과정을 거치지 않고 바로 generate해서 파일을 생성해서 그런지 dcmtk.dll, dcmtk.lib이 나타나지 않네요. 혹시 어떻게 하면 이 부분을 해결할 수 있을지 알려주시면 정말 감사하겠습니다.

  3. 회사원1 2015.09.04 17:55

    좋은 정보 감사합니다.
    Visual studio 2012 pro 버전을 사용하는 사용자로 저의 경우에는 프로젝트의 Character Set을 멀티바이트로 사용하여야만 동작했었습니다.
    참고하시면 도움이 될거라 생각합니다.