From d16eb37cbc73f67fc29a60645e0b1ac7fe32767e Mon Sep 17 00:00:00 2001 From: "Kevin M. Rosenberg" Date: Sun, 18 Mar 2001 18:08:26 +0000 Subject: [PATCH] r640: no message --- ChangeLog | 30 ++++--- NEWS | 7 +- doc/ctsim-algorithms.tex | 63 ++++++++------- doc/ctsim.prj | 10 +-- include/fourier.h | 4 +- include/imagefile.h | 8 +- libctsim/backprojectors.cpp | 5 +- libctsim/fourier.cpp | 14 ++-- libctsim/imagefile.cpp | 149 ++++++++++++++++++++++++++++++++--- libctsim/procsignal.cpp | 4 +- libctsim/projections.cpp | 6 +- libctsim/scanner.cpp | 10 +-- msvc/ctsim/ctsim.plg | 16 ++-- src/backgroundmgr.cpp | 6 +- src/ctsim.h | 4 +- src/graph3dview.cpp | 153 +++++++++++++++++++++--------------- src/graph3dview.h | 7 +- src/views.cpp | 136 +++++++++++++++++++++++++++++--- src/views.h | 4 +- 19 files changed, 463 insertions(+), 173 deletions(-) diff --git a/ChangeLog b/ChangeLog index a9677d8..fe1e673 100644 --- a/ChangeLog +++ b/ChangeLog @@ -14,28 +14,35 @@ background processing as well as taking advantage of multiple-CPU's on SMP systems. - * ctsim: Added PPM/PGM and PNG file import to imagefile. - - * ctsim: Added import of DICOM grayscale files. - - * ctsim: Added imagefile export to DICOM files. - - * ctsim: Added clipboard cut/copy/paste for image files. - + * ctsim: Added plot t-theta sampling to projection file menu. + * ctsim: Added Reconstruction with Rebinning for faster divergent beam reconstructions. - * ctsim: Added plot t-theta sampling to projection file menu. + * ctsim: Added import and export of DICOM image files. + + * ctsim: Added PPM, PGM, and PNG file import to imagefile. + + * ctsim: Added FFT/IFFT of columns of image files. + * ctsim: Added clipboard cut/copy/paste for image files. + * ctsim: Added region of interest to reconstructions. + * ctsim: Added Convert Rectangular Image to Projection files + * ctsim: Added "Verbose Logging", "Startup Tips", and "Background processes" options to Preferences dialog. + * ctsim: Added plotting of projection file histograms. + * plotfile: Added scattergram plot functions * ctsim: Added accelerator key for File-Properties + * ctsim: Improved wireframe 3-d display to remove hidden + surfaces. + * sgp.cpp/ezplot.cpp: Improved plotting with markers. * views.cpp: Added out of memory checks to display for huge @@ -47,6 +54,11 @@ * sgp.cpp: Fixed bug in drawCircle * filter.cpp: Fixed Hanning parameter to be 0.5 rather than 0.54 + + * imagefile.cpp: Fixed scaling factor in fftRows + + * backprojectors.cpp: Added setting of axis extent and increment + to reconstructions. 3.0.3 - Released 2/20/01 diff --git a/NEWS b/NEWS index a4a4c47..aa4b9f7 100644 --- a/NEWS +++ b/NEWS @@ -19,10 +19,11 @@ Version 3.5 New Features * Scattergram of T-Theta sampling. Useful for understand divergent beam scanning. -* Added startup-tips to help new users learn features. +* Startup-tips to help new users learn features. -* Added ability to place detector at any arbitray position +* Ability to place detector at any arbitray position +* Histogram plotting of projection files Version 3.0 New Features @@ -46,7 +47,7 @@ Version 3.0 New Features * Plotting of row and column data of single and comparison images -* Histogram Plotting +* Histogram Plotting of Image files. * Conversion of projections to polar images diff --git a/doc/ctsim-algorithms.tex b/doc/ctsim-algorithms.tex index 504aba1..b9ac22f 100644 --- a/doc/ctsim-algorithms.tex +++ b/doc/ctsim-algorithms.tex @@ -104,16 +104,9 @@ running on a SMP computer, and \ctsim\ is directed to perform reconstruction, rasterization, or projections, \ctsim\ will spawn a \emph{Background Supervisor} thread. This supervisor thread then creates a \emph{Supervisor Event Handler} (supervisor). The -supervisor communicates with the rest of \ctsim\ by using message -passing to avoid issues with re-entrant code. - -Though the various threads do not directly call each other, it is -prudent to lock the class data structures with \emph{Critical -Sections}. Critical sections lock areas of code and prevent more -than one thread to access a section of code at a time. This is -used when maintaining the tables of worker threads in the -supervisor and also when maintaining the tables of supervisors in -the background manager. +supervisor communicates with the rest of graphical user interface +of \ctsim\ by using message passing to avoid issues with +re-entrant code. The supervisor registers itself via message passing with the \emph{Background Manager} which will display the execution @@ -125,17 +118,22 @@ supervisor directing the supervisor to cancel the calculation. After registering with \ctsim\ components, the supervisor creates \emph{Worker Threads}. These worker threads are the processes that actually perform the calculations. By default, \ctsim\ will create -one worker thread for every CPU in the system. The workers -communicate with the supervisor via message passing. As the -workers complete unit blocks, they send progress messages to the -supervisor. The supervisor then sends progress messages to -background manager which displays a gauge of the progress. - -After the workers have completed their tasks, they send a status -message to the supervisor. When all the workers have finished, the -supervisor will kill the worker threads. The supervisor then -collates the work units from the workers and creates a new \ctsim\ -window to display the finished work. +one worker thread for every CPU in the system. As the workers +complete unit blocks, they notify the supervisor. The supervisor +then sends progress messages to background manager which displays +a gauge of the progress. + +As the worker threads directly call the supervisor, it is crucial +to lock the class data structures with \emph{Critical Sections}. +Critical sections lock areas of code and prevent more than one +thread to access a section of code at a time. This is used when +maintaining the tables of worker threads in the supervisor. + +After the workers have completed their tasks, they notify the +supervisor. When all the workers have finished, the supervisor +kills the worker threads. The supervisor then collates the work +units from the workers and sends a message to \ctsim\ to create a +new window to display the finished work. The supervisor then deregisters itself via messages with the background manager and the document. The background manager @@ -155,8 +153,9 @@ This structure may seem more complex than is necessary, but it has several advantages: \begin{itemize} -\item Since the various threads do not call objects in other threads, problems -with re-entrant code are eliminated. +\item Since the background threads do not directly call objects in the graphical +user interface thread, problems with re-entrant code in the +graphical interface are eliminated. \item A supervisor can parallel process with any number of worker threads to take advantage of potentially large numbers of CPU's in SMP computers. @@ -175,14 +174,14 @@ include: results. When the worker threads finish, the supervisor allocates memory for the final result and collates the results for the workers. This collation results in a doubling of the memory -requirements. Of course, after collation the supervisor -deallocates the memory used by the workers. +requirements. Of course, after collation the supervisor releases +the memory used by the workers. \item Slower execution on single CPU systems. \\ -The message passing between threads and collation -of results for worker threads adds overhead compared to simply -calculating the result directly in the foreground. On single CPU -systems this results in slower processing compared to foreground -processing. On dual-CPU and greater SMP systems, though, the -advantage of using multiple CPU's in parallel exceeds the overhead -of background processing. +Creating multiple threads, sending progress messages to the +background manager, and collation of results for worker threads +adds overhead compared to simply calculating the result directly +in the foreground. On single CPU systems this results in slower +processing compared to foreground processing. On dual-CPU and +greater SMP systems, though, the advantage of using multiple CPU's +in parallel exceeds the overhead of background processing. \end{itemize} diff --git a/doc/ctsim.prj b/doc/ctsim.prj index e7dbe76..33a26c6 100644 --- a/doc/ctsim.prj +++ b/doc/ctsim.prj @@ -5,19 +5,19 @@ 1 ctsim.tex 21 -4 -3 +0 +0 ctsim-algorithms.tex TeX -4091 0 65 52 0 43 44 44 960 631 +268439547 0 179 17 187 1 44 44 960 631 ctsim-concepts.tex TeX -12282 0 302 40 280 44 110 110 1187 697 +268447738 0 302 40 280 44 110 110 1187 697 ctsim.tex TeX -134230010 0 50 12 33 1 22 22 1099 609 +402665466 0 50 12 33 1 22 22 1099 609 ctsim-install.tex TeX 268447738 0 70 62 71 1 110 110 1187 697 diff --git a/include/fourier.h b/include/fourier.h index c449b80..5fce2f6 100644 --- a/include/fourier.h +++ b/include/fourier.h @@ -9,7 +9,7 @@ ** This is part of the CTSim program ** Copyright (c) 1983-2001 Kevin Rosenberg ** -** $Id: fourier.h,v 1.5 2001/01/28 19:20:15 kevin Exp $ +** $Id: fourier.h,v 1.6 2001/03/18 18:08:25 kevin Exp $ ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License (version 2) as @@ -40,6 +40,6 @@ public: static void shuffleFourierToNaturalOrder (float* pdVector, const int n); static void shuffleFourierToNaturalOrder (double* pdVector, const int n); static void shuffleFourierToNaturalOrder (std::complex* pdVector, const int n); +}; -}; // namespace Fourier diff --git a/include/imagefile.h b/include/imagefile.h index 5b440a0..140955c 100644 --- a/include/imagefile.h +++ b/include/imagefile.h @@ -9,7 +9,7 @@ ** This is part of the CTSim program ** Copyright (c) 1983-2001 Kevin Rosenberg ** -** $Id: imagefile.h,v 1.33 2001/03/02 02:08:14 kevin Exp $ +** $Id: imagefile.h,v 1.34 2001/03/18 18:08:25 kevin Exp $ ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License (version 2) as @@ -136,6 +136,7 @@ public: static const int EXPORT_FORMAT_INVALID; static const int IMPORT_FORMAT_INVALID; + static const int EXPORT_FORMAT_TEXT; static const int EXPORT_FORMAT_PGM; static const int EXPORT_FORMAT_PGMASCII; static const int IMPORT_FORMAT_PPM; @@ -221,16 +222,17 @@ public: bool importImage (const char* const pszFormat, const char* const pszFilename); -#if HAVE_PNG +#ifdef HAVE_PNG bool writeImagePNG (const char* const outfile, int bitdepth, int nxcell, int nycell, double densmin, double densmax); bool readImagePNG (const char* const pszFile); #endif -#if HAVE_GD +#ifdef HAVE_GD bool writeImageGIF (const char* const outfile, int nxcell, int nycell, double densmin, double densmax); #endif bool writeImagePGM (const char* const outfile, int nxcell, int nycell, double densmin, double densmax); bool writeImagePGMASCII (const char* const outfile, int nxcell, int nycell, double densmin, double densmax); bool readImagePPM (const char* const pszFile); + bool writeImageText (const char* const outfile); static double redGrayscaleFactor() {return s_dRedGrayscaleFactor;} static double greenGrayscaleFactor() {return s_dGreenGrayscaleFactor;} diff --git a/libctsim/backprojectors.cpp b/libctsim/backprojectors.cpp index 06ad159..825b1e3 100644 --- a/libctsim/backprojectors.cpp +++ b/libctsim/backprojectors.cpp @@ -8,7 +8,7 @@ ** This is part of the CTSim program ** Copyright (c) 1983-2001 Kevin Rosenberg ** -** $Id: backprojectors.cpp,v 1.31 2001/03/11 15:27:30 kevin Exp $ +** $Id: backprojectors.cpp,v 1.32 2001/03/18 18:08:25 kevin Exp $ ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License (version 2) as @@ -313,6 +313,9 @@ Backproject::Backproject (const Projections& proj, ImageFile& im, int interpType xInc = (xMax - xMin) / nx; // size of cells yInc = (yMax - yMin) / ny; + im.setAxisIncrement (xInc, yInc); + im.setAxisExtent (xMin, xMax, yMin, yMax); + m_dFocalLength = proj.focalLength(); m_dSourceDetectorLength = proj.sourceDetectorLength(); } diff --git a/libctsim/fourier.cpp b/libctsim/fourier.cpp index 06145a3..f7357cd 100644 --- a/libctsim/fourier.cpp +++ b/libctsim/fourier.cpp @@ -9,7 +9,7 @@ ** This is part of the CTSim program ** Copyright (c) 1983-2001 Kevin Rosenberg ** -** $Id: fourier.cpp,v 1.4 2001/01/28 19:10:18 kevin Exp $ +** $Id: fourier.cpp,v 1.5 2001/03/18 18:08:25 kevin Exp $ ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License (version 2) as @@ -112,7 +112,7 @@ Fourier::shuffleNaturalToFourierOrder (double* pdVector, const int n) { double* pdTemp = new double [n]; int i; - if (n % 2) { // Odd + if (isOdd(n)) { // Odd int iHalfN = (n - 1) / 2; pdTemp[0] = pdVector[iHalfN]; @@ -139,7 +139,7 @@ Fourier::shuffleNaturalToFourierOrder (std::complex* pdVector, const int { std::complex* pdTemp = new std::complex [n]; int i; - if (n % 2) { // Odd + if (isOdd(n)) { // Odd int iHalfN = (n - 1) / 2; pdTemp[0] = pdVector[iHalfN]; @@ -167,7 +167,7 @@ Fourier::shuffleNaturalToFourierOrder (float* pdVector, const int n) { float* pdTemp = new float [n]; int i; - if (n % 2) { // Odd + if (isOdd (n)) { // Odd int iHalfN = (n - 1) / 2; pdTemp[0] = pdVector[iHalfN]; @@ -196,7 +196,7 @@ Fourier::shuffleFourierToNaturalOrder (double* pdVector, const int n) { double* pdTemp = new double [n]; int i; - if (n % 2) { // Odd + if (isOdd(n)) { // Odd int iHalfN = (n - 1) / 2; pdTemp[iHalfN] = pdVector[0]; @@ -224,7 +224,7 @@ Fourier::shuffleFourierToNaturalOrder (std::complex* pdVector, const int { std::complex* pdTemp = new std::complex [n]; int i; - if (n % 2) { // Odd + if (isOdd(n)) { // Odd int iHalfN = (n - 1) / 2; pdTemp[iHalfN] = pdVector[0]; @@ -254,7 +254,7 @@ Fourier::shuffleFourierToNaturalOrder (float* pVector, const int n) { float* pTemp = new float [n]; int i; - if (n % 2) { // Odd + if (isOdd (n)) { // Odd int iHalfN = (n - 1) / 2; pTemp[iHalfN] = pVector[0]; diff --git a/libctsim/imagefile.cpp b/libctsim/imagefile.cpp index 7c9e91e..1c8a063 100644 --- a/libctsim/imagefile.cpp +++ b/libctsim/imagefile.cpp @@ -9,7 +9,7 @@ ** This is part of the CTSim program ** Copyright (c) 1983-2001 Kevin Rosenberg ** -** $Id: imagefile.cpp,v 1.40 2001/03/07 16:34:47 kevin Exp $ +** $Id: imagefile.cpp,v 1.41 2001/03/18 18:08:25 kevin Exp $ ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License (version 2) as @@ -36,18 +36,20 @@ const double ImageFile::s_dBlueGrayscaleFactor = 0.114; const int ImageFile::EXPORT_FORMAT_INVALID = -1; -const int ImageFile::EXPORT_FORMAT_PGM = 0; -const int ImageFile::EXPORT_FORMAT_PGMASCII = 1; +const int ImageFile::EXPORT_FORMAT_TEXT = 0; +const int ImageFile::EXPORT_FORMAT_PGM = 1; +const int ImageFile::EXPORT_FORMAT_PGMASCII = 2; #ifdef HAVE_PNG -const int ImageFile::EXPORT_FORMAT_PNG = 2; -const int ImageFile::EXPORT_FORMAT_PNG16 = 3; +const int ImageFile::EXPORT_FORMAT_PNG = 3; +const int ImageFile::EXPORT_FORMAT_PNG16 = 4; #endif #ifdef HAVE_CTN_DICOM -const int ImageFile::EXPORT_FORMAT_DICOM = 4; +const int ImageFile::EXPORT_FORMAT_DICOM = 5; #endif const char* ImageFile::s_aszExportFormatName[] = { + {"text"}, {"pgm"}, {"pgmascii"}, #ifdef HAVE_PNG @@ -61,6 +63,7 @@ const char* ImageFile::s_aszExportFormatName[] = const char* ImageFile::s_aszExportFormatTitle[] = { + {"Text"}, {"PGM"}, {"PGM ASCII"}, {"PNG"}, @@ -926,8 +929,8 @@ ImageFile::fftRows (ImageFile& result) const Fourier::shuffleFourierToNaturalOrder (pcRow, m_nx); for (ix = 0; ix < m_nx; ix++) { - vReal[ix][iy] = pcRow[ix].real(); - vImag[ix][iy] = pcRow[ix].imag(); + vReal[ix][iy] = pcRow[ix].real() / m_nx; + vImag[ix][iy] = pcRow[ix].imag() / m_nx; } } delete [] pcRow; @@ -994,13 +997,105 @@ ImageFile::ifftRows (ImageFile& result) const bool ImageFile::fftCols (ImageFile& result) const { - return false; + if (m_nx != result.nx() || m_ny != result.ny()) { + sys_error (ERR_WARNING, "Difference sizes of images [ImageFile::fftRows]"); + return false; + } + + if (result.dataType() == Array2dFile::DATA_TYPE_REAL) { + if (! result.convertRealToComplex ()) + return false; + } + + fftw_complex* in = new fftw_complex [m_ny]; + + ImageFileArrayConst vReal = getArray(); + ImageFileArrayConst vImag = getImaginaryArray(); + + fftw_plan plan = fftw_create_plan (m_ny, FFTW_FORWARD, FFTW_IN_PLACE); + std::complex* pcCol = new std::complex [m_ny]; + + unsigned int ix, iy; + unsigned int iArray = 0; + for (ix = 0; ix < m_nx; ix++) { + for (iy = 0; iy < m_ny; iy++) { + in[iy].re = vReal[ix][iy]; + if (isComplex()) + in[iy].im = vImag[ix][iy]; + else + in[iy].im = 0; + } + + fftw_one (plan, in, NULL); + + for (iy = 0; iy < m_ny; iy++) + pcCol[iy] = std::complex(in[iy].re, in[iy].im); + + Fourier::shuffleFourierToNaturalOrder (pcCol, m_ny); + for (iy = 0; iy < m_ny; iy++) { + vReal[ix][iy] = pcCol[iy].real() / m_ny; + vImag[ix][iy] = pcCol[iy].imag() / m_ny; + } + } + delete [] pcCol; + + fftw_destroy_plan (plan); + delete in; + + return true; } bool ImageFile::ifftCols (ImageFile& result) const { - return false; + if (m_nx != result.nx() || m_ny != result.ny()) { + sys_error (ERR_WARNING, "Difference sizes of images [ImageFile::fftRows]"); + return false; + } + + if (result.dataType() == Array2dFile::DATA_TYPE_REAL) { + if (! result.convertRealToComplex ()) + return false; + } + + fftw_complex* in = new fftw_complex [m_ny]; + + ImageFileArrayConst vReal = getArray(); + ImageFileArrayConst vImag = getImaginaryArray(); + + fftw_plan plan = fftw_create_plan (m_ny, FFTW_BACKWARD, FFTW_IN_PLACE); + std::complex* pcCol = new std::complex [m_ny]; + + unsigned int ix, iy; + unsigned int iArray = 0; + for (ix = 0; ix < m_nx; ix++) { + for (iy = 0; iy < m_ny; iy++) { + double dImag = 0; + if (isComplex()) + dImag = vImag[ix][iy]; + pcCol[iy] = std::complex (vReal[ix][iy], dImag); + } + + Fourier::shuffleNaturalToFourierOrder (pcCol, m_ny); + + for (iy = 0; iy < m_ny; iy++) { + in[iy].re = pcCol[iy].real(); + in[iy].im = pcCol[iy].imag(); + } + + fftw_one (plan, in, NULL); + + for (iy = 0; iy < m_ny; iy++) { + vReal[ix][iy] = in[iy].re; + vImag[ix][iy] = in[iy].im; + } + } + delete [] pcCol; + + fftw_destroy_plan (plan); + delete in; + + return true; } #endif // HAVE_FFTW @@ -1564,10 +1659,14 @@ ImageFile::exportImage (const char* const pszFormat, const char* const pszFilena return writeImagePGM (pszFilename, nxcell, nycell, densmin, densmax); else if (iFormatID == EXPORT_FORMAT_PGMASCII) return writeImagePGMASCII (pszFilename, nxcell, nycell, densmin, densmax); + else if (iFormatID == EXPORT_FORMAT_TEXT) + return writeImageText (pszFilename); +#ifdef HAVE_PNG else if (iFormatID == EXPORT_FORMAT_PNG) return writeImagePNG (pszFilename, 8, nxcell, nycell, densmin, densmax); else if (iFormatID == EXPORT_FORMAT_PNG16) return writeImagePNG (pszFilename, 16, nxcell, nycell, densmin, densmax); +#endif #ifdef HAVE_CTN_DICOM else if (iFormatID == EXPORT_FORMAT_DICOM) { DicomExporter dicomExport (this); @@ -1660,6 +1759,36 @@ ImageFile::writeImagePGMASCII (const char* const outfile, int nxcell, int nycell return true; } +bool +ImageFile::writeImageText (const char* const outfile) +{ + FILE *fp; + int nx = m_nx; + int ny = m_ny; + ImageFileArray v = getArray(); + ImageFileArray vImag = getImaginaryArray(); + + if ((fp = fopen (outfile, "w")) == NULL) + return false; + + for (int irow = ny - 1; irow >= 0; irow--) { + for (int icol = 0; icol < nx; icol++) { + if (isComplex()) { + if (vImag[icol][irow] >= 0) + fprintf (fp, "%.9g+%.9gi ", v[icol][irow], vImag[icol][irow]); + else + fprintf (fp, "%.9g-%.9gi ", v[icol][irow], -vImag[icol][irow]); + } else + fprintf (fp, "%12.8g ", v[icol][irow]); + } + fprintf(fp, "\n"); + } + + fclose(fp); + + return true; +} + #ifdef HAVE_PNG bool diff --git a/libctsim/procsignal.cpp b/libctsim/procsignal.cpp index 711307f..2d6600d 100644 --- a/libctsim/procsignal.cpp +++ b/libctsim/procsignal.cpp @@ -9,7 +9,7 @@ ** This is part of the CTSim program ** Copyright (c) 1983-2001 Kevin Rosenberg ** -** $Id: procsignal.cpp,v 1.28 2001/03/13 14:53:44 kevin Exp $ +** $Id: procsignal.cpp,v 1.29 2001/03/18 18:08:25 kevin Exp $ ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License (version 2) as @@ -261,7 +261,7 @@ ProcessSignal::init (const int idFilter, const int idFilterMethod, double dBandw m_nFilterPoints = addZeropadFactor (m_nSignalPoints, m_iZeropad); m_nOutputPoints = m_nFilterPoints * m_iPreinterpolationFactor; - if (m_nFilterPoints % 2) { // Odd + if (isOdd (m_nFilterPoints)) { // Odd m_dFilterMin = -1. / (2 * m_dSignalInc); m_dFilterMax = 1. / (2 * m_dSignalInc); m_dFilterInc = (m_dFilterMax - m_dFilterMin) / (m_nFilterPoints - 1); diff --git a/libctsim/projections.cpp b/libctsim/projections.cpp index 95cbc89..1aee958 100644 --- a/libctsim/projections.cpp +++ b/libctsim/projections.cpp @@ -8,7 +8,7 @@ ** This is part of the CTSim program ** Copyright (c) 1983-2001 Kevin Rosenberg ** -** $Id: projections.cpp,v 1.66 2001/03/13 14:53:44 kevin Exp $ +** $Id: projections.cpp,v 1.67 2001/03/18 18:08:25 kevin Exp $ ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License (version 2) as @@ -810,7 +810,7 @@ Projections::calcArrayPolarCoordinates (unsigned int nx, unsigned int ny, double // +1 is correct for frequency data, ndet-1 is correct for projections int iDetCenter = (iNumDetWithZeros - 1) / 2; // index refering to L=0 projection - if (iNumDetWithZeros % 2 == 0) + if (isEven (iNumDetWithZeros)) iDetCenter = (iNumDetWithZeros + 1) / 2; // Calculates polar coordinates (view#, det#) for each point on phantom grid @@ -1002,7 +1002,7 @@ Projections::interpolateToParallel () const #endif pProjNew->m_detStart = -m_dViewDiameter / 2; pProjNew->m_detInc = m_dViewDiameter / nDet; - if (nDet % 2 == 0) // even + if (isEven (nDet)) // even pProjNew->m_detInc = m_dViewDiameter / (nDet - 1); ParallelRaysums parallel (this, ParallelRaysums::THETA_RANGE_NORMALIZE_TO_TWOPI); diff --git a/libctsim/scanner.cpp b/libctsim/scanner.cpp index 34899f5..88f99f0 100644 --- a/libctsim/scanner.cpp +++ b/libctsim/scanner.cpp @@ -9,7 +9,7 @@ ** This is part of the CTSim program ** Copyright (c) 1983-2001 Kevin Rosenberg ** -** $Id: scanner.cpp,v 1.36 2001/03/11 15:27:30 kevin Exp $ +** $Id: scanner.cpp,v 1.37 2001/03/18 18:08:25 kevin Exp $ ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License (version 2) as @@ -130,7 +130,7 @@ Scanner::Scanner (const Phantom& phm, const char* const geometryName, double dDetectorArrayEndOffset = 0; // For even number of detectors, make detInc slightly larger so that center lies // at nDet/2. Also, extend detector array by one detInc so that all of the phantom is scanned - if (m_nDet % 2 == 0) { // Adjust for Even number of detectors + if (isEven (m_nDet)) { // Adjust for Even number of detectors m_detInc = m_detLen / (m_nDet - 1); // center detector = (nDet/2) dDetectorArrayEndOffset = m_detInc; } @@ -160,7 +160,7 @@ Scanner::Scanner (const Phantom& phm, const char* const geometryName, m_detStart = -dHalfDetLen; m_detInc = m_detLen / m_nDet; double dDetectorArrayEndOffset = 0; - if (m_nDet % 2 == 0) { // Adjust for Even number of detectors + if (isEven (m_nDet)) { // Adjust for Even number of detectors m_detInc = m_detLen / (m_nDet - 1); // center detector = (nDet/2) dDetectorArrayEndOffset = m_detInc; m_detLen += dDetectorArrayEndOffset; @@ -189,7 +189,7 @@ Scanner::Scanner (const Phantom& phm, const char* const geometryName, m_detStart = -dAngle; m_detInc = m_detLen / m_nDet; double dDetectorArrayEndOffset = 0; - if (m_nDet % 2 == 0) { // Adjust for Even number of detectors + if (isEven (m_nDet)) { // Adjust for Even number of detectors m_detInc = m_detLen / (m_nDet - 1); // center detector = (nDet/2) dDetectorArrayEndOffset = m_detInc; } @@ -469,7 +469,7 @@ Scanner::projectSingleView (const Phantom& phm, DetectorArray& detArray, const d if (phm.getComposition() == P_UNIT_PULSE) { // put unit pulse in center of view for (int d = 0; d < detArray.nDet(); d++) - if (detArray.nDet() / 2 == d && (d % 2) == 1) + if (detArray.nDet() / 2 == d && isOdd (d)) detval[d] = 1; else detval[d] = 0; diff --git a/msvc/ctsim/ctsim.plg b/msvc/ctsim/ctsim.plg index efee3ce..4c73f26 100644 --- a/msvc/ctsim/ctsim.plg +++ b/msvc/ctsim/ctsim.plg @@ -6,13 +6,13 @@ --------------------Configuration: libctsim - Win32 Debug--------------------

