Extracting Tag Data using Borland C++ Builder
Moderator: Moderator Team
Extracting Tag Data using Borland C++ Builder
We are writing a program (using Borland C++ Builder 6) to calculate Dose per Monitor Unit in proton therapy treatment, and we need to extract programmatically about 30 non-image related tag values from a treatment plan file. How reasonable is it to "port" the necessary code from DCMTK to C++Builder? Is all the necesary code in the DCMDATA module?
Any and all help or guidance is much appreciated.
Thanks in advance.
David
Any and all help or guidance is much appreciated.
Thanks in advance.
David
Re: Extracting Tag Data using Borland C++ Builder
I guess we are looking for suggestions about an approach as much as anything more specific, although specific is good, too. For example, would it be best to try to compile the whole toolkit under C++Builder, then make our program a dependency of the toolkit? Or can we select out - obviously with much appreciation and acknowledgement - just the sections of code needed to extract the data?dave wrote:We are writing a program (using Borland C++ Builder 6) to calculate Dose per Monitor Unit in proton therapy treatment, and we need to extract programmatically about 30 non-image related tag values from a treatment plan file. How reasonable is it to "port" the necessary code from DCMTK to C++Builder? Is all the necesary code in the DCMDATA module?
dave
-
- DCMTK Developer
- Posts: 2052
- Joined: Fri, 2004-11-05, 13:47
- Location: Oldenburg, Germany
- Contact:
Hi Dave,
Borland C++ Compiler is not actively supported by the DCMTK. However it should be possible to compile the toolkit using this compiler, too.
The necessary code to extract tags from a DICOM file is contained in the module "dcmdata", which itself needs classes from module "ofstd". Using these two modules, you can read and write DICOM files and work with tags, sequences, items etc.
Take a look at the doxygen documentation included in the toolkit (and published on the DCMTK-website), how to access tags. The dcmdata-documentation should be a good point to start with:
http://support.dcmtk.org/docs/mod_dcmdata.html
Regards
Michael Onken
Borland C++ Compiler is not actively supported by the DCMTK. However it should be possible to compile the toolkit using this compiler, too.
The necessary code to extract tags from a DICOM file is contained in the module "dcmdata", which itself needs classes from module "ofstd". Using these two modules, you can read and write DICOM files and work with tags, sequences, items etc.
Take a look at the doxygen documentation included in the toolkit (and published on the DCMTK-website), how to access tags. The dcmdata-documentation should be a good point to start with:
http://support.dcmtk.org/docs/mod_dcmdata.html
Regards
Michael Onken
-
- Posts: 25
- Joined: Wed, 2005-07-20, 22:42
- Location: Payson, Arizona
Borland C++
There has already been a discussion in the installation forum regarding Borland C++. You should look at this. Also, since I have now compiled DCMTK using Borland C++ 5.5, I would be prepared to answer any questions about getting the DCMTK compiled, in case you have any difficulties. In short, apart from a couple of tweaks, everything was reasonably painless. This is of course the command line implementation. Personally, if you need some kind of GUI environment, I would advocate the separate process approach, ie, run the dcmtk command line tools as child processes of your GUI app, supplying either parameters or query files, and redirecting the results for extraction.
Extracting the tags from specific dicom formatted files is also easy. There are some other tools and samples available, besides the dcmtk itself, eg look at ezdicom from Chris Riorden, implemented in Delphi, but should be easy to port to C++.
Hope this helps, and I would be happy to assist any way I can. Also I did promise to post all of my Borland patches, if there is any interest.
Regards,
Roy Dobbins
Extracting the tags from specific dicom formatted files is also easy. There are some other tools and samples available, besides the dcmtk itself, eg look at ezdicom from Chris Riorden, implemented in Delphi, but should be easy to port to C++.
Hope this helps, and I would be happy to assist any way I can. Also I did promise to post all of my Borland patches, if there is any interest.
Regards,
Roy Dobbins
-
- Posts: 49
- Joined: Wed, 2005-02-16, 16:27
-
- Posts: 25
- Joined: Wed, 2005-07-20, 22:42
- Location: Payson, Arizona
Borland c++ 5.5 patches to compile dcmtk
These are the changes I have made to get the dcmtk to compile and run:-
I have not exhaustively tested the effect of these changes, but I have tested, and used several of the major tools, such as findscu,movescu,storescu, and they are working great so far.
Some of these patches are ugly, eg those in the files dcdict.cxx, and storescp.cxx, which have borland specific patches. These have to do with the strchr() and strrchr() function prototypes, and whether the parameters are const or non const.
I am sure there is a cleaner way to get the right function prototypes to be defined in a compatible way. However, these are #ifdef'd with __BORLANDC__ sections, so at least wont affect other compilers.
If I have time to investigate this further, I will try to propose a better solution.
Let me know if this works, or if you discover other problems,
Regards,
--roy
Here are the changes:-
Patches to dcmtk to allow compiling the source using Borland C++ 5.5
options in cmakecache.txt:
These are not specific to BCC5.5, however they may cause problems in initially getting everything compiled and linked:
--turn off support for zlib, jpeg, tiff, png depending on your requirements, if you have problems linking to these libraries.
other compile,link errors which you may encounter:
if you get the linker error:-
netapi32.lib not found
then add c:\borland\bcc32\lib\psdk to the bcc32.cfg
(found in the borland\bin directory)
you should have something like:-
-L"c:\Borland\Bcc55\lib;c:\Borland\Bcc55\lib\psdk"
-if you get the compiler error:-
_errno not found
in \dcmtk\config\include\cfwin32.h, in the _BORLANDC section,
ie after:-
/* Additional settings for Borland C++ Builder */
#ifdef __BORLANDC__
add:
#define _MT
(to kick in the multithreading library defs, including for errno,
which is what the compiler is complaining about here)
***you must run cmake at this point***
something like:-
cmake -G"Borland Makefiles"
provisional (working) patches, to be investigated further:-
File ..\..\dcmdata\libsrc\dcdict.cxx:
inside the function def:-
static int
splitFields(const char* line, char* fields[], int maxFields, char splitChar)
change:-
p = strchr(line, splitChar);
to:-
#ifdef __BORLANDC__
/* borland c++ builder strchr() expects non const line, so...
* force char * line typecast
* roy todo: is there a cleaner way?
*/
p = strchr((char *)line, splitChar);
#else
p = strchr(line, splitChar);
#endif
File ..\..\dcmnet\apps\storescp.cxx:
inside the function def:-
static void
storeSCPCallback(
change:-
const char *tmpstr6 = strrchr( fileName.c_str(), PATH_SEPARATOR ); //roy TODO: changed to const for bcc32
to:-
#ifdef __BORLANDC__
/* borland c++ builder strchr() expects non const filename, so...
* force char * typecast
* roy todo: is there a cleaner way?
*/
char *tmpstr6 = strrchr( (char *)fileName.c_str(), PATH_SEPARATOR );
#else
char *tmpstr6 = strrchr( fileName.c_str(), PATH_SEPARATOR );
#endif
I have not exhaustively tested the effect of these changes, but I have tested, and used several of the major tools, such as findscu,movescu,storescu, and they are working great so far.
Some of these patches are ugly, eg those in the files dcdict.cxx, and storescp.cxx, which have borland specific patches. These have to do with the strchr() and strrchr() function prototypes, and whether the parameters are const or non const.
I am sure there is a cleaner way to get the right function prototypes to be defined in a compatible way. However, these are #ifdef'd with __BORLANDC__ sections, so at least wont affect other compilers.
If I have time to investigate this further, I will try to propose a better solution.
Let me know if this works, or if you discover other problems,
Regards,
--roy
Here are the changes:-
Patches to dcmtk to allow compiling the source using Borland C++ 5.5
options in cmakecache.txt:
These are not specific to BCC5.5, however they may cause problems in initially getting everything compiled and linked:
--turn off support for zlib, jpeg, tiff, png depending on your requirements, if you have problems linking to these libraries.
other compile,link errors which you may encounter:
if you get the linker error:-
netapi32.lib not found
then add c:\borland\bcc32\lib\psdk to the bcc32.cfg
(found in the borland\bin directory)
you should have something like:-
-L"c:\Borland\Bcc55\lib;c:\Borland\Bcc55\lib\psdk"
-if you get the compiler error:-
_errno not found
in \dcmtk\config\include\cfwin32.h, in the _BORLANDC section,
ie after:-
/* Additional settings for Borland C++ Builder */
#ifdef __BORLANDC__
add:
#define _MT
(to kick in the multithreading library defs, including for errno,
which is what the compiler is complaining about here)
***you must run cmake at this point***
something like:-
cmake -G"Borland Makefiles"
provisional (working) patches, to be investigated further:-
File ..\..\dcmdata\libsrc\dcdict.cxx:
inside the function def:-
static int
splitFields(const char* line, char* fields[], int maxFields, char splitChar)
change:-
p = strchr(line, splitChar);
to:-
#ifdef __BORLANDC__
/* borland c++ builder strchr() expects non const line, so...
* force char * line typecast
* roy todo: is there a cleaner way?
*/
p = strchr((char *)line, splitChar);
#else
p = strchr(line, splitChar);
#endif
File ..\..\dcmnet\apps\storescp.cxx:
inside the function def:-
static void
storeSCPCallback(
change:-
const char *tmpstr6 = strrchr( fileName.c_str(), PATH_SEPARATOR ); //roy TODO: changed to const for bcc32
to:-
#ifdef __BORLANDC__
/* borland c++ builder strchr() expects non const filename, so...
* force char * typecast
* roy todo: is there a cleaner way?
*/
char *tmpstr6 = strrchr( (char *)fileName.c_str(), PATH_SEPARATOR );
#else
char *tmpstr6 = strrchr( fileName.c_str(), PATH_SEPARATOR );
#endif
-
- Posts: 49
- Joined: Wed, 2005-02-16, 16:27
Roy,
In another thread on the installation list, you wrote
> you are compiling and linking with the VCL which is another
> environment altogether.
> another approach to take would be to compile your GUI application
> independently of the dcmtk, compile the (command line) dcmtk tools,
> then launch the tool(s) as separate processes, from your application to
> do the dicom work, and capture the results in your application.
Can you say more about the last part, about capturing the result in the application? We have called dcmdump from within a Borland VCL app, written the output to file, then parsed the file to get the data we needed. It was quick and dirty just to get the job done. The reason I started this thread was to learn how to avoid first writing to file to get the data.
David
In another thread on the installation list, you wrote
> you are compiling and linking with the VCL which is another
> environment altogether.
> another approach to take would be to compile your GUI application
> independently of the dcmtk, compile the (command line) dcmtk tools,
> then launch the tool(s) as separate processes, from your application to
> do the dicom work, and capture the results in your application.
Can you say more about the last part, about capturing the result in the application? We have called dcmdump from within a Borland VCL app, written the output to file, then parsed the file to get the data we needed. It was quick and dirty just to get the job done. The reason I started this thread was to learn how to avoid first writing to file to get the data.
David
-
- Posts: 49
- Joined: Wed, 2005-02-16, 16:27
Hi Dave,
you have to pipe the output of the console to avoid saving the output to a file. I am more into Delphi than C++ but the following example should work with the Builder nevertheless.
function GetConsoleOutput(const Command: STRING;
var Output, Errors: TStringList): BOOLEAN;
var
StartupInfo: TStartupInfo;
ProcessInfo: TProcessInformation;
SecurityAttr: TSecurityAttributes;
PipeOutputRead: THandle;
PipeOutputWrite: THandle;
PipeErrorsRead: THandle;
PipeErrorsWrite: THandle;
Succeed: BOOLEAN;
Buffer: array [0..255] of CHAR;
NumberOfBytesRead: DWORD;
Stream: TMemoryStream;
begin
//Initialize ProcessInfo
FillChar(ProcessInfo, SizeOf(TProcessInformation), 0);
//Initialize SecurityAttr
FillChar(SecurityAttr, SizeOf(TSecurityAttributes), 0);
SecurityAttr.nLength := SizeOf(SecurityAttr);
SecurityAttr.bInheritHandle := True;
SecurityAttr.lpSecurityDescriptor := nil;
//Create pipe
CreatePipe(PipeOutputRead, PipeOutputWrite, @SecurityAttr, 0);
CreatePipe(PipeErrorsRead, PipeErrorsWrite, @SecurityAttr, 0);
//Initialize startupInof
FillChar(StartupInfo, SizeOf(TStartupInfo), 0);
StartupInfo.cb := SizeOf(StartupInfo);
StartupInfo.hStdInput := 0;
StartupInfo.hStdOutput := PipeOutputWrite;
StartupInfo.hStdError := PipeErrorsWrite;
StartupInfo.wShowWindow := sw_Hide;
StartupInfo.dwFlags := STARTF_USESHOWWINDOW or STARTF_USESTDHANDLES;
if CreateProcess(nil, PChar(command), nil, nil, True,
CREATE_DEFAULT_ERROR_MODE or CREATE_NEW_CONSOLE or NORMAL_PRIORITY_CLASS,
nil, nil, StartupInfo, ProcessInfo) then
begin
Result := True;
//Write-Pipes schließen
CloseHandle(PipeOutputWrite);
CloseHandle(PipeErrorsWrite);
//Read read-pipe output
Stream := TMemoryStream.Create;
try
while True do
begin
succeed := ReadFile(PipeOutputRead, Buffer, 255, NumberOfBytesRead, nil);
if not succeed then
break;
Stream.Write(Buffer, NumberOfBytesRead);
end;
Stream.Position := 0;
Output.LoadFromStream(Stream);
finally
Stream.Free;
end;
CloseHandle(PipeOutputRead);
//error reading pipe output
Stream := TMemoryStream.Create;
try
while True do
begin
succeed := ReadFile(PipeErrorsRead, Buffer, 255, NumberOfBytesRead, nil);
if not succeed then
break;
Stream.Write(Buffer, NumberOfBytesRead);
end;
Stream.Position := 0;
Errors.LoadFromStream(Stream);
finally
Stream.Free;
end;
CloseHandle(PipeErrorsRead);
WaitForSingleObject(ProcessInfo.hProcess, INFINITE);
CloseHandle(ProcessInfo.hProcess);
end
else
begin
Result := False;
CloseHandle(PipeOutputRead);
CloseHandle(PipeOutputWrite);
CloseHandle(PipeErrorsRead);
CloseHandle(PipeErrorsWrite);
end;
end;
found this code on the net and it works fine in my application
call it like:
GetConsoleOutput(dcmtkdir+ 'dcmdump.exe' +filename, output, errors)
hope I could help,
regards,
Andreas
you have to pipe the output of the console to avoid saving the output to a file. I am more into Delphi than C++ but the following example should work with the Builder nevertheless.
function GetConsoleOutput(const Command: STRING;
var Output, Errors: TStringList): BOOLEAN;
var
StartupInfo: TStartupInfo;
ProcessInfo: TProcessInformation;
SecurityAttr: TSecurityAttributes;
PipeOutputRead: THandle;
PipeOutputWrite: THandle;
PipeErrorsRead: THandle;
PipeErrorsWrite: THandle;
Succeed: BOOLEAN;
Buffer: array [0..255] of CHAR;
NumberOfBytesRead: DWORD;
Stream: TMemoryStream;
begin
//Initialize ProcessInfo
FillChar(ProcessInfo, SizeOf(TProcessInformation), 0);
//Initialize SecurityAttr
FillChar(SecurityAttr, SizeOf(TSecurityAttributes), 0);
SecurityAttr.nLength := SizeOf(SecurityAttr);
SecurityAttr.bInheritHandle := True;
SecurityAttr.lpSecurityDescriptor := nil;
//Create pipe
CreatePipe(PipeOutputRead, PipeOutputWrite, @SecurityAttr, 0);
CreatePipe(PipeErrorsRead, PipeErrorsWrite, @SecurityAttr, 0);
//Initialize startupInof
FillChar(StartupInfo, SizeOf(TStartupInfo), 0);
StartupInfo.cb := SizeOf(StartupInfo);
StartupInfo.hStdInput := 0;
StartupInfo.hStdOutput := PipeOutputWrite;
StartupInfo.hStdError := PipeErrorsWrite;
StartupInfo.wShowWindow := sw_Hide;
StartupInfo.dwFlags := STARTF_USESHOWWINDOW or STARTF_USESTDHANDLES;
if CreateProcess(nil, PChar(command), nil, nil, True,
CREATE_DEFAULT_ERROR_MODE or CREATE_NEW_CONSOLE or NORMAL_PRIORITY_CLASS,
nil, nil, StartupInfo, ProcessInfo) then
begin
Result := True;
//Write-Pipes schließen
CloseHandle(PipeOutputWrite);
CloseHandle(PipeErrorsWrite);
//Read read-pipe output
Stream := TMemoryStream.Create;
try
while True do
begin
succeed := ReadFile(PipeOutputRead, Buffer, 255, NumberOfBytesRead, nil);
if not succeed then
break;
Stream.Write(Buffer, NumberOfBytesRead);
end;
Stream.Position := 0;
Output.LoadFromStream(Stream);
finally
Stream.Free;
end;
CloseHandle(PipeOutputRead);
//error reading pipe output
Stream := TMemoryStream.Create;
try
while True do
begin
succeed := ReadFile(PipeErrorsRead, Buffer, 255, NumberOfBytesRead, nil);
if not succeed then
break;
Stream.Write(Buffer, NumberOfBytesRead);
end;
Stream.Position := 0;
Errors.LoadFromStream(Stream);
finally
Stream.Free;
end;
CloseHandle(PipeErrorsRead);
WaitForSingleObject(ProcessInfo.hProcess, INFINITE);
CloseHandle(ProcessInfo.hProcess);
end
else
begin
Result := False;
CloseHandle(PipeOutputRead);
CloseHandle(PipeOutputWrite);
CloseHandle(PipeErrorsRead);
CloseHandle(PipeErrorsWrite);
end;
end;
found this code on the net and it works fine in my application
call it like:
GetConsoleOutput(dcmtkdir+ 'dcmdump.exe' +filename, output, errors)
hope I could help,
regards,
Andreas
-
- Posts: 8
- Joined: Wed, 2006-05-31, 07:31
Re: Borland C++
Hi,
Currently i am working on running dcmtk with borland c++ compiler 5.5 (command line).Not able to compile now.I know that you have done this already. Can you tell me steps that are needed to run dcmtk with borland compiler.
Muraly
Currently i am working on running dcmtk with borland c++ compiler 5.5 (command line).Not able to compile now.I know that you have done this already. Can you tell me steps that are needed to run dcmtk with borland compiler.
Muraly
roydobbins wrote:There has already been a discussion in the installation forum regarding Borland C++. You should look at this. Also, since I have now compiled DCMTK using Borland C++ 5.5, I would be prepared to answer any questions about getting the DCMTK compiled, in case you have any difficulties. In short, apart from a couple of tweaks, everything was reasonably painless. This is of course the command line implementation. Personally, if you need some kind of GUI environment, I would advocate the separate process approach, ie, run the dcmtk command line tools as child processes of your GUI app, supplying either parameters or query files, and redirecting the results for extraction.
Extracting the tags from specific dicom formatted files is also easy. There are some other tools and samples available, besides the dcmtk itself, eg look at ezdicom from Chris Riorden, implemented in Delphi, but should be easy to port to C++.
Hope this helps, and I would be happy to assist any way I can. Also I did promise to post all of my Borland patches, if there is any interest.
Regards,
Roy Dobbins
-
- Posts: 25
- Joined: Wed, 2005-07-20, 22:42
- Location: Payson, Arizona
Who is online
Users browsing this forum: Google [Bot] and 1 guest