본문 바로가기

Visual C++/DCMTK, VC++ Setup

DCMTK VC++ 2012 Setup

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;