Command Lines

-Creating temporary file "C:\DOCUME~1\kevin\LOCALS~1\Temp\RSP282.tmp" with contents +Creating temporary file "C:\DOCUME~1\kevin\LOCALS~1\Temp\RSP5E5.tmp" with contents [ /nologo /G6 /MTd /W3 /Gm /Gi /GR /GX /Zi /Od /Gy /I "..\..\..\wx2.2.5\src\png" /I "..\..\..\wx2.2.5\src\zlib" /I "..\..\INCLUDE" /I "..\..\getopt" /I "..\..\..\fftw-2.1.3\fftw" /I "..\..\..\fftw-2.1.3\rfftw" /I "..\..\..\wx2.2.5\include" /I "\dicom\ctn\include" /D "_DEBUG" /D "HAVE_WXWIN" /D "HAVE_STRING_H" /D "HAVE_GETOPT_H" /D "WIN32" /D "_MBCS" /D "_LIB" /D "MSVC" /D "HAVE_FFTW" /D "HAVE_PNG" /D "HAVE_SGP" /D "HAVE_WXWINDOWS" /D "__WXMSW__" /D "__WIN95__" /D "__WIN32__" /D WINVER=0x0400 /D "HAVE_CTN_DICOM" /D VERSION=\"3.1.0\" /FR"Debug/" /Fp"Debug/libctsim.pch" /YX /Fo"Debug/" /Fd"Debug/" /FD /GZ /c -"C:\ctsim\libctsim\projections.cpp" +"C:\ctsim\libctsim\imagefile.cpp" ] -Creating command line "cl.exe @C:\DOCUME~1\kevin\LOCALS~1\Temp\RSP282.tmp" -Creating temporary file "C:\DOCUME~1\kevin\LOCALS~1\Temp\RSP283.tmp" with contents +Creating command line "cl.exe @C:\DOCUME~1\kevin\LOCALS~1\Temp\RSP5E5.tmp" +Creating temporary file "C:\DOCUME~1\kevin\LOCALS~1\Temp\RSP5E6.tmp" with contents [ /nologo /out:"Debug\libctsim.lib" .\Debug\array2dfile.obj @@ -48,16 +48,16 @@ Creating temporary file "C:\DOCUME~1\kevin\LOCALS~1\Temp\RSP283.tmp" with conten .\Debug\transformmatrix.obj .\Debug\xform.obj ] -Creating command line "link.exe -lib @C:\DOCUME~1\kevin\LOCALS~1\Temp\RSP283.tmp" +Creating command line "link.exe -lib @C:\DOCUME~1\kevin\LOCALS~1\Temp\RSP5E6.tmp"

Output Window

Compiling... -projections.cpp +imagefile.cpp Creating library...

--------------------Configuration: ctsim - Win32 Debug--------------------

Command Lines

-Creating temporary file "C:\DOCUME~1\kevin\LOCALS~1\Temp\RSP284.tmp" with contents +Creating temporary file "C:\DOCUME~1\kevin\LOCALS~1\Temp\RSP5E7.tmp" with contents [ winmm.lib rpcrt4.lib ws2_32.lib ../libctsim/Debug/libctsim.lib libcmtd.lib ..\..\..\fftw-2.1.3\Win32\FFTW2st\Debug\FFTW2st.lib ..\..\..\fftw-2.1.3\Win32\RFFTW2st\Debug\RFFTW2st.lib wxd.lib xpmd.lib tiffd.lib zlibd.lib pngd.lib comctl32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib opengl32.lib glu32.lib htmlhelp.lib ctn_lib.lib /nologo /subsystem:windows /incremental:yes /pdb:"Debug/ctsim.pdb" /debug /machine:I386 /out:"Debug/ctsim.exe" /pdbtype:sept /libpath:"\wx2.2.5\lib" /libpath:"\dicom\ctn\winctn\ctn_lib\Debug" .\Debug\backgroundmgr.obj @@ -84,7 +84,7 @@ winmm.lib rpcrt4.lib ws2_32.lib ../libctsim/Debug/libctsim.lib libcmtd.lib ..\.. \wx2.2.5\lib\zlibd.lib \wx2.2.5\lib\tiffd.lib ] -Creating command line "link.exe @C:\DOCUME~1\kevin\LOCALS~1\Temp\RSP284.tmp" +Creating command line "link.exe @C:\DOCUME~1\kevin\LOCALS~1\Temp\RSP5E7.tmp"

Output Window

Linking... diff --git a/src/backgroundmgr.cpp b/src/backgroundmgr.cpp index 274165e..275f20b 100644 --- a/src/backgroundmgr.cpp +++ b/src/backgroundmgr.cpp @@ -9,7 +9,7 @@ ** This is part of the CTSim program ** Copyright (C) 1983-2001 Kevin Rosenberg ** -** $Id: backgroundmgr.cpp,v 1.19 2001/03/13 04:44:25 kevin Exp $ +** $Id: backgroundmgr.cpp,v 1.20 2001/03/18 18:08:26 kevin Exp $ ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License (version 2) as @@ -152,7 +152,6 @@ BackgroundManager::OnAddTask (wxCommandEvent& event) resizeWindow(); if (m_iNumTasks == 1) { m_pCanvas->SetFocus(); - pGauge->SetFocus(); Show(true); } } @@ -188,9 +187,8 @@ BackgroundManager::OnRemoveTask (wxCommandEvent& event) pSupervisor->ackRemoveBackgroundManager(); resizeWindow(); if (m_iNumTasks <= 0) { - theApp->getMainFrame()->SetFocus(); + m_pCanvas->SetFocus(); Show(false); - theApp->getMainFrame()->SetFocus(); } } diff --git a/src/ctsim.h b/src/ctsim.h index b7d20a9..06d4cc3 100644 --- a/src/ctsim.h +++ b/src/ctsim.h @@ -9,7 +9,7 @@ ** This is part of the CTSim program ** Copyright (c) 1983-2001 Kevin Rosenberg ** -** $Id: ctsim.h,v 1.59 2001/03/13 08:24:41 kevin Exp $ +** $Id: ctsim.h,v 1.60 2001/03/18 18:08:26 kevin Exp $ ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License (version 2) as @@ -308,10 +308,12 @@ enum { PJMENU_RECONSTRUCT_FBP, PJMENU_RECONSTRUCT_FBP_REBIN, PJMENU_RECONSTRUCT_FOURIER, + PJMENU_CONVERT_RECTANGULAR, PJMENU_CONVERT_POLAR, PJMENU_CONVERT_FFT_POLAR, PJMENU_CONVERT_PARALLEL, PJMENU_PLOT_TTHETA_SAMPLING, + PJMENU_PLOT_HISTOGRAM, PJMENU_ARTIFACT_REDUCTION, IFMENU_FILE_EXPORT, diff --git a/src/graph3dview.cpp b/src/graph3dview.cpp index d92912f..526ab64 100644 --- a/src/graph3dview.cpp +++ b/src/graph3dview.cpp @@ -9,7 +9,7 @@ ** This is part of the CTSim program ** Copyright (c) 1983-2001 Kevin Rosenberg ** -** $Id: graph3dview.cpp,v 1.19 2001/03/13 04:44:25 kevin Exp $ +** $Id: graph3dview.cpp,v 1.20 2001/03/18 18:08:26 kevin Exp $ ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License (version 2) as @@ -117,40 +117,20 @@ Graph3dFileView::intensityToColor (double dIntensity, GLfloat* vecColor) // fNormalZ == Z vector for the normal vector //************************************************************************* -void -CalculateVectorNormal (GLfloat fVert1[], GLfloat fVert2[], - GLfloat fVert3[], GLfloat *fNormalX, - GLfloat *fNormalY, GLfloat *fNormalZ) -{ - GLfloat Qx = fVert2[0] - fVert1[0]; - GLfloat Qy = fVert2[1] - fVert1[1]; - GLfloat Qz = fVert2[2] - fVert1[2]; - GLfloat Px = fVert3[0] - fVert1[0]; - GLfloat Py = fVert3[1] - fVert1[1]; - GLfloat Pz = fVert3[2] - fVert1[2]; - - *fNormalX = Py*Qz - Pz*Qy; - *fNormalY = Pz*Qx - Px*Qz; - *fNormalZ = Px*Qy - Py*Qx; - -} - -void -CalculateVectorNormal (GLdouble fVert1[], GLdouble fVert2[], - GLdouble fVert3[], GLdouble *fNormalX, - GLdouble *fNormalY, GLdouble *fNormalZ) +template +static void +CalculateVectorNormal (T fVert1[], T fVert2[], T fVert3[], T *fNormalX, T *fNormalY, T *fNormalZ) { - GLdouble Qx = fVert2[0] - fVert1[0]; - GLdouble Qy = fVert2[1] - fVert1[1]; - GLdouble Qz = fVert2[2] - fVert1[2]; - GLdouble Px = fVert3[0] - fVert1[0]; - GLdouble Py = fVert3[1] - fVert1[1]; - GLdouble Pz = fVert3[2] - fVert1[2]; + T Qx = fVert2[0] - fVert1[0]; + T Qy = fVert2[1] - fVert1[1]; + T Qz = fVert2[2] - fVert1[2]; + T Px = fVert3[0] - fVert1[0]; + T Py = fVert3[1] - fVert1[1]; + T Pz = fVert3[2] - fVert1[2]; *fNormalX = Py*Qz - Pz*Qy; *fNormalY = Pz*Qx - Px*Qz; *fNormalZ = Px*Qy - Py*Qx; - } IMPLEMENT_DYNAMIC_CLASS(Graph3dFileView, wxView) @@ -171,15 +151,16 @@ Graph3dFileView::Graph3dFileView () { m_bDoubleBuffer = true; m_bSmooth = true; - m_bLighting = true; - m_bSurface = true; - m_bLighting = true; - m_bColor = false; - m_dXRotate = -45; - m_dYRotate = 0; - m_dZRotate = -45; + m_bLighting = false; + m_bSurface = false; + m_bLighting = false; + m_bColor = true; + m_dXRotate = -180; + m_dYRotate = -210; + m_dZRotate = -195; m_bColorScaleMinSet = false; m_bColorScaleMaxSet = false; + m_bCalculatedSurfaceBackground = false; } Graph3dFileView::~Graph3dFileView() @@ -236,6 +217,9 @@ Graph3dFileView::CreateCanvas (wxFrame* parent) void Graph3dFileView::DrawSurface() { + if (! GetDocument()) + return; + if (m_bSmooth) { glShadeModel (GL_SMOOTH); } else { @@ -248,23 +232,37 @@ Graph3dFileView::DrawSurface() glDisable (GL_LIGHTING); } - if (m_bSurface) - glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); - else - glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); - - InitMaterials(); - - if (! GetDocument()) - return; - unsigned int nx = GetDocument()->nx(); unsigned int ny = GetDocument()->ny(); + glRotated (m_dZRotate, 0.0, 1.0, 0.0); glRotated (m_dXRotate, 0.0, 0.0, 1.0); glRotated (m_dYRotate, 1.0, 0.0, 0.0); glTranslated (-static_cast(nx - 1) / 2, 0.0, -static_cast(ny - 1) / 2); - glCallList (DISPLAYLIST_SURFACE); + + InitMaterials(); + if (m_bSurface) { + glPolygonMode (GL_FRONT_AND_BACK, GL_FILL); + if (! m_bColor) { + glColor3f (1.0f, 1.0f, 1.0f); + glCallList (DISPLAYLIST_NO_COLOR); + } else + glCallList (DISPLAYLIST_COLOR); + } + else { + if (! m_bColor) + glColor3f (1.0f, 1.0f, 1.0f); + glPolygonOffset (1.0f, 1.0f); + glEnable (GL_POLYGON_OFFSET_FILL); + glPolygonMode (GL_FRONT_AND_BACK, GL_FILL); + glCallList (DISPLAYLIST_COLOR); + + glColor3f (0.0f, 0.0f, 0.0f); + glPolygonOffset (0.0f, 0.0f); + glPolygonMode (GL_FRONT_AND_BACK, GL_LINE); + glCallList (DISPLAYLIST_NO_COLOR); + } + } void @@ -279,7 +277,7 @@ Graph3dFileView::CreateDisplayList() if (nx == 0 || ny == 0 || ! v) return; - glNewList (DISPLAYLIST_SURFACE, GL_COMPILE); + glNewList (DISPLAYLIST_COLOR, GL_COMPILE); double dMin = m_dColorScaleMin; double dIntensityScale = m_dColorScaleMax - m_dColorScaleMin; @@ -290,9 +288,6 @@ Graph3dFileView::CreateDisplayList() dXOffset = 0; dYOffset = 0; - if (! m_bColor) - glColor3f (1.0f, 1.0f, 1.0f); - double dXPos = -dXOffset; for (unsigned ix = 0; ix < nx - 1; ix++, dXPos++) { double dYPos = -dYOffset; @@ -301,7 +296,7 @@ Graph3dFileView::CreateDisplayList() p1[0] = dXPos; p1[1] = actScale * (v[ix][0] + actOffset); p1[2] = dYPos; p2[0] = dXPos+1; p2[1] = actScale * (v[ix+1][0] + actOffset); p2[2] = dYPos; p3[0] = dXPos; p3[1] = actScale * (v[ix][1] + actOffset); p3[2] = dYPos + 1; - CalculateVectorNormal (p1, p2, p3, &n1[0], &n1[1], &n1[2]); + CalculateVectorNormal (p1, p2, p3, &n1[0], &n1[1], &n1[2]); double dIntensity1, dIntensity2; if (m_bColor) { @@ -315,10 +310,8 @@ Graph3dFileView::CreateDisplayList() } glVertex3dv (p1); glNormal3dv (n1); - if (m_bColor) { intensityToColor (dIntensity2, vecColor); glColor3fv (vecColor); - } glVertex3dv (p2); glNormal3dv (n1); double lastP[3]; @@ -328,19 +321,43 @@ Graph3dFileView::CreateDisplayList() p2[0] = dXPos+1; p2[1] = actScale * (v[ix+1][iy] + actOffset); p2[2] = dYPos; CalculateVectorNormal (p1, p2, lastP, &n1[0], &n1[1], &n1[2]); lastP[0] = p1[0]; lastP[1] = p1[1]; lastP[2] = p1[2]; - if (m_bColor) { dIntensity1 = (v[ix][iy] - dMin) / dIntensityScale; dIntensity2 = (v[ix+1][iy] - dMin) / dIntensityScale; - } - if (m_bColor) { intensityToColor (dIntensity1, vecColor); glColor3fv (vecColor); - } glVertex3dv (p1); glNormal3dv (n1); - if (m_bColor) { intensityToColor (dIntensity2, vecColor); glColor3fv (vecColor); - } + glVertex3dv (p2); glNormal3dv (n1); + } + glEnd(); // QUAD_STRIP + } + glEndList(); + + + glNewList (DISPLAYLIST_NO_COLOR, GL_COMPILE); + dXPos = -dXOffset; + for (ix = 0; ix < nx - 1; ix++, dXPos++) { + double dYPos = -dYOffset; + glBegin(GL_QUAD_STRIP); + double p1[3], p2[3], p3[3], n1[3]; + p1[0] = dXPos; p1[1] = actScale * (v[ix][0] + actOffset); p1[2] = dYPos; + p2[0] = dXPos+1; p2[1] = actScale * (v[ix+1][0] + actOffset); p2[2] = dYPos; + p3[0] = dXPos; p3[1] = actScale * (v[ix][1] + actOffset); p3[2] = dYPos + 1; + CalculateVectorNormal (p1, p2, p3, &n1[0], &n1[1], &n1[2]); + + glVertex3dv (p1); + glNormal3dv (n1); + glVertex3dv (p2); + glNormal3dv (n1); + double lastP[3]; + lastP[0] = ix; lastP[1] = actScale * (v[ix][0] + actOffset); lastP[2] = 0; + for (unsigned int iy = 1; iy < ny - 1; iy++, dYPos++) { + p1[0] = dXPos; p1[1] = actScale * (v[ix][iy] + actOffset); p1[2] = dYPos; + p2[0] = dXPos+1; p2[1] = actScale * (v[ix+1][iy] + actOffset); p2[2] = dYPos; + CalculateVectorNormal (p1, p2, lastP, &n1[0], &n1[1], &n1[2]); + lastP[0] = p1[0]; lastP[1] = p1[1]; lastP[2] = p1[2]; + glVertex3dv (p1); glNormal3dv (n1); glVertex3dv (p2); glNormal3dv (n1); } glEnd(); // QUAD_STRIP @@ -349,6 +366,13 @@ Graph3dFileView::CreateDisplayList() } +void +Graph3dFileView::calculateSurfaceBackground () +{ + if (m_bCalculatedSurfaceBackground) + return; +} + void Graph3dFileView::OnProperties (wxCommandEvent& event) { @@ -380,7 +404,7 @@ Graph3dFileView::OnColor (wxCommandEvent& event) { m_bColor = ! m_bColor; m_pViewMenu->Check (GRAPH3D_VIEW_COLOR, m_bColor); - OnUpdate (this, NULL); + m_pCanvas->Refresh(); } void @@ -434,8 +458,8 @@ Graph3dFileView::InitMaterials() #if 1 static float ambient[] = {0.1f, 0.1f, 0.1f, 1.0f}; static float diffuse[] = {1.0f, 1.0f, 1.0f, 1.0f}; - static float position0[] = {-nx/2, -ny/2, -ny/2, 0.0f, 0.0f}; - static float position1[] = {-nx/2, -ny/2, ny/2, 0.0f}; + static float position0[] = {-nx/2, -ny/2, ny/2, 0.0f, 0.0f}; + static float position1[] = {-nx/2, -ny/2, -ny/2, 0.0f}; static float ambient1[] = {0.5f, 0.5f, 0.5f, 1.0f}; static float diffuse1[] = {1.0f, 1.0f, 1.0f, 1.0f}; // static float position0[] = {0.0f, 0.0f, 20.0f, 0.0f}; @@ -600,7 +624,6 @@ Graph3dFileView::OnUpdate (wxView *WXUNUSED(sender), wxObject *WXUNUSED(hint) ) if (! m_bColorScaleMaxSet) m_dColorScaleMax = max; } - double dRadius = maxValue (nx, ny) * SQRT2 / 2; glMatrixMode(GL_PROJECTION); glLoadIdentity(); diff --git a/src/graph3dview.h b/src/graph3dview.h index f5ae3e4..44adb86 100644 --- a/src/graph3dview.h +++ b/src/graph3dview.h @@ -9,7 +9,7 @@ ** This is part of the CTSim program ** Copyright (c) 1983-2001 Kevin Rosenberg ** -** $Id: graph3dview.h,v 1.6 2001/02/04 22:58:41 kevin Exp $ +** $Id: graph3dview.h,v 1.7 2001/03/18 18:08:26 kevin Exp $ ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License (version 2) as @@ -59,7 +59,8 @@ private: bool m_bSurface; bool m_bColor; enum { - DISPLAYLIST_SURFACE = 1, + DISPLAYLIST_COLOR = 1, + DISPLAYLIST_NO_COLOR = 2, }; double m_dGraphMin; @@ -68,6 +69,7 @@ private: double m_dColorScaleMax; bool m_bColorScaleMinSet; bool m_bColorScaleMaxSet; + bool m_bCalculatedSurfaceBackground; void Draw(); void DrawSurface(); @@ -113,6 +115,7 @@ public: void OnScaleSet (wxCommandEvent& event); void OnScaleAuto (wxCommandEvent& event); void OnScaleFull (wxCommandEvent& event); + void calculateSurfaceBackground(); #if CTSIM_MDI wxDocMDIChildFrame* getFrame() { return m_pFrame; } diff --git a/src/views.cpp b/src/views.cpp index 56fe8cf..6c59f8e 100644 --- a/src/views.cpp +++ b/src/views.cpp @@ -9,7 +9,7 @@ ** This is part of the CTSim program ** Copyright (c) 1983-2001 Kevin Rosenberg ** -** $Id: views.cpp,v 1.138 2001/03/13 10:35:06 kevin Exp $ +** $Id: views.cpp,v 1.139 2001/03/18 18:08:26 kevin Exp $ ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License (version 2) as @@ -887,7 +887,7 @@ ImageFileView::CreateChildFrame(wxDocument *doc, wxView *view) view_menu->Append(IFMENU_VIEW_SCALE_FULL, "Display F&ull Scale\tCtrl-U"); wxMenu* filter_menu = new wxMenu; - filter_menu->Append (IFMENU_FILTER_INVERTVALUES, "&Invert Values"); + filter_menu->Append (IFMENU_FILTER_INVERTVALUES, "In&vert Values"); filter_menu->Append (IFMENU_FILTER_SQUARE, "&Square"); filter_menu->Append (IFMENU_FILTER_SQRT, "Square &Root"); filter_menu->Append (IFMENU_FILTER_LOG, "&Log"); @@ -968,7 +968,7 @@ ImageFileView::CreateChildFrame(wxDocument *doc, wxView *view) accelEntries[4].Set (wxACCEL_CTRL, static_cast('C'), IFMENU_EDIT_COPY); accelEntries[5].Set (wxACCEL_CTRL, static_cast('X'), IFMENU_EDIT_CUT); accelEntries[6].Set (wxACCEL_CTRL, static_cast('V'), IFMENU_EDIT_PASTE); -#if wxUSE_GLCANVAS +#ifdef wxUSE_GLCANVAS accelEntries[7].Set (wxACCEL_CTRL, static_cast('3'), IFMENU_IMAGE_CONVERT3D); wxAcceleratorTable accelTable (8, accelEntries); #else @@ -1203,6 +1203,10 @@ ImageFileView::OnExport (wxCommandEvent& event) strWildcard = "DICOM Files (*.*)|*.*"; } #endif + else if (m_iDefaultExportFormatID == ImageFile::EXPORT_FORMAT_TEXT) { + strExt = ".txt"; + strWildcard = "Text (*.txt)|*.txt"; + } else { strExt = ""; strWildcard = "Miscellaneous (*.*)|*.*"; @@ -1873,7 +1877,7 @@ ImageFileView::OnPlotHistogram (wxCommandEvent& event) s += rIF.labelGet(iL).getLabelString(); rPlotFile.addDescription (s.c_str()); } - os << " Plot of " << GetDocument()->GetFirstView()->GetFrame()->GetTitle().c_str(); + os << " plot of " << GetDocument()->GetFirstView()->GetFrame()->GetTitle().c_str(); *theApp->getLog() << os.str().c_str() << "\n"; rPlotFile.addDescription (os.str().c_str()); delete pX; @@ -2380,10 +2384,12 @@ EVT_MENU(PJMENU_FILE_PROPERTIES, ProjectionFileView::OnProperties) EVT_MENU(PJMENU_RECONSTRUCT_FBP, ProjectionFileView::OnReconstructFBP) EVT_MENU(PJMENU_RECONSTRUCT_FBP_REBIN, ProjectionFileView::OnReconstructFBPRebin) EVT_MENU(PJMENU_RECONSTRUCT_FOURIER, ProjectionFileView::OnReconstructFourier) +EVT_MENU(PJMENU_CONVERT_RECTANGULAR, ProjectionFileView::OnConvertRectangular) EVT_MENU(PJMENU_CONVERT_POLAR, ProjectionFileView::OnConvertPolar) EVT_MENU(PJMENU_CONVERT_FFT_POLAR, ProjectionFileView::OnConvertFFTPolar) EVT_MENU(PJMENU_CONVERT_PARALLEL, ProjectionFileView::OnConvertParallel) EVT_MENU(PJMENU_PLOT_TTHETA_SAMPLING, ProjectionFileView::OnPlotTThetaSampling) +EVT_MENU(PJMENU_PLOT_HISTOGRAM, ProjectionFileView::OnPlotHistogram) EVT_MENU(PJMENU_ARTIFACT_REDUCTION, ProjectionFileView::OnArtifactReduction) END_EVENT_TABLE() @@ -2439,6 +2445,40 @@ ProjectionFileView::OnProperties (wxCommandEvent& event) } +void +ProjectionFileView::OnConvertRectangular (wxCommandEvent& event) +{ + Projections& rProj = GetDocument()->getProjections(); + + int nDet = rProj.nDet(); + int nView = rProj.nView(); + ImageFile* pIF = new ImageFile (nDet, nView); + ImageFileArray v = pIF->getArray(); + for (int iv = 0; iv < nView; iv++) { + DetectorValue* detval = rProj.getDetectorArray(iv).detValues(); + + for (int id = 0; id < nDet; id++) + v[id][iv] = detval[id]; + } + + ImageFileDocument* pRectDoc = theApp->newImageDoc (); + if (! pRectDoc) { + sys_error (ERR_SEVERE, "Unable to create image file"); + return; + } + pRectDoc->setImageFile (pIF); + pIF->labelAdd (rProj.getLabel().getLabelString().c_str(), rProj.calcTime()); + std::ostringstream os; + os << "Convert projection file " << GetFrame()->GetTitle().c_str() << " to rectangular image"; + *theApp->getLog() << os.str().c_str() << "\n"; + pIF->labelAdd (os.str().c_str()); + if (theApp->getAskDeleteNewDocs()) + pRectDoc->Modify (true); + pRectDoc->getView()->getFrame()->Show(true); + pRectDoc->UpdateAllViews (); + pRectDoc->Activate(); +} + void ProjectionFileView::OnConvertPolar (wxCommandEvent& event) { @@ -2564,6 +2604,80 @@ ProjectionFileView::OnPlotTThetaSampling (wxCommandEvent& event) pPlotDoc->Activate(); } + +void +ProjectionFileView::OnPlotHistogram (wxCommandEvent& event) +{ + Projections& rProj = GetDocument()->getProjections(); + int nDet = rProj.nDet(); + int nView = rProj.nView(); + + if (nDet < 1 || nView < 1) + return; + + PlotFileDocument* pPlotDoc = theApp->newPlotDoc(); + if (! pPlotDoc) { + sys_error (ERR_SEVERE, "Internal error: unable to create Plot file"); + return; + } + + DetectorValue* pdDetval = rProj.getDetectorArray(0).detValues(); + double dMin = pdDetval[0], dMax = pdDetval[0]; + + for (int iv = 0; iv < nView; iv++) { + pdDetval = rProj.getDetectorArray(iv).detValues(); + for (int id = 0; id < nDet; id++) { + double dV = pdDetval[id]; + if (dV < dMin) + dMin = dV; + else if (dV > dMax) + dMax = dV; + } + } + + double* pX = new double [NUMBER_HISTOGRAM_BINS]; + double* pY = new double [NUMBER_HISTOGRAM_BINS]; + double dBinWidth = (dMax - dMin) / NUMBER_HISTOGRAM_BINS; + + for (int i = 0; i < NUMBER_HISTOGRAM_BINS; i++) { + pX[i] = dMin + (i + 0.5) * dBinWidth; + pY[i] = 0; + } + for (int j = 0; j < nView; j++) { + pdDetval = rProj.getDetectorArray(j).detValues(); + for (int id = 0; id < nDet; id++) { + int iBin = nearest ((pdDetval[id] - dMin) / dBinWidth); + if (iBin >= 0 && iBin < NUMBER_HISTOGRAM_BINS) + pY[iBin] += 1; + } + } + PlotFile& rPlotFile = pPlotDoc->getPlotFile(); + std::ostringstream os; + os << "Histogram"; + std::string title("title "); + title += os.str(); + rPlotFile.addEzsetCommand (title.c_str()); + rPlotFile.addEzsetCommand ("xlabel Detector Value"); + rPlotFile.addEzsetCommand ("ylabel Count"); + rPlotFile.addEzsetCommand ("box"); + rPlotFile.addEzsetCommand ("grid"); + rPlotFile.setCurveSize (2, NUMBER_HISTOGRAM_BINS); + rPlotFile.addColumn (0, pX); + rPlotFile.addColumn (1, pY); + rPlotFile.addDescription (rProj.remark()); + os << " plot of " << GetDocument()->GetFirstView()->GetFrame()->GetTitle().c_str(); + *theApp->getLog() << os.str().c_str() << "\n"; + rPlotFile.addDescription (os.str().c_str()); + delete pX; + delete pY; + if (theApp->getAskDeleteNewDocs()) + pPlotDoc->Modify (true); + pPlotDoc->getView()->getFrame()->Show(true); + pPlotDoc->UpdateAllViews (); + pPlotDoc->Activate(); +} + + void ProjectionFileView::OnConvertParallel (wxCommandEvent& event) { @@ -2657,7 +2771,7 @@ ProjectionFileView::doReconstructFBP (const Projections& rProj, bool bRebinToPar os << "Reconstruct " << rProj.getFilename() << ": xSize=" << m_iDefaultNX << ", ySize=" << m_iDefaultNY << ", Filter=" << optFilterName.c_str() << ", FilterParam=" << m_dDefaultFilterParam << ", FilterMethod=" << optFilterMethodName.c_str() << ", FilterGeneration=" << optFilterGenerationName.c_str() << ", Zeropad=" << m_iDefaultZeropad << ", Interpolation=" << optInterpName.c_str() << ", InterpolationParam=" << m_iDefaultInterpParam << ", Backprojection=" << optBackprojectName.c_str(); if (bRebinToParallel) os << "; Interpolate to Parallel"; - + Timer timerRecon; ImageFile* pImageFile = NULL; if (m_iDefaultTrace > Trace::TRACE_CONSOLE) { @@ -2805,8 +2919,9 @@ ProjectionFileView::CreateChildFrame(wxDocument *doc, wxView *view) GetDocumentManager()->FileHistoryUseMenu(m_pFileMenu); wxMenu *convert_menu = new wxMenu; + convert_menu->Append (PJMENU_CONVERT_RECTANGULAR, "&Rectangular Image"); convert_menu->Append (PJMENU_CONVERT_POLAR, "&Polar Image...\tCtrl-L"); - convert_menu->Append (PJMENU_CONVERT_FFT_POLAR, "&FFT->Polar Image...\tCtrl-M"); + convert_menu->Append (PJMENU_CONVERT_FFT_POLAR, "FF&T->Polar Image...\tCtrl-T"); convert_menu->AppendSeparator(); convert_menu->Append (PJMENU_CONVERT_PARALLEL, "&Interpolate to Parallel"); @@ -2814,8 +2929,9 @@ ProjectionFileView::CreateChildFrame(wxDocument *doc, wxView *view) filter_menu->Append (PJMENU_ARTIFACT_REDUCTION, "&Artifact Reduction"); wxMenu* analyze_menu = new wxMenu; - analyze_menu->Append (PJMENU_PLOT_TTHETA_SAMPLING, "&Plot T-Theta Sampling\tCtrl-T"); - + analyze_menu->Append (PJMENU_PLOT_HISTOGRAM, "&Plot Histogram"); + analyze_menu->Append (PJMENU_PLOT_TTHETA_SAMPLING, "Plot T-T&heta Sampling...\tCtrl-H"); + wxMenu *reconstruct_menu = new wxMenu; reconstruct_menu->Append (PJMENU_RECONSTRUCT_FBP, "&Filtered Backprojection...\tCtrl-R", "Reconstruct image using filtered backprojection"); reconstruct_menu->Append (PJMENU_RECONSTRUCT_FBP_REBIN, "Filtered &Backprojection (Rebin to Parallel)...\tCtrl-B", "Reconstruct image using filtered backprojection"); @@ -2842,12 +2958,12 @@ ProjectionFileView::CreateChildFrame(wxDocument *doc, wxView *view) wxAcceleratorEntry accelEntries[7]; accelEntries[0].Set (wxACCEL_CTRL, static_cast('L'), PJMENU_CONVERT_POLAR); - accelEntries[1].Set (wxACCEL_CTRL, static_cast('M'), PJMENU_CONVERT_FFT_POLAR); + accelEntries[1].Set (wxACCEL_CTRL, static_cast('T'), PJMENU_CONVERT_FFT_POLAR); accelEntries[2].Set (wxACCEL_CTRL, static_cast('R'), PJMENU_RECONSTRUCT_FBP); accelEntries[3].Set (wxACCEL_CTRL, static_cast('B'), PJMENU_RECONSTRUCT_FBP_REBIN); accelEntries[4].Set (wxACCEL_CTRL, static_cast('E'), PJMENU_RECONSTRUCT_FOURIER); accelEntries[5].Set (wxACCEL_CTRL, static_cast('I'), PJMENU_FILE_PROPERTIES); - accelEntries[6].Set (wxACCEL_CTRL, static_cast('T'), PJMENU_PLOT_TTHETA_SAMPLING); + accelEntries[6].Set (wxACCEL_CTRL, static_cast('H'), PJMENU_PLOT_TTHETA_SAMPLING); wxAcceleratorTable accelTable (7, accelEntries); subframe->SetAcceleratorTable (accelTable); diff --git a/src/views.h b/src/views.h index 69c2a14..3f902cf 100644 --- a/src/views.h +++ b/src/views.h @@ -9,7 +9,7 @@ ** This is part of the CTSim program ** Copyright (c) 1983-2001 Kevin Rosenberg ** -** $Id: views.h,v 1.51 2001/03/13 08:24:41 kevin Exp $ +** $Id: views.h,v 1.52 2001/03/18 18:08:26 kevin Exp $ ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License (version 2) as @@ -243,9 +243,11 @@ public: void OnReconstructFBP (wxCommandEvent& event); void OnReconstructFBPRebin (wxCommandEvent& event); void OnReconstructFourier (wxCommandEvent& event); + void OnConvertRectangular (wxCommandEvent& event); void OnConvertPolar (wxCommandEvent& event); void OnConvertFFTPolar (wxCommandEvent& event); void OnPlotTThetaSampling (wxCommandEvent& event); + void OnPlotHistogram (wxCommandEvent& event); void OnConvertParallel (wxCommandEvent& event); void OnArtifactReduction (wxCommandEvent& event); -- 2.34.